Миграция приложения из OpenShift в «ванильный» Kubernetes

от автора

OpenShift, Rancher и другие зарубежные Kubernetes-платформы официально больше не поддерживаются в России. Многим компаниям приходится искать альтернативные решения для управления контейнеризированными приложениями — например, «ванильный» Kubernetes или российские платформы.

Хотя у Kubernetes-платформ одинаковая технологическая база, миграция сервисов с одной на другую неизбежно сопряжена с различными трудностями, связанными с особенностями реализации компонентов. В этой статье рассмотрен пример переезда приложения из OpenShift в «ванильный» кластер Kubernetes. В конце статьи приведена таблица соответствия примитивов OpenShift и Kubernetes — с информацией о том, какие из этих примитивов требуют замены, а какие нет.

Есть инструменты, которые автоматизируют процесс миграции — например, move2kube. Однако они требуют отдельного рассмотрения и, соответственно, отдельной статьи. Здесь же мы сосредоточимся именно на «ручном» переносе приложения.

Исходные данные

Рассмотрим template OpenShift с простым веб-сервисом:

apiVersion: template.openshift.io/v1 kind: Template labels:   nginx: master metadata:   annotations:     description: example-template     iconClass: icon-nginx     tags: web,example   name: web-app-example objects: - apiVersion: apps/v1   kind: Deployment   metadata:     name: ${NAME}   spec:     replicas: ${{REPLICAS}}     revisionHistoryLimit: 3     selector:       matchLabels:         app: ${NAME}     template:       metadata:         labels:           app: ${NAME}       spec:         containers:         - image: camunda/camunda-bpm-platform:run-7.15.0           imagePullPolicy: Always           name: camunda           ports:           - containerPort: 8080             name: http             protocol: TCP           resources:             limits:               memory: ${BACK_MEMORY}             requests:               cpu: ${BACK_CPU}               memory: ${BACK_MEMORY}         - command:           - /usr/sbin/nginx           - -g           - daemon off;           image: nginx:stable-alpine           imagePullPolicy: Always           lifecycle:             preStop:               exec:                 command:                 - /bin/bash                 - -c                 - sleep 5; kill -QUIT 1           name: nginx           ports:           - containerPort: 9000             name: http             protocol: TCP           resources:             limits:               memory: ${FRONT_MEMORY}             requests:               cpu: ${FRONT_CPU}               memory: ${FRONT_MEMORY}           volumeMounts:           - mountPath: /etc/nginx/nginx.conf             name: configs             subPath: nginx.conf         volumes:         - configMap:             name: ${NAME}-config           name: configs - apiVersion: v1   kind: Service   metadata:     annotations:       description: Exposes and load balances the application pods     name: ${NAME}-service   spec:     ports:     - name: http       port: 9000       targetPort: 9000     selector:       app: ${NAME} - apiVersion: v1   kind: ConfigMap   metadata:     name: ${NAME}-config   data:     nginx.conf: |       user nginx;       worker_processes 1;       pid /run/nginx.pid;        events {         worker_connections 1024;       }        http {         include /etc/nginx/mime.types;         default_type application/octet-stream;          upstream backend {           server 127.0.0.1:8080 fail_timeout=0;         }          server {           listen 9000;           server_name _;            root /www;            client_max_body_size 100M;           keepalive_timeout 10s;            location / {             proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;             proxy_set_header X-Real-IP $remote_addr;             proxy_set_header X-Forwarded-Proto $scheme;             proxy_set_header Host $http_host;             proxy_redirect off;              proxy_pass http://backend;           }         }       }  - apiVersion: route.openshift.io/v1   kind: Route   metadata:     name: ${NAME}-route   spec:     host: ${DOMAIN}.apps-crc.testing     port:       targetPort: http     to:       kind: Service       name: ${NAME}-service       weight: 100     wildcardPolicy: None parameters: - description: Name for application   from: '[A-Z]{8}'   generate: expression   name: NAME - description: Domain for application   from: '[A-Z]{8}'   generate: expression   name: DOMAIN - description: Number of replicas   from: '[0-9]{1}'   generate: expression   name: REPLICAS - description: Memory request and limit for frontend container   from: '[A-Z0-9]{4}'   generate: expression   name: FRONT_MEMORY - description: CPU request for frontend container   from: '[A-Z0-9]{3}'   generate: expression   name: FRONT_CPU - description: Memory request and limit for backend container   from: '[A-Z0-9]{4}'   generate: expression   name: BACK_MEMORY - description: CPU request for backend container   from: '[A-Z0-9]{3}'   generate: expression   name: BACK_CPU

Что внутри этого template:

  • Deployment приложения. В качестве примера использованы nginx, который выступает в роли фронтенда, и демонстрационная stateless-версия camunda в качестве бэкенда.

  • ConfigMap с конфигурацией для nginx, подключаемый в контейнер.

  • Route — принимает трафик на целевой домен снаружи кластера.

  • Service — направляет трафик непосредственно к Pod’ам с приложением.

Также есть возможность параметризации ряда настроек.

Значения параметров, используемых в template, находятся в файле values.env:

NAME=example-application DOMAIN=example REPLICAS=1 FRONT_MEMORY=128Mi FRONT_CPU=50m BACK_MEMORY=512Mi BACK_CPU=50m

Переменные подставляются в раздел parameters.

Миграция кластера

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

Для переезда приложения из OpenShift в Kubernetes-кластер необходимо:

  1. Вынести описания всех сущностей из template в отдельные YAML-файлы, так как template — это специфичный для OpenShift объект.

  2. Изменить параметризацию c помощью файла values.yaml вместо values.env.

  3. Заменить Route на Ingress-NGINX Controller.

Deployment, Service и ConfigMap требуют меньше всего изменений. Для каждого из них нужно создать свой файл с описанием. Начнем с приложения в файле app.yaml:

apiVersion: apps/v1 kind: Deployment metadata:   name: {{ .Chart.Name }} spec:   replicas: {{ .Values.app.replicas }}   revisionHistoryLimit: 3   selector:     matchLabels:       app: {{ .Chart.Name }}   template:     metadata:       labels:         app: {{ .Chart.Name }}     spec:       containers:       - image: camunda/camunda-bpm-platform:run-7.15.0         imagePullPolicy: Always         name: camunda         ports:         - containerPort: 8080           name: http           protocol: TCP         resources:           limits:             memory: {{ .Values.app.backend.memory }}           requests:             cpu: {{ .Values.app.backend.cpu }}             memory: {{ .Values.app.backend.memory }}       - command:         - /usr/sbin/nginx         - -g         - daemon off;         image: nginx:stable-alpine         imagePullPolicy: Always         lifecycle:           preStop:             exec:               command:               - /bin/bash               - -c               - sleep 5; kill -QUIT 1         name: nginx         ports:         - containerPort: 9000           name: http           protocol: TCP         resources:           limits:             memory: {{ .Values.app.frontend.memory }}           requests:             cpu: {{ .Values.app.frontend.cpu }}             memory: {{ .Values.app.frontend.memory }}         volumeMounts:         - mountPath: /etc/nginx/nginx.conf           name: configs           subPath: nginx.conf       volumes:       - configMap:           name: {{ .Chart.Name }}-config         name: configs

Service разместим в файле service.yaml:

apiVersion: v1 kind: Service metadata:   annotations:     description: Exposes and load balances the application pods   name: {{ .Chart.Name }} spec:   ports:   - name: http     port: 9000     targetPort: 9000   selector:     app: {{ .Chart.Name }}

ConfigMap — в файле configmap.yaml:

apiVersion: v1 kind: ConfigMap metadata:   name: {{ .Chart.Name }}-config data:   nginx.conf: |     user nginx;     worker_processes 1;     pid /run/nginx.pid;      events {       worker_connections 1024;     }      http {       include /etc/nginx/mime.types;       default_type application/octet-stream;        upstream backend {         server 127.0.0.1:8080 fail_timeout=0;       }        server {         listen 9000;         server_name _;          root /www;          client_max_body_size 100M;         keepalive_timeout 10s;          location / {           proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;           proxy_set_header X-Real-IP $remote_addr;           proxy_set_header X-Forwarded-Proto $scheme;           proxy_set_header Host $http_host;           proxy_redirect off;            proxy_pass http://backend;         }       }     }

Теперь заменим values.env на values.yaml:

app:   replicas: 1   host: example.kubernetes.testing   backend:     memory: 512Mi     cpu: 50m   frontend:     memory: 128Mi     cpu: 50m

Route — тоже объект OpenShift. Заменим его на привычный Ingress (файл ingress.yaml):

apiVersion: networking.k8s.io/v1 kind: Ingress metadata:   annotations:     kubernetes.io/ingress.class: nginx   name: {{ .Chart.Name }} spec:   rules:   - host: {{ .Values.app.host }}     http:       paths:       - path: /         pathType: Prefix         backend:           service:             name: {{ .Chart.Name }}             port:               number: 9000

На этом можно считать подготовку оконченной. 

Созданный Helm-чарт готов к деплою в «ванильный» Kubernetes. 

В нашем примере используется GitLab CI и werf, однако это не обязательное условие. Чарт совместим с любыми CI/CD-системами.

Запустим деплой командой werf converge:

Проверим, что все ресурсы действительно появились в кластере:

И, наконец, убедимся, что приложение работает корректно и доступно для пользователей:

Немного о DeploymentConfig

В OpenShift часто используется ресурс DeploymentConfig, который также стоит упомянуть. Это версия Deployment, «расширенная» за счет ресурсов ImageStream и BuildConfig: они предназначены для сборки образов и деплоя приложения в кластер. 

Прямая замена DeploymentConfig на аналогичный ресурс в «ванильном» Kubernetes невозможна. Поэтому для сохранения функциональности, которую предоставляет связка DeploymentConfig + ImageStream + BuildConfig, потребуются дополнительные инструменты.

Чтобы перенести DeploymentConfig в Kubernetes, можно заменить его на Deployment, а неподдерживаемые функции реализовать сторонними инструментами — например, CI-системой, инструментом для сборки образов, а также внешним registry для их хранения.

 selector:       name: ...

Ниже — примерный список действий для превращения DeploymentConfig в Deployment.

  1. apiVersion: apps.openshift.io/v1 заменить на apiVersion: apps/v1.

  2. kind: DeploymentConfig заменить на kind: Deployment.

  3. spec.selectors заменить с selector: name: ... на selector: matchLabels: name: ...

  4. Убедиться, что секция spec.template.spec.containers.image описана для каждого контейнера.

  5. Удалить секции spec.triggers, spec.strategy и spec.test.

Обратите внимание, что эта инструкция не универсальна. Каждый конкретный случай стоит рассмотреть отдельно. Рекомендуем ознакомиться с официальной документацией по DeploymentConfig.

Подытожим

Миграция приложения из OpenShift в «ванильный» Kubernetes требует декомпозиции templates на отдельные YAML-ресурсы, а также замены ряда специфичных для OpenShift объектов на сущности K8s.

Ниже — краткая таблица соответствия ресурсов OpenShift и K8s, которая поможет при миграции:

OpenShift

Kubernetes

Template

Отказываемся в пользу Helm chart

DeploymentConfig

Меняем на Deployment (не забывая об особенностях, связанных с ImageStream и BuildConfig)

Route

Меняем на Ingress

Deployment/Statefulset/Daemonset

Не требуют изменений (только замена параметризации)

Service/ConfigMap и т. д.

Не требуют изменений (только замена параметризации)

Рассмотренный в статье пример переезда с OpenShift в кластер под управлением Deckhouse актуален в том числе и для бесплатной версии платформы (community edition). Мы уже не раз переносили рабочие нагрузки наших клиентов с OpenShift, Rancher и других зарубежных решений, накопили лучшие практики и готовы помочь с миграцией на Deckhouse.

P.S.

Читайте также в нашем блоге:


ссылка на оригинал статьи https://habr.com/ru/company/flant/blog/715908/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *