10 häufige Fehler bei der Verwendung von Kubernetes

Notiz. übersetzen: Die Autoren dieses Artikels sind Ingenieure einer kleinen tschechischen Firma, Pipetail. Es ist ihnen gelungen, eine wunderbare Liste [manchmal banaler, aber dennoch] sehr dringender Probleme und Missverständnisse im Zusammenhang mit dem Betrieb von Kubernetes-Clustern zusammenzustellen.

10 häufige Fehler bei der Verwendung von Kubernetes

Im Laufe der Jahre, in denen wir Kubernetes verwenden, haben wir mit einer großen Anzahl von Clustern (sowohl verwalteten als auch nicht verwalteten – auf GCP, AWS und Azure) gearbeitet. Mit der Zeit bemerkten wir, dass sich einige Fehler ständig wiederholten. Das ist jedoch keine Schande: Die meisten davon haben wir selbst gemacht!

Der Artikel enthält die häufigsten Fehler und nennt auch deren Behebung.

1. Ressourcen: Anforderungen und Grenzen

Dieser Artikel verdient definitiv die größte Aufmerksamkeit und den ersten Platz auf der Liste.

Normalerweise CPU-Anforderung entweder gar nicht angegeben oder hat einen sehr niedrigen Wert (um so viele Pods wie möglich auf jedem Knoten zu platzieren). Dadurch werden die Knoten überlastet. In Zeiten hoher Auslastung wird die Rechenleistung des Knotens voll ausgenutzt und eine bestimmte Arbeitslast erhält nur das, was sie „anfordert“. CPU-Drosselung. Dies führt zu einer erhöhten Anwendungslatenz, Zeitüberschreitungen und anderen unangenehmen Folgen. (Lesen Sie mehr darüber in unserer anderen aktuellen Übersetzung: „CPU-Limits und aggressive Drosselung in Kubernetes" - ca. übersetzt)

BestEffort (äußerst nicht empfohlen):

resources: {}

Extrem niedrige CPU-Anforderung (extrem nicht empfohlen):

   resources:
      Requests:
        cpu: "1m"

Andererseits kann das Vorhandensein eines CPU-Limits dazu führen, dass Pods unangemessen Taktzyklen überspringen, selbst wenn der Knotenprozessor nicht vollständig ausgelastet ist. Auch dies kann zu erhöhten Verzögerungen führen. Die Kontroverse um den Parameter geht weiter CPU-CFS-Kontingent im Linux-Kernel und CPU-Drosselung abhängig von den eingestellten Limits sowie Deaktivierung des CFS-Kontingents... Leider können CPU-Limits mehr Probleme verursachen als sie lösen können. Weitere Informationen hierzu finden Sie unter dem untenstehenden Link.

Übermäßige Auswahl (übermäßiges Engagement) Speicherprobleme können zu größeren Problemen führen. Das Erreichen des CPU-Limits führt zum Überspringen von Taktzyklen, während das Erreichen des Speicherlimits zum Abbruch des Pods führt. Haben Sie schon einmal beobachtet? OOMkill? Ja, genau darüber reden wir.

Möchten Sie die Wahrscheinlichkeit, dass dies geschieht, minimieren? Weisen Sie nicht zu viel Speicher zu und nutzen Sie die garantierte QoS (Quality of Service), indem Sie die Speicheranforderung auf das Limit festlegen (wie im Beispiel unten). Lesen Sie mehr dazu in Henning Jacobs Präsentationen (Leitender Ingenieur bei Zalando).

Berstbar (höhere Chance, von OOM getötet zu werden):

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

Garantierter:

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

Was hilft möglicherweise beim Einrichten von Ressourcen?

Mit Metrik-Server Sie können den aktuellen CPU-Ressourcenverbrauch und die Speichernutzung nach Pods (und darin enthaltenen Containern) sehen. Höchstwahrscheinlich verwenden Sie es bereits. Führen Sie einfach die folgenden Befehle aus:

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

Sie zeigen jedoch nur die aktuelle Nutzung an. Es kann Ihnen eine ungefähre Vorstellung von der Größenordnung geben, aber letztendlich werden Sie es brauchen Verlauf der Änderungen der Metriken im Laufe der Zeit (um Fragen zu beantworten wie: „Wie hoch war die maximale CPU-Auslastung?“, „Wie hoch war die Auslastung gestern Morgen?“ usw.). Hierfür können Sie verwenden Prometheus, DataDog und andere Werkzeuge. Sie erhalten einfach Metriken vom Metrikserver und speichern sie, und der Benutzer kann sie abfragen und entsprechend grafisch darstellen.

VerticalPodAutoscaler ermöglicht automatisieren dieser Prozess. Es verfolgt den CPU- und Speichernutzungsverlauf und richtet auf der Grundlage dieser Informationen neue Anforderungen und Grenzwerte ein.

Die effiziente Nutzung der Rechenleistung ist keine leichte Aufgabe. Es ist, als würde man ständig Tetris spielen. Wenn Sie zu viel für Rechenleistung bei niedrigem Durchschnittsverbrauch (z. B. ~10 %) bezahlen, empfehlen wir Ihnen, sich Produkte anzusehen, die auf AWS Fargate oder Virtual Kubelet basieren. Sie basieren auf einem serverlosen/Pay-per-Use-Abrechnungsmodell, das sich unter solchen Bedingungen als günstiger erweisen kann.

2. Liveness- und Bereitschaftssonden

Standardmäßig sind Liveness- und Bereitschaftsprüfungen in Kubernetes nicht aktiviert. Und manchmal vergessen sie, sie einzuschalten ...

Aber wie sonst kann man im Falle eines schwerwiegenden Fehlers einen Neustart des Dienstes veranlassen? Und woher weiß der Load Balancer, dass ein Pod bereit ist, Datenverkehr anzunehmen? Oder dass es mehr Verkehr bewältigen kann?

Diese Tests werden oft miteinander verwechselt:

  • Lebendigkeit — „Überlebensfähigkeitsprüfung“, die den Pod neu startet, wenn er ausfällt;
  • Bereitschaft — Bereitschaftsprüfung: Wenn sie fehlschlägt, wird der Pod vom Kubernetes-Dienst getrennt (dies kann mit überprüft werden). kubectl get endpoints) und der Verkehr kommt dort erst an, wenn die nächste Prüfung erfolgreich abgeschlossen wurde.

Beide Kontrollen WIRD WÄHREND DES GESAMTEN LEBENSZYKLUS DES POD DURCHGEFÜHRT. Es ist sehr wichtig.

Ein häufiges Missverständnis besteht darin, dass Bereitschaftsprüfungen nur beim Start ausgeführt werden, damit der Balancer wissen kann, dass der Pod bereit ist (Ready) und kann mit der Verarbeitung des Datenverkehrs beginnen. Dies ist jedoch nur eine der Einsatzmöglichkeiten.

Eine weitere Möglichkeit besteht darin, herauszufinden, dass der Verkehr auf dem Pod übermäßig hoch ist und überlastet es (oder der Pod führt ressourcenintensive Berechnungen durch). In diesem Fall hilft der Bereitschaftscheck Reduzieren Sie die Belastung des Pods und „kühlen“ Sie ihn ab. Der erfolgreiche Abschluss einer Bereitschaftsprüfung in der Zukunft ermöglicht Erhöhen Sie die Belastung des Pods erneut. In diesem Fall (wenn der Bereitschaftstest fehlschlägt) wäre ein Scheitern des Lebendigkeitstests sehr kontraproduktiv. Warum einen Pod neu starten, der gesund ist und hart arbeitet?

Daher ist es in manchen Fällen besser, überhaupt keine Prüfungen durchzuführen, als sie mit falsch konfigurierten Parametern zu aktivieren. Wie oben erwähnt, wenn Lebendigkeitsprüfung Kopien Bereitschaftsprüfung, dann bist du in großen Schwierigkeiten. Mögliche Option ist die Konfiguration Nur BereitschaftstestUnd gefährliche Lebendigkeit beiseite lassen.

Beide Arten von Prüfungen sollten nicht fehlschlagen, wenn gemeinsame Abhängigkeiten fehlschlagen, da dies sonst zu einem kaskadierenden (lawinenartigen) Ausfall aller Pods führt. Mit anderen Worten, schade dir nicht.

3. LoadBalancer für jeden HTTP-Dienst

Höchstwahrscheinlich verfügen Sie in Ihrem Cluster über HTTP-Dienste, die Sie an die Außenwelt weiterleiten möchten.

Wenn Sie den Dienst als öffnen type: LoadBalancer, sein Controller (abhängig vom Dienstanbieter) stellt einen externen LoadBalancer bereit und verhandelt ihn (der nicht unbedingt auf L7, sondern sogar auf L4 läuft), und dies kann sich auf die Kosten auswirken (externe statische IPv4-Adresse, Rechenleistung, Abrechnung pro Sekunde). ) aufgrund der Notwendigkeit, eine große Anzahl solcher Ressourcen zu erstellen.

In diesem Fall ist es viel logischer, einen externen Load Balancer zu verwenden und Dienste als zu öffnen type: NodePort. Oder noch besser, erweitern Sie so etwas wie Nginx-Ingress-Controller (oder Verkehr), der der Einzige sein wird KnotenPort Endpunkt, der mit dem externen Load Balancer verknüpft ist und den Datenverkehr im Cluster weiterleitet Eintritt-Kubernetes-Ressourcen.

Andere Cluster-interne (Mikro-)Dienste, die miteinander interagieren, können über Dienste wie „kommunizieren“. ClusterIP und ein integrierter Diensterkennungsmechanismus über DNS. Verwenden Sie einfach nicht ihr öffentliches DNS/IP, da dies die Latenz beeinträchtigen und die Kosten für Cloud-Dienste erhöhen kann.

4. Automatische Skalierung eines Clusters ohne Berücksichtigung seiner Funktionen

Beim Hinzufügen und Entfernen von Knoten zu einem Cluster sollten Sie sich nicht auf einige grundlegende Metriken wie die CPU-Auslastung dieser Knoten verlassen. Bei der Pod-Planung müssen viele Faktoren berücksichtigt werden Beschränkungen, wie Pod/Knoten-Affinität, Taints und Toleranzen, Ressourcenanforderungen, QoS usw. Die Verwendung eines externen Autoscalings, das diese Nuancen nicht berücksichtigt, kann zu Problemen führen.

Stellen Sie sich vor, dass ein bestimmter Pod geplant werden soll, aber die gesamte verfügbare CPU-Leistung vom Pod angefordert/zerlegt wird bleibt in einem Zustand stecken Pending. Der externe Autoscaler erkennt die durchschnittliche aktuelle CPU-Auslastung (nicht die angeforderte) und leitet keine Erweiterung ein (Scale-out) - Fügt keinen weiteren Knoten hinzu. Daher wird dieser Pod nicht geplant.

In diesem Fall umgekehrte Skalierung (Skalierung) – Das Entfernen eines Knotens aus einem Cluster ist immer schwieriger zu implementieren. Stellen Sie sich vor, Sie verfügen über einen zustandsbehafteten Pod (mit angeschlossenem persistenten Speicher). Persistente Bände gehören normalerweise dazu spezifische Verfügbarkeitszone und werden in der Region nicht repliziert. Wenn also ein externer Autoscaler einen Knoten mit diesem Pod löscht, kann der Scheduler diesen Pod nicht auf einem anderen Knoten einplanen, da dies nur in der Verfügbarkeitszone möglich ist, in der sich der persistente Speicher befindet. Der Pod bleibt im Status hängen Pending.

Sehr beliebt in der Kubernetes-Community Cluster-Autoscaler. Es läuft auf einem Cluster, unterstützt APIs großer Cloud-Anbieter, berücksichtigt alle Einschränkungen und ist in den oben genannten Fällen skalierbar. Es ist außerdem in der Lage, unter Beibehaltung aller festgelegten Grenzwerte zu skalieren und so Geld zu sparen (das andernfalls für ungenutzte Kapazität ausgegeben würde).

5. IAM/RBAC-Funktionen werden vernachlässigt

Hüten Sie sich vor der Verwendung von IAM-Benutzern mit dauerhaften Geheimnissen für Maschinen und Anwendungen. Organisieren Sie den temporären Zugriff mithilfe von Rollen und Dienstkonten (Dienstkonten).

Wir stoßen häufig auf die Tatsache, dass Zugriffsschlüssel (und Geheimnisse) in der Anwendungskonfiguration fest codiert sind und dass die Rotation von Geheimnissen trotz Zugriff auf Cloud IAM vernachlässigt wird. Verwenden Sie gegebenenfalls IAM-Rollen und Dienstkonten anstelle von Benutzern.

10 häufige Fehler bei der Verwendung von Kubernetes

Vergessen Sie kube2iam und wechseln Sie direkt zu IAM-Rollen für Dienstkonten (wie in beschrieben). gleichnamige Notiz Š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

Eine Anmerkung. Nicht so schwer, oder?

Gewähren Sie Dienstkonten und Instanzprofilen außerdem keine Berechtigungen admin и cluster-adminwenn sie es nicht brauchen. Dies ist insbesondere in RBAC K8s etwas schwieriger zu implementieren, aber die Mühe lohnt sich auf jeden Fall.

6. Verlassen Sie sich nicht auf die automatische Anti-Affinität für Pods

Stellen Sie sich vor, Sie haben drei Replikate einer Bereitstellung auf einem Knoten. Der Knoten fällt und mit ihm alle Replikate. Unangenehme Situation, oder? Aber warum befanden sich alle Replikate auf demselben Knoten? Soll Kubernetes nicht Hochverfügbarkeit (HA) bieten?!

Leider hält sich der Kubernetes-Scheduler aus eigener Initiative nicht an die Regeln der separaten Existenz (Anti-Affinität) für Schoten. Sie müssen ausdrücklich angegeben werden:

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

Das ist alles. Jetzt werden Pods auf verschiedenen Knoten geplant (diese Bedingung wird nur während der Planung überprüft, nicht jedoch während ihres Betriebs – daher). requiredDuringSchedulingIgnoredDuringExecution).

Hier reden wir darüber podAntiAffinity auf verschiedenen Knoten: topologyKey: "kubernetes.io/hostname", - und nicht um unterschiedliche Verfügbarkeitszonen. Um eine vollwertige HA zu implementieren, müssen Sie sich tiefer mit diesem Thema befassen.

7. PodDisruptionBudgets ignorieren

Stellen Sie sich vor, Sie haben eine Produktionslast auf einem Kubernetes-Cluster. In regelmäßigen Abständen müssen Knoten und der Cluster selbst aktualisiert (oder außer Betrieb genommen) werden. PodDisruptionBudget (PDB) ist so etwas wie eine Servicegarantievereinbarung zwischen Clusteradministratoren und Benutzern.

Mit PDB können Sie Dienstunterbrechungen aufgrund fehlender Knoten vermeiden:

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

In diesem Beispiel sagen Sie als Benutzer des Clusters den Administratoren: „Hey, ich habe einen Zookeeper-Dienst, und egal, was Sie tun, ich möchte, dass immer mindestens zwei Replikate dieses Dienstes verfügbar sind.“

Mehr dazu können Sie hier lesen hier.

8. Mehrere Benutzer oder Umgebungen in einem gemeinsamen Cluster

Kubernetes-Namespaces (Namensräume) bieten keine starke Isolierung.

Ein häufiges Missverständnis besteht darin, dass, wenn Sie eine Nicht-Produkt-Last in einem Namespace und eine Produkt-Last in einem anderen bereitstellen, dies der Fall ist werden sich in keiner Weise gegenseitig beeinflussen... Ein gewisses Maß an Isolation kann jedoch durch Ressourcenanforderungen/-beschränkungen, das Festlegen von Kontingenten und das Festlegen von Prioritätsklassen erreicht werden. Eine gewisse „physische“ Isolation auf der Datenebene wird durch Affinitäten, Toleranzen, Taints (oder Knotenselektoren) bereitgestellt, aber eine solche Trennung ist durchaus möglich schwierig implementieren.

Wer beide Arten von Workloads im selben Cluster kombinieren muss, muss sich mit der Komplexität auseinandersetzen. Wenn kein solcher Bedarf besteht und Sie es sich leisten können, einen zu haben noch ein Cluster (z. B. in einer öffentlichen Cloud), dann ist es besser, dies zu tun. Dadurch wird ein wesentlich höherer Isolationsgrad erreicht.

9. externalTrafficPolicy: Cluster

Sehr oft sehen wir, dass der gesamte Datenverkehr innerhalb des Clusters über einen Dienst wie NodePort erfolgt, für den die Standardrichtlinie festgelegt ist externalTrafficPolicy: Cluster. Dies bedeutet, dass KnotenPort ist auf jedem Knoten im Cluster geöffnet, und Sie können jeden von ihnen verwenden, um mit dem gewünschten Dienst (Satz von Pods) zu interagieren.

10 häufige Fehler bei der Verwendung von Kubernetes

Gleichzeitig sind echte Pods, die mit dem oben genannten NodePort-Dienst verbunden sind, normalerweise nur an einem bestimmten Ort verfügbar Teilmenge dieser Knoten. Mit anderen Worten: Wenn ich mich mit einem Knoten verbinde, der nicht über den erforderlichen Pod verfügt, leitet dieser den Datenverkehr an einen anderen Knoten weiter. einen Hopfen hinzufügen und zunehmende Latenz (wenn sich Knoten in verschiedenen Verfügbarkeitszonen/Rechenzentren befinden, kann die Latenz recht hoch sein; außerdem steigen die Kosten für den ausgehenden Datenverkehr).

Andererseits, wenn ein bestimmter Kubernetes-Dienst über einen Richtliniensatz verfügt externalTrafficPolicy: Local, dann wird NodePort nur auf den Knoten geöffnet, auf denen die erforderlichen Pods tatsächlich ausgeführt werden. Bei Verwendung eines externen Load Balancers, der den Zustand prüft (Gesundheitscheck) Endpunkte (wie funktioniert das? AWS-ELB), Er sendet Datenverkehr nur an die erforderlichen Knoten, was sich positiv auf Verzögerungen, Rechenbedarf und Ausgangsrechnungen auswirken wird (und der gesunde Menschenverstand schreibt dasselbe vor).

Es besteht eine hohe Wahrscheinlichkeit, dass Sie so etwas bereits verwenden Verkehr oder Nginx-Ingress-Controller als NodePort-Endpunkt (oder LoadBalancer, der auch NodePort verwendet) zum Weiterleiten von HTTP-Ingress-Verkehr. Durch das Festlegen dieser Option kann die Latenz für solche Anforderungen erheblich reduziert werden.

В dieser Veröffentlichung Erfahren Sie mehr über externalTrafficPolicy und seine Vor- und Nachteile.

10. Lassen Sie sich nicht an Cluster binden und missbrauchen Sie die Kontrollebene nicht

Früher war es üblich, Server mit Eigennamen zu bezeichnen: Anton, HAL9000 und Colossus... Heute wurden sie durch zufällig generierte Identifikatoren ersetzt. Die Gewohnheit blieb jedoch bestehen, und jetzt gehen Eigennamen an Cluster.

Eine typische Geschichte (basierend auf realen Ereignissen): Alles begann mit einem Proof of Concept, daher hatte der Cluster einen stolzen Namen Natürlich sind wir auch auf Facebook zu finden: <br> <a href="https://www.facebook.com/tijhof.de" target="_blank" rel="noopener"><img class="alignleft wp-image-15850 size-full" src="https://tijhof.nl/wp-content/uploads/2024/03/facebookGmBh.png" alt="" width="250" height="50"></a>… Jahre sind vergangen und es wird immer noch in der Produktion verwendet, und jeder hat Angst, es zu berühren.

Es macht keinen Spaß, Cluster in Haustiere zu verwandeln. Wir empfehlen daher, sie während des Übens regelmäßig zu entfernen Notfallwiederherstellung (das wird helfen Chaos-Engineering - ca. übersetzt). Darüber hinaus würde es nicht schaden, auf der Kontrollschicht zu arbeiten (Kontrollebene). Angst zu haben, ihn zu berühren, ist kein gutes Zeichen. usw tot? Leute, ihr seid wirklich in Schwierigkeiten!

Andererseits sollte man sich nicht zu Manipulationen hinreißen lassen. Mit der Zeit Die Steuerschicht kann langsam werden. Dies ist höchstwahrscheinlich darauf zurückzuführen, dass eine große Anzahl von Objekten ohne Rotation erstellt wird (eine häufige Situation bei der Verwendung von Helm mit Standardeinstellungen, weshalb sein Status in configmaps/secrets nicht aktualisiert wird – als Ergebnis sammeln sich Tausende von Objekten an der Kontrollschicht) oder mit ständiger Bearbeitung von Kube-API-Objekten (für automatische Skalierung, für CI/CD, für Überwachung, Ereignisprotokolle, Controller usw.).

Darüber hinaus empfehlen wir, die SLA/SLO-Vereinbarungen mit dem verwalteten Kubernetes-Anbieter zu prüfen und auf die Garantien zu achten. Der Anbieter kann garantieren Verfügbarkeit der Kontrollschicht (oder seine Unterkomponenten), aber nicht die p99-Verzögerung der Anfragen, die Sie an ihn senden. Mit anderen Worten: Sie können teilnehmen kubectl get nodes, und erhalten Sie erst nach 10 Minuten eine Antwort, und dies stellt keinen Verstoß gegen die Bedingungen der Servicevereinbarung dar.

11. Bonus: Verwendung des neuesten Tags

Aber das ist schon ein Klassiker. In letzter Zeit sind wir seltener auf diese Technik gestoßen, da viele, die aus bitterer Erfahrung gelernt haben, aufgehört haben, das Tag zu verwenden :latest und begann, Versionen anzuheften. Hurra!

ECR sorgt für die Unveränderlichkeit von Bild-Tags; Wir empfehlen Ihnen, sich mit dieser bemerkenswerten Funktion vertraut zu machen.

Zusammenfassung

Erwarten Sie nicht, dass alles über Nacht funktioniert: Kubernetes ist kein Allheilmittel. Schlechte App wird auch in Kubernetes so bleiben (und es wird wahrscheinlich noch schlimmer werden). Nachlässigkeit führt zu übermäßiger Komplexität, langsamer und stressiger Arbeit der Kontrollebene. Darüber hinaus besteht die Gefahr, dass Sie keine Disaster-Recovery-Strategie haben. Erwarten Sie nicht, dass Kubernetes sofort Isolation und Hochverfügbarkeit bietet. Nehmen Sie sich etwas Zeit, um Ihre Anwendung wirklich cloudnativ zu gestalten.

Sie können sich mit den erfolglosen Erfahrungen verschiedener Teams vertraut machen diese Sammlung von Geschichten von Henning Jacobs.

Wer die Fehlerliste in diesem Artikel ergänzen möchte, kann uns auf Twitter kontaktieren (@MarekBartik, @MstrsObserver).

PS vom Übersetzer

Lesen Sie auch auf unserem Blog:

Source: habr.com

Kommentar hinzufügen