Liveness probes в Kubernetes могат да бъдат опасни.

Прим. прев.: Главният инженер на компанията Zalando – Хенинг Якобс – неведнъж е забелязвал, че потребителите на Kubernetes имат проблеми с разбирането на предназначението на liveness (и readiness) probes и тяхното правилно прилагане. Затова той събра мислите си в тази съкратена бележка, която с времето ще стане част от документацията на K8s.

Liveness probes в Kubernetes могат да бъдат опасни.

Проверки на състоянието, известни в Kubernetes като liveness probes (т.е. буквално, „тестове за жизнеспособност“ – бел. прев.), могат да бъдат доста опасни. Препоръчвам, доколкото е възможно, да се избягват: изключенията са само случаи, когато те наистина са необходими и вие напълно разбирате спецификата и последствията от тяхното използване. В тази публикация ще стане дума за liveness и readiness проверки, а също така ще бъде обяснено в кои случаи е целесъобразно и не е целесъобразно да се прилагат.

Моят колега Сандор наскоро сподели в 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).

Проби за живост помагат на Kubernetes да разбере кога е време да рестартира контейнера. Например, такава проверка позволява да се предотврати задъненица, когато приложението се 'задържа' на едно място. Рестартиране на контейнера в такова състояние помага да се премести приложението от мъртвата точка, независимо от грешките, но също така може да доведе до каскадни провали (вижте по-долу).

Ако опитате да развернете ъпдейт на приложението, което не минава проверките за живост/готовност, неговото внедряване ще спре, тъй като Kubernetes ще изчака статус Готов от всички pod'ове.

Пример

Ето пример за проба за готовност, която проверява пътя /health чрез HTTP с настройки по подразбиране (интервал: 10 секунди, timeout: 1 секунда, праг на успех: 1, праг на неуспех: 3):

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

Препоръки

  1. За микросервизи с HTTP крайна точка (REST и т.н.) винаги определяйте проба за готовност, която проверява дали приложението (pod) е готово да приема трафик.
  2. Уверете се, че пробата за готовност покрива готовността на действителния порт на уеб сървъра:
    • използвайки портове за административни нужди, наречени 'admin' или 'management' (например, 9090), за readinessProbe, уверете се, че крайният точка връща ОК само ако основният HTTP порт (като 8080) е готов да приема трафик*;

      * Знам поне за един случай в Zalando, когато това не се случи, а именно readinessProbe провери порта 'management', но самия сървър не започна да работи поради проблеми с зареждането на кеша.

    • Прикрепянето на проба за готовност на отделен порт може да доведе до това, че натоварването на основния порт да не се отразява в проверката за здравословно състояние (т.е. пуловете на потоците на сървъра са запълнени, но проверката все още показва, че всичко е ОК).
  3. Уверете се, че пробата за готовност включва инициализация/миграция на базата данни;
    • най-простият начин да постигнете това е да се свързвате с HTTP сървъра само след завършване на инициализацията (например, миграция на БД с Flyway и т.н.); тоест вместо да променяте статуса на проверката за здравословно състояние, просто не стартирайте уеб сървъра до завършването на миграцията на БД*.

      * Можете също така да стартирате миграции на базата данни от init-контейнери извън pod-а. Още съм почитател на самостоятелни приложения, т.е. такива, в които контейнерът на приложението знае без външна координация как да настрои базата данни в необходимото състояние.

  4. Използвайте httpGet за проверки на readiness чрез типични endpoint-и на health check-ове (например, /health).
  5. Разберете параметрите на проверките, зададени по подразбиране (интервал: 10s, таймаут: 1s, успехов праг: 1, праг на неуспех: 3):
    • параметрите по подразбиране означават, че pod-ът ще стане not-ready приблизително след 30 секунди (3 неуспешни проверки на работоспособност).
  6. Използвайте отделен порт за "админ" или "управление", ако технологичният стек (например, 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, за да определите кога pod е готов да приема трафик.
  • Използвайте 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, ако проверката му се провали, но не рестартирайте всички, за да не направим още по-лошо".

Брайън отлично формулира: «Използвайте liveness проби, когато сте сигурни, че най-доброто, което може да направите, е да «убиете» приложението» (отново, не прекалявайте с това).

Liveness probes в Kubernetes могат да бъдат опасни.

Актуализация №2 от 2019-09-29

По отношение на четенето на документацията преди употреба: създадох съответно искане (искания за функции) за допълване на документацията за liveness probes.

P.S. от преводача

Прочетете също в нашия блог:

Източник: habr.com

Купете надежден хостинг за сайтове с защита от DDoS, VPS VDS сървъри 🔥 Купете надежден хостинг за сайтове с защита от DDoS, VPS VDS сървъри | ProHoster