🐳 Раздел 14 · Вопрос #17

Что такое liveness probe

Если процесс внутри контейнера завис (например, возник Deadlock в Java-потоках или бесконечный цикл), операционная система считает процесс живым, но реальные запросы он больше н...


🟢 Junior Level

30-секундный ответ

Liveness Probe (Проба живучести) — это диагностический механизм Kubernetes, с помощью которого агент kubelet периодически проверяет, работает ли приложение внутри контейнера корректно или оно безнадёжно зависло.

Если процесс внутри контейнера завис (например, возник Deadlock в Java-потоках или бесконечный цикл), операционная система считает процесс живым, но реальные запросы он больше не обрабатывает:

  • Kubelet отправляет проверочный запрос (HTTP, TCP или команду в терминале).
  • Если приложение не отвечает или возвращает ошибку несколько раз подряд (failureThreshold), Kubernetes считает контейнер мёртвым и принудительно перезапускает его (Self-Healing).

Наглядная аналогия из жизни

Liveness Probe — это проверка пульса пациента в реанимации:

  • Врач (kubelet) каждые 10 секунд проверяет пульс.
  • Если человек дышит и пульс есть — всё в порядке, работаем дальше.
  • Если пульс пропал на 3 проверки подряд — врач немедленно применяет дефибриллятор (перезапуск контейнера), чтобы вернуть приложение к жизни.

Базовый манифест для Spring Boot 3 на Java 21

apiVersion: v1
kind: Pod
metadata:
  name: payment-app
spec:
  containers:
  - name: payment-service
    image: payment-service:1.0
    livenessProbe:
      httpGet:
        path: /actuator/health/liveness # Эндпоинт Spring Boot Actuator
        port: 8080
      initialDelaySeconds: 30 # Пауза перед первой проверкой (время на старт)
      periodSeconds: 10       # Интервал проверок (каждые 10 секунд)
      timeoutSeconds: 3       # Ожидание ответа не более 3 секунд
      failureThreshold: 3     # Перезапуск после 3 неудач подряд

[!IMPORTANT] Главная цель Liveness: поймать ситуацию, когда приложение зашло в тупик и не может восстановиться самостоятельно без полного перезапуска процесса.


🟡 Middle Level

4 механизма выполнения проб

Механизм Как работает Где применяется
httpGet Kubelet шлёт HTTP GET-запрос. Коды 200–399 считаются успехом. Web-приложения, REST API (Spring Boot Actuator).
tcpSocket Проверяет возможность открыть TCP-сокет на указанном порту. СУБД, кэши (Redis, PostgreSQL), брокеры сообщений.
grpc Вызывает стандартный gRPC Health Checking Protocol. Микросервисы на gRPC (Kubernetes 1.24+ GA).
exec Выполняет команду внутри контейнера (exit 0 — успех). Legacy-приложения, проверка создания сигнальных lock-файлов.

Главное архитектурное правило: Liveness vs Внешние зависимости

[!CAUTION] НИКОГДА НЕ ПРОВЕРЯЙТЕ ВНЕШНИЕ ЗАВИСИМОСТИ (БД, KAFKA, ДРУГИЕ СЕРВИСЫ) В LIVENESS PROBE!

Что произойдёт, если включить проверку PostgreSQL в Liveness:

  1. База данных PostgreSQL испытывает кратковременную перегрузку и перестаёт отвечать на 15 секунд.
  2. Liveness-проба на всех 50 ваших подах Java-бэкенда падает с ошибкой.
  3. Kubelet одновременно убивает и перезапускает все 50 подов приложения!
  4. При одновременном старте 50 подов набрасываются на восстанавливающуюся БД с тысячами новых соединений пула HikariCP, окончательно добивая сервер базы данных (Каскадная спираль смерти — Cascading Death Spiral).
    • Правило: Liveness Probe проверяет ТОЛЬКО внутреннее состояние самого контейнера (память, потоки, внутренний цикл). Проверка внешних связей делегируется исключительно пробе готовности (Readiness Probe).

Поддержка в Spring Boot Actuator

Начиная со Spring Boot 2.3+ и в Spring Boot 3+, фреймворк автоматически адаптируется к Kubernetes и разделяет эндпоинты:

  • http://localhost:8080/actuator/health/liveness — возвращает статус внутреннего состояния (класс LivenessState). Включает только локальные проверки.
  • http://localhost:8080/actuator/health/readiness — проверяет готовность пулов соединений к БД и доступность внешних ресурсов.

Включение в application.yaml:

management:
  health:
    livenessstate:
      enabled: true
    readinessstate:
      enabled: true
  endpoint:
    health:
      probes:
        enabled: true

🔴 Senior Level

Тандем startupProbe и livenessProbe

Историческая проблема Java в Kubernetes — долгий холодный старт (Spring Boot может инициализировать контекст и миграции Flyway от 40 до 90 секунд):

  • Если поставить initialDelaySeconds: 10, Liveness Probe убьёт здоровый под прямо во время загрузки контекста Spring.
  • Если поставить initialDelaySeconds: 90, то при реальном зависании приложения в продакшене Kubelet будет ждать целых 90 секунд перед первой реакцией.

Решение — startupProbe:

startupProbe:
  httpGet:
    path: /actuator/health/liveness
    port: 8080
  periodSeconds: 5
  failureThreshold: 30 # Даёт приложению: 30 * 5с = 150 секунд на спокойный старт!

livenessProbe:
  httpGet:
    path: /actuator/health/liveness
    port: 8080
  periodSeconds: 10
  failureThreshold: 3 # Быстрая реакция на зависание: 3 * 10с = 30 секунд

Пока startupProbe не завершится успехом, проверки livenessProbe и readinessProbe даже не активируются!


HotSpot Internals: Ложные срабатывания при STW GC Паузах

Если сборщик мусора JVM (например, Parallel GC или G1 при фрагментированной памяти) уходит в тяжелую фазу Full GC Stop-The-World (STW):

  1. Все потоки приложения (включая поток HTTP-сервера Tomcat/Netty, отвечающий на Actuator) полностью замораживаются ядром JVM.
  2. Сетевой проверочный пакет от Kubelet поступает в сокет хоста, но Tomcat не может сформировать HTTP-ответ.
  3. Если таймаут пробы timeoutSeconds: 1 слишком агрессивен, Kubelet фиксирует сбой. Три такие паузы подряд приведут к убийству абсолютно здорового Java-приложения, которое всего лишь выполняло очистку памяти! Рекомендация: значение timeoutSeconds для Java-микросервисов должно составлять не менее 3–5 секунд, а порог failureThreshold — не менее 3.

Программное объявление аварии в Spring Boot 3

Разработчик может программно сообщить Kubernetes о необходимости перезапуска контейнера (например, при обнаружении неисправимой ошибки в фоновом демоне):

@Service
public class CriticalWorkerService {

    private final ApplicationEventPublisher eventPublisher;

    public CriticalWorkerService(ApplicationEventPublisher eventPublisher) {
        this.eventPublisher = eventPublisher;
    }

    public void processCriticalQueue() {
        try {
            // обработка критической очереди
        } catch (FatalCorruptionException ex) {
            // Переводим LivenessState в BROKEN -> Actuator начинает отдавать HTTP 503
            // Kubelet фиксирует ошибку и принудительно перезапускает контейнер
            AvailabilityChangeEvent.publish(eventPublisher, this, LivenessState.BROKEN);
        }
    }
}

4 Tricky Questions

1. Что произойдёт, если контейнер провалит проверку Liveness Probe, но его спецификация содержит директиву restartPolicy: Never?

Ответ: Kubelet НЕ станет перезапускать контейнер. В соответствии с логикой Kubernetes, если restartPolicy: Never или restartPolicy: OnFailure, а процесс завершился с кодом 0:

  • Kubelet принудительно остановит зависший контейнер сигналом SIGTERM, а затем SIGKILL.
  • Однако пересоздавать его заново Kubelet не будет.
  • Контейнер перейдёт в состояние Terminated, а весь Pod перейдёт в терминальную фазу Failed и останется в ней до тех пор, пока контроллер (Deployment) или инженер не удалит его.

2. Как детектировать взаимную блокировку потоков (Deadlock) в многопоточном Java-приложении с помощью Liveness Probe?

Ответ: Создаётся кастомный индикатор здоровья Spring Actuator с использованием системного MXBean виртуальной машины HotSpot:

@Component
public class DeadlockHealthIndicator implements HealthIndicator {
    private final ThreadMXBean threadBean = ManagementFactory.getThreadMXBean();

    @Override
    public Health health() {
        long[] deadlocked = threadBean.findDeadlockedThreads();
        if (deadlocked != null && deadlocked.length > 0) {
            return Health.down()
                         .withDetail("deadlockedThreadsCount", deadlocked.length)
                         .build();
        }
        return Health.up().build();
    }
}

При возникновении взаимоблокировки эндпоинт Actuator немедленно вернёт HTTP 503, и Kubelet перезапустит JVM, сбросив заблокированное состояние потоков.


3. В чём фундаментальная разница в реакции Kubernetes на сбой livenessProbe по сравнению со сбоем readinessProbe?

Ответ:

  • При провале livenessProbe: Kubernetes выполняет жесткое действие — убивает и перезапускает контейнер (Restart Container). IP-адрес пода не меняется, но все активные транзакции обрываются, а память сбрасывается.
  • При провале readinessProbe: Kubernetes выполняет мягкое действие — НЕ перезапускает контейнер, а лишь временно удаляет IP-адрес пода из списка Endpoints сервиса. Под продолжает жить и обрабатывать текущие фоновые задачи, но новый входящий трафик от пользователей на него перестаёт поступать до тех пор, пока проба готовности снова не вернёт успех.

4. Почему выполнение Liveness Probe через механизм exec (например, command: ["curl", "localhost:8080/health"]) в высоконагруженных системах считается плохой практикой?

Ответ: При использовании exec агент Kubelet на каждый опрос (каждые 5–10 секунд) через рантайм containerd порождает новый дочерний процесс операционной системы Linux внутри пространства имён пода (fork + exec).

  • Это создает существенный оверхед по CPU на создание структур ядра, переключение контекста и выделение дескрипторов.
  • Кроме того, в образах distroless или минималистичных образах на базе Alpine часто отсутствуют curl, bash или sh из соображений безопасности.
  • Использование нативного httpGet выполняется Kubelet’ом через открытие прямого сокета снаружи контейнера без порождения лишних процессов внутри пода.

🎯 Шпаргалка для интервью

Главное о Liveness Probe

  • Назначение: проверка жизнеспособности процесса; при провале — перезапуск контейнера.
  • Критический антипаттерн: проверка внешних БД/очередей (приведёт к лавинообразному отказу всего кластера).
  • Startup Probe: даёт долгим Java-сервисам время на инициализацию без опасности быть убитыми Liveness-пробой.
  • Spring Boot 3 Actuator: стандартный эндпоинт /actuator/health/liveness.
  • GC STW Pauses: таймаут timeoutSeconds должен быть больше максимальной паузы Full GC (3–5 сек).
  • Liveness vs Readiness: Liveness убивает процесс; Readiness временно выводит под из балансировки сервиса.

Сравнение трёх типов проб

| Параметр | startupProbe | livenessProbe | readinessProbe | | :— | :— | :— | :— | | Когда выполняется? | Только при старте | После успеха startup | После успеха startup, постоянно | | Действие при сбое | Убийство контейнера | Перезапуск контейнера | Отключение сетевого трафика | | Основная цель | Дождаться загрузки JVM | Снять Deadlock / зависание | Дождаться прогрева / доступности БД |

Красные флаги на собеседовании (Чего говорить нельзя)

  • ❌ «В Liveness Probe мы проверяем доступность PostgreSQL и Redis» — фатальная ошибка, гарантирующая Cascading Failure при сбое БД.
  • ❌ «Для Java-приложений достаточно initialDelaySeconds: 5» — Spring Boot не успеет стартовать и попадёт в бесконечный CrashLoop.
  • ❌ «Readiness и Liveness — это одно и то же» — они преследуют диаметрально противоположные цели и действия при сбое.

Связанные вопросы