Миграция приложений в Kubernetes: практическое руководство по переносу из Docker Compose | AdminWiki
Timeweb Cloud — сервера, Kubernetes, S3, Terraform. Лучшие цены IaaS.
Попробовать

Миграция приложений в Kubernetes: практическое руководство по переносу из Docker Compose

10 мая 2026 12 мин. чтения
Содержание статьи

Почему Kubernetes вместо Docker Compose? Анализ целесообразности миграции

Переход с Docker Compose на Kubernetes для production-среды решает фундаментальные проблемы управления жизненным циклом приложений. Docker Compose работает как инструмент оркестрации на уровне одной ноды, а Kubernetes создан для распределенных кластеров. Основное различие лежит в парадигме управления: Compose использует императивный подход, где вы явно указываете команды для запуска, а Kubernetes оперирует декларативным состоянием. Вы описываете желаемое состояние системы в YAML-манифестах, а контроллеры кластера непрерывно приводят реальное состояние к этому описанию.

Миграция становится необходимостью при росте нагрузки, требованиях к высокой доступности, работе в multi-host окружениях или необходимости внедрения продвинутых стратегий развертывания, таких как canary-релизы.

Ограничения Docker Compose в production-средах

Docker Compose не имеет встроенного оркестратора для развертывания на нескольких физических хостах или виртуальных машинах. Все сервисы запускаются на одной ноде, что создает единую точку отказа. Механизмы самовосстановления ограничены примитивными политиками перезапуска, которые не учитывают состояние здоровья приложения.

Масштабирование выполняется вручную через команду docker-compose up --scale и не является автоматическим. Сетевые возможности Compose обеспечивают базовую изоляцию, но не предлагают аналогов Kubernetes Network Policies для тонкого контроля трафика между сервисами. Управление конфиденциальными данными, такими как пароли и токены, через переменные окружения или файлы небезопасно при работе на нескольких нодах.

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

Что Kubernetes дает для масштабирования и отказоустойчивости

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

Liveness и Readiness Probes обеспечивают self-healing. Liveness Probe определяет, жив ли контейнер, и перезапускает его при сбое. Readiness Probe проверяет, готов ли контейнер принимать трафик, что исключает отправку запросов на нерабочие экземпляры.

Horizontal Pod Autoscaler автоматически увеличивает или уменьшает количество реплик на основе метрик потребления CPU, памяти или пользовательских метрик из Prometheus. Встроенный Service Discovery и балансировка нагрузки работают через kube-proxy и объекты Service. ClusterIP предоставляет внутренний стабильный IP-адрес для доступа к группе подов, NodePort открывает порт на каждой ноде кластера, а LoadBalancer интегрируется с облачными провайдерами для создания внешних балансировщиков.

Ingress-контроллеры, такие как Nginx или Traefik, управляют маршрутизацией HTTP/HTTPS трафика на основе домена и пути, заменяя традиционные reverse proxy.

Подготовка к миграции: анализ приложения и планирование

Системный подход к миграции начинается с детального аудита текущей конфигурации. Не пытайтесь перенести все приложение сразу. Разбейте процесс на этапы, начиная с наименее критичных stateless-компонентов. Это снижает риски и позволяет команде набраться опыта.

Первый шаг - полный анализ файла docker-compose.yml и всех его зависимостей: файлов окружения, volume, пользовательских сетей.

Аудит docker-compose.yml: выявление stateless и stateful компонентов

Классификация компонентов определяет выбор ресурсов Kubernetes. Stateless-сервисы (веб-серверы, API, фоновые workers) не хранят уникальных данных внутри пода и являются кандидатами на использование Deployment. Stateful-сервисы (PostgreSQL, Redis, Kafka) хранят данные или требуют стабильных сетевых идентификаторов и используют StatefulSet.

Проанализируйте секцию volumes: в Compose. Локальные маппинги (./data:/var/lib/postgresql/data) указывают на необходимость PersistentVolume в Kubernetes. Зависимости, описанные в depends_on, в Kubernetes заменяются комбинацией initContainers и правильно настроенных readinessProbe, чтобы сервис БД был готов до запуска приложения.

Планирование архитектуры в Kubernetes: namespaces, сеть, ресурсы

Используйте namespaces для логического разделения окружений. Например, создайте prod, staging, monitoring. Это упрощает управление правами доступа и ресурсами.

Спроектируйте сетевую модель. Внутренняя коммуникация между микросервисами должна идти через Service типа ClusterIP. Внешний доступ к API или веб-интерфейсу организуется через Ingress. Прямой доступ по специфическим протоколам может потребовать Service типа NodePort или LoadBalancer.

Заранее рассчитайте requests и limits для CPU и памяти в манифестах. Requests - гарантированные ресурсы, limits - жесткий лимит. Неправильная настройка ведет к eviction подов при нехватке памяти на ноде. Для хранения данных выберите подходящий storage class: local для тестов, NFS для shared-доступа или cloud-provider volumes (например, AWS EBS, GCE PD).

Выбор стратегии миграции: Big Bang (полный перенос за один раз) или поэтапный (сервис за сервисом). Для большинства проектов рекомендуем поэтапную миграцию, которая позволяет тестировать каждый компонент отдельно. Во время перехода можно временно использовать bridge-сети для связи между контейнерами в Compose и подами в Kubernetes.

Ключевые изменения: от Docker Compose к Kubernetes-манифестам

Трансляция конструкций Docker Compose в объекты Kubernetes - это ядро технической работы. Рассмотрим прямое сопоставление основных элементов.

Сервис в Compose разделяется на два объекта в Kubernetes: Deployment (или StatefulSet) для управления жизненным циклом подов и Service для обеспечения сетевого доступа к ним. Переменные окружения переезжают в ConfigMap и Secret. Тома данных становятся PersistentVolumeClaim. Проброс портов реализуется через Service и containerPort.

Трансляция сервисов: Deployment, Service и Pod

Deployment - основной объект для управления stateless-приложениями. Его манифест содержит метаданные (имя, namespace), spec с количеством реплик, селектором для выбора подов и шаблоном пода.

Внутри шаблона пода определяется один или несколько контейнеров с указанием образа, портов, переменных окружения и точек монтирования томов. Service типа ClusterIP создает стабильную внутреннюю DNS-запись вида <service-name>.<namespace>.svc.cluster.local и балансирует трафик между всеми подами, соответствующими селектору.

# Пример docker-compose.yml для nginx
services:
  web:
    image: nginx:alpine
    ports:
      - "8080:80"
    environment:
      NGINX_HOST: example.com

# Эквивалент в Kubernetes: deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-deployment
spec:
  replicas: 2
  selector:
    matchLabels:
      app: web
  template:
    metadata:
      labels:
        app: web
    spec:
      containers:
      - name: nginx
        image: nginx:alpine
        ports:
        - containerPort: 80
        env:
        - name: NGINX_HOST
          value: "example.com"

# service.yaml
apiVersion: v1
kind: Service
metadata:
  name: web-service
spec:
  selector:
    app: web
  ports:
    - protocol: TCP
      port: 80
      targetPort: 80
  type: ClusterIP

Управление конфигурацией: ConfigMaps и Secrets вместо environment и env_file

ConfigMap хранит конфигурационные данные в виде пар ключ-значение или целых файлов. Создать его можно из файла: kubectl create configmap app-config --from-file=config.properties, или из литералов: kubectl create configmap app-config --from-literal=log_level=INFO.

ConfigMap подключается к поду как переменные окружения через spec.containers.env.valueFrom.configMapKeyRef или как volume, что позволяет монтировать конфигурационные файлы. Для хранения чувствительных данных используйте Secret. Данные в Secret хранятся в base64-кодировке. Создайте его из файла: kubectl create secret generic db-password --from-literal=password=MyP@ssw0rd.

Никогда не храните Secret в Git-репозитории. Используйте специализированные системы, такие как HashiCorp Vault, или механизмы secret management от облачных провайдеров (AWS Secrets Manager, GCP Secret Manager), интегрируя их с Kubernetes через CSI драйверы.

Сетевой доступ: Services (ClusterIP, NodePort, LoadBalancer) и Ingress

Service в Kubernetes - это абстракция над группой подов. ClusterIP предоставляет внутренний IP-адрес, доступный только внутри кластера. NodePort открывает статический порт на IP-адресе каждой ноды кластера, перенаправляя трафик на сервис. LoadBalancer создает внешний балансировщик нагрузки у облачного провайдера.

Ingress не является типом Service. Это отдельный объект API, который определяет правила маршрутизации HTTP/HTTPS трафика. Для его работы в кластере должен быть развернут Ingress-контроллер, например, ingress-nginx.

# Пример Ingress для маршрутизации по доменам
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: example-ingress
spec:
  rules:
  - host: app.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: web-service
            port:
              number: 80
  - host: api.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: api-service
            port:
              number: 8080

Эта конфигурация направляет трафик с app.example.com на web-service, а с api.example.com - на api-service.

Сложные случаи: миграция stateful-приложений (БД, кеши)

Перенос stateful-приложений - самый ответственный этап миграции. Основная задача - обеспечить сохранность данных и бесперебойную работу службы. Для stateful-нагрузок в Kubernetes используется StatefulSet, а не Deployment.

Перед началом миграции создайте полную резервную копию данных. Для PostgreSQL используйте pg_dumpall, для Redis - SAVE или BGSAVE, для файловых данных - rsync или инструменты бэкапа соответствующего storage-провайдера.

Использование StatefulSet для баз данных

StatefulSet гарантирует предсказуемые и устойчивые идентификаторы для подов. Поды получают имена по шаблону <statefulset-name>-<ordinal>, например, postgres-statefulset-0, postgres-statefulset-1. Эти имена сохраняются при пересоздании пода.

Каждому поду в StatefulSet соответствует свой PersistentVolumeClaim, созданный на основе volumeClaimTemplates. Это обеспечивает уникальное постоянное хранилище для каждой реплики. Для внутреннего discovery реплик БД используется Headless Service (с полем clusterIP: None). Он создает DNS-записи для каждого пода, позволяя репликам находить друг друга по стабильным доменным именам: postgres-statefulset-0.postgres-headless.default.svc.cluster.local.

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: postgres-statefulset
spec:
  serviceName: "postgres-headless"
  replicas: 1
  selector:
    matchLabels:
      app: postgres
  template:
    metadata:
      labels:
        app: postgres
    spec:
      containers:
      - name: postgres
        image: postgres:15
        env:
        - name: POSTGRES_PASSWORD
          valueFrom:
            secretKeyRef:
              name: postgres-secret
              key: password
        volumeMounts:
        - name: postgres-data
          mountPath: /var/lib/postgresql/data
  volumeClaimTemplates:
  - metadata:
      name: postgres-data
    spec:
      accessModes: [ "ReadWriteOnce" ]
      storageClassName: "standard"
      resources:
        requests:
          storage: 10Gi

Перенос данных и обеспечение persistence

Существует два основных сценария переноса данных. Первый: остановить контейнер БД в Docker Compose, создать дамп данных, развернуть StatefulSet в Kubernetes и импортировать дамп в новую БД. Этот способ подходит для сред, где допустим краткий простой.

Второй сценарий предполагает использование общего хранилища, доступного с любой ноды кластера, например, NFS или cloud storage (AWS EFS, GCP Filestore). В этом случае PersistentVolume в Kubernetes монтирует ту же самую файловую систему, что использовалась Docker volume. Данные остаются на месте, и миграция сводится к переключению endpoint'а приложения с контейнера Compose на под Kubernetes. Проверьте права доступа (fsGroup, runAsUser) в SecurityContext пода, чтобы контейнер мог писать в смонтированный том.

После миграции обязательно протестируйте отказоустойчивость: удалите один из подов StatefulSet командой kubectl delete pod и убедитесь, что контроллер пересоздает его с теми же сетевым идентификатором и томом данных.

Упаковка и управление: Helm-чарты vs Kustomize

После создания набора raw YAML-манифестов встает вопрос управления ими для разных окружений. Два основных инструмента - Helm и Kustomize - предлагают разные парадигмы.

Helm - это пакетный менеджер для Kubernetes, использующий шаблонизацию на основе Go templates. Он подходит для сложных приложений с множеством опциональных компонентов и является стандартом для публикации чартов в репозиториях. Kustomize - это инструмент декларативного наложения патчей, встроенный в kubectl через флаг -k. Он работает по принципу наследования базовой конфигурации и применяет к ней специфичные для окружения изменения.

Выбор зависит от задачи. Для внутренних проектов с необходимостью быстрой настройки под dev/staging/prod часто удобнее Kustomize. Для публикации приложения или управления сложными зависимостями (как в случае с миграцией с использованием Velero) может подойти Helm.

Создание базового Helm-чарта для мигрированного приложения

Инициализируйте структуру чарта командой helm create myapp. Внутри каталога myapp/templates/ разместите ваши YAML-манифесты, преобразовав статические значения в переменные шаблонов Helm вида {{ .Values.image.tag }}.

Параметры, которые могут меняться (версия образа, количество реплик, ресурсы), выносятся в файл values.yaml. Для установки чарта в кластер используйте команду helm install myapp ./myapp, а для обновления - helm upgrade myapp ./myapp.

# Пример фрагмента шаблона deployment.yaml в Helm
apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ .Chart.Name }}
spec:
  replicas: {{ .Values.replicaCount }}
  template:
    spec:
      containers:
      - name: {{ .Chart.Name }}
        image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
        resources:
          requests:
            memory: {{ .Values.resources.requests.memory }}
            cpu: {{ .Values.resources.requests.cpu }}

Настройка Kustomize для разных окружений (dev, prod)

Структура проекта Kustomize обычно выглядит так:

base/
  kustomization.yaml  # Общие ресурсы (deployment.yaml, service.yaml)
  deployment.yaml
  service.yaml
overlays/
  dev/
    kustomization.yaml # Наследует base, добавляет патчи для dev
    patch_replicas.yaml
  prod/
    kustomization.yaml # Наследует base, добавляет патчи для prod
    patch_resources.yaml
    ingress.yaml

В overlays/dev/kustomization.yaml вы можете уменьшить количество реплик, использовать образ с тегом latest для разработки и задать низкие requests/limits. В overlays/prod/ увеличиваете реплики, указываете стабильный тег образа, настраиваете лимиты ресурсов и добавляете манифест Ingress с TLS. Применить конфигурацию для prod можно командой kubectl apply -k overlays/prod.

Интеграция в CI/CD: адаптация пайплайнов под Kubernetes

После миграции приложения необходимо обновить пайплайны непрерывной интеграции и доставки. Основные этапы CI/CD остаются прежними: сборка образа, тестирование, push в registry. Изменяется этап деплоя.

Вместо команды docker-compose up -d пайплайн должен обновить манифесты в Git (например, подставить новый тег образа в kustomization.yaml или values.yaml Helm) и применить их к кластеру с помощью kubectl apply, helm upgrade или kubectl apply -k.

Безопасная настройка доступа CI-раннера к кластеру

Никогда не используйте админский kubeconfig файл в CI/CD. Создайте отдельный ServiceAccount в namespace, куда происходит деплой.

apiVersion: v1
kind: ServiceAccount
metadata:
  name: ci-cd-sa
  namespace: prod
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: prod
  name: deploy-role
rules:
- apiGroups: ["apps"]
  resources: ["deployments", "statefulsets"]
  verbs: ["get", "list", "watch", "create", "update", "patch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: ci-cd-rolebinding
  namespace: prod
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: deploy-role
subjects:
- kind: ServiceAccount
  name: ci-cd-sa
  namespace: prod

Получите токен ServiceAccount и сформируйте kubeconfig, который будет храниться в секретных переменных CI-системы (GitLab CI/CD Variables, GitHub Actions Secrets).

Реализация стратегий развертывания: blue-green и canary в CI/CD

Blue-green деплой в Kubernetes реализуется с помощью двух Deployment'ов (blue и green) и одного Service. Service selector указывает на labels текущей активной версии (например, version: blue). Пайплайн разворачивает новую версию в green Deployment, выполняет smoke-тесты, а затем обновляет selector Service на version: green. Это мгновенно переключает весь трафик.

Canary-деплой предполагает постепенное наращивание трафика на новую версию. Простой способ - использовать аннотации Ingress-контроллера nginx. В манифесте Ingress для нового canary Deployment можно указать аннотации nginx.ingress.kubernetes.io/canary: "true" и nginx.ingress.kubernetes.io/canary-weight: "10", чтобы направить 10% трафика. После проверки метрик пайплайн увеличивает вес до 100% и удаляет старый Deployment.

Для более сложных сценариев, включая маршрутизацию на основе заголовков, используйте Service Mesh, например, Istio. В пайплайн можно включить этап проверки метрик здоровья (успешность HTTP-запросов, latency) из Prometheus и автоматический откат при превышении пороговых значений.

Подробнее о стратегиях развертывания и их автоматизации можно прочитать в руководстве по Docker в production.

Чек-лист миграции и частые ошибки

Используйте этот список для самопроверки перед финальным запуском в production.

  1. Аудит и планирование: Выделили stateless и stateful компоненты. Рассчитали requests/limits для CPU и памяти. Выбрали стратегию миграции (поэтапная).
  2. Безопасность данных: Создали полные бэкапы всех stateful-сервисов. Проверили процедуру восстановления из бэкапа.
  3. Создание манифестов: Stateless-сервисы описаны в Deployment, stateful - в StatefulSet. Конфигурации вынесены в ConfigMap, секреты - в Secret (или внешний Vault). Настроены Services для внутреннего доступа и Ingress для внешнего.
  4. Проверки здоровья: Для каждого контейнера настроены осмысленные livenessProbe и readinessProbe. Это критически важно для работы self-healing и service discovery.
  5. Хранение данных: Для stateful-приложений использованы volumeClaimTemplates в StatefulSet. Проверена работа PersistentVolume при перемещении пода на другую ноду.
  6. Безопасность: Задан SecurityContext для подов (runAsNonRoot, readOnlyRootFilesystem). Используются образы со стабильными тегами, а не latest. Настроены RBAC для CI/CD ServiceAccount.
  7. CI/CD: Пайплайн обновляет манифесты и применяет их с помощью kubectl/helm/kustomize. Реализована стратегия деплоя (например, blue-green) и есть этап отката.
  8. Мониторинг и логи: Настроен сбор метрик приложения и кластера в Prometheus. Настроено логирование через Loki или EFK-стек. Определены ключевые алерты.

Частые ошибки:

  • Забыть про readinessProbe, из-за чего трафик начинает поступать на еще не готовые к работе поды.
  • Не настроить requests/limits, что приводит к eviction подов при нехватке памяти на ноде или к невозможности запланировать под из-за нехватки CPU.
  • Использовать тег образа latest в production, что делает деплой непредсказуемым.
  • Хранить секреты в Git, даже в base64.
  • Не тестировать поведение PersistentVolume при смене ноды. Локальные тома (local) привязаны к конкретной ноде, и под не может переехать.
  • Игнорировать настройку Pod Disruption Budget для защиты от случайного eviction'а при обслуживании нод.

После успешной миграции рассмотрите возможность дальнейшей оптимизации инфраструктуры, например, автоматизацию развертывания кластеров с помощью инструментов, описанных в статье об автоматизации переноса инфраструктуры. Для управления уже развернутыми приложениями обратитесь к полному руководству по Kubernetes Deployment.

Поделиться:
Сохранить гайд? В закладки браузера