
TL;DR
- За да постигнете висока наблюдаемост на контейнерите и микросервисите, като журнали и основни метрики, само това не е достатъчно.
- За по-бързо възстановяване и повишаване на устойчивостта, приложенията трябва да следват Принципа на висока наблюдаемост (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?
По подразбиране k8s следи състоянието на pod-овете чрез един от контролерите (, , , и др., др.). Обявявайки, че pod е паднал по някаква причина, контролерът се опитва да рестартира него или да го премести на друг възел. Въпреки това pod може да се сблъска с проблема, че е стартиран и работи, но всъщност не функционира. Например: вашето приложение използва Apache като уеб сървър, вие инсталирахте компонента на няколко pod-а от клъстера. Поради неправилната конфигурация на библиотеката – всички заявки към приложението дават код 500 (вътрешна грешка на сървъра). При проверка на доставката проверката на състоянието на pod-овете дава успешен резултат, но клиентите мислят обратното. Тази нежелателна ситуация можем да опишем по следния начин:

В нашия пример k8s извършва проверка на работоспособността. При този вид проверка kubelet постоянно следи състоянието на процеса в контейнера. Щом разбере, че процесът не работи, той го рестартира. Ако грешката може да се отстрани с прост рестарт на приложението, а програмата е проектирана така, че да се отказва при всяка грешка, тогава дори проверката на работоспособността на процеса е достатъчна, за да следвате НОР и Шаблона за проверка на работоспособността. Жалко е само, че не всички грешки могат да се отстранят чрез рестарт. В такъв случай k8s предлага два по-дълбоки начина за откриване на проблеми в работата на pod’а: и .
LivenessProbe
По време на livenessProbe kubelet извършва три типа проверки: не само установява дали 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("Срещнахме грешка\n");
});
server.listen(3000, function() {
console.log('Сървърът работи на 3000')
})Dockerfile
FROM node
COPY app.js /
EXPOSE 3000
ENTRYPOINT [ "node","/app.js" ]Важно е да се обърне внимание на следното: livenessProbe ще рестартира контейнера само при отказ. Ако рестартирането не коригира грешката, която пречи на работата на контейнера, kubelet няма да може да предприеме действия за отстраняване на несъответствието.
readinessProbe
readinessProbe работи по аналогичен начин на livenessProbes (GET заявки, TCP свързвания и изпълнение на команди), с изключение на действията за отстраняване на несъответствия. Контейнерът, в който е регистриран отказ, не се рестартира, а се изолира от входящия трафик. Представете си, че един от контейнерите извършва много изчисления или е под тежко натоварване, поради което времето за отговор на заявките нараства. При livenessProbe се извършва проверка на достъпността на отговор (чрез параметъра timeoutSeconds), след което kubelet рестартира контейнера. При рестартиране контейнерът започва да изпълнява ресурсоемки задачи и отново се рестартира. Това може да бъде критично за приложения, за които бързината на отговор е важна. Например, машина, която е на път, очаква отговор от сървъра, отговорът закъснява – и машината попада в авария.
Нека напишем дефиниция на 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'ове.
Обърнете внимание: сега, когато натоварването от 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('Сървърът работи на 3000')
})TL;DR
Преди появата на облачните приложения основният инструмент за мониторинг и проверка на състоянието на приложенията бяха логовете. Въпреки това нямаше средства за предприемане на мерки за отстраняване на проблеми. Логовете и днес са полезни, трябва да се събират и изпращат в система за събиране на логове за анализ на аварийни ситуации и вземане на решения. [това всичко можеше да се прави и без облачни приложения с помощта на monit, например, но с k8s стана много по-лесно 🙂 – бел. ред. ]
Днес обаче корекциите трябва да се правят почти в реално време, така че приложенията не трябва да бъдат черни кутии. Не, те трябва да показват крайни точки, позволяващи на мониторинговите системи да запитват и събират ценни данни за състоянието на процесите, за да реагират незабавно при необходимост. Това се нарича Шаблон за проектиране на проверка на работоспособността, който следва Принципа на висока наблюдаемост (НОР).
Kubernetes по подразбиране предлага 2 вида проверки на работоспособността: readinessProbe и livenessProbe. И двата използват същите видове проверки (HTTP GET заявки, TCP връзки и изпълнение на команди). Те се различават по начина, по който вземат решения в отговор на неизправности в pod-овете. livenessProbe рестартира контейнера, в надеждата че грешката няма да се повтори, а readinessProbe изолира pod от входящия трафик – до отстраняване на причината за неизправността.
Правилното проектиране на приложението трябва да включва и двата вида проверки и да събира достатъчно данни, особено когато е създадена изключителна ситуация. То също така трябва да показва необходимите крайни точки на API, които предават важни метрики за състоянието на работоспособността на мониторинговата система (като Prometheus).
Източник: habr.com
