Liveness probes у Kubernetes можуть бути небезпечними

Прим. перев.: Провідний інженер з компанії Zalando - Henning Jacobs - не раз помічав у користувачів Kubernetes проблеми у розумінні призначення liveness (і readiness) probes та їх коректного застосування. Тому він зібрав свої думки в цю ємну нотатку, яка згодом стане частиною документації K8s.

Liveness probes у Kubernetes можуть бути небезпечними

Перевірки стану, відомі в Kubernetes як liveness probes (тобто, дослівно, «тести на життєздатність» - прим. перекл.)можуть бути дуже небезпечними. Рекомендую по можливості уникати їх: винятками є лише випадки, коли вони дійсно необхідні, і ви повністю усвідомлюєте специфіку та наслідки їх використання. У цій публікації мова піде про liveness- та readiness-перевірки, а також буде розказано, в яких випадках коштує і не варто їх застосовувати.

Мій колега Sandor нещодавно поділився у Twitter'і найчастішими помилками, які йому зустрічаються, у тому числі пов'язаними з використанням readiness/liveness probes:

Liveness probes у Kubernetes можуть бути небезпечними

Неправильно налаштована livenessProbe може посилити ситуації з високим навантаженням (лавиноподібне відключення + потенційно довгий запуск контейнера/додатку) та призвести до інших негативних наслідків на кшталт падіння залежностей (Див. також мою недавню статтю про обмеження кількості запитів у зв'язці K3s+ACME). Ще гірше, коли liveness probe поєднується з перевіркою здоров'я залежності (health check'ом), у ролі якої виступає зовнішня база даних: єдиний збій БД перезапустить усі ваші контейнери!

Загальне посилання "Не використовуйте liveness probes" в даному випадку допомагає мало, тому розглянемо, для чого призначені readiness-і liveness-перевірки.

Примітка: більша частина наведеного нижче тесту спочатку була включена у внутрішню документацію для розробників Zalando.

Перевірки Readiness та Liveness

Kubernetes надає два важливі механізми, які називаються liveness probes та readiness probes. Вони періодично виконують певну дію, наприклад, посилають HTTP-запит, відкривають TCP-з'єднання або виконують команду в контейнері, щоб підтвердити, що програма працює належним чином.

Kubernetes використовує readiness probesщоб зрозуміти, коли контейнер готовий приймати трафік. Pod вважається готовим до роботи, якщо всі його контейнери готові. Одне із застосувань цього механізму полягає в тому, щоб контролювати, які pod'и використовуються як бекенди для сервісів Kubernetes (і особливо Ingress'а).

Liveness probes допомагають Kubernetes зрозуміти, коли настав час перезапустити контейнер. Наприклад, подібна перевірка дозволяє перехопити deadlock, коли програма «застряє» на одному місці. Перезапуск контейнера в такому стані допомагає зрушити програму з мертвої точки, незважаючи на помилки, при цьому він може призвести до каскадних збоїв (див. нижче).

Якщо ви спробуєте розгорнути оновлення програми, яка провалює перевірки liveness/readiness, його викочування зупиниться, оскільки Kubernetes чекатиме на статус Ready від усіх pod'ів.

Приклад

Ось приклад readiness probe, що перевіряє шлях /health через HTTP з налаштуваннями за замовчуванням (інтервал: 10 секунд, Тайм-аут: 1 секунда, success threshold: 1, failure threshold: 3):

# часть общего описания deployment'а/стека
podTemplate:
  spec:
    containers:
    - name: my-container
      # ...
      readinessProbe:
        httpGet:
          path: /health
          port: 8080

Рекомендації

  1. Для мікросервісів з HTTP endpoint'ом (REST тощо) завжди визначайте readiness probe, яка перевіряє, чи готова програма (pod) приймати трафік.
  2. Переконайтеся, що readiness probe покриває готовність фактичного порту веб-сервера:
    • використовуючи порти для адміністративних потреб, званих «admin» або «management» (наприклад, 9090), для readinessProbe, переконайтеся, що endpoint повертає ОК тільки в тому випадку, якщо основний HTTP-порт (на зразок 8080) готовий приймати трафік*;

      * Мені відомо принаймні про один випадок у Zalando, коли цього не сталося, тобто readinessProbe перевірила порт «management», але сам сервер так і не почав працювати через проблеми із завантаженням кешу.

    • навішування readiness probe на окремий порт може призвести до того, що навантаження на основному порті не буде відображатися в health check'е (тобто пул потоків на сервері заповнений, проте health check як і раніше показує, що все ОК).
  3. Переконайтесь, що readiness probe включає ініціалізацію/міграцію бази даних;
    • найпростіший спосіб досягти цього - звертатися до HTTP-сервера тільки після закінчення ініціалізації (наприклад, міграції БД з Пролітний шлях і т.п.); тобто замість того, щоб змінювати статус health check'у, просто не запускайте веб-сервер до завершення міграції БД*.

      * Також можна запускати міграції БД із init-контейнерів зовні pod'а. Я, як і раніше, є шанувальником самостійних (self-contained) додатків, тобто таких, у яких контейнер програми без зовнішньої координації знає, як привести БД у потрібний стан.

  4. Використовуйте httpGet для readiness-перевірок через типові endpoint'и health check'ів (наприклад, /health).
  5. Розберіться у параметрах перевірок, заданих за умовчанням (interval: 10s, timeout: 1s, successThreshold: 1, failureThreshold: 3):
    • параметри за замовчуванням означають, що під стане not-ready через 30 секунд (3 невдалих перевірок працездатності).
  6. Використовуйте окремий порт для «admin» або «management», якщо технологічний стек (наприклад, Java/Spring) дозволяє це відокремити управління «здоров'ям» і метриками від звичайного трафіку:
    • але не забувайте пункт 2.
  7. При необхідності readiness probe можна використовувати для розігріву/завантаження кешу і повертати код стану 503, поки контейнер не розігріється:

застереження

  1. Не покладайтеся на зовнішні залежності (такі як сховища даних) під час проведення тестів на readiness/liveness це може призвести до каскадних збоїв:
    • як приклад візьмемо stateful-сервіс REST з 10-ю pod'ами, що залежать від однієї бази даних Postgres: коли перевірка залежить від працюючого підключення до БД, всі 10 pod'ів можуть впасти, якщо виникне затримка в мережі/на стороні БД зазвичай усе це закінчується гірше, ніж міг би;
    • зверніть увагу, що Spring Data за промовчанням перевіряє з'єднання з БД*;

      * Така поведінка за умовчанням Spring Data Redis (принаймні, вона була такою, коли я перевіряв минулого разу), що призвело до «катастрофічного» збою: коли на короткий час Redis виявився недоступним, усі pod'и «впали».

    • «зовнішній» у цьому сенсі також може означати інші pod'и того ж додатка, тобто в ідеалі перевірка не повинна залежати від стану інших pod'ів того ж кластера для запобігання каскадним падінням:
      • результати можуть змінюватись для додатків з розподіленим станом (наприклад, in-memory-кешування в pod'ах).
  2. Не використовуйте liveness probe для pod'ів (винятками є випадки, коли вони дійсно необхідні і ви повністю усвідомлюєте специфіку та наслідки їх застосування):
    • liveness probe може сприяти відновленню «завислих» контейнерів, але, оскільки ви маєте повний контроль над своїм додатком, таких речей, як «завислі» процеси та deadlock'и, в ідеалі не повинно траплятися: найкращою альтернативою є навмисне падіння програми та її повернення до попереднього сталого стану;
    • невдала liveness probe призведе до перезапуску контейнера, тим самим потенційно посилюючи наслідки помилок, пов'язаних із завантаженням: перезапуск контейнера призведе до простою (принаймні на час запуску програми, скажімо, на 30 з лишком секунд), викликаючи нові помилки, збільшуючи навантаження на інші контейнери та підвищуючи ймовірність їх збою, тощо;
    • liveness-перевірки у поєднанні із зовнішньою залежністю — найгірша з можливих комбінацій, що загрожує каскадними відмовами: незначна затримка на боці БД призведе до перезапуску всіх ваших контейнерів!
  3. Параметри liveness- та readiness-перевірок повинні бути різними:
    • можна використовувати liveness probe з тим же health check'ом, але вищим порогом спрацьовування (failureThreshold), наприклад, надавати статус not-ready після 3 спроб і вважати, що liveness probe провалився після 10 спроб;
  4. Не використовуйте exec-перевіркиоскільки з ними пов'язані відомі проблеми, що призводять до появи зомбі-процесів:

Резюме

  • Використовуйте readiness probes, щоб визначити, коли під готовий приймати трафік.
  • Використовуйте liveness probes тільки тоді, коли вони дійсно потрібні.
  • Неправильне використання readiness/liveness probes може призвести до зниження доступності та каскадних збоїв.

Liveness probes у Kubernetes можуть бути небезпечними

Додаткові матеріали по темі

Оновлення №1 від 2019-09-29

Про init-контейнери для міграції БД: додано виноску.

EJ нагадав мені про PDB: одна з бід liveness-перевірок - відсутність координації між pod'ами. У Kubernetes є Pod Disruption Budgets (PDB) для обмеження числа паралельних збоїв, яке може випробовувати програму, проте перевірки не враховують PDB. В ідеалі ми можемо наказати K8s: "Перезапусти один pod, якщо його перевірка виявиться невдалою, але не перезапускай їх все, щоб не зробити ще гірше".

Bryan чудово сформулював: «Використовуйте liveness-зондування, коли точно знаєте, що найкраще, що можна зробити, - це «вбити» додаток»(Знову ж, захоплюватися не варто).

Liveness probes у Kubernetes можуть бути небезпечними

Оновлення №2 від 2019-09-29

Щодо читання документації перед використанням: я створив відповідний запит (запит функції) на додаток документації про liveness probes.

PS від перекладача

Читайте також у нашому блозі:

Джерело: habr.com

Додати коментар або відгук