Migliori pratiche per i container Kubernetes: controlli di funzionalità

Migliori pratiche per i container Kubernetes: controlli di funzionalità

TL;DR

  • Per ottenere un'elevata osservabilità di contenitori e microservizi, non bastano solo i log e le metriche primarie.
  • Per un ripristino più rapido e una maggiore resilienza, le applicazioni devono adottare il Principio di Alta Osservabilità (HOP, High Observability Principle).
  • A livello applicativo, per il HOP sono richiesti: logging adeguato, monitoraggio accurato, controlli di stato e tracciamento delle performance/transitioni.
  • Utilizza controlli come elemento del HOP. readinessProbe e livenessProbe Kubernetes.

Cos'è il Modello di controllo dello stato?

Quando si progetta un'applicazione critica e ad alta disponibilità, è fondamentale considerare un aspetto come la resilienza. Un'applicazione è considerata resiliente se riesce a riprendersi rapidamente da un guasto. Un'applicazione cloud tipica utilizza un'architettura a microservizi, in cui ogni componente è collocato in un contenitore separato. Per garantire che l'applicazione su k8s sia altamente disponibile, quando si progetta un cluster, è necessario seguire determinati modelli. Tra questi c'è il Modello di verifica dello stato. Questo modello definisce come l'applicazione comunica a k8s il proprio stato di operatività. Non si tratta solo di informazioni su se un pod è attivo, ma anche su come riceve e risponde alle richieste. Più Kubernetes sa sulla disponibilità di un pod, migliori decisioni può prendere riguardo al routing del traffico e al bilanciamento del carico. In questo modo, il Principio di alta osservabilità consente all'applicazione di rispondere tempestivamente alle richieste.

Principio di alta osservabilità (HAO)

Il principio di alta osservabilità è uno dei principi di progettazione delle applicazioni containerizzate. Nell'architettura a microservizi, ai servizi non interessa come viene elaborata la loro richiesta (ed è corretto), ma è importante come ottenere le risposte dai servizi che le accettano. Ad esempio, per l'autenticazione dell'utente, un contenitore invia una richiesta HTTP a un altro, attendendo una risposta in un formato specifico: è tutto qui. La richiesta può essere elaborata da PythonJS e la risposta può provenire da Python Flask. I contenitori si rapportano l'uno all'altro come scatole nere con contenuti nascosti. Tuttavia, il principio di HEALTH CHECK richiede che ogni servizio esponga diverse endpoint API, mostrando quanto sia operativo, nonché il suo stato di prontezza e resilienza. Questi parametri vengono richiesti da Kubernetes per pianificare i prossimi passi nel routing e nel bilanciamento del carico.

Un'applicazione cloud ben progettata registra i suoi eventi principali utilizzando i flussi di input-output standard STDERR e STDOUT. Successivamente, un servizio ausiliario, come filebeat, logstash o fluentd, invia i log a un sistema di monitoraggio centralizzato (ad esempio Prometheus) e a un sistema di raccolta log (la suite software ELK). Nello schema sottostante è mostrato come l'applicazione cloud opera secondo il Template di Health Check e il Principio di Alta Osservabilità.

Migliori pratiche per i container Kubernetes: controlli di funzionalità

Come applicare il Template di Health Check in Kubernetes?

Di default, k8s monitora lo stato dei pod attraverso uno dei controllori (Deployments, ReplicaSets, DaemonSets, StatefulSets e altri). Scoprendo che un pod è caduto per qualche motivo, il controller cerca di riavviarlo o di spostarlo su un altro nodo. Tuttavia, il pod potrebbe segnalare di essere attivo e funzionante, mentre in realtà non lo è. Facciamo un esempio: la tua applicazione utilizza Apache come server web e hai distribuito il componente su più pod nel cluster. Poiché la libreria è stata configurata in modo errato, tutte le richieste all'applicazione restituiscono un codice 500 (errore interno del server). Durante il controllo dell'erogazione, il controllo dello stato dei pod dà un esito positivo, tuttavia i clienti hanno un'opinione diversa. Descriveremo questa situazione indesiderata nel modo seguente:

Migliori pratiche per i container Kubernetes: controlli di funzionalità

Nel nostro esempio, k8s esegue il controllo dello stato. In questo tipo di controllo, kubelet verifica costantemente lo stato del processo all'interno del contenitore. Se rileva che il processo si è bloccato, lo riavvia. Se un errore può essere risolto semplicemente riavviando l'applicazione, e il programma è progettato per disattivarsi in caso di errore, allora per rispettare NORM e il Modello di controllo della funzionalità, è sufficiente controllare il funzionamento del processo. È un peccato che non tutti gli errori possano essere risolti con un riavvio. Per tali casi, k8s offre 2 metodi più approfonditi per diagnosticare i problemi nelle pod: livenessProbe e readinessProbe.

LivenessProbe

Durante livenessProbe kubelet esegue 3 tipi di controlli: non solo verifica se la pod è in esecuzione, ma anche se è pronta a ricevere e rispondere adeguatamente alle richieste:

  • Stabilire una richiesta HTTP alla pod. La risposta deve contenere un codice di stato HTTP compreso tra 200 e 399. Pertanto, i codici 5xx e 4xx segnalano che ci sono problemi nella pod, anche se il processo è in esecuzione.
  • Per controllare le pod con servizi non HTTP (ad esempio, il server di posta Postfix), è necessario stabilire una connessione TCP.
  • Esecuzione di un comando arbitrario per la pod (internamente). Il controllo è considerato riuscito se il codice di uscita del comando è 0.

Ecco un esempio di come funziona. La definizione del pod successivo contiene un'applicazione NodeJS che restituisce un errore 500 su richieste HTTP. Per assicurarci che il container si riavvii al verificarsi di tale errore, utilizziamo il parametro 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

Non è diverso da qualsiasi altra definizione di pod, ma aggiungiamo l'oggetto .spec.containers.livenessProbe. Il parametro httpGet prende il percorso a cui invia la richiesta HTTP GET (nel nostro esempio è /, ma in scenari reali potrebbe essere qualcosa come /api/v1/status). Inoltre, l'livenessProbe accetta il parametro initialDelaySeconds, che prescrive all'operazione di verifica di attendere un certo numero di secondi. Questa attesa è necessaria perché il container ha bisogno di tempo per avviarsi e, durante il riavvio, potrebbe non essere disponibile per un po'.

Per applicare questa impostazione al cluster, usa:

kubectl apply -f pod.yaml

Dopo alcuni secondi, puoi controllare il contenuto del pod con il seguente comando:

kubectl describe pods node500

In fondo all'output, cerca ecco cosa.

Come potete vedere, il livenessProbe ha avviato una richiesta HTTP GET, il contenitore ha restituito un errore 500 (come previsto), e kubelet lo ha riavviato.

Se vi interessa sapere come è stato programmato l'applicativo NodeJS, ecco il file app.js e il Dockerfile utilizzati:

app.js

var http = require('http');

var server = http.createServer(function(req, res) {
    res.writeHead(500, { "Content-type": "text/plain" });
    res.end("Ci siamo imbattuti in un errore\n");
});

server.listen(3000, function() {
    console.log('Server in esecuzione su 3000')
})

Dockerfile

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

È importante notare che il livenessProbe riavvierà il contenitore solo in caso di guasto. Se il riavvio non risolve l'errore che impedisce il corretto funzionamento del contenitore, kubelet non sarà in grado di prendere misure correttive.

readinessProbe

readinessProbe funziona in modo simile a livenessProbes (richieste GET, connessioni TCP ed esecuzione di comandi), tranne per le azioni di risoluzione dei problemi. Un contenitore che ha subito un guasto non viene riavviato, ma isolato dal traffico in entrata. Immagina che uno dei contenitori stia eseguendo molti calcoli o sia sotto un carico pesante, il che fa aumentare il tempo di risposta alle richieste. In caso di livenessProbe, si attiva un controllo di disponibilità della risposta (tramite il parametro di controllo timeoutSeconds) e successivamente kubelet riavvia il contenitore. All'avvio, il contenitore inizia a eseguire compiti che richiedono molte risorse e viene riavviato di nuovo. Questo può essere critico per le applicazioni che richiedono una rapida risposta. Ad esempio, un'auto in movimento attende una risposta dal server; se la risposta è in ritardo, l'auto può andare in crash.

Scriviamo una definizione di readinessProbe che stabilisca un tempo di risposta alla richiesta GET non superiore a due secondi, mentre l'applicazione risponderà alla richiesta GET dopo 5 secondi. Il file pod.yaml deve apparire come segue:

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

Distribuiamo il pod con kubectl:

kubectl apply -f pod.yaml

Aspettiamo un paio di secondi e poi vediamo come ha funzionato la readinessProbe:

kubectl describe pods nodedelayed

Alla fine dell'output, si può notare che alcuni eventi sono simili a questo.

Come potete vedere, kubectl non ha riavviato il pod quando il tempo di controllo ha superato i 2 secondi. Invece, ha annullato la richiesta. Le connessioni in ingresso vengono reindirizzate ad altri pod funzionanti.

Notate: ora che il pod ha meno carico, kubectl indirizza nuovamente le richieste a lui: le risposte alle richieste GET non sono più ritardate.

A titolo di confronto, qui sotto è riportato il file app.js modificato:

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
Fino all'avvento delle applicazioni cloud, il principale strumento per il monitoraggio e la verifica dello stato delle applicazioni erano i log. Tuttavia, non c'erano strumenti per intraprendere azioni correttive. I log sono ancora utili oggi, vanno raccolti e inviati al sistema di raccolta log per l'analisi degli incidenti e per prendere decisioni.Questo si poteva fare anche senza applicazioni cloud, ad esempio, utilizzando monit, ma con k8s è diventato molto più semplice 🙂 – nota della redazione. ]

Oggi, le correzioni devono essere apportate quasi in tempo reale, quindi le applicazioni non devono più essere delle scatole nere. No, devono mostrare i punti finali che consentono ai sistemi di monitoraggio di richiedere e raccogliere dati preziosi sullo stato dei processi, in modo da poter reagire immediatamente se necessario. Questo è conosciuto come il Template di design per il controllo dell'operatività, che segue il Principio dell'alta osservabilità (HOP).

Kubernetes offre due tipi di controlli di integrità: readinessProbe e livenessProbe. Entrambi utilizzano gli stessi tipi di controlli (richieste HTTP GET, connessioni TCP e l'esecuzione di comandi). Si differenziano per le azioni che intraprendono in risposta ai malfunzionamenti nei pod. La livenessProbe riavvia il contenitore nella speranza che l'errore non si ripeta, mentre la readinessProbe isola il pod dal traffico in ingresso fino a quando non viene risolta la causa del malfunzionamento.

Una corretta progettazione dell'applicazione deve includere entrambi i tipi di controllo e raccogliere dati sufficienti, specialmente in caso di situazioni eccezionali. Deve anche esporre i necessari endpoint API che inviano al sistema di monitoraggio (come Prometheus) le metriche di stato operative importanti.

Fonte: habr.com

Acquista hosting affidabile per siti web con protezione DDoS, VPS VDS server 🔥 Acquista hosting affidabile per siti web con protezione DDoS, VPS VDS server | ProHoster