🐳 Розділ 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 — це одне й те саме» — вони мають діаметрально протилежні цілі та дії при збої.

Пов’язані питання