Унаследованные приложения, как правило, имеют монолитную архитектуру и запускаются на одной или нескольких виртуальных машинах. Некоторые из таких систем легче поддаются модернизации, поскольку в мире контейнеров для них есть неплохие аналоги (EAP, Spring Boot и т.д.). Однако большие классические приложения на .Net, работающие под IIS на Windows-сервере, модернизировать гораздо сложнее, тем более за одну итерацию. Но благодаря OpenShift Virtualization такие ВМ-нагрузки можно импортировать в OpenShift как есть и затем контейнеризировать их поэтапно.
В OpenShift виртуальные машины являются привилегированными гражданами и имеют ровно те же возможности, что и pod’ы, включая обращение к другим объектам и предоставление доступа к себе через конечные точки служб (service endpoints). Поэтому выполнив первичный перенос ВМ-приложения в OpenShift, его затем можно поэтапно модернизировать, а заодно и расширять функционал.
Стратегия миграции Shift and Modernize
Чтобы показать, как это делается, мы для примера возьмем приложение .Net, работающее под IIS на виртуалке Windows Server, импортируем его в OpenShift Virtualization, а затем поэтапно контейнеризуем все его логические слои. Обратите внимание, что стратегия «shift and modernize» подходит и для других связок «операционная система–связующее ПО».

Этап 1. Импорт ВМ в OpenShift Virtualization
Импорт ВМ в OpenShift возможен несколькими способами. Для виртуальных машин VMware можно использовать встроенный инструментарий миграции, который позволяет подключиться к экземпляру VSphere и напрямую импортировать эти виртуалки в OpenShift Virtualization. Для других платформ виртуализации, включая Hyper-V и Xen, можно использовать virt-v2v, чтобы конвертировать образ ВМ в формат, поддерживаемый OpenShift Virtualization, а затем импортировать этот образ, используя методы описанные здесь (EN).
Виртуальная машина запускается с помощью Yaml-конфигурации, базовый вариант которой можно найти здесь. Этот Yaml-файл содержит определение ВМ, а также объектов service и route, плюс, некоторые функциональные конфигурации для более эффективной работы Windows-ВМ на платформе OpenShift Virtualization.
Нас интересует следующее секция:
features: acpi: {} apic: {} hyperv: reenlightenment: {} ipi: {} synic: {} synictimer: {} spinlocks: spinlocks: 8191 reset: {} relaxed: {} vpindex: {} runtime: {} tlbflush: {} frequencies: {} vapic: {} evmcs: {} # do not use evmcs if testing with nested virt
После применения этого yaml, мы получаем доступ к графическому интерфейсу Windows UI через VNC Console, которую нам дает OpenShift Virtualization.

Кроме того, теперь у нас есть доступ к приложению через exposed route.

Этап 2. Контейнеризация UI приложения
Не всегда можно мигрировать всё сразу, особенно в случае больших приложений. Например, этому могут препятствовать зависимости от библиотек, которые есть в классическом .NET, но отсутствуют в .Net Core. В этом случае код приложения придется доработать.
И поскольку это можно делать поэтапно, мы начнем с веб-интерфейса и после того, как переведем UI-слой на .Net Core, упакуем его в контейнерный образ для OpenShift. Этот новый UI-pod будет потреблять API, которые предоставляются «старым» сайтом, работающем под IIS на виртуальной машине.
Приведенный ниже Dockerfile компилирует ваш код .Net Core и создает runtime-образ. Это также можно сделать через сборки S2I.
# https://hub.docker.com/_/microsoft-dotnet-core FROM mcr.microsoft.com/dotnet/core/sdk:3.1 AS build WORKDIR /source # copy csproj and restore as distinct layers COPY ./source . RUN dotnet restore # copy and publish app and libraries COPY . . RUN dotnet publish -c release -o /app --no-restore # final stage/image FROM mcr.microsoft.com/dotnet/core/aspnet:3.1 WORKDIR /app COPY --from=build /app . ENTRYPOINT ["dotnet", "ClientListUI.dll"]
Для сборки и отправки этого образа в реестр можно использовать podman. Затем остается только развернуть этот отвечающий за веб-интерфейс pod вместе с виртуальной машиной приложения.
Этап 3. Контейнеризация API
Вслед за UI-слоем мигрируем на .Net Core слой API и развернем его в виде отдельного pod’а. После чего на исходной виртуалке остается только база данных SQL Server. Соответствующим образом поправим сервис ВМ в части описания доступа:
kind: Service apiVersion: v1 metadata: name: stage3-win2019-db-vm namespace: stage3 labels: kubevirt.io: virt-launcher kubevirt.io/domain: stage3-win2019-iis-vm spec: ports: - protocol: TCP port: 1433 targetPort: 1433 selector: kubevirt.io: virt-launcher kubevirt.io/domain: stage3-win2019-iis-vm
Итак, теперь UI-pod нашего приложения вызывает API через service URL нашего API-pod’а, а тот в свою очередь использует service URL нашей виртуальной машины, чтобы делать запросы к базе данных.

Этап 4. Контейнеризация БД
Да, вы правильно поняли: миграция экземпляра MS SQL в контейнер RHEL. Лет пять назад такое и в голову бы никому не пришло, но сегодня это вполне рабочий вариант и подробнее узнать о том, как настроить и запустить MS SQL в контейнере, можно узнать здесь (EN).
После того, SQL-pod будет готов, остается настроить port forwarding, чтобы можно было работать с БД через MS SQL Management Studio или Azure Data Studio:
└──╼ oc get pod | grep sql | grep Running sql2019-1-1-gp29h 1/1 Running 0 39h └──╼ oc port-forward sql2019-1-1-gp29h 1433:1433 Forwarding from 127.0.0.1:1433 -> 1433 Forwarding from [::1]:1433 -> 1433 Handling connection for 1433
Теперь можно переносить БД из экземпляра MS SQL на Windows-виртуалке в контейнер SQL Server. После чего, необходимо изменить строку подключения для API-pod’а приложения и указать в ней pod, где теперь живет ваш MS SQL.
Всё, приложение полностью, на 100% контейнеризовано, и виртуальную машину которую, мы перенесли на первом этапе, можно навсегда отключить.

Заключение
Поэтапная миграция ВМ-нагрузки по методу «shift and modernize» позволяет снизить технический долг, минимизировать любого рода регрессии, которые могут возникнуть входе этого процесса, ну и попутно расширить функционал приложения в рамках проводимой при такой миграции доработки кода.
