Перейти к содержимому

Docker Compose → Kubernetes: когда пора делать шаг (и стоит ли)

Константин Потапов
22 мин

Честный разбор перехода с Docker Compose на Kubernetes: реальные причины, Helm charts на практике, local development с kind/minikube и альтернативы вроде Nomad и Fly.io. Без хайпа, только опыт.

Когда Docker Compose упёрся в потолок

Помню тот день отчётливо. Наш API обрабатывал ~5000 RPS в пиковое время. Docker Compose запускал 8 копий приложения на трёх серверах. Деплой выглядел так: SSH на каждый сервер, git pull, docker-compose up -d --build, молимся, чтобы всё не упало. Обычно падало.

Проблемы нарастали как снежный ком:

  • Rolling update? Только руками, по одному серверу. Downtime на каждом — 30-60 секунд.
  • Один сервер упал ночью. Load balancer продолжал слать туда трафик — узнали об этом утром от разгневанных клиентов.
  • Автоскейлинг? Забудьте. Нагрузка растёт → я вручную запускаю ещё один сервер → через час всё успокоилось → сервер простаивает, я плачу за воздух.
  • Health checks были, но примитивные: контейнер живой ≠ приложение работает.

В какой-то момент стало ясно: дальнейшее масштабирование с Compose превращается в ад поддержки. Нужен был оркестратор. Вопрос был — какой?

Docker Compose отлично подходит для локальной разработки и небольших продакшенов (1-3 сервера, до ~500 RPS). Но когда нагрузка растёт и простои стоят денег, нужны инструменты серьёзнее.

Реальные причины перехода на Kubernetes (не hype)

Давайте без маркетинговой лапши. Вот конкретные проблемы, которые решает Kubernetes, и которые Compose не решает или решает костылями.

Причина 1: Zero-downtime deployments из коробки

С Compose: Вы останавливаете контейнер → собираете новый образ → запускаете. Даже если используете docker-compose up -d --no-deps --build service, там есть gap в несколько секунд. В продакшене это видят пользователи.

С Kubernetes: Rolling update нативно. Новые поды запускаются → проходят readiness probe → начинают принимать трафик → старые gracefully завершаются. Downtime = 0. Откат (rollback) — одна команда: kubectl rollout undo.

# Деплой новой версии
kubectl set image deployment/myapp myapp=myapp:v2
 
# Откат, если что-то пошло не так
kubectl rollout undo deployment/myapp

Причина 2: Автоматическое восстановление (self-healing)

С Compose: Контейнер умер → restart: unless-stopped перезапустит его. Но если упал весь сервер? Вручную SSH на новый, разворачиваете стек заново.

С Kubernetes: Под упал → scheduler автоматически запускает новый. Нода (сервер) упала → поды мигрируют на живые ноды. Всё автоматически, без вашего участия в 3 ночи.

Причина 3: Декларативная конфигурация и Git Ops

С Compose: docker-compose.yml есть, но state управляется императивными командами (docker-compose up, docker-compose scale). Что сейчас работает на проде? Нужно SSH и смотреть.

С Kubernetes: Всё описано в YAML манифестах. Хотите 5 реплик приложения? Пишете replicas: 5, применяете kubectl apply -f deployment.yaml. Kubernetes приведёт состояние кластера к желаемому. Git — единственный источник правды.

GitOps пример:

# Commit изменений в манифестах
git commit -m "Scale app to 10 replicas"
git push
 
# ArgoCD или Flux автоматически применят изменения
# Rollback? Просто git revert и push

Причина 4: Встроенные health checks и liveness probes

С Compose: Можете прописать healthcheck в docker-compose.yml, но это примитивно: контейнер отвечает на /health → считается живым. Если база недоступна, а контейнер живой — получите 500 ошибки.

С Kubernetes: Два типа проверок:

  • Liveness probe: Приложение живое? Если нет → убить под и запустить новый.
  • Readiness probe: Приложение готово принимать трафик? Если нет → не слать запросы на этот под.
livenessProbe:
  httpGet:
    path: /health/live
    port: 8080
  initialDelaySeconds: 30
  periodSeconds: 10
 
readinessProbe:
  httpGet:
    path: /health/ready
    port: 8080
  initialDelaySeconds: 5
  periodSeconds: 5

Результат: поды, которые ещё стартуют или уже тормозят, не получают трафик. Меньше 502, меньше криков клиентов.

Причина 5: Горизонтальное автомасштабирование (HPA)

С Compose: Автоскейлинга нет. Нагрузка выросла → идёте в SSH, руками запускаете docker-compose scale app=10. Нагрузка упала → руками уменьшаете. Или пишете костыльный скрипт с cron.

С Kubernetes: Horizontal Pod Autoscaler (HPA) из коробки. Настраиваете метрику (CPU, память, custom метрика из Prometheus), и k8s автоматически масштабирует поды.

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: myapp-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: myapp
  minReplicas: 3
  maxReplicas: 20
  metrics:
    - type: Resource
      resource:
        name: cpu
        target:
          type: Utilization
          averageUtilization: 70

CPU выше 70% → автоматически добавляются поды. CPU ниже → убираются. Вы спите спокойно.

Docker Compose
Kubernetes
Деплой
SSH + молитва
kubectl apply (zero-downtime)
Downtime
30-60 сек на сервер
0 секунд
100%
Автоскейлинг
Руками или костыли
HPA автоматически
Восстановление
Вручную в 3 ночи
Self-healing встроен

Когда Compose достаточно (честно)

Не спешите переписывать всё на Kubernetes. Вот сценарии, когда Compose — правильный выбор:

  1. Локальная разработка — Compose проще, быстрее, меньше ресурсов.
  2. Монолит на 1-2 серверах — Если у вас 100-200 RPS и не критично, если раз в месяц будет минута downtime на деплой.
  3. Прототипы и MVP — Зачем усложнять, если проект может не взлететь?
  4. Команда без DevOps — Kubernetes требует знаний. Если их нет, Compose или managed решения (Render, Fly.io) — лучше.

Kubernetes — не серебряная пуля. Это сложный инструмент, который решает сложные задачи. Если ваши задачи не сложные, не усложняйте себе жизнь.

Kubernetes изнутри: что это на самом деле

Kubernetes (k8s) — это оркестратор контейнеров. Упрощённо: вы говорите k8s "мне нужно 5 копий приложения, каждая с 2 CPU и 4GB RAM", а он решает, на каких серверах их запустить, следит за их здоровьем, перезапускает при падении.

Ключевые концепции (минимум для старта)

1. Pod — минимальная единица развёртывания. Один или несколько контейнеров, которые всегда работают вместе на одной ноде.

apiVersion: v1
kind: Pod
metadata:
  name: myapp-pod
spec:
  containers:
    - name: myapp
      image: myapp:latest
      ports:
        - containerPort: 8080

2. Deployment — описывает желаемое состояние: сколько реплик пода, какой образ, стратегия обновления.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp
spec:
  replicas: 3
  selector:
    matchLabels:
      app: myapp
  template:
    metadata:
      labels:
        app: myapp
    spec:
      containers:
        - name: myapp
          image: myapp:v1.0.0
          ports:
            - containerPort: 8080

3. Service — сетевая абстракция. Даёт стабильный IP/DNS для набора подов. Типы: ClusterIP (внутри кластера), NodePort (доступ извне), LoadBalancer (облачный балансировщик).

apiVersion: v1
kind: Service
metadata:
  name: myapp-service
spec:
  selector:
    app: myapp
  ports:
    - protocol: TCP
      port: 80
      targetPort: 8080
  type: LoadBalancer

4. Ingress — HTTP/HTTPS маршрутизация извне в сервисы. Аналог Nginx reverse proxy, но декларативный.

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: myapp-ingress
spec:
  rules:
    - host: myapp.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: myapp-service
                port:
                  number: 80

5. ConfigMap и Secret — конфигурация и секреты. Вместо переменных в docker-compose.yml, они живут отдельно.

apiVersion: v1
kind: ConfigMap
metadata:
  name: myapp-config
data:
  DATABASE_URL: "postgresql://user@db:5432/mydb"
  LOG_LEVEL: "info"
---
apiVersion: v1
kind: Secret
metadata:
  name: myapp-secrets
type: Opaque
data:
  DB_PASSWORD: cGFzc3dvcmQxMjM= # base64 encoded

Что k8s решает

  • Оркестрация: Где запускать поды, как их балансировать
  • Масштабирование: Горизонтальное и вертикальное
  • Self-healing: Автоматический restart и rescheduling
  • Service discovery: Поды находят друг друга по DNS
  • Rolling updates и rollbacks
  • Secrets management: Хранение и инъекция секретов

Что k8s НЕ решает

  • Управление состоянием приложения — это ваша задача
  • Мониторинг и логирование — нужны Prometheus, Loki, Grafana
  • CI/CD — нужны GitLab CI, ArgoCD, Flux
  • Backup и disaster recovery — нужны Velero и ручное планирование
  • Безопасность — это целая отдельная область (RBAC, Network Policies, Pod Security Standards)

Kubernetes — это платформа, а не решение "под ключ". Это Lego, из которого вы собираете свою инфраструктуру. Нужны дополнительные инструменты и знания.

Helm charts на практике

Helm — это "пакетный менеджер" для Kubernetes. Аналог apt для Ubuntu или npm для Node.js, только для k8s манифестов.

Зачем нужен Helm

Представьте: у вас есть приложение с Deployment, Service, Ingress, ConfigMap, Secret. Это ~200 строк YAML. Теперь хотите развернуть это в 3 окружениях: dev, staging, prod. С разными параметрами: количество реплик, лимиты ресурсов, домены.

Без Helm: Копипаста YAML файлов, Find & Replace значений. Кошмар поддержки.

С Helm: Один шаблон (chart), параметры в values.yaml. Деплой в любое окружение — одна команда.

Структура Helm Chart

myapp/
  Chart.yaml          # Метаданные: имя, версия
  values.yaml         # Дефолтные значения переменных
  templates/
    deployment.yaml   # Шаблон Deployment
    service.yaml      # Шаблон Service
    ingress.yaml      # Шаблон Ingress
    configmap.yaml    # Шаблон ConfigMap

Миграция с docker-compose на Helm (практический пример)

Было (docker-compose.yml):

version: "3.8"
services:
  app:
    image: myapp:latest
    ports:
      - "8080:8080"
    environment:
      DATABASE_URL: postgresql://user:pass@db:5432/mydb
      REDIS_URL: redis://redis:6379
    depends_on:
      - db
      - redis
 
  db:
    image: postgres:15
    environment:
      POSTGRES_PASSWORD: secretpass
    volumes:
      - db_data:/var/lib/postgresql/data
 
  redis:
    image: redis:7-alpine
 
volumes:
  db_data:

Стало (Helm Chart):

values.yaml:

replicaCount: 3
 
image:
  repository: myapp
  tag: "v1.0.0"
  pullPolicy: IfNotPresent
 
service:
  type: LoadBalancer
  port: 80
  targetPort: 8080
 
ingress:
  enabled: true
  host: myapp.example.com
 
env:
  DATABASE_URL: postgresql://user@postgres:5432/mydb
  REDIS_URL: redis://redis:6379
 
resources:
  limits:
    cpu: 500m
    memory: 512Mi
  requests:
    cpu: 250m
    memory: 256Mi
 
autoscaling:
  enabled: true
  minReplicas: 3
  maxReplicas: 10
  targetCPUUtilizationPercentage: 70

templates/deployment.yaml:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: { { include "myapp.fullname" . } }
spec:
  replicas: { { .Values.replicaCount } }
  selector:
    matchLabels:
      app: myapp
  template:
    metadata:
      labels:
        app: myapp
    spec:
      containers:
        - name: myapp
          image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
          ports:
            - containerPort: { { .Values.service.targetPort } }
          env:
            - name: DATABASE_URL
              value: { { .Values.env.DATABASE_URL } }
            - name: REDIS_URL
              value: { { .Values.env.REDIS_URL } }
          resources: { { - toYaml .Values.resources | nindent 10 } }
          livenessProbe:
            httpGet:
              path: /health
              port: { { .Values.service.targetPort } }
            initialDelaySeconds: 30
          readinessProbe:
            httpGet:
              path: /health/ready
              port: { { .Values.service.targetPort } }
            initialDelaySeconds: 5

Деплой:

# Установка chart
helm install myapp ./myapp
 
# Обновление с новыми параметрами
helm upgrade myapp ./myapp --set image.tag=v1.1.0
 
# Откат к предыдущей версии
helm rollback myapp
 
# Удаление
helm uninstall myapp

Разные окружения:

# Dev (1 реплика, маленькие ресурсы)
helm install myapp-dev ./myapp -f values-dev.yaml
 
# Staging (3 реплики)
helm install myapp-staging ./myapp -f values-staging.yaml
 
# Production (10 реплик, HPA)
helm install myapp-prod ./myapp -f values-prod.yaml
KubernetesHelmDockerkubectl

Готовые Helm Charts

Не нужно писать всё с нуля. Есть огромный репозиторий готовых charts: Artifact Hub.

Популярные charts:

# PostgreSQL
helm repo add bitnami https://charts.bitnami.com/bitnami
helm install postgres bitnami/postgresql
 
# Redis
helm install redis bitnami/redis
 
# Nginx Ingress Controller
helm install nginx-ingress ingress-nginx/ingress-nginx
 
# Prometheus + Grafana (мониторинг)
helm install monitoring prometheus-community/kube-prometheus-stack

Helm экономит десятки часов. Вместо написания сотен строк YAML, берёте готовый chart, кастомизируете values.yaml, деплоите. Profit.

Local development с Kubernetes

Перед тем, как запускать k8s в продакшене, нужно научиться работать с ним локально. Проблема: полноценный k8s требует несколько серверов и гигабайты RAM. Решение: локальные k8s дистрибутивы.

kind vs minikube vs k3d — сравнение

Критерийkindminikubek3d
ОсноваDocker контейнерыVM (VirtualBox/Docker)k3s в Docker
Скорость запуска✅ Очень быстро (10-20 сек)⚠️ Медленно (1-2 мин)✅ Быстро (20-30 сек)
RAM✅ Мало (~2GB)❌ Много (~4-8GB)✅ Мало (~1-2GB)
Multi-node кластер✅ Да✅ Да (сложнее)✅ Да
LoadBalancer support⚠️ Через MetalLB✅ Из коробки✅ Из коробки
Близость к prod✅ Очень близко✅ Близко⚠️ k3s != полный k8s
Лучше дляCI/CD, тестированиеОбучение, devБыстрый dev

Мой выбор:

  • kind — для CI/CD и если хотите максимальной близости к реальному k8s.
  • minikube — для обучения и экспериментов, если RAM не проблема.
  • k3d — для ежедневной разработки, когда нужна скорость.

Практический setup: kind

Установка (macOS):

brew install kind kubectl
 
# Или через binary
curl -Lo ./kind https://kind.sigs.k8s.io/dl/v0.20.0/kind-darwin-arm64
chmod +x ./kind
mv ./kind /usr/local/bin/kind

Создание кластера:

# Базовый single-node кластер
kind create cluster --name dev
 
# Multi-node кластер (1 control-plane + 2 workers)
cat <<EOF | kind create cluster --config=-
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
- role: worker
- role: worker
EOF

Проверка:

kubectl cluster-info
kubectl get nodes

Деплой приложения:

# Загрузка локального образа в kind
kind load docker-image myapp:latest --name dev
 
# Деплой
kubectl apply -f deployment.yaml
kubectl get pods
kubectl logs -f <pod-name>
 
# Проброс порта для доступа
kubectl port-forward deployment/myapp 8080:8080
# Теперь доступно на http://localhost:8080

Удаление кластера:

kind delete cluster --name dev

Практический setup: k3d

Установка:

brew install k3d
 
# Или curl
curl -s https://raw.githubusercontent.com/k3d-io/k3d/main/install.sh | bash

Создание кластера с LoadBalancer:

# Кластер с 3 worker нодами и LoadBalancer на порту 8080
k3d cluster create dev \
  --agents 3 \
  --port "8080:80@loadbalancer"

Деплой:

kubectl apply -f deployment.yaml
kubectl apply -f service.yaml
 
# Если Service type: LoadBalancer, доступен на localhost:8080
curl http://localhost:8080

Остановка/запуск кластера:

k3d cluster stop dev
k3d cluster start dev

Как не убить свой лаптоп

Ресурсы:

  • Минимум: 8GB RAM, 4 CPU cores
  • Рекомендуется: 16GB RAM, 6+ CPU cores

Оптимизация:

  • Ограничьте ресурсы Docker Desktop: Settings → Resources → 6GB RAM, 4 CPUs
  • Используйте resource limits в манифестах:
    resources:
      limits:
        cpu: 200m
        memory: 256Mi
  • Не запускайте всё разом. Нужно тестировать API? Запускайте только API + БД, без фронтенда и микросервисов.
  • Используйте kubectl delete после тестов. Остановленные поды всё равно жрут RAM.

Локальный k8s — не игрушка. Он реально потребляет ресурсы. Если лаптоп тормозит, используйте облачный dev кластер (GKE Autopilot, EKS, DigitalOcean Kubernetes). 1-2 worker ноды стоят $10-20/мес.

Мониторинг и debugging в Kubernetes

Kubernetes — чёрный ящик, пока не настроите наблюдаемость. Вот минимальный набор для выживания.

kubectl — ваш главный инструмент

Основные команды:

# Получить список подов
kubectl get pods
 
# Подробная информация о поде
kubectl describe pod <pod-name>
 
# Логи пода
kubectl logs <pod-name>
kubectl logs <pod-name> -f  # follow (real-time)
kubectl logs <pod-name> --previous  # логи предыдущего упавшего контейнера
 
# Логи всех подов deployment
kubectl logs -l app=myapp --all-containers=true
 
# Exec в под (как docker exec)
kubectl exec -it <pod-name> -- /bin/bash
 
# Проброс порта
kubectl port-forward pod/<pod-name> 8080:8080
 
# События кластера
kubectl get events --sort-by='.lastTimestamp'
 
# Top (CPU/Memory usage)
kubectl top nodes
kubectl top pods

Troubleshooting типичных проблем:

1. Pod в статусе Pending

kubectl describe pod <pod-name>
# Смотрите Events: обычно "Insufficient CPU/Memory" или "No nodes available"

Решение: Увеличьте ресурсы кластера или уменьшите requests пода.

2. Pod в статусе CrashLoopBackOff

kubectl logs <pod-name> --previous
# Смотрите, почему упал контейнер

Решение: Обычно ошибка в коде, неправильная конфигурация или недоступная зависимость (БД, Redis).

3. Pod Running, но не отвечает

kubectl describe pod <pod-name>
# Смотрите Readiness probe

Решение: Под не прошёл readiness probe → не получает трафик. Проверьте /health/ready endpoint.

4. Service недоступен

kubectl get svc
kubectl get endpoints <service-name>
# Endpoints пустой? Поды не прошли readiness probe

Мониторинг: Prometheus + Grafana

Быстрый setup через Helm:

# Добавляем репозиторий
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm repo update
 
# Устанавливаем kube-prometheus-stack (Prometheus + Grafana + Alertmanager)
helm install monitoring prometheus-community/kube-prometheus-stack
 
# Получаем пароль Grafana
kubectl get secret monitoring-grafana -o jsonpath="{.data.admin-password}" | base64 --decode
 
# Проброс порта для доступа
kubectl port-forward svc/monitoring-grafana 3000:80
# Grafana доступна на http://localhost:3000 (admin / <password>)

Что получите:

  • Prometheus с метриками кластера, нод, подов
  • Grafana с готовыми дашбордами (Kubernetes Overview, Node Exporter, etc.)
  • Alertmanager для алертов

Полезные дашборды (уже встроены):

  • Kubernetes / Compute Resources / Cluster
  • Kubernetes / Compute Resources / Namespace (Pods)
  • Node Exporter / Nodes

Логирование: Loki + Promtail

Setup через Helm:

helm repo add grafana https://grafana.github.io/helm-charts
helm install loki grafana/loki-stack \
  --set grafana.enabled=false \
  --set promtail.enabled=true
 
# Добавьте Loki как data source в Grafana
# URL: http://loki:3100

Просмотр логов в Grafana:

  • Explore → Loki data source
  • Query: {app="myapp"}
  • Фильтрация: {app="myapp"} |= "ERROR"

Альтернативы Kubernetes

Kubernetes — не единственный оркестратор. Иногда альтернативы лучше подходят. Честное сравнение.

Nomad — простота и практичность

Что это: Оркестратор от HashiCorp. Умеет управлять контейнерами (Docker), виртуалками, standalone приложениями.

Плюсы:

  • Простота: Конфигурация в разы проще k8s. 100 строк Nomad = 300 строк k8s.
  • Lightweight: Один бинарь, 50MB RAM. k8s — это десятки компонентов.
  • Multi-runtime: Docker, Podman, Java, binaries, VMs.
  • Интеграция с Consul (service mesh) и Vault (secrets).

Минусы:

  • Экосистема меньше: Меньше готовых решений и community charts.
  • Нет встроенного Ingress: Нужен отдельный Nginx/Traefik.
  • Меньше managed опций: AWS/GCP/Azure не предлагают managed Nomad.

Когда выбирать:

  • Небольшие команды (до 10 человек), которым нужна простота.
  • Если уже используете HashiCorp стек (Consul, Vault, Terraform).
  • Хотите управлять не только контейнерами, но и legacy приложениями.

Пример конфига Nomad:

job "myapp" {
  datacenters = ["dc1"]
 
  group "app" {
    count = 3
 
    task "web" {
      driver = "docker"
 
      config {
        image = "myapp:v1.0.0"
        ports = ["http"]
      }
 
      resources {
        cpu    = 500
        memory = 512
      }
 
      service {
        name = "myapp"
        port = "http"
 
        check {
          type     = "http"
          path     = "/health"
          interval = "10s"
          timeout  = "2s"
        }
      }
    }
  }
}

AWS ECS — если уже в AWS

Что это: Managed оркестратор от Amazon. Глубокая интеграция с AWS (ALB, RDS, IAM).

Плюсы:

  • Managed: AWS управляет control plane, вы только деплоите задачи.
  • AWS-native: Интеграция с IAM ролями, секретами, логами, метриками.
  • Fargate: Serverless режим — не управляете серверами вообще.

Минусы:

  • Vendor lock-in: Привязка к AWS, миграция сложна.
  • Дороже, чем самостоятельный k8s (особенно Fargate).
  • Меньше гибкости, чем k8s.

Когда выбирать:

  • Вы уже полностью в AWS.
  • Не хотите управлять инфраструктурой (Fargate).
  • Простота важнее гибкости.

Fly.io — современный подход

Что это: Платформа для деплоя контейнеров ближе к пользователям (edge computing). Под капотом — их собственный оркестратор на базе Nomad.

Плюсы:

  • Простота: fly deploy — и готово. Как Heroku, но с Docker.
  • Global distribution: Автоматически деплоит ваше приложение в дата-центры по всему миру.
  • Бесплатный тир: До 3 VM, достаточно для экспериментов.
  • Managed PostgreSQL, Redis из коробки.

Минусы:

  • Vendor lock-in.
  • Меньше контроля, чем в k8s.
  • Стоимость растёт при масштабировании.

Когда выбирать:

  • Стартапы и малые проекты.
  • Хотите скорость деплоя без DevOps-команды.
  • Global latency важна (пользователи по всему миру).

Пример деплоя:

# Установка CLI
brew install flyctl
 
# Вход
fly auth login
 
# Инициализация приложения
fly launch
 
# Деплой
fly deploy
 
# Масштабирование
fly scale count 5
fly scale vm shared-cpu-2x

Docker Swarm — забытый, но живой

Что это: Встроенный в Docker оркестратор. Упрощённая версия k8s.

Плюсы:

  • Простота: Проще, чем k8s. Если знаете Docker, Swarm освоите за день.
  • Встроен в Docker: Не нужна дополнительная установка.
  • docker-compose.yml совместимость: Можно деплоить существующие compose файлы.

Минусы:

  • Умирающий проект: Docker Inc почти не развивает его.
  • Нет экосистемы: Мало готовых решений.
  • Меньше функций, чем k8s.

Когда выбирать:

  • Если Docker Compose уже недостаточно, но k8s — overkill.
  • Для небольших продакшенов (3-5 серверов).

Миграция с Compose:

# Инициализация Swarm
docker swarm init
 
# Деплой compose файла
docker stack deploy -c docker-compose.yml myapp
 
# Масштабирование
docker service scale myapp_web=5

Итоговая таблица выбора

КритерийKubernetesNomadAWS ECSFly.ioDocker Swarm
Сложность❌ Высокая⚠️ Средняя✅ Низкая✅ Очень низкая✅ Низкая
Экосистема✅ Огромная⚠️ Средняя⚠️ AWS-only⚠️ Маленькая❌ Мёртвая
Гибкость✅ Максимум✅ Высокая⚠️ Средняя❌ Низкая⚠️ Средняя
Vendor lock-in✅ Нет✅ Нет❌ AWS❌ Fly.io✅ Нет
Стоимость⚠️ DIY дёшево, managed дорого✅ DIY дёшево❌ Дорого⚠️ Растёт✅ Дёшево
Для когоСредние/крупные командыМалые команды, HashiCorp стекAWS-native проектыСтартапыМалые проекты

Мой совет: начинайте с простого. Если Docker Compose достаточно — оставайтесь на нём. Перерастёте — сначала попробуйте Fly.io или Nomad. Kubernetes — когда реально нужна его мощь и гибкость.

Реальная цена перехода на Kubernetes

Давайте честно: Kubernetes — это инвестиция. Не только в инфраструктуру, но и во время и знания.

Время на изучение

Минимальный уровень (чтобы деплоить): 20-40 часов

  • Концепции: Pods, Deployments, Services, Ingress
  • kubectl базовые команды
  • Helm установка готовых charts
  • Troubleshooting: логи, describe, events

Средний уровень (чтобы управлять в продакшене): 100-200 часов

  • Сетевая модель k8s, NetworkPolicies
  • RBAC и безопасность
  • Stateful приложения (StatefulSets, PersistentVolumes)
  • Мониторинг и логирование
  • CI/CD интеграция
  • Disaster recovery

Экспертный уровень (архитектор k8s): 500+ часов

  • Multi-cluster setup
  • Service mesh (Istio, Linkerd)
  • Custom operators
  • Performance tuning
  • Кастомные schedulers и admission controllers

Сложность эксплуатации

Managed Kubernetes (EKS, GKE, AKS):

  • ✅ Control plane управляется провайдером
  • ✅ Автоматические обновления
  • ⚠️ Вы управляете worker nodes, addons, мониторингом
  • 💰 Стоимость: $70-150/мес за control plane + $50-200/мес за worker nodes

Self-hosted Kubernetes:

  • ❌ Вы управляете всем: control plane, etcd, сетью, обновлениями
  • ❌ Нужен DevOps/SRE в команде
  • ❌ Высокий риск, если нет экспертизы
  • 💰 Стоимость: $0 за софт, но нужно время команды

Реальные затраты (из моей практики):

Проект на 5 микросервисах, 20-30 подов, ~2000 RPS.

  • Managed GKE: $250/мес (control plane + 3 worker ноды e2-standard-4)
  • Время DevOps: 10-15 часов/мес на поддержку
  • Мониторинг: Prometheus + Grafana self-hosted, $0 доп. затрат
  • Обучение команды: 2 недели на онбординг разработчиков

Итого: ~$250/мес + 10-15 часов времени. Окупается, если экономите на ручном масштабировании и downtimes.

Когда НЕ стоит переходить на Kubernetes

  1. Команда < 3 разработчиков — overhead на k8s съест всё время.
  2. Монолит на 1 сервере — Docker Compose достаточно.
  3. Нет DevOps экспертизы — лучше Fly.io, Render, Railway.
  4. Стартап на MVP стадии — фокусируйтесь на продукте, а не инфраструктуре.
  5. Budget < $500/мес — managed k8s дорого, self-hosted — риск.

Kubernetes — это не статус и не модный тренд. Это инструмент для решения конкретных проблем. Если этих проблем нет, не создавайте себе новые, внедряя k8s.

Пошаговый переход с Compose на Kubernetes

Если решили, что k8s — это ваш путь, вот план миграции без боли.

Этап 1: Подготовка (1-2 недели)

1. Изучите основы k8s

  • Пройдите официальный туториал: kubernetes.io/docs/tutorials
  • Поднимите локальный кластер (kind или k3d)
  • Деплойте тестовое приложение

2. Аудит текущей инфраструктуры

  • Какие сервисы работают?
  • Какие зависимости между ними?
  • Где хранится состояние (базы, файлы, кеши)?
  • Какие переменные окружения и секреты используются?

3. Выберите managed k8s или self-hosted

  • Managed (GKE, EKS, AKS) — если бюджет позволяет
  • Self-hosted (kubeadm, k3s) — если есть DevOps экспертиза

4. Настройте CI/CD для k8s

  • Интегрируйте kubectl/helm в пайплайн
  • Настройте image registry (Docker Hub, GCR, ECR)

Этап 2: Миграция stateless сервисов (2-4 недели)

1. Начните с самого простого сервиса

  • Выберите stateless сервис без зависимостей
  • Создайте Deployment и Service манифесты
  • Деплойте в dev namespace

Пример миграции API сервиса:

Было (docker-compose.yml):

api:
  image: myapi:latest
  ports:
    - "8080:8080"
  environment:
    DATABASE_URL: postgresql://db:5432/mydb

Стало (k8s/api-deployment.yaml):

apiVersion: apps/v1
kind: Deployment
metadata:
  name: api
spec:
  replicas: 3
  selector:
    matchLabels:
      app: api
  template:
    metadata:
      labels:
        app: api
    spec:
      containers:
        - name: api
          image: myapi:v1.0.0
          ports:
            - containerPort: 8080
          env:
            - name: DATABASE_URL
              valueFrom:
                secretKeyRef:
                  name: api-secrets
                  key: database-url
          resources:
            requests:
              cpu: 100m
              memory: 128Mi
            limits:
              cpu: 500m
              memory: 512Mi
          livenessProbe:
            httpGet:
              path: /health
              port: 8080
          readinessProbe:
            httpGet:
              path: /health/ready
              port: 8080
---
apiVersion: v1
kind: Service
metadata:
  name: api
spec:
  selector:
    app: api
  ports:
    - port: 80
      targetPort: 8080

2. Постепенно мигрируйте остальные stateless сервисы

3. Настройте Ingress для HTTP трафика

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: main-ingress
  annotations:
    cert-manager.io/cluster-issuer: "letsencrypt-prod"
spec:
  ingressClassName: nginx
  tls:
    - hosts:
        - api.example.com
      secretName: api-tls
  rules:
    - host: api.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: api
                port:
                  number: 80

Этап 3: Миграция stateful сервисов (4-8 недель)

1. Базы данных — НЕ спешите мигрировать

  • Managed БД (RDS, Cloud SQL) проще, чем PostgreSQL в k8s
  • Если мигрируете — используйте StatefulSets и PersistentVolumes
  • Обязательно настройте backup и disaster recovery

Managed БД (рекомендуется):

env:
  - name: DATABASE_URL
    value: postgresql://user@rds-endpoint:5432/mydb

PostgreSQL в k8s (сложно, но возможно):

# Используйте готовый Helm chart
helm install postgres bitnami/postgresql \
  --set persistence.size=100Gi \
  --set primary.persistence.storageClass=ssd

2. Redis/Memcached — можно в k8s

helm install redis bitnami/redis

3. Файловое хранилище

  • Используйте S3-compatible хранилище (AWS S3, MinIO)
  • Или PersistentVolumeClaims с ReadWriteMany (NFS, Ceph)

Этап 4: Настройка мониторинга и логирования (1-2 недели)

# Prometheus + Grafana
helm install monitoring prometheus-community/kube-prometheus-stack
 
# Loki для логов
helm install loki grafana/loki-stack

Этап 5: Переключение трафика (1 день, но тестируйте неделю)

1. Parallel run

  • Запустите новый k8s стек параллельно со старым Compose
  • Переключите небольшой процент трафика (10%) на k8s
  • Мониторьте метрики и логи

2. Постепенное переключение

  • 10% трафика → проверка → 50% → проверка → 100%

3. Откат всегда возможен

  • Держите старый Compose стек живым ещё 1-2 недели
  • Если что-то пошло не так — откатываете трафик обратно

Post-migration checklist

✅ Все сервисы работают в k8s
✅ Мониторинг настроен (Prometheus, Grafana)
✅ Логирование работает (Loki или CloudWatch)
✅ Алерты настроены (Alertmanager)
✅ CI/CD интегрирован с k8s
✅ Backup и disaster recovery протестированы
✅ Команда обучена работе с kubectl и troubleshooting
✅ Документация обновлена

Выводы

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

Что запомнить:

  1. Docker Compose достаточно для малых проектов (1-3 сервера, до 500 RPS). Не усложняйте без необходимости.

  2. Переходите на k8s, когда:

    • Нужен zero-downtime deployment
    • Горизонтальное автомасштабирование критично
    • Команда готова инвестировать время в обучение
    • Managed k8s доступен и бюджет позволяет
  3. Helm charts экономят десятки часов. Не пишите YAML с нуля, используйте готовые charts.

  4. Local development: kind для CI/CD, k3d для ежедневной разработки, minikube для обучения.

  5. Альтернативы существуют:

    • Nomad — простота для HashiCorp стека
    • Fly.io — скорость для стартапов
    • AWS ECS — если в AWS и хотите managed
    • Docker Swarm — если k8s overkill, а Compose мало
  6. Мониторинг — не опция. Prometheus + Grafana + Loki — минимальный стек для production k8s.

  7. Миграция — постепенная. Начните с stateless сервисов, тестируйте, мониторьте, только потом мигрируйте критичные stateful компоненты.

  8. Не мигрируйте базы в k8s без веских причин. Managed БД (RDS, Cloud SQL) проще и надёжнее.

Мой личный вывод:

Я прошёл путь от Compose к k8s в 2021 году. Первые 2 месяца — боль, обучение, грабли. Но когда всё заработало, я получил инфраструктуру, которая масштабируется без моего участия, восстанавливается сама и позволяет деплоить 10 раз в день без простоев.

Kubernetes стоит своих денег и времени, если ваша задача достаточно сложна. Если нет — используйте более простые инструменты и спите спокойно.


P.S. Если у вас вопросы по миграции на k8s или нужна помощь в выборе оркестратора, пишите — помогу разобраться.

См. также: