• /
  • /

Как достичь нулевого простоя при rolling deployment в Kubernetes: избегаем разрывов клиентских соединений

Статья поможет понять поведение pod-ов при rolling deployment и методы достижения настоящего нулевого простоя, чтобы клиентские соединения оставались стабильными даже при обновлении приложения.
Благодаря инструментам оркестрации, как Kubernetes, внесение изменений в приложения стало более бесшовным. Но как сделать так, чтобы эти изменения не оказывали негативного влияния на пользователей? Одним из таких негативных эффектов являются разрывы соединений.

Rolling deployment в Kubernetes: что происходит на самом деле

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

Примечание: можно добиться нулевого простоя при развертывании с помощью service mesh (например, Istio) или реализовать blue-green deployment. Однако эти методы требуют много ресурсов, что увеличивает стоимость инфраструктуры.

Итак, вопрос «Что происходит во время rolling deployment?» можно разделить на две части:


  • Что происходит при запуске pod-а?
  • Что происходит при выключении pod-а?

Дальнейшее обсуждение посвящено глубокому пониманию поведения pod-ов при rolling deployment и методам достижения настоящего нулевого простоя, чтобы клиентские соединения оставались стабильными даже при обновлении приложения.

Этап запуска pod-а

Когда рod запускается во время rolling deployment без настройки readiness probe, Endpoint Controller обновляет соответствующие объекты Service с новыми endpoint-ами pod-а. Это означает, что pod начинает получать трафик, даже если он еще не готов обрабатывать запросы. Отсутствие readiness probe может сделать приложение нестабильным.


Решение: Настройка readiness probe для приложений в Kubernetes.


Это гарантирует, что pod начнет получать трафик только тогда, когда он готов. Endpoint Controller продолжит мониторинг состояния pod-а с помощью readiness probe и обновит endpoint-ы service, как только проверка будет успешной.


Пример Kubernetes Deployment с настроенной readiness probe:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-highly-available-app
spec:
  replicas: 3 # Define desired number of replicas
  selector:
    matchLabels:
      app: highly-available
  template:
    metadata:
      labels:
        app: highly-available
    spec:
      containers:
      - name: highly-available
        image: highly-available-image:latest
        ports:
        - containerPort: 80
        readinessProbe: # Readiness probe configuration
          httpGet:
            path: /health-check
            port: 80
            initialDelaySeconds: 5 # Wait 5 seconds before starting probes
            periodSeconds: 10 # Check every 10 seconds
            failureThreshold: 3 # Mark unhealthy after 3 consecutive failures

Мы рассмотрели, что происходит на этапе запуска pod-а. Теперь перейдем к этапу завершения работы.

Этап завершения работы pod-а

Компоненты в кластере Kubernetes функционируют как микросервисы, а не монолитные процессы. Микросервисы требуют больше времени для синхронизации между всеми компонентами.


  1. Удаление Pod-а:

Когда API Server получает запрос на удаление Pod-а (из клиента или в процессе rolling deployment), он:


  • Меняет состояние Pod-а в etcd.
  • Уведомляет Endpoint Controller и Kubelet.

2. Удаление endpoint-ов из Service:


После получения уведомления об удалении Endpoint Controller отправляет запрос обратно в API Server для удаления Pod endpoint-ов из всех объектов Service, с которыми Pod был связан.


3. Обновление iptables:


Kube-proxy (на worker-нодах) отслеживает изменения в API Server и обновляет iptables для удаления маршрутизации трафика к удаляемому Pod-у.

Причина простоя и разрывов соединений

Downtime происходит из-за разницы во времени выполнения задач:


  • Kubelet отправляет SIGTERM контейнеру и завершает его быстрее.
  • В то же время Endpoint Controller требует больше времени, чтобы обновить Service и удалить endpoint-ы через Kube-proxy.

Пока iptables еще не обновлены, трафик продолжает направляться на endpoint-ы завершающегося Pod-а, который уже не обрабатывает запросы. Это приводит к ошибкам, таким как "connection error" или "connection refused".

Как решить проблему?

Kubernetes изначально не был создан как инструмент с подходом “plug and play”; он требует грамотной настройки для каждого конкретного случая. Как мы выяснили, разница во времени выполнения задач является основной проблемой. Простое решение заключается в добавлении времени ожидания, чтобы Kube-proxy успели обновить iptables.


Мы можем реализовать это с помощью preStop hook в конфигурации Deployment. Перед тем как контейнер полностью завершит работу, мы настроим ожидание в 20 секунд. Контейнер завершится только по истечении времени ожидания. К этому моменту Kube-proxy уже обновят маршруты iptables, и новые подключения будут направляться на активные Pod-ы, а не на Pod-ы, которые находятся в процессе завершения.


Примечание: preStop hook — это механизм в Kubernetes для выполнения определенной команды или действия перед завершением работы Pod-а.


Важно понимать, что после обновления iptables:

  1. Существующие соединения с завершенными Pod-ами сохраняются, обеспечивая гладкое завершение процессов.
  2. Новые соединения перенаправляются на стабильные Pod-ы.

Пример Deployment-файла с preStop hook:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-highly-available-app
spec:
  replicas: 3 # Define desired number of replicas
  selector:
    matchLabels:
      app: highly-available
  template:
    metadata:
      labels:
        app: highly-available
    spec:
      containers:
      - name: highly-available
        image: highly-available-image:latest
        ports:
        - containerPort: 80
        readinessProbe: # Readiness probe configuration
          httpGet:
            path: /health-check
            port: 80
            initialDelaySeconds: 5 # Wait 5 seconds before starting probes
            periodSeconds: 10 # Check every 10 seconds
            failureThreshold: 3 # Mark unhealthy after 3 consecutive failures
        lifecycle:
          preStop:
            exec:
              command: ["/bin/bash", "-c", "sleep 20"]

С такой конфигурацией rolling deployment больше не вызовет простоев в инфраструктуре.


Примечание: всегда убеждайтесь, что время ожидания (sleep time) меньше значения terminationGracePeriodSeconds (по умолчанию 30 секунд). Если время ожидания превысит это значение, контейнер будет завершен принудительно.

Заключение

Мы добились стабильных соединений при rolling deployment, независимо от количества релизов в день. Мы модифицировали наш Deployment-файл, добавив:


  • Readiness probe для корректного старта Pod-а.
  • PreStop hook для корректного завершения Pod-а.

Эти изменения позволяют эффективно управлять трафиком во время запуска и остановки Pod-ов, обеспечивая нулевой простой для пользователей.


Автоматизируйте с удовольствием! 🚀

Ответим на все вопросы по переносу, сборке, оркестрации и развертывании.
Настроим ваш Kubernetes