TL; DR
- Aby osiągnąć wysoką obserwowalność kontenerów i mikrousług, logi i podstawowe metryki nie wystarczą.
- Aby zapewnić szybsze odzyskiwanie i większą odporność, aplikacje powinny stosować zasadę wysokiej obserwowalności (HOP).
- Na poziomie aplikacji NOP wymaga: odpowiedniego rejestrowania, ścisłego monitorowania, kontroli poprawności i śledzenia wydajności/przejścia.
- Użyj czeków jako elementu NOR Sonda gotowości и Sonda żywotności Kubernetesa.
Co to jest szablon kontroli stanu?
Projektując aplikację o znaczeniu krytycznym i charakteryzującą się wysoką dostępnością, bardzo ważne jest, aby pomyśleć o takim aspekcie, jak odporność na awarie. Aplikacja jest uważana za odporną na awarie, jeśli szybko odzyskuje sprawność po awarii. Typowa aplikacja chmurowa wykorzystuje architekturę mikrousług – gdzie każdy komponent jest umieszczony w osobnym kontenerze. Aby podczas projektowania klastra mieć pewność, że aplikacja na K8s będzie wysoce dostępna, należy postępować zgodnie z określonymi wzorcami. Wśród nich znajduje się szablon oceny funkcjonowania. Definiuje sposób, w jaki aplikacja komunikuje k8s, że jest zdrowa. To nie tylko informacja o tym, czy pod działa, ale także o tym, w jaki sposób odbiera żądania i odpowiada na nie. Im więcej Kubernetes wie o kondycji podu, tym mądrzejsze decyzje podejmuje dotyczące routingu ruchu i równoważenia obciążenia. Zatem zasada wysokiej obserwowalności pozwala aplikacji reagować na żądania w odpowiednim czasie.
Zasada wysokiej obserwowalności (HOP)
Jedną z nich jest zasada wysokiej obserwowalności
Dobrze zaprojektowana aplikacja w chmurze rejestruje swoje główne zdarzenia przy użyciu standardowych strumieni I/O STDERR i STDOUT. Następnie pojawia się usługa pomocnicza, np. filebeat, logstash czy fluentd, dostarczająca logi do scentralizowanego systemu monitorowania (np. Prometheus) i systemu gromadzenia logów (pakiet oprogramowania ELK). Poniższy diagram pokazuje, jak aplikacja w chmurze działa zgodnie ze wzorcem testu kondycji i zasadą wysokiej obserwowalności.
Jak zastosować wzorzec kontroli stanu w Kubernetesie?
Po wyjęciu z pudełka k8s monitoruje stan podów za pomocą jednego z kontrolerów (
W naszym przykładzie k8s tak kontrola funkcjonalności. W tego typu weryfikacji kubelet na bieżąco sprawdza stan procesu w kontenerze. Gdy zrozumie, że proces się zatrzymał, uruchomi go ponownie. Jeśli błąd można rozwiązać, po prostu ponownie uruchamiając aplikację, a program jest zaprojektowany tak, aby zamykał się w przypadku dowolnego błędu, wówczas wystarczy sprawdzenie stanu procesu, aby postępować zgodnie z NOP i wzorcem testu kondycji. Szkoda tylko, że nie wszystkie błędy są eliminowane przez ponowne uruchomienie. W tym przypadku k8s oferuje 2 głębsze sposoby identyfikowania problemów z kapsułą:
Sonda Żywotności
Podczas Sonda żywotności kubelet wykonuje 3 rodzaje kontroli: nie tylko sprawdza, czy pod działa, ale także czy jest gotowy do przyjmowania żądań i odpowiedniego reagowania na nie:
- Skonfiguruj żądanie HTTP do modułu. Odpowiedź musi zawierać kod odpowiedzi HTTP z zakresu od 200 do 399. Zatem kody 5xx i 4xx sygnalizują, że pod ma problemy, mimo że proces jest uruchomiony.
- Aby przetestować pody z usługami innymi niż HTTP (na przykład serwerem pocztowym Postfix), musisz ustanowić połączenie TCP.
- Wykonaj dowolne polecenie dla poda (wewnętrznie). Sprawdzenie uznaje się za pomyślne, jeśli kod zakończenia polecenia wynosi 0.
Przykład jak to działa. Kolejna definicja poda zawiera aplikację NodeJS, która przy żądaniach HTTP zgłasza błąd 500. Aby mieć pewność, że kontener zostanie zrestartowany po otrzymaniu takiego błędu, używamy parametru 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
Nie różni się to od innych definicji podów, ale dodajemy obiekt .spec.containers.livenessProbe
. Parametr httpGet
akceptuje ścieżkę, na którą wysyłane jest żądanie HTTP GET (w naszym przykładzie jest to /
, ale w scenariuszach bojowych może być coś takiego /api/v1/status
). Inna sonda livenessProbe akceptuje parametr initialDelaySeconds
, który instruuje operację weryfikacji, aby czekała określoną liczbę sekund. Opóźnienie jest potrzebne, ponieważ kontener potrzebuje czasu na uruchomienie, a po ponownym uruchomieniu będzie przez jakiś czas niedostępny.
Aby zastosować to ustawienie do klastra, użyj:
kubectl apply -f pod.yaml
Po kilku sekundach możesz sprawdzić zawartość kapsuły za pomocą następującego polecenia:
kubectl describe pods node500
Na końcu wyniku znajdź
Jak widać, livenessProbe zainicjował żądanie HTTP GET, kontener wygenerował błąd 500 (tak właśnie został zaprogramowany), a kubelet go zrestartował.
Jeśli zastanawiasz się, jak została zaprogramowana aplikacja NideJS, oto pliki app.js i Dockerfile, które zostały użyte:
aplikacja.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')
})
Dockerfile
FROM node
COPY app.js /
EXPOSE 3000
ENTRYPOINT [ "node","/app.js" ]
Warto o tym pamiętać: livenessProbe zrestartuje kontener tylko w przypadku niepowodzenia. Jeśli ponowne uruchomienie nie naprawi błędu uniemożliwiającego uruchomienie kontenera, kubelet nie będzie mógł podjąć działań w celu rozwiązania problemu.
Sonda gotowości
ReadinessProbe działa podobnie do livenessProbes (żądania GET, komunikacja TCP i wykonywanie poleceń), z wyjątkiem działań związanych z rozwiązywaniem problemów. Kontener, w którym wykryto awarię, nie jest uruchamiany ponownie, ale jest izolowany od ruchu przychodzącego. Wyobraź sobie, że jeden z kontenerów wykonuje wiele obliczeń lub jest pod dużym obciążeniem, co powoduje wydłużenie czasu odpowiedzi. W przypadku livenessProbe wyzwalane jest sprawdzenie dostępności odpowiedzi (poprzez parametr timeoutSeconds check), po czym kubelet restartuje kontener. Po uruchomieniu kontener zaczyna wykonywać zadania wymagające dużej ilości zasobów i zostaje ponownie uruchomiony. Może to mieć kluczowe znaczenie w przypadku aplikacji wymagających szybkości reakcji. Na przykład samochód w drodze czeka na odpowiedź z serwera, odpowiedź jest opóźniona - i samochód ulega wypadkowi.
Napiszmy definicję redinessProbe, która ustawi czas odpowiedzi na żądanie GET na nie więcej niż dwie sekundy, a aplikacja odpowie na żądanie GET po 5 sekundach. Plik pod.yaml powinien wyglądać następująco:
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
Wdróżmy kapsułę za pomocą kubectl:
kubectl apply -f pod.yaml
Poczekajmy kilka sekund, a następnie zobaczmy, jak działał ReadinessProbe:
kubectl describe pods nodedelayed
Na końcu wyniku widać, że niektóre zdarzenia są podobne
Jak widać, kubectl nie uruchomił ponownie kapsuły, gdy czas sprawdzania przekroczył 2 sekundy. Zamiast tego anulował żądanie. Komunikacja przychodząca jest przekierowywana do innych, działających kapsuł.
Zauważ, że teraz, gdy pod jest odciążony, kubectl ponownie kieruje do niego żądania: odpowiedzi na żądania GET nie są już opóźnione.
Dla porównania poniżej zmodyfikowany plik 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; DR
Przed pojawieniem się aplikacji w chmurze dzienniki były głównym sposobem monitorowania i sprawdzania kondycji aplikacji. Nie było jednak możliwości podjęcia jakichkolwiek działań naprawczych. Dzienniki są nadal przydatne, należy je gromadzić i przesyłać do systemu gromadzenia dzienników w celu analizy sytuacji awaryjnych i podejmowania decyzji. [Wszystko to dałoby się zrobić bez aplikacji chmurowych wykorzystujących np. monit, ale z k8s stało się to dużo prostsze :) – przyp. red. ]
Dziś korekty trzeba wprowadzać niemal w czasie rzeczywistym, więc aplikacje nie muszą już być czarnymi skrzynkami. Nie, powinny pokazywać punkty końcowe, które umożliwiają systemom monitorującym odpytywanie i zbieranie cennych danych o stanie procesów, aby w razie potrzeby mogły natychmiast zareagować. Nazywa się to wzorcem projektowym testu wydajności i jest zgodny z zasadą wysokiej obserwowalności (HOP).
Kubernetes domyślnie oferuje 2 typy kontroli stanu: ReadinessProbe i livenessProbe. Obydwa korzystają z tych samych typów kontroli (żądania HTTP GET, komunikacja TCP i wykonywanie poleceń). Różnią się decyzjami, które podejmują w odpowiedzi na problemy w strąkach. livenessProbe ponownie uruchamia kontener w nadziei, że błąd się nie powtórzy, a ReadinessProbe izoluje pod od ruchu przychodzącego do czasu usunięcia przyczyny problemu.
Prawidłowy projekt aplikacji powinien obejmować oba typy sprawdzania i zapewniać, że zbierają one wystarczającą ilość danych, szczególnie w przypadku zgłoszenia wyjątku. Powinien także pokazywać niezbędne punkty końcowe API, które dostarczają systemowi monitorowania (Prometheus) ważnych wskaźników kondycji.
Źródło: www.habr.com