Сервис развивается: тестируем формат, собираем идеи, улучшаем сервис. Есть идеи?

Написать
Войти
Дайджесты
Схема миграции приложения из Docker Compose в Kubernetes на AWS

Практический гид по миграции: из Docker Compose в Kubernetes на AWS

Подробный разбор практического опыта переноса 5-сервисного Java-приложения в production-grade кластер Kubernetes на базе AWS (EKS). Карточка детально описывает шаги миграции, встреченные сетевые и конфигурационные ошибки, а также способы их решения при настройке VPC, Helm, баз данных и Ingress-контроллеров.

Практический гид по миграции: из Docker Compose в Kubernetes на AWS

Перенос приложения из Docker Compose в Kubernetes — это переход от локального запуска контейнеров к промышленному управлению инфраструктурой. Compose прост, но не дает self-healing, rolling updates, лимитов ресурсов и декларативного контроля. Ниже описан опыт переноса 5-сервисного Java-приложения (Tomcat, MySQL, Memcached, RabbitMQ, Nginx) в кластер Kubernetes на AWS, разбор ошибок Compose и пошаговый чек-лист миграции.

Сетевой контракт: сохранение имен хостов

Java-приложение на базе Spring 6 и Tomcat 10 использовало конфигурацию application.properties со следующими хостами зависимостей: базы данных db01:3306, кэша cache01:11211 и брокера mq01:5672. В Docker Compose имена служб выступали хостами в общей сети. В Kubernetes встроенная служба DNS автоматически выдает имена сервисам (Services) внутри пространства имен (Namespace). Создав Service с именами db01, cache01 и mq01, мы сохраняем сетевой контракт без изменения кода приложения.

Тестирование в Minikube и разбор локальных ошибок

При отладке манифестов в Minikube проявились допущения, скрытые Compose:

  1. Сборка образов. Kubernetes не собирает образы на лету. Все образы должны быть опубликованы в доступном реестре (Registry) — например, Docker Hub или AWS ECR.
  2. newline в секретах. Команда echo "password" | base64 кодирует символ переноса строки (\n). В результате MySQL получает пароль с невидимым символом и отклоняет авторизацию. Правильно кодировать через echo -n.
  3. Разрешения базы данных. В Kubernetes поды получают динамические адреса из сети Pod CIDR. В образе MySQL пришлось заменить локальный доступ на MYSQL_ROOT_HOST: "%".
  4. Конфликт обновления Stateful-подов. При стратегии RollingUpdate новый под БД пытается смонтировать тот же диск PersistentVolumeClaim (PVC) с доступом ReadWriteOnce до того, как старый под отпустит его. Для одиночной базы необходимо выставить стратегию type: Recreate.

Подготовка облачного кластера AWS через kOps

Для развертывания продакшен-окружения использовался kOps (Kubernetes Operations), создающий всю инфраструктуру AWS (VPC, подсети, группы безопасности, серверы EC2) по декларативной модели.

Перед запуском kOps, согласно kOps AWS guide, нужно:

  1. Настроить DNS: делегировать субдомен (в примере: prod.wzops.de).
  2. Создать S3-хранилище конфигураций кластера и экспортировать путь: export KOPS_STATE_STORE=s3://<bucket>.

Параметры кластера в регионе eu-north-1 (Стокгольм) на две зоны доступности:

  • CNI Cilium без kube-proxy для eBPF-маршрутизации.
  • Рабочие узлы: 2 инстанса t3.small с дисками 12 GiB.
  • Control plane: инстанс t3.medium (12 GiB) с резервным копированием etcd на 90 дней и отключенной анонимной авторизацией Kubelet.

Важный нюанс: сборка образов на процессорах Apple Silicon (arm64) приводит к сбою запуска на узлах AWS EC2 (amd64). Сборку необходимо проводить под целевую архитектуру: docker buildx build --platform linux/amd64 -t registry/app:1.0 --push ..

Публикация трафика: Ingress и переход на Gateway API

Для внешнего доступа одного ресурса Ingress недостаточно — требуется контроллер (например, ingress-nginx). Трафик идет по цепочке: Browser -> Cloudflare CNAME -> AWS Load Balancer (NLB/ELB) -> Ingress controller -> Service.

При работе с Cloudflare доступны режимы:

  • Flexible SSL: шифрование только до Cloudflare. Трафик внутри AWS идет по HTTP, что упрощает настройку, но снижает безопасность.
  • Full (или Full Strict): шифрование до балансировщика AWS, что требует наличия валидного SSL-сертификата на стороне Ingress-контроллера.

В современных стеках Ingress заменяют на Gateway API — стандарт для ролевого управления шлюзами (см. Kubernetes Gateway API).

При миграции на Gateway API выполняют действия:

  1. Полное удаление старого Ingress-контроллера (kubectl delete -f <manifest-url>).
  2. Установка манифестов Gateway API CRD.
  3. Развертывание шлюза NGINX Gateway Fabric v2.x с помощью Helm-чарта.
  4. Описание ресурсов: GatewayClass (тип реализации), Gateway (слушатели портов и хосты) и HTTPRoute (связь домена с сервисом).
  5. Обновление DNS у провайдера для привязки домена к новому балансировщику Gateway.

Чек-лист миграции и готовности к продакшену

Ниже приведена пошаговая процедура переноса сервисов:

1. Анализ зависимостей (Локально)
  • Разделите сервисы на stateless (приложение) и stateful (MySQL).
  • Выпишите внутренние имена хостов и сопоставьте порты.
2. Сборка и публикация контейнеров (CI/CD)
  • Создайте multi-stage Dockerfile для уменьшения размера образов.
  • Соберите образы под архитектуру linux/amd64 с помощью docker buildx и отправьте в реестр.
3. Создание манифестов и запуск (kubectl CLI в Minikube)
  • Создайте Deployment для каждого процесса и Service для сетевых имен.
  • Запишите секреты (пароли, API-токены) в Kubernetes Secret:
    echo -n "my-db-password" | base64
    
  • Для MySQL создайте PVC на 3 GiB и выставьте стратегию Recreate в Deployment.
  • Для ожидания зависимостей (базы данных, кэша) в под приложения добавьте initContainers. Пример скрипта ожидания DNS:
    until nslookup db01; do echo "waiting for db01"; sleep 2; done
    
    Предупреждение: Доступность DNS не гарантирует готовность приложения БД принимать запросы. Настройте readinessProbes для портов или повторные попытки подключения в коде Java.
  • Выставите лимиты ресурсов контейнеров для стабильности: requests.cpu: 250m, requests.memory: 256Mi, limits.cpu: 500m, limits.memory: 512Mi.
4. Развертывание кластера в AWS (kOps CLI)
  • Создайте S3 state store и инициализируйте кластер:
    kops create cluster --name=prod.wzops.de --state=s3://kops-state-bucket --zones=eu-north-1a,eu-north-1b --node-count=2 --node-size=t3.small --control-plane-size=t3.medium --dns-zone=prod.wzops.de --yes
    
  • Проверьте готовность инфраструктуры:
    kops validate cluster --name=prod.wzops.de --state=s3://kops-state-bucket --wait 10m
    
    Безопасность: SSH-ключ используется только для аварийного доступа к узлам; повседневное управление идет через kubectl и API-сервер.
5. Публикация и маршрутизация (Helm и kubectl CLI)
  • Примените манифесты приложения, баз данных и секретов в облаке.
  • Установите реализацию Gateway API и примените манифесты Gateway и HTTPRoute.
  • Проверьте применение маршрутов:
    kubectl get httproute -n production
    
  • Привяжите домен prod.wzops.de в DNS Cloudflare или Route 53 к хосту внешнего балансировщика, созданного Gateway.
  • Проверьте работоспособность curl-запросом к домену.
6. Эксплуатация
  • Подключите мониторинг Prometheus/Grafana, настройте сбор логов и алерты на сбои подов или TLS-сертификатов.
  • Перейдите на GitOps-доставку манифестов с помощью Argo CD.