Найкращі практики для контейнерів Kubernetes: перевірки працездатності

Найкращі практики для контейнерів Kubernetes: перевірки працездатності

TL, д-р

  • Щоб досягти високої спостерігальності контейнерів та мікросервісів, журналів та первинних метрик мало.
  • Для більш швидкого відновлення та підвищення стійкості до відмов додатки повинні застосовувати Принцип високої спостерігальності (HOP, High Observability Principle).
  • На рівні додаток для НОР потрібно: належне журналування, ретельний моніторинг, перевірки працездатності та трасування продуктивності/переходів.
  • Як елемент НОР використовуйте перевірки readinessProbe и livenessProbe Kubernetes.

Що таке шаблон перевірки працездатності?

Коли проектуєш критично важливий і високодоступний додаток, дуже важливо подумати про такий аспект, як стійкість до відмов. Додаток вважається стійким до відмови, якщо він швидко відновлюється після відмови. Типова хмарна програма використовує архітектуру мікросервісів – коли кожен компонент міститься в окремому контейнері. А для того, щоб переконатися, що додаток на k8s високодоступний, коли проектуєш кластер, треба дотримуватися певних шаблонів. Серед них – шаблон перевірки працездатності. Він визначає, як програма повідомляє k8s про свою працездатність. Це не тільки інформація про те, чи працює pod, а ще й про те, як він приймає запити та відповідає на них. Чим більше Kubernetes знає про працездатність pod'а, тим розумніші рішення приймає про маршрутизацію трафіку та балансування навантаження. Таким чином, принцип високої спостерігальності додатку своєчасно відповідатиме на запити.

Принцип високої спостерігальності (НОР)

Принцип високої спостерігальності - це один з принципів проектування контейнеризованих додатків. У мікросеврисної архітектурі сервісам байдуже, як їх запит обробляється (і це правильно), але важливо, як отримати відповіді приймаючих сервісів. Наприклад, для аутентифікації користувача один контейнер посилає іншому запит HTTP, очікуючи відповіді у певному форматі – і все. Обробляти запит може і PythonJS, а відповісти – Python Flask. Контйнери один для одного – що чорні ящики із прихованим вмістом. Однак принцип НОР вимагає, щоб кожен сервіс розкривав кілька кінцевих точок API, що показують, наскільки він працездатний, а також стан його готовності та стійкості до відмов. Ці показники і запитує Kubernetes, щоб продумувати наступні кроки щодо маршрутизації та балансування навантаження.

Грамотно спроектований хмарний додаток журналює свої основні події, використовуючи стандартні потоки введення-виводу STDERR та STDOUT. Далі працює допоміжний сервіс, наприклад filebeat, logstash або fluentd, що доставляють журнали в централізовану систему моніторингу (наприклад Prometheus) і систему збору журналів (набір ELK). На схемі нижче показано, як хмарний додаток працює відповідно до Шаблону перевірки працездатності та Принципу високої спостерігальності.

Найкращі практики для контейнерів Kubernetes: перевірки працездатності

Як застосувати шаблон перевірки працездатності в Kubernetes?

З коробки k8s моніторить стан pod'ів за допомогою одного з контролерів (Розгортання, ReplicaSets, DaemonSets, StatefulSets та ін., ін.). Виявивши, що pod з якоїсь причини впав, контролер намагається відремонтувати його або перешедлити на інший вузол. Однак pod може повідомити, що він запущений та працює, а сам при цьому не функціонує. Наведемо приклад: ваш додаток використовує як веб-сервер Apache, ви встановили компонент на кілька pod'ів кластера. Оскільки бібліотека була налаштована некоректно, всі запити до програми відповідають кодом 500 (внутрішня помилка сервера). Під час перевірки постачання перевірка стану pod`ів дає успішний результат, проте клієнти вважають інакше. Цю небажану ситуацію ми опишемо так:

Найкращі практики для контейнерів Kubernetes: перевірки працездатності

У нашому прикладі k8s виконує перевірку працездатності. У цьому виді перевірки кубелет постійно перевіряє стан процесу в контейнері. Варто йому зрозуміти, що процес устав, і він його рестартить. Якщо помилка усувається простим перезапуском програми, а програма спроектована так, щоб відключатися за будь-якої помилки, тоді вам для проходження НОР і Шаблону перевірки працездатності достатньо перевірки працездатності процесу. Жаль тільки, що не всі помилки усуваються перезапуском. На цей випадок k8s пропонує 2 глибші способи виявлення несправностей у роботі pod'а: livenessProbe и readinessProbe.

LivenessProbe

Під час livenessProbe kubelet виконує 3 типи перевірок: не тільки з'ясовує, чи працює pod, але і чи готовий він отримувати та адекватно відповідати на запити:

  • Встановити HTTP-запит до pod'у. Відповідь має містити HTTP-код відповіді в діапазоні від 200 до 399. Таким чином, коди 5хх та 4хх сигналізують про те, що у pod'а проблеми, нехай навіть процес працює.
  • Для перевірки pod'ів з не-HTTP сервісами (наприклад, поштовий сервер Postfix) треба встановити TCP-зв'язок.
  • Виконання довільної команди для pod'а (внутрішньо). Перевірка вважається успішною, якщо код завершення команди – 0.

Приклад того як це працює. Визначення наступного pod'а містить NodeJS додаток, який на HTTP-запити видає помилку 500. Щоб переконатися, що контейнер перезапускається, отримавши таку помилку, ми використовуємо параметр livenessProbe:

apiVersion: v1
kind: Pod
metadata:
 name: node500
spec:
 containers:
   - image: magalix/node500
     name: node500
     ports:
       - containerPort: 3000
         protocol: TCP
     livenessProbe:
       httpGet:
         path: /
         port: 3000
       initialDelaySeconds: 5

Це нічим не відрізняється від будь-якого іншого визначення pod'а, але ми додаємо об'єкт .spec.containers.livenessProbe. Параметр httpGet приймає шлях, яким відправляє HTTP GET запит (у нашому прикладі це /, але в бойових сценаріях може бути щось на зразок /api/v1/status). Ще livenessProbe приймає параметр initialDelaySeconds, який наказує операції перевірки чекати на задану кількість секунд. Затримка потрібна, тому що контейнеру потрібен час для запуску, а при перезапуску він деякий час буде недоступним.

Щоб застосувати це налаштування до кластера, використовуйте:

kubectl apply -f pod.yaml

Через кілька секунд можна перевірити вміст pod'а за допомогою наступної команди:

kubectl describe pods node500

Наприкінці висновку знайдіть ось що.

Як бачите, livenessProbe ініціювала HTTP GET запит, контейнер видав помилку 500 (на що і був запрограмований), kubelet його перезапустив.

Якщо вам цікаво, як було запрограмовано NideJS додаток, ось файл app.js та Dockerfile, які були використані:

app.js

var http = require('http');

var server = http.createServer(function(req, res) {
    res.writeHead(500, { "Content-type": "text/plain" });
    res.end("We have run into an errorn");
});

server.listen(3000, function() {
    console.log('Server is running at 3000')
})

Докер-файл

FROM node
COPY app.js /
EXPOSE 3000
ENTRYPOINT [ "node","/app.js" ]

Важливо звернути увагу ось на що: LivenessProbe перезапустить контейнер тільки при відмові. Якщо перезапуск не виправить помилку, що заважає роботі контейнера, kubelet не зможе вжити заходів для усунення несправності.

readinessProbe

readinessProbe працює аналогічно livenessProbes (GET-запити, ТСР зв'язку та виконання команд), за винятком дій щодо усунення несправностей. Контейнер, у якому зафіксовано збій, не перезапускається, а ізолюється від вхідного трафіку. Уявіть, один із контейнерів виконує багато обчислень або зазнає важкого навантаження, через що зростає час відповіді на запити. У разі життяпроба спрацьовує перевірка доступності відповіді (через параметр перевірки timeoutSeconds), після чого кубелет перезапускає контейнер. При запуску контейнер починає виконувати ресурсомісткі завдання і його знову перезапускають. Це може бути критичним для додатків, яким важлива швидкість відповіді. Наприклад, машина прямо в дорозі чекає на відповідь від сервера, відповідь затримується – і машина потрапляє в аварію.

Давайте напишемо визначення redinessProbe, яке встановить час відповіді на GET-запит не більше двох секунд, а програма буде відповідати на GET-запит через 5 секунд. Файл pod.yaml повинен виглядати так:

apiVersion: v1
kind: Pod
metadata:
 name: nodedelayed
spec:
 containers:
   - image: afakharany/node_delayed
     name: nodedelayed
     ports:
       - containerPort: 3000
         protocol: TCP
     readinessProbe:
       httpGet:
         path: /
         port: 3000
       timeoutSeconds: 2

Розгорнемо pod з kubectl:

kubectl apply -f pod.yaml

Чекаємо пару секунд, а потім глянемо, як спрацювала readinessProbe:

kubectl describe pods nodedelayed

Наприкінці висновку можна побачити, що частина подій аналогічна ось цьому.

Як бачите, kubectl не став перезапускати pod, коли час перевірки перевищив 2 секунди. Натомість він скасував запит. Вхідні зв'язки перенаправляються на інші, робочі під'ї.

Зверніть увагу: тепер, коли з pod'а знято зайве навантаження, kubectl знову надсилає запити йому: відповіді на GET-запит більше не затримуються.

Для порівняння: нижче наведено змінений файл app.js:

var http = require('http');

var server = http.createServer(function(req, res) {
   const sleep = (milliseconds) => {
       return new Promise(resolve => setTimeout(resolve, milliseconds))
   }
   sleep(5000).then(() => {
       res.writeHead(200, { "Content-type": "text/plain" });
       res.end("Hellon");
   })
});

server.listen(3000, function() {
   console.log('Server is running at 3000')
})

TL, д-р
До появи хмарних програм основним засобом моніторингу та перевірки стану програм були логи. Однак не було коштів вживати якихось заходів щодо усунення несправностей. Логи і сьогодні корисні, їх треба збирати та відправляти до системи складання логів для аналізу аварійних ситуацій та прийняття рішень. [це все можна було робити і без хмарних програм за допомогою monit, наприклад, але з k8s це стало набагато простіше 🙂 – прим.ред. ]

Сьогодні ж виправлення доводиться вносити майже в режимі реального часу, тому програми більше не повинні бути чорними ящиками. Ні, вони повинні показувати кінцеві точки, що дозволяють системам моніторингу вимагати та збирати цінні дані про стан процесів, щоб у разі потреби реагувати миттєво. Це називається шаблон проектування перевірки працездатності, який слідує принципу високої спостерігальності (НОР).

Kubernetes за промовчанням пропонує 2 види перевірки працездатності: readinessProbe та livenessProbe. Обидва використовують однакові типи перевірок (HTTP GET запити, ТСР-зв'язку та виконання команд). Відрізняються вони у тому, які рішення приймають у відповідь на неполадки у pod'ах. livenessProbe перезапускає контейнер, сподіваючись, що помилка більше не повториться, а readinessProbe ізолює pod від вхідного трафіку – до усунення причини неполадки.

Правильне проектування програми має включати і той, і інший вид перевірки, і щоб вони збирали достатньо даних, особливо коли створено виняткову ситуацію. Воно також має показувати необхідні кінцеві точки API, які передають системі моніторингу (тому ж Prometheus) важливі метрики стану працездатності.

Джерело: habr.com

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