10 errori comuni quando si utilizza Kubernetes

Nota. trad.: Gli autori di questo articolo sono ingegneri di una piccola azienda ceca, pipetail. Sono riusciti a mettere insieme un meraviglioso elenco di problemi e idee sbagliate [a volte banali, ma comunque] molto urgenti legati al funzionamento dei cluster Kubernetes.

10 errori comuni quando si utilizza Kubernetes

Nel corso degli anni di utilizzo di Kubernetes, abbiamo lavorato con un gran numero di cluster (sia gestiti che non gestiti - su GCP, AWS e Azure). Nel corso del tempo, abbiamo iniziato a notare che alcuni errori si ripetevano costantemente. Tuttavia, non c’è nulla di cui vergognarsi: la maggior parte di essi l’abbiamo realizzata noi stessi!

L'articolo contiene gli errori più comuni e menziona anche come correggerli.

1. Risorse: richieste e limiti

Questo articolo merita sicuramente la massima attenzione e il primo posto nella lista.

Richiesta CPU di solito o non è specificato affatto o ha un valore molto basso (per posizionare il maggior numero possibile di pod su ciascun nodo). Pertanto, i nodi vengono sovraccaricati. Durante i periodi di carico elevato, la potenza di elaborazione del nodo viene completamente utilizzata e un particolare carico di lavoro riceve solo ciò che "richiesto" da Limitazione della CPU. Ciò porta a una maggiore latenza delle applicazioni, timeout e altre conseguenze spiacevoli. (Maggiori informazioni su questo argomento nella nostra altra traduzione recente: "Limiti della CPU e limitazione aggressiva in Kubernetes" - ca. trad.)

Miglior sforzo (estremamente no consigliato):

resources: {}

Richiesta di CPU estremamente bassa (estremamente no consigliato):

   resources:
      Requests:
        cpu: "1m"

D'altro canto, la presenza di un limite della CPU può portare a un irragionevole salto dei cicli di clock da parte dei pod, anche se il processore del nodo non è completamente carico. Ancora una volta, ciò può comportare un aumento dei ritardi. Continua la polemica attorno al parametro Quota CFS della CPU nel kernel Linux e limitazione della CPU in base ai limiti impostati, oltre alla disabilitazione della quota CFS... Purtroppo, i limiti della CPU possono causare più problemi di quanti ne possano risolvere. Maggiori informazioni a riguardo possono essere trovate al link sottostante.

Selezione eccessiva (impegno eccessivo) i problemi di memoria possono portare a problemi più grandi. Raggiungere il limite della CPU comporta il salto dei cicli di clock, mentre raggiungere il limite della memoria comporta l'uccisione del pod. Hai mai osservato OOMkill? Sì, è proprio di questo che stiamo parlando.

Vuoi ridurre al minimo la probabilità che ciò accada? Non sovraallocare memoria e utilizzare QoS (Qualità del servizio) garantito impostando la richiesta di memoria al limite (come nell'esempio seguente). Maggiori informazioni su questo argomento in Presentazioni di Henning Jacobs (Ingegnere capo presso Zalando).

Esplosiva (maggiori possibilità di essere ucciso da OOM):

   resources:
      requests:
        memory: "128Mi"
        cpu: "500m"
      limits:
        memory: "256Mi"
        cpu: 2

Garantito:

   resources:
      requests:
        memory: "128Mi"
        cpu: 2
      limits:
        memory: "128Mi"
        cpu: 2

Cosa sarà potenzialmente utile durante l'impostazione delle risorse?

Con server delle metriche puoi vedere l'attuale consumo di risorse della CPU e l'utilizzo della memoria da parte dei pod (e dei contenitori al loro interno). Molto probabilmente lo stai già utilizzando. Basta eseguire i seguenti comandi:

kubectl top pods
kubectl top pods --containers
kubectl top nodes

Tuttavia, mostrano solo l'utilizzo corrente. Può darti un'idea approssimativa dell'ordine di grandezza, ma alla fine ti servirà cronologia dei cambiamenti nelle metriche nel tempo (per rispondere a domande del tipo: "Qual è stato il carico massimo della CPU?", "Qual è stato il carico ieri mattina?", ecc.). Per questo puoi usare Prometeo, DataDog e altri strumenti. Ottengono semplicemente le metriche dal server delle metriche e le memorizzano e l'utente può interrogarle e tracciarle di conseguenza.

VerticalPodAutoscaler permette automatizzare questo processo. Tiene traccia della cronologia di utilizzo della CPU e della memoria e imposta nuove richieste e limiti in base a queste informazioni.

Utilizzare la potenza di calcolo in modo efficiente non è un compito facile. È come giocare sempre a Tetris. Se stai pagando troppo per la potenza di calcolo con un consumo medio basso (diciamo ~10%), ti consigliamo di guardare prodotti basati su AWS Fargate o Virtual Kubelet. Sono basati su un modello di fatturazione serverless/pay-per-use, che potrebbe rivelarsi più economico in tali condizioni.

2. Sonde di attività e disponibilità

Per impostazione predefinita, i controlli di attività e disponibilità non sono abilitati in Kubernetes. E a volte si dimenticano di accenderli...

Ma in quale altro modo è possibile avviare il riavvio del servizio in caso di errore fatale? E come fa il sistema di bilanciamento del carico a sapere che un pod è pronto ad accettare il traffico? O che possa gestire più traffico?

Questi test vengono spesso confusi tra loro:

  • Vitalità — controllo di “sopravvivenza”, che riavvia il pod se fallisce;
  • prontezza — controllo di disponibilità, se fallisce, disconnette il pod dal servizio Kubernetes (questo può essere controllato utilizzando kubectl get endpoints) e il traffico non vi arriva finché il controllo successivo non viene completato con successo.

Entrambi questi controlli EFFETTUATO DURANTE L'INTERO CICLO DI VITA DEL POD. È molto importante.

Un malinteso comune è che le sonde di disponibilità vengano eseguite solo all'avvio in modo che il sistema di bilanciamento possa sapere che il pod è pronto (Ready) e può iniziare a elaborare il traffico. Tuttavia, questa è solo una delle opzioni per il loro utilizzo.

Un'altra è la possibilità di scoprire che il traffico sul pod è eccessivo e lo sovraccarica (o il pod esegue calcoli ad uso intensivo di risorse). In questo caso, il controllo di disponibilità aiuta ridurre il carico sul pod e “raffreddarlo”.. Il completamento con successo di un controllo di prontezza in futuro consente aumentare nuovamente il carico sul pod. In questo caso (se il readreadness test fallisce), il fallimento del liveness test sarebbe molto controproducente. Perché riavviare un pod che è sano e lavora sodo?

Pertanto, in alcuni casi, è meglio non effettuare alcun controllo piuttosto che abilitarli con parametri configurati in modo errato. Come detto sopra, se controllo dell'attività controllo della disponibilità delle copie, allora sei in grossi guai. L'opzione possibile è configurare solo test di preparazioneE vivacità pericolosa lasciare da parte.

Entrambi i tipi di controlli non dovrebbero fallire quando le dipendenze comuni falliscono, altrimenti ciò porterà a un fallimento a cascata (a valanga) di tutti i pod. In altre parole, non farti del male.

3. LoadBalancer per ciascun servizio HTTP

Molto probabilmente, nel tuo cluster sono presenti servizi HTTP che desideri inoltrare al mondo esterno.

Se apri il servizio come type: LoadBalancer, il suo controller (a seconda del fornitore di servizi) fornirà e negozierà un LoadBalancer esterno (non necessariamente in esecuzione su L7, ma piuttosto anche su L4), e ciò potrebbe influire sul costo (indirizzo IPv4 statico esterno, potenza di calcolo, fatturazione al secondo ) a causa della necessità di creare un gran numero di tali risorse.

In questo caso, è molto più logico utilizzare un bilanciatore del carico esterno, aprendo i servizi come type: NodePort. O meglio ancora, espandi qualcosa di simile controller di ingresso nginx (o traefik), che sarà l'unico NodoPort endpoint associato al bilanciatore del carico esterno e instraderà il traffico nel cluster utilizzando ingresso-Risorse Kubernetes.

Altri (micro)servizi intra-cluster che interagiscono tra loro possono “comunicare” utilizzando servizi come IP cluster e un meccanismo di rilevamento dei servizi integrato tramite DNS. Basta non utilizzare il DNS/IP pubblico, poiché ciò può influire sulla latenza e aumentare il costo dei servizi cloud.

4. Scalabilità automatica di un cluster senza tener conto delle sue funzionalità

Quando aggiungi e rimuovi nodi da un cluster, non dovresti fare affidamento su alcune metriche di base come l'utilizzo della CPU su tali nodi. La pianificazione dei pod deve tenerne conto molti restrizioni, ad esempio affinità pod/nodo, incompatibilità e tolleranze, richieste di risorse, QoS e così via. L'utilizzo di un autoscaler esterno che non tenga conto di queste sfumature può causare problemi.

Immagina che un determinato pod debba essere programmato, ma tutta la potenza della CPU disponibile venga richiesta/disassemblata e il pod rimane bloccato in uno stato Pending. Il scalatore automatico esterno rileva il carico medio attuale della CPU (non quello richiesto) e non avvia l'espansione (scala orizzontale) - non aggiunge un altro nodo. Di conseguenza, questo pod non verrà programmato.

In questo caso, ridimensionamento inverso (ridimensionamento) — rimuovere un nodo da un cluster è sempre più difficile da implementare. Immagina di avere un pod con stato (con archiviazione persistente connessa). Volumi persistenti di solito appartengono zona di disponibilità specifica e non sono replicati nella regione. Pertanto, se un autoscaler esterno elimina un nodo con questo pod, lo scheduler non sarà in grado di pianificare questo pod su un altro nodo, poiché ciò può essere fatto solo nella zona di disponibilità in cui si trova l'archiviazione persistente. Il pod rimarrà bloccato nello stato Pending.

Molto popolare nella comunità Kubernetes scalabilità automatica del cluster. Funziona su un cluster, supporta le API dei principali fornitori di servizi cloud, tiene conto di tutte le restrizioni ed è scalabile nei casi sopra indicati. È inoltre in grado di espandersi mantenendo tutti i limiti stabiliti, risparmiando così denaro (che altrimenti verrebbe speso in capacità inutilizzata).

5. Trascurare le funzionalità IAM/RBAC

Fare attenzione a non utilizzare utenti IAM con segreti persistenti per macchine e applicazioni. Organizza l'accesso temporaneo utilizzando ruoli e account di servizio (conti di servizio).

Spesso riscontriamo il fatto che le chiavi di accesso (e i segreti) sono codificati nella configurazione dell'applicazione, oltre a trascurare la rotazione dei segreti pur avendo accesso a Cloud IAM. Utilizza i ruoli e gli account di servizio IAM anziché gli utenti, ove appropriato.

10 errori comuni quando si utilizza Kubernetes

Dimentica kube2iam e vai direttamente ai ruoli IAM per gli account di servizio (come descritto in nota con lo stesso nome Štěpán Vraný):

apiVersion: v1
kind: ServiceAccount
metadata:
  annotations:
    eks.amazonaws.com/role-arn: arn:aws:iam::123456789012:role/my-app-role
  name: my-serviceaccount
  namespace: default

Una annotazione. Non è così difficile, vero?

Inoltre, non concedere privilegi agli account di servizio e ai profili di istanza admin и cluster-adminse non ne hanno bisogno. Questo è un po' più difficile da implementare, specialmente negli RBAC K8, ma ne vale sicuramente la pena.

6. Non fare affidamento sull'anti-affinità automatica per i pod

Immagina di avere tre repliche di una distribuzione su un nodo. Il nodo cade e con esso tutte le repliche. Situazione spiacevole, vero? Ma perché tutte le repliche erano sullo stesso nodo? Kubernetes non dovrebbe fornire alta disponibilità (HA)?!

Sfortunatamente, lo scheduler Kubernetes, di propria iniziativa, non rispetta le regole dell'esistenza separata (anti-affinità) per i baccelli. Devono essere esplicitamente indicati:

// опущено для краткости
      labels:
        app: zk
// опущено для краткости
      affinity:
        podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            - labelSelector:
                matchExpressions:
                  - key: "app"
                    operator: In
                    values:
                    - zk
              topologyKey: "kubernetes.io/hostname"

È tutto. Ora i pod verranno pianificati su nodi diversi (questa condizione viene verificata solo durante la pianificazione, ma non il loro funzionamento, quindi requiredDuringSchedulingIgnoredDuringExecution).

Qui stiamo parlando podAntiAffinity su nodi diversi: topologyKey: "kubernetes.io/hostname", - e non sulle diverse zone di disponibilità. Per implementare un'HA a tutti gli effetti, dovrai approfondire questo argomento.

7. Ignorare i budget di PodDisruption

Immagina di avere un carico di produzione su un cluster Kubernetes. Periodicamente, i nodi e il cluster stesso devono essere aggiornati (o disattivati). PodDisruptionBudget (PDB) è qualcosa come un accordo di garanzia del servizio tra gli amministratori del cluster e gli utenti.

PDB consente di evitare interruzioni del servizio causate dalla mancanza di nodi:

apiVersion: policy/v1beta1
kind: PodDisruptionBudget
metadata:
  name: zk-pdb
spec:
  minAvailable: 2
  selector:
    matchLabels:
      app: zookeeper

In questo esempio, tu, come utente del cluster, dichiari agli amministratori: "Ehi, ho un servizio di guardia dello zoo e, qualunque cosa tu faccia, vorrei che almeno 2 repliche di questo servizio fossero sempre disponibili .”

Puoi leggere di più a riguardo qui.

8. Più utenti o ambienti in un cluster comune

Spazi dei nomi Kubernetes (spazi dei nomi) non fornire un forte isolamento.

Un malinteso comune è che se si distribuisce un carico non di produzione in uno spazio dei nomi e un carico di produzione in un altro, allora non si influenzeranno a vicenda in alcun modo... Tuttavia, è possibile ottenere un certo livello di isolamento utilizzando richieste/limitazioni di risorse, impostando quote e impostando prioritàClasses. Un certo isolamento “fisico” nel piano dati è fornito da affinità, tolleranze, incompatibilità (o selettori di nodo), ma tale separazione è abbastanza difficile strumento.

Coloro che necessitano di combinare entrambi i tipi di carichi di lavoro nello stesso cluster dovranno affrontare la complessità. Se non esiste tale necessità e puoi permetterti di averne uno un altro grappolo (ad esempio, in un cloud pubblico), allora è meglio farlo. Ciò consentirà di ottenere un livello di isolamento molto più elevato.

9. Politica del traffico esterno: cluster

Molto spesso vediamo che tutto il traffico all'interno del cluster passa attraverso un servizio come NodePort, per il quale è impostata la policy predefinita externalTrafficPolicy: Cluster. Ciò significa che NodoPort è aperto su ogni nodo del cluster ed è possibile utilizzarne uno qualsiasi per interagire con il servizio desiderato (set di pod).

10 errori comuni quando si utilizza Kubernetes

Allo stesso tempo, i pod reali associati al suddetto servizio NodePort sono solitamente disponibili solo su alcuni sottoinsieme di questi nodi. In altre parole, se mi connetto a un nodo che non ha il pod richiesto, inoltrerà il traffico a un altro nodo, aggiungendo un salto e aumento della latenza (se i nodi si trovano in zone/data center diversi, la latenza può essere piuttosto elevata; inoltre, i costi del traffico in uscita aumenteranno).

D'altra parte, se un determinato servizio Kubernetes ha una serie di politiche externalTrafficPolicy: Local, NodePort si apre solo su quei nodi in cui i pod richiesti sono effettivamente in esecuzione. Quando si utilizza un bilanciatore del carico esterno che controlla lo stato (controllo sanitario) endpoint (come funziona AWSELB), Lui invierà il traffico solo ai nodi necessari, che avrà un effetto benefico sui ritardi, sulle esigenze informatiche, sulle bollette in uscita (e il buon senso impone lo stesso).

C'è un'alta probabilità che tu stia già utilizzando qualcosa di simile traefik o controller di ingresso nginx come endpoint NodePort (o LoadBalancer, che utilizza anche NodePort) per instradare il traffico in entrata HTTP e l'impostazione di questa opzione può ridurre significativamente la latenza per tali richieste.

В questa pubblicazione Puoi saperne di più sulla externalTrafficPolicy, sui suoi vantaggi e svantaggi.

10. Non legarti ai cluster e non abusare del piano di controllo

In precedenza, era consuetudine chiamare i server con nomi propri: Anton, HAL9000 e Colossus... Oggi sono stati sostituiti da identificatori generati casualmente. Tuttavia, l'abitudine è rimasta e ora i nomi propri vanno ai cluster.

Una storia tipica (basata su eventi reali): tutto è iniziato con una prova di concetto, quindi il cluster aveva un nome orgoglioso analisi… Sono passati gli anni ed è ANCORA utilizzato nella produzione, e tutti hanno paura di toccarlo.

Non c'è niente di divertente nel trasformare i grappoli in animali domestici, quindi ti consigliamo di rimuoverli periodicamente durante la pratica ripristino di emergenza (questo aiuterà ingegneria del caos - ca. trad.). Inoltre non sarebbe male lavorare sul livello di controllo (piano di controllo). Avere paura di toccarlo non è un buon segno. ecc morto? Ragazzi, siete davvero nei guai!

D'altra parte, non dovresti lasciarti trasportare dalla manipolazione. Col tempo il livello di controllo potrebbe diventare lento. Molto probabilmente, ciò è dovuto al fatto che un gran numero di oggetti vengono creati senza la loro rotazione (una situazione comune quando si utilizza Helm con le impostazioni predefinite, motivo per cui il suo stato in configmaps/secrets non viene aggiornato - di conseguenza, migliaia di oggetti si accumulano in il livello di controllo) o con la modifica costante degli oggetti kube-api (per il ridimensionamento automatico, per CI/CD, per il monitoraggio, i registri eventi, i controller, ecc.).

Inoltre consigliamo di verificare gli accordi SLA/SLO con il provider Kubernetes gestito e di prestare attenzione alle garanzie. Il venditore può garantire disponibilità del livello di controllo (o i suoi sottocomponenti), ma non il ritardo p99 delle richieste che gli invii. In altre parole, puoi entrare kubectl get nodese riceverai una risposta solo dopo 10 minuti e ciò non costituirà una violazione dei termini del contratto di servizio.

11. Bonus: utilizzo dell'ultimo tag

Ma questo è già un classico. Ultimamente ci siamo imbattuti in questa tecnica meno spesso, poiché molti, avendo imparato dall'amara esperienza, hanno smesso di usare il tag :latest e ho iniziato a bloccare le versioni. Evviva!

ECR mantiene l'immutabilità dei tag delle immagini; Ti consigliamo di familiarizzare con questa straordinaria funzionalità.

Riassunto

Non aspettarti che tutto funzioni dall'oggi al domani: Kubernetes non è una panacea. Applicazione pessima rimarrà così anche in Kubernetes (e probabilmente peggiorerà). La disattenzione porterà a un'eccessiva complessità, a un lavoro lento e stressante del livello di controllo. Inoltre, rischi di rimanere senza una strategia di ripristino di emergenza. Non aspettarti che Kubernetes fornisca isolamento e disponibilità elevata immediatamente. Dedica un po' di tempo a rendere la tua applicazione realmente nativa del cloud.

Puoi conoscere le esperienze infruttuose di varie squadre in questa raccolta di storie di Henning Jacobs.

Coloro che desiderano aggiungere all'elenco degli errori riportati in questo articolo possono contattarci su Twitter (@MarekBartik, @MstrsObserver).

PS da traduttore

Leggi anche sul nostro blog:

Fonte: habr.com

Aggiungi un commento