- Надёжность и стабильность
Надёжность и стабильность
Развёртывание приложения в Kubernetes — это только начало. Для надёжной работы в production нужны механизмы, которые обеспечивают доступность при сбоях, контролируют потребление ресурсов и адаптируют систему к изменяющейся нагрузке. Kubernetes предоставляет инструменты для решения этих задач: пробы живучести и готовности, ограничения ресурсов, стратегии обновления и автоматическое масштабирование.
Проблема определения состояния приложения
Представьте ситуацию: Pod запущен, контейнер работает, но приложение внутри зависло. HTTP-сервер не отвечает на запросы, база данных не принимает подключения, обработчик очереди не забирает сообщения. С точки зрения Kubernetes Pod находится в состоянии Running — процесс работает, exit code не возвращён.
Kubernetes не знает о внутреннем состоянии приложения. Он видит только, что процесс запущен. Без дополнительных проверок трафик будет направляться на неработающий Pod, пользователи получат ошибки, а система будет считать, что всё в порядке.
Пробы (probes) решают эту проблему, позволяя Kubernetes проверять реальное состояние приложения и принимать решения на основе этих проверок.
Типы проб
Kubernetes поддерживает три типа проб: Liveness Probe, Readiness Probe и Startup Probe. Каждая решает свою задачу.
Liveness Probe: проверка живучести
Liveness Probe определяет, работает ли приложение. Если проверка не проходит несколько раз подряд, Kubernetes считает контейнер неработоспособным и перезапускает его.
apiVersion: v1
kind: Pod
metadata:
name: web-app
spec:
containers:
- name: app
image: nginx:1.21
ports:
- containerPort: 80
livenessProbe:
httpGet:
path: /healthz
port: 80
initialDelaySeconds: 15
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 3
Kubernetes будет отправлять HTTP GET запрос на /healthz каждые 10 секунд. Первая проверка начнётся через 15 секунд после запуска контейнера. Если три проверки подряд вернут ошибку (не 200-399 статус код), контейнер будет перезапущен.
Параметры:
initialDelaySeconds— задержка перед первой проверкой (даёт время на инициализацию)periodSeconds— интервал между проверкамиtimeoutSeconds— таймаут для одной проверкиfailureThreshold— количество неудачных проверок подряд для признания контейнера неработоспособнымsuccessThreshold— количество успешных проверок для восстановления статуса (обычно 1)
Readiness Probe: проверка готовности
Readiness Probe определяет, готов ли Pod принимать трафик. В отличие от Liveness Probe, неудачная проверка не перезапускает контейнер, а только убирает Pod из Endpoints Service.
apiVersion: v1
kind: Pod
metadata:
name: web-app
spec:
containers:
- name: app
image: myapp:1.0
ports:
- containerPort: 8080
readinessProbe:
httpGet:
path: /ready
port: 8080
initialDelaySeconds: 5
periodSeconds: 5
timeoutSeconds: 3
failureThreshold: 3
Когда Pod запускается, он не получает трафик до тех пор, пока Readiness Probe не вернёт успешный результат. Если приложение временно перегружено или зависит от внешнего сервиса, который недоступен, Readiness Probe можно вернуть ошибку — трафик перестанет поступать на этот Pod, но контейнер не будет перезапущен.
Это полезно при Rolling Update: новый Pod не получит трафик, пока не пройдёт проверку готовности. Старый Pod продолжит обрабатывать запросы до полной готовности нового.
Startup Probe: проверка запуска
Startup Probe используется для медленно запускающихся приложений. Пока Startup Probe не пройдёт успешно, Liveness и Readiness Probe не выполняются.
apiVersion: v1
kind: Pod
metadata:
name: slow-app
spec:
containers:
- name: app
image: java-app:1.0
ports:
- containerPort: 8080
startupProbe:
httpGet:
path: /startup
port: 8080
initialDelaySeconds: 0
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 30
livenessProbe:
httpGet:
path: /healthz
port: 8080
periodSeconds: 10
readinessProbe:
httpGet:
path: /ready
port: 8080
periodSeconds: 5
Startup Probe будет проверять приложение каждые 10 секунд, максимум 30 раз (5 минут). Это даёт Java-приложению время на инициализацию JVM, загрузку классов и подключение к базе данных. После успешной проверки Startup Probe больше не выполняется, а Liveness и Readiness начинают работать.
Без Startup Probe пришлось бы устанавливать большой initialDelaySeconds для Liveness Probe, что замедлит обнаружение проблем в уже работающем приложении.
Способы проверки
Kubernetes поддерживает несколько способов проверки состояния приложения.
HTTP GET
HTTP GET запрос — самый распространённый способ:
livenessProbe:
httpGet:
path: /healthz
port: 8080
httpHeaders:
- name: Custom-Header
value: Awesome
Приложение должно вернуть HTTP статус код 200-399. Любой другой код (404, 500, 503) считается ошибкой. Можно добавить custom headers для аутентификации или идентификации источника проверки.
TCP Socket
TCP Socket проверка пытается открыть TCP соединение:
livenessProbe:
tcpSocket:
port: 3306
initialDelaySeconds: 15
periodSeconds: 10
Если соединение успешно открывается, проверка считается успешной. Это полезно для приложений, которые не используют HTTP (базы данных, message brokers, gRPC-сервисы).
Exec
Exec проверка выполняет команду внутри контейнера:
livenessProbe:
exec:
command:
- cat
- /tmp/healthy
initialDelaySeconds: 5
periodSeconds: 5
Если команда возвращает exit code 0, проверка успешна. Любой другой код считается ошибкой. Можно использовать собственные скрипты проверки:
livenessProbe:
exec:
command:
- /bin/sh
- -c
- ps aux | grep myapp | grep -v grep
gRPC
gRPC проверка использует gRPC Health Checking Protocol:
livenessProbe:
grpc:
port: 50051
service: myservice
initialDelaySeconds: 10
Приложение должно реализовать gRPC Health service. Это стандартный способ проверки для gRPC-приложений.
Практический пример: веб-приложение с пробами
Создадим приложение с правильно настроенными пробами:
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-app
spec:
replicas: 3
selector:
matchLabels:
app: web-app
template:
metadata:
labels:
app: web-app
spec:
containers:
- name: app
image: example/webapp:1.0
ports:
- containerPort: 8080
env:
- name: DATABASE_HOST
value: postgres-service
startupProbe:
httpGet:
path: /startup
port: 8080
initialDelaySeconds: 0
periodSeconds: 5
failureThreshold: 12
livenessProbe:
httpGet:
path: /healthz
port: 8080
initialDelaySeconds: 0
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 3
readinessProbe:
httpGet:
path: /ready
port: 8080
initialDelaySeconds: 0
periodSeconds: 5
timeoutSeconds: 3
failureThreshold: 2
resources:
requests:
memory: "128Mi"
cpu: "100m"
limits:
memory: "256Mi"
cpu: "200m"
Приложение должно реализовать три endpoint:
/startup— возвращает 200 после полной инициализации/healthz— возвращает 200, если приложение работает (нет deadlock, panic)/ready— возвращает 200, если приложение готово обрабатывать запросы (база данных доступна, кеш прогрет)
Ограничения ресурсов
Каждый контейнер потребляет CPU и память. Без ограничений один контейнер может захватить все ресурсы узла, что приведёт к деградации или падению других приложений. Kubernetes позволяет задавать requests (запрос) и limits (ограничение) для ресурсов.
Requests: планирование ресурсов
requests указывает минимальные ресурсы, необходимые контейнеру. Kubernetes использует эту информацию для планирования: Pod размещается только на узле, где достаточно свободных ресурсов.
resources:
requests:
memory: "128Mi"
cpu: "100m"
Если на всех узлах недостаточно ресурсов, Pod останется в состоянии Pending. Это гарантирует, что приложение не будет запущено в условиях нехватки ресурсов.
CPU измеряется в миллиядрах (m): 100m = 0.1 CPU = 10% одного ядра. 1000m = 1 CPU = одно полное ядро.
Память измеряется в байтах: 128Mi = 128 мегибайт (1 Mi = 1024 * 1024 байт), 1Gi = 1 гигибайт.
Limits: ограничение ресурсов
limits устанавливает максимальное потребление ресурсов. Контейнер не может использовать больше указанного лимита.
resources:
requests:
memory: "128Mi"
cpu: "100m"
limits:
memory: "256Mi"
cpu: "200m"
Для CPU: если контейнер пытается использовать больше, чем limit, он будет throttled (ограничен). Приложение продолжит работать, но медленнее.
Для памяти: если контейнер превышает limit, он будет убит (OOMKilled — Out Of Memory Killed). Kubernetes перезапустит контейнер, но данные в памяти будут потеряны.
Quality of Service (QoS)
На основе requests и limits Kubernetes назначает каждому Pod класс QoS:
Guaranteed — самый высокий приоритет. Назначается, когда requests равны limits для всех контейнеров:
resources:
requests:
memory: "256Mi"
cpu: "200m"
limits:
memory: "256Mi"
cpu: "200m"
Burstable — средний приоритет. Назначается, когда requests меньше limits или указаны только requests:
resources:
requests:
memory: "128Mi"
cpu: "100m"
limits:
memory: "256Mi"
cpu: "200m"
BestEffort — самый низкий приоритет. Назначается, когда requests и limits не указаны:
# Нет секции resources
При нехватке ресурсов на узле Kubernetes сначала убивает BestEffort Pod, затем Burstable, и в последнюю очередь Guaranteed. Для production-приложений используйте Guaranteed или Burstable, но не BestEffort.
LimitRange и ResourceQuota
LimitRange
LimitRange задаёт ограничения по умолчанию для namespace:
apiVersion: v1
kind: LimitRange
metadata:
name: default-limits
namespace: default
spec:
limits:
- max:
memory: "1Gi"
cpu: "1000m"
min:
memory: "64Mi"
cpu: "50m"
default:
memory: "256Mi"
cpu: "200m"
defaultRequest:
memory: "128Mi"
cpu: "100m"
type: Container
Если контейнер не указывает requests/limits, будут использованы значения по умолчанию. Значения max и min ограничивают диапазон возможных значений.
ResourceQuota
ResourceQuota ограничивает общее потребление ресурсов в namespace:
apiVersion: v1
kind: ResourceQuota
metadata:
name: namespace-quota
namespace: default
spec:
hard:
requests.cpu: "10"
requests.memory: "10Gi"
limits.cpu: "20"
limits.memory: "20Gi"
persistentvolumeclaims: "10"
pods: "50"
Никто не сможет создать больше 50 Pod в namespace или запросить больше 10 CPU. Это защищает от случайного переполнения кластера и обеспечивает справедливое распределение ресурсов между командами.
Автоматическое масштабирование
Horizontal Pod Autoscaler (HPA)
HPA автоматически изменяет количество реплик в Deployment на основе метрик:
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: web-app-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: web-app
minReplicas: 2
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 80
HPA поддерживает среднюю загрузку CPU на уровне 70% и памяти на уровне 80%. Когда нагрузка превышает целевое значение, HPA добавляет реплики. Когда нагрузка падает, HPA уменьшает количество реплик.
Создать HPA можно командой:
kubectl autoscale deployment web-app --cpu-percent=70 --min=2 --max=10
HPA требует metrics-server для получения метрик:
# Установить metrics-server
kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml
# Проверить метрики
kubectl top nodes
kubectl top pods
HPA проверяет метрики каждые 15 секунд и принимает решение о масштабировании. Для предотвращения flapping (частых изменений) HPA применяет cooldown periods: после scale-up ждёт 3 минуты, после scale-down — 5 минут.
Custom Metrics
HPA поддерживает произвольные метрики через Custom Metrics API:
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: web-app-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: web-app
minReplicas: 2
maxReplicas: 10
metrics:
- type: Pods
pods:
metric:
name: http_requests_per_second
target:
type: AverageValue
averageValue: "1000"
Это требует установки адаптера метрик (Prometheus Adapter, Datadog Cluster Agent) и настройки экспорта метрик из приложения.
Vertical Pod Autoscaler (VPA)
VPA автоматически подстраивает requests и limits на основе реального потребления:
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
name: web-app-vpa
spec:
targetRef:
apiVersion: apps/v1
kind: Deployment
name: web-app
updatePolicy:
updateMode: "Auto"
resourcePolicy:
containerPolicies:
- containerName: app
minAllowed:
cpu: "100m"
memory: "128Mi"
maxAllowed:
cpu: "1000m"
memory: "1Gi"
VPA наблюдает за реальным потреблением ресурсов и рекомендует оптимальные значения. В режиме Auto VPA автоматически обновляет requests/limits и перезапускает Pod. В режиме Off VPA только даёт рекомендации, не изменяя Pod.
VPA и HPA не рекомендуется использовать одновременно на одних и тех же метриках, так как они могут конфликтовать.
PodDisruptionBudget
PodDisruptionBudget (PDB) ограничивает количество одновременно недоступных Pod при добровольных disruption (обновление узлов, drain, eviction):
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: web-app-pdb
spec:
minAvailable: 2
selector:
matchLabels:
app: web-app
Kubernetes гарантирует, что минимум 2 Pod с меткой app: web-app будут доступны. При обновлении узла система не сможет удалить Pod, если это нарушит PDB.
Альтернатива — указать максимум недоступных Pod:
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: web-app-pdb
spec:
maxUnavailable: 1
selector:
matchLabels:
app: web-app
При 5 репликах может быть недоступен максимум 1 Pod. PDB не защищает от непредвиденных сбоев (crash узла, OOM), но обеспечивает доступность при плановых операциях.
Лучшие практики
При настройке надёжности и стабильности следуйте рекомендациям для обеспечения высокой доступности. Всегда настраивайте Liveness и Readiness Probe для всех приложений. Liveness должна проверять критические компоненты, Readiness — готовность к обработке трафика.
Используйте правильные значения для проб. initialDelaySeconds должен быть достаточным для инициализации, но не слишком большим. failureThreshold и periodSeconds определяют, как быстро обнаруживается проблема.
Указывайте requests и limits для всех контейнеров. Для production используйте QoS класс Guaranteed или Burstable. Мониторьте реальное потребление и корректируйте значения.
Настройте HPA для приложений с переменной нагрузкой. Это обеспечит эффективное использование ресурсов и доступность при пиковых нагрузках.
Используйте PodDisruptionBudget для критичных приложений. Это защитит от простоев при плановом обслуживании кластера.
Тестируйте поведение при сбоях. Используйте chaos engineering инструменты (Chaos Mesh, Litmus) для проверки отказоустойчивости.
Мониторьте метрики и события. Настройте алерты на рестарты контейнеров, OOMKilled, превышение лимитов ресурсов.
Заключение
Надёжность и стабильность приложений в Kubernetes обеспечивается комплексом механизмов. Пробы живучести и готовности позволяют Kubernetes правильно оценивать состояние приложения и принимать решения о перезапуске или исключении из балансировки.
Ограничения ресурсов защищают приложения друг от друга и обеспечивают предсказуемую производительность. Автоматическое масштабирование адаптирует систему к изменяющейся нагрузке без ручного вмешательства.
PodDisruptionBudget гарантирует доступность при плановых операциях. Вместе эти инструменты создают надёжную платформу для запуска production-приложений.
Для полного доступа к курсу нужен базовый план
Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.