По умолчанию в Kubernetes используется стратегия rolling deployment. Она означает постепенную замену текущих pod-ов на новые. Однако в этом процессе всегда есть период простоя, который может длиться от микросекунд до секунд. Для небольших приложений с низкой нагрузкой это незначительно, но для крупных систем, особенно платежных шлюзов, каждая секунда на счету.
Итак, вопрос «Что происходит во время rolling deployment?» можно разделить на две части:
Дальнейшее обсуждение посвящено глубокому пониманию поведения pod-ов при rolling deployment и методам достижения настоящего нулевого простоя, чтобы клиентские соединения оставались стабильными даже при обновлении приложения.
Когда р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-а. Теперь перейдем к этапу завершения работы.
Компоненты в кластере Kubernetes функционируют как микросервисы, а не монолитные процессы. Микросервисы требуют больше времени для синхронизации между всеми компонентами.
Когда API Server получает запрос на удаление Pod-а (из клиента или в процессе rolling deployment), он:
2. Удаление endpoint-ов из Service:
После получения уведомления об удалении Endpoint Controller отправляет запрос обратно в API Server для удаления Pod endpoint-ов из всех объектов Service, с которыми Pod был связан.
3. Обновление iptables:
Kube-proxy (на worker-нодах) отслеживает изменения в API Server и обновляет iptables для удаления маршрутизации трафика к удаляемому Pod-у.
Downtime происходит из-за разницы во времени выполнения задач:
Пока 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:
Пример 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-файл, добавив:
Эти изменения позволяют эффективно управлять трафиком во время запуска и остановки Pod-ов, обеспечивая нулевой простой для пользователей.
Автоматизируйте с удовольствием! 🚀