Зарегистрируйтесь, чтобы продолжить обучение

Надёжность и стабильность Kubernetes

Надёжность и стабильность

Развёртывание приложения в 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-приложений.

Для полного доступа к курсу нужен базовый план

Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.

Получить доступ
1000
упражнений
2000+
часов теории
3200
тестов

Открыть доступ

Курсы программирования для новичков и опытных разработчиков. Начните обучение бесплатно

  • 130 курсов, 2000+ часов теории
  • 1000 практических заданий в браузере
  • 360 000 студентов
Отправляя форму, вы принимаете «Соглашение об обработке персональных данных» и условия «Оферты», а также соглашаетесь с «Условиями использования»

Наши выпускники работают в компаниях:

Логотип компании Альфа Банк
Логотип компании Aviasales
Логотип компании Yandex
Логотип компании Tinkoff