Как бы ты организовал обновление StatefulSet в Kubernetes без простоя?

«Как бы ты организовал обновление StatefulSet в Kubernetes без простоя?» — вопрос из категории Kubernetes, который задают на 23% собеседований Devops Инженер. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Для StatefulSet с stateful-приложениями (например, Kafka, Elasticsearch, базы данных) я использую стратегию RollingUpdate с управляемым порядком и проверками готовности, чтобы гарантировать доступность и сохранность данных.

Ключевые настройки в манифесте StatefulSet:

apiVersion: apps/v1
kind: StatefulSet
spec:
  updateStrategy:
    type: RollingUpdate
    rollingUpdate:
      # partition: 2  # Управляет порядком обновления (см. ниже)
  podManagementPolicy: OrderedReady # Дефолтное поведение: обновление по одному
  replicas: 3
  template:
    spec:
      containers:
      - name: app
        readinessProbe: # КРИТИЧНО! Подает трафик только когда под готов
          httpGet:
            path: /health
            port: 8080
          initialDelaySeconds: 10
          periodSeconds: 5
        lifecycle:
          preStop: # Дает время на graceful shutdown
            exec:
              command: ["sh", "-c", "sleep 30 && /bin/graceful_shutdown.sh"]

Процесс обновления (например, образа контейнера):

  1. Поэтапное обновление (Canary для StatefulSet): Использую поле partition. Если установить partition: 2 для StatefulSet с 3 репликами, Kubernetes обновит только поды с индексами 2 и выше (т.е. под с индексом 2). Поды 0 и 1 останутся на старой версии.
    kubectl patch statefulset my-app -p '{"spec":{"updateStrategy":{"rollingUpdate":{"partition":2}}}}'
    kubectl set image statefulset/my-app app=myrepo/app:v2.0.0
  2. Проверка: После обновления пода-2 тщательно проверяю его работу, метрики и репликацию данных (если это БД).
  3. Продвижение обновления: Если все в порядке, уменьшаю partition до 1, чтобы обновился под-1, затем до 0 для пода-0. Это гарантирует, что как минимум один лидер (часто это под-0) всегда на старой, стабильной версии до последнего момента.

Дополнительные меры для баз данных:

  • Для мастер-реплика структур (например, PostgreSQL с Patroni) сначала обновляю реплики, затем вручную выполняю switchover, чтобы мастером стала уже обновленная реплика, и затем обновляю старый мастер.
  • Всегда предварительно тестирую процедуру обновления и отката на staging-окружении, имитирую сбои пода во время обновления.