Kubernetes

Теория: Deployment и ReplicaSet

Deployment и ReplicaSet

В предыдущем уроке мы создавали Pod вручную. Удалили — он исчез навсегда. Изменили образ — пришлось пересоздавать. Для учебных целей это работает, но в реальной системе так жить нельзя. Нужен кто-то, кто будет следить за Pod и восстанавливать его при сбоях.

Зачем нужен ReplicaSet

Представьте: ваш Pod упал из-за ошибки в коде, нехватки памяти или проблемы с нодой. Если это был единственный экземпляр приложения — сервис недоступен. Вы узнаёте об этом из алерта, заходите в кластер, разбираетесь, поднимаете заново. Минуты простоя превращаются в десятки минут.

ReplicaSet решает эту проблему. Это контроллер, который следит за количеством запущенных Pod и поддерживает его на заданном уровне. Упал один Pod — ReplicaSet создаст новый. Упала нода — Pod будут пересозданы на других нодах. Вы указываете «должно быть три экземпляра», а Kubernetes делает так, чтобы их всегда было три.

apiVersion: apps/v1
kind: ReplicaSet
metadata:
  name: nginx-replicaset
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
        - name: nginx
          image: nginx:1.27
          ports:
            - containerPort: 80

Три ключевые части:

  • replicas: 3 — желаемое количество Pod.
  • selector — по каким меткам ReplicaSet находит «свои» Pod. Здесь он ищет Pod с меткой app: nginx.
  • template — шаблон Pod, который ReplicaSet будет создавать. Это тот же манифест Pod, только без apiVersion и kind.

Применим и посмотрим:

kubectl apply -f nginx-replicaset.yaml
kubectl get replicaset
kubectl get pods

Вы увидите три Pod с именами вида nginx-replicaset-xxxxx. Суффикс генерируется автоматически.

Теперь удалите один Pod:

kubectl delete pod nginx-replicaset-xxxxx
kubectl get pods

ReplicaSet мгновенно создаст новый. Он постоянно сравнивает текущее состояние с желаемым и устраняет расхождения.

Проблема ReplicaSet: обновления

ReplicaSet отлично справляется с поддержанием количества Pod, но у него есть критический недостаток. Измените образ в манифесте на nginx:1.26 и примените:

kubectl apply -f nginx-replicaset.yaml
kubectl get pods

Pod остались прежними. ReplicaSet не пересоздаёт существующие Pod при изменении шаблона. Он использует новый шаблон только для создания новых Pod. Чтобы обновить приложение, придётся удалять Pod вручную — ReplicaSet создаст новые уже с обновлённым образом.

Для одного-двух Pod это терпимо. Для десятков — это ручная работа с риском ошибки и простоем.

Deployment: контроллер над контроллером

Deployment решает проблему обновлений. Это объект уровнем выше, который управляет ReplicaSet. Когда вы обновляете Deployment, он создаёт новый ReplicaSet с новым шаблоном и плавно переключает Pod со старого на новый.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
        - name: nginx
          image: nginx:1.27
          ports:
            - containerPort: 80

Структура идентична ReplicaSet, только kind: Deployment. Это не совпадение — Deployment создаёт ReplicaSet внутри себя.

kubectl apply -f nginx-deployment.yaml
kubectl get deployments
kubectl get replicaset
kubectl get pods

Вы увидите цепочку: Deployment → ReplicaSet → Pod. Имя ReplicaSet будет содержать хеш шаблона: nginx-deployment-5d4f7b8c9.

Зафиксируем это как правило: Deployment управляет ReplicaSet, ReplicaSet управляет Pod'ами, Pod — это то, что реально запускается. Вы почти никогда не работаете с ReplicaSet напрямую и никогда не создаёте Pod вручную в production. Вы описываете Deployment, а Kubernetes выстраивает всю цепочку сам.

Как работает обновление

Теперь обновим образ:

kubectl set image deployment/nginx-deployment nginx=nginx:1.26
kubectl get replicaset -w

Происходит следующее:

  1. Deployment создаёт новый ReplicaSet с образом nginx:1.26.
  2. Новый ReplicaSet начинает создавать Pod.
  3. Когда новый Pod готов, старый ReplicaSet уменьшает количество своих Pod на один.
  4. Процесс повторяется, пока все Pod не обновятся.

Ключевой момент: старый ReplicaSet остаётся в системе с нулём Pod. Это не мусор — это история. Благодаря этому работает откат:

kubectl get replicaset

Старый ReplicaSet хранит предыдущую конфигурацию. По умолчанию Kubernetes хранит 10 последних ревизий (настраивается через revisionHistoryLimit).

Стратегии обновления

Это частый вопрос на собеседованиях: какие стратегии обновления существуют в Kubernetes?

Recreate

Самая простая стратегия: сначала удалить все старые Pod, потом создать новые.

spec:
  strategy:
    type: Recreate

Плюсы: никогда не будет двух версий одновременно. Это важно, если старая и новая версия несовместимы — например, при миграции базы данных.

Минусы: полный простой. Пока старые Pod удаляются и новые запускаются, сервис недоступен.

Когда использовать: изменение схемы данных, несовместимые версии API, shared-ресурсы без поддержки конкурентного доступа.

Rolling Update

Стратегия по умолчанию. Постепенная замена Pod: создаём новый, ждём готовности, удаляем старый.

spec:
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 1

maxSurge — на сколько Pod больше желаемого количества можно создать во время обновления. Значение 1 означает: если replicas: 3, во время обновления может быть до 4 Pod.

maxUnavailable — сколько Pod может быть недоступно. Значение 1 означает: минимум 2 из 3 Pod всегда работают.

Для zero-downtime деплоя используйте maxUnavailable: 0:

rollingUpdate:
  maxSurge: 1
  maxUnavailable: 0

Теперь Kubernetes сначала создаст новый Pod, дождётся его готовности, и только потом удалит старый. В любой момент времени работают минимум 3 Pod.

Blue/Green Deployment

Kubernetes не имеет встроенной стратегии Blue/Green, но её легко реализовать через два Deployment и переключение Service.

Идея: поднять полную копию приложения с новой версией (Green) рядом со старой (Blue). Когда Green готов и протестирован — переключить трафик одним действием.

# blue-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: app-blue
spec:
  replicas: 3
  selector:
    matchLabels:
      app: myapp
      version: blue
  template:
    metadata:
      labels:
        app: myapp
        version: blue
    spec:
      containers:
        - name: app
          image: myapp:1.0

Service направляет трафик на одну из версий:

apiVersion: v1
kind: Service
metadata:
  name: myapp
spec:
  selector:
    app: myapp
    version: blue  # Переключить на green для смены версии
  ports:
    - port: 80
      targetPort: 8080

Переключение — изменение одного лейбла в Service. Откат — обратное изменение. Минус: двойное потребление ресурсов во время деплоя.

Canary Deployment

Идея: направить небольшой процент трафика на новую версию, убедиться, что всё работает, и постепенно увеличивать долю.

Простейший вариант в Kubernetes — через количество реплик:

# Старая версия: 9 реплик
apiVersion: apps/v1
kind: Deployment
metadata:
  name: app-stable
spec:
  replicas: 9
  selector:
    matchLabels:
      app: myapp
      track: stable
  template:
    metadata:
      labels:
        app: myapp
        track: stable
    spec:
      containers:
        - name: app
          image: myapp:1.0

Service выбирает Pod по общему лейблу:

apiVersion: v1
kind: Service
metadata:
  name: myapp
spec:
  selector:
    app: myapp  # Без track — берёт и stable, и canary
  ports:
    - port: 80

При 9 stable + 1 canary примерно 10% запросов пойдут на новую версию. Если всё хорошо — увеличиваете canary, уменьшаете stable. Если проблемы — удаляете canary.

Для точного контроля процента трафика используют service mesh (Istio, Linkerd) или ingress-контроллеры с поддержкой weighted routing.

Probes: проверки состояния контейнеров

Ещё один частый вопрос на собеседованиях: какие типы проверок есть в Kubernetes?

Kubernetes не знает, что происходит внутри контейнера. Процесс может работать, но приложение — зависнуть, уйти в deadlock, потерять соединение с базой. Без проверок Kubernetes будет считать такой Pod здоровым и отправлять на него трафик.

Liveness Probe — проверка живучести

Отвечает на вопрос: приложение ещё работает или зависло?

Если liveness probe не проходит, Kubernetes перезапускает контейнер. Это жёсткое действие — используйте с осторожностью.

livenessProbe:
  httpGet:
    path: /healthz
    port: 8080
  initialDelaySeconds: 15
  periodSeconds: 10
  timeoutSeconds: 3
  failureThreshold: 3

initialDelaySeconds — сколько ждать после старта контейнера перед первой проверкой. periodSeconds — интервал между проверками. timeoutSeconds — таймаут на ответ. failureThreshold — сколько неудачных проверок подряд считать сбоем.

Типичная ошибка: слишком агрессивный liveness probe. Приложение стартует 30 секунд, а probe начинает проверять через 10. Контейнер не успевает подняться — probe фейлится — Kubernetes перезапускает — и так по кругу. Результат: CrashLoopBackOff.

Readiness Probe — проверка готовности

Отвечает на вопрос: приложение готово принимать трафик?

Если readiness probe не проходит, Pod исключается из Service — трафик на него не идёт. Но контейнер не перезапускается. Когда probe снова пройдёт, Pod вернётся в ротацию.

readinessProbe:
  httpGet:
    path: /ready
    port: 8080
  initialDelaySeconds: 5
  periodSeconds: 5
  timeoutSeconds: 2
  failureThreshold: 3

Это критически важно для Rolling Update. Без readiness probe Kubernetes создаст новый Pod и сразу начнёт отправлять на него трафик, даже если приложение ещё не готово. Со старым Pod'ом уже попрощались — в итоге часть запросов падает.

Startup Probe — проверка запуска

Отвечает на вопрос: приложение уже запустилось?

Startup probe используется для приложений с долгим стартом. Пока startup probe не пройдёт, liveness и readiness probe не запускаются.

startupProbe:
  httpGet:
    path: /healthz
    port: 8080
  initialDelaySeconds: 0
  periodSeconds: 5
  timeoutSeconds: 3
  failureThreshold: 30  # 30 * 5 = 150 секунд на старт

Это позволяет дать приложению много времени на старт (здесь — до 150 секунд), но после старта использовать агрессивный liveness probe.

Типы проверок

Все три probe поддерживают три способа проверки:

  • HTTP GET — запрос на указанный путь и порт. Код 200-399 = успех.

    httpGet:
      path: /healthz
      port: 8080
  • TCP Socket — попытка открыть соединение. Если порт отвечает = успех.

    tcpSocket:
      port: 3306
  • Exec — выполнение команды внутри контейнера. Exit code 0 = успех.

    exec:
      command:
        - cat
        - /tmp/healthy

Рекомендации по настройке probes:

  • Всегда добавляйте readiness probe. Без него Rolling Update небезопасен.
  • Liveness probe — только для реальных зависаний. Не проверяйте через liveness доступность внешних зависимостей. Если база недоступна, перезапуск приложения не поможет, а только создаст лишнюю нагрузку.
  • Для долго стартующих приложений используйте startup probe вместо большого initialDelaySeconds в liveness.
  • Endpoint для liveness должен быть лёгким — просто «я жив». Для readiness можно проверять готовность зависимостей.

Масштабирование

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

# Через команду
kubectl scale deployment nginx-deployment --replicas=5

# Или изменить манифест и применить
kubectl apply -f nginx-deployment.yaml

Kubernetes создаст или удалит Pod до нужного количества. Понаблюдайте за процессом в реальном времени:

kubectl get pods -w

Для автоматического масштабирования используется HorizontalPodAutoscaler:

# Создать автомасштабирование
kubectl autoscale deployment nginx-deployment --min=3 --max=10 --cpu-percent=80

# Посмотреть статус автомасштабирования
kubectl get hpa

Важно понимать: HPA масштабирует Deployment, а не Pod напрямую. Он изменяет поле replicas, а дальше работает обычная цепочка — ReplicaSet создаёт или удаляет Pod. Kubernetes никогда не «растягивает» существующий Pod — масштабирование всегда означает создание новых Pod или удаление лишних.

Для работы HPA нужен metrics-server в кластере.

Откат

Если новая версия сломала приложение:

kubectl rollout undo deployment/nginx-deployment

# Откат к конкретной ревизии
kubectl rollout undo deployment/nginx-deployment --to-revision=2

# Посмотреть историю
kubectl rollout history deployment/nginx-deployment

Kubernetes переключит ReplicaSet — тот, что был с нулём Pod, снова станет активным.

Рекомендуемые программы

+7 800 100 22 47

бесплатно по РФ

+7 495 085 21 62

бесплатно по Москве

108813 г. Москва, вн.тер.г. поселение Московский,
г. Московский, ул. Солнечная, д. 3А, стр. 1, помещ. 20Б/3
ОГРН 1217300010476
ИНН 7325174845