Найдена формула безболезненного перехода на .Net Core

    На все про все достаточно 50 чашек кофе.


    Помимо обозначенного выше эмпирического правила мы публикуем краткую заметку о моментах, на которые нужно обратить пристальное внимание, чтобы на бою и в процессах ничего не сломалось. Заметку составили по горячим следам релиза мобильного сервиса, совсем мигрировавшего на .Net Сore (начало было положено тут). Нам удалось выполнить эту операцию незаметно для заказчика, почти не останавливая основной процесс разработки.


    Ниже будет готовый план действий, будет очень емкий тест-лист, будет вот эта картинка для настроения:



    Итак, по шагам:


    1. Запланировать длинный спринт с большими фичами и/или регрессом


    При фундаментальных переписываниях кода сервису понадобится время как можно дольше настояться, чтобы на тестовом окружении успеть исправить все огрехи.


    2. В аккурат к спринту переписать код на .Net Core


    Почему важно не начинать это дело раньше времени? Потому что придется тянуть две ветки кода с новым и старым .net, ведь в любой момент может прилететь птица срочность либо нужно будет провести демо новых фич, и тогда понадобится внести изменения в старую стабильную ветку. Чтобы испытывать минимальное беспокойство по этому поводу, лучше момент переходного состояния укоротить.


    Кстати, при работе с кодом мы быстро пришли к тому, что удобнее держать локально две копии репозитория. Так проще и удобнее, чем переключать две массивные ветки.


    • По возможности в используемых сервисах переписать WCF интерфейсы на webapi

    Реализация .Net Core WCF-клиента все еще далека от идеала. Несмотря на то, что старые болячки в каком-то смысле пофикшены, в новых версиях все равно приходится использовать workaround (1, 2).


    Для истории: на .Net Core 2.0 стабильной рабочей версией WCF является 4.4.2 из myget репозитория. У нее, например, нет проблем с досрочным таймаутом

    В момент начала миграции мы использовали версию .Net Core 2.0. Тем временем Microsoft релизнула .Net Core 2.1. Кому интересно полюбоваться успехами ребят из Редмонда в оптимизации платформы, просим почитать, какого прогресса добился поисковик Bing при апгрейде на новую версию (спойлер: латенси упало на 34%!)


    Мы тоже обновились до .Net Core 2.1 и WCF 4.5.3. И не забыли в Dockerfile указать свежий базовый образ microsoft/dotnet:2.1-aspnetcore-runtime. Каково было удивление, когда вместо 1.4Гб увидели размер образа в 0,5Гб (речь про Windows-образ, если вдруг).


    3. Задеплоить на тест и демо


    У нас в распоряжении два окружения. Демо мы оставили со старой версией как эталон. На тестовое окружение задеплоили новый сервис — обкатать на разработчиках и тестировщиках.


    Возникла некоторая путаница в связи с тем, что обычно разработчики работают с тестом, а тестировщики преимущественно с демо. В случае, если требовалось освежить старый сервис, получалась ситуация ровно обратная нормальной. Поэтому пригодились обсуждение и шпаргалка, где и что искать.


    • Настроить IIS

    Для запуска .Net Core сервиса в IIS необходимо установить модуль, который идет в комплекте с рантаймом.


    AppPool переключаем на CLR Runtime = No Managed Code.


    В солюшене в стандартном web.config важно не забыть выставить нужный requestTimeout и отключить WebDAV модуль, если есть DELETE-методы.


    Далее, для публикации сервиса в IIS есть два варианта:


    • вы делаете MSDeploy sync — значит дополнительно нужен ключ -enableRule:AppOffline
    • вы делает file publish — значит ровно перед публикацией нужно подложить файлик app_offline.htm в директорию сервиса, а после публикации удалить его

    И то, и другое позволяет остановить рабочий процесс и разлочить исполняемые файлы. В противном случае вылетет ошибка, что файлы не доступны для перезаписи.


    Мы отказались от логирования через Nlog в пользу Serilog, и потеряли автоматическое сжатие логов — в Serilog такой фичи просто нет. В этом случае можно спастись штатными средствами Windows и в свойствах директории установить NTFS compression.

    4. Протестировать


    Вот максимально ужатый чеклист по самым хрупким местам:


    • проверить возврат статус-кодов Bad request, Unauthorized, Not modified, Not found — все, что API может отдать
    • проверить логирование запросов для всех статус-кодов
    • составить схему внешних зависимостей; как правило вся необходимая информация есть в appsettings
      • прогнать методы, которые затрагивают их работу
      • проверить логирование внешних запросов
    • проверить функционирование настроек для параметров appsettings; попробовать поменять их на горячую
    • проверить http-кэширование для положительных и отрицательных статус-кодов
      • хедер ETag
      • хедер Сache-Сontrol
    • проверить длительные запросы и таймауты
    • проверить запросы с пустым ответом
    • проверить DELETE-методы (WebDAV отключен или нет)
    • проверить работу с raw content
      • заливка и выгрузка одного/нескольких файлов
      • заливка файлов с размером выше лимита
      • простановка хедера Content-Disposition
    • проверить все остальные хедеры; собрать их все в кучу по коду довольно легко
    • проверить условное выполнение кода при переключении окружений if (env.IsDevelopment())
    • проверить разрыв соединения с клиента и на сервере
    • сравнить с эталоном swagger.json — поможет обнаружить разницу в передаваемых полях
      У нас в мобильном приложении используется кодогенератор для работы с API на базе описания swagger.json, поэтому было важно, чтобы отличие от исходного описания было минимальным. В последней версии Swashbuckle.AspNetCore сильно поменялся интерфейс и генерируемый swagger.json. Пришлось откатиться на ветхую версию Swashbuckle.AspNetCore 1.2.0 и дописать пару фильтров.

    5. Задеплоить на бой, попивая кофеек


    В нашем случае боевое окружение состоит из двух нод: активной и пассивной.
    Для того, чтобы переключение на новый сервис произошло незаметно, мы продублировали пул и сайт на каждой ноде, и написали скрипт для переключения биндинга между старым и новым сайтом.


    Таким образом мы получили возможность в случае ЧП быстро переключиться на старую версию.


    Далее после деплоя на бой мы в течение недели убедились в жизнеспособности сервиса и зажгли зеленый свет для релиза мобильного приложения. Жизнь на проекте благополучно вернулась в прежнее русло.


    Промежуточный итог


    Теперь наш сервис полностью готов обрасти docker-контейнером для поставки в кластер. Мы готовы к деплою и в Kubernetes, и в Service Fabric.


    Сейчас полным ходом идет подготовка к презентации новой инфраструктуры заказчику. О своих достижениях расскажем в следующей серии, держите руку на пульсе ;)

    EastBanc Technologies

    101,72

    Специалисты по цифровой трансформации бизнеса

    Поделиться публикацией
    Комментарии 6
      +3
      А как быть с 100500 пакетами, которые не торопятся переходить на .NET Core?
      Как быть с песочницей через AppDomain? как быть с .NET Remoting (есть некрофилы)? Вопросы риторические.

      Переход на .NET Core это дань богу боли, нельзя сделать переход и не отдать дань.
        +2
        Опередили, как раз хотел написать:
        0. Просмотреть все зависимости проекта и найти их версии для Core.
          0

          Именно про само переписывание кода писали в предыдущей статье. Ваш комментарий — хорошее дополнение к тем пунктам.

          +3
          Настроил автоплатеж для бога боли
          +1

          «Кстати, при работе с кодом мы быстро пришли к тому, что удобнее держать локально две копии репозитория.»


          Посмотрите пожалуйста в сторону функциональности git-worktree, по факту получаете один репозиторий, и каждая ветка в своей папке. GitExtensions поддерживает работу с worktree, другие gui — не знаю.

            0

            О, спасибо за рекомендацию!

          Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

          Самое читаемое