Richtiger Vergleich von Kubernetes Apply, Replace und Patch

Für Kubernetes gibt es mehrere Möglichkeiten, Ressourcen zu aktualisieren: apply, edit, patch und replace. Es gibt Verwirrung darüber, was jeder von ihnen tut und wann man sie verwenden sollte. Lassen Sie uns das klären.

Richtiger Vergleich von Kubernetes Apply, Replace und Patch

Wenn in Google suchen den Begriff "kubernetes apply vs replace", findet eine Antwort auf StackOverflow, die nicht korrekt ist. Bei der Suche nach "kubernetes apply vs patch" ist der erste Link die Dokumentation zu kubectl patch, die keinen Vergleich beinhaltet apply und patch. In diesem Artikel werden die verschiedenen Optionen sowie die richtige Verwendung jeder von ihnen behandelt.

Im Verlauf des Lebenszyklus einer Kubernetes-Ressource (z. B. Service, Deployment, Ingress usw.) muss manchmal ein bestimmtes Attribut dieser Ressource geändert, hinzugefügt oder entfernt werden. Zum Beispiel eine Notiz hinzufügen, die Anzahl der Replikate erhöhen oder verringern.

Kubernetes CLI

Wenn Sie bereits mit Kubernetes-Clustern über die CLI arbeiten, sind Sie bereits vertraut mit apply und edit. Der Befehl apply liest die Ressourcenspezifikation aus einer Datei und führt ein "Upsert" im Kubernetes-Cluster durch, d. h. er erstellt die Ressource, wenn sie nicht vorhanden ist, und aktualisiert sie, wenn sie existiert. Der Befehl edit liest die Ressource über die API und schreibt die Ressourcenspezifikation in eine lokale Datei, die dann in einem Texteditor geöffnet wird. Nachdem Sie die Datei bearbeitet und gespeichert haben, kubectl sendet die Änderungen über die API zurück, die sich um die Anwendung dieser Änderungen an der Ressource kümmert.

Nicht jeder kennt die Befehle patch und replace. Der Befehl patch ermöglicht die Änderung eines Teils der Ressourcenspezifikation, indem nur der ändernde Teil in der Befehlszeile übergeben wird. Der Befehl replace funktioniert ähnlich wie edit, aber alles muss manuell gemacht werden: Sie müssen die aktuelle Version der Ressourcenspezifikation herunterladen, z. B. mit Hilfe von kubectl get -o yaml, sie bearbeiten und dann verwenden replace , um die Ressource mit der geänderten Spezifikation zu aktualisieren. Der Befehl replace funktioniert nicht, wenn zwischen dem Lesen und dem Ersetzen der Ressource Änderungen aufgetreten sind.

Kubernetes API

Sie sind wahrscheinlich mit den Methoden CoreV1().Pods().Update(), replaceNamespacedService oder patch_namespaced_deployment, vertraut, wenn Sie mit Clustern über eine Client-Bibliothek für die Kubernetes-API unter Verwendung einer Programmiersprache arbeiten. Die Bibliothek behandelt diese Methoden durch HTTP-Anfragen unter Verwendung der Methoden PUT und PATCH. Dabei nutzt update und replace genutzt wird. PUT, und patch, wie banal es auch erscheinen mag, auch PATCH.

Es ist erwähnenswert, dass kubectl arbeitet ebenfalls mit Clustern über die API. Mit anderen Worten, kubectl– ist eine Wrapper-Bibliothek über der Client-Bibliothek für die Go-Sprache, die erhebliche Möglichkeiten bietet, Unterbefehle kompakter und lesbarer im Vergleich zu den Standard-API-Funktionen bereitzustellen. Zum Beispiel, wie Sie vielleicht bereits bemerkt haben, die Methode apply wurde im vorherigen Absatz nicht erwähnt. Aktuell (Mai 2020, Anm. des Übersetzers) funktioniert die gesamte Logik kubectl apply, d.h. das Erstellen nicht existierender Ressourcen und das Aktualisieren bestehender läuft vollständig auf der Code-Seite kubectl. Es werden Anstrengungen unternommen die Logik apply auf die API-Seite zu verlagern, aber es befindet sich noch in der Beta-Testphase. Ich werde es unten näher ausführen.

Standard-Patch

Es ist am besten anzuwenden patch, wenn Sie eine Ressource aktualisieren möchten. So funktionieren sowohl die Client-Bibliotheken über der Kubernetes-API als auch kubectl (was nicht überrascht, da es eine Wrapper-Bibliothek ist, Anm. des Übersetzers).

Strategisch arbeiten

Alle Befehle kubectl apply, edit und patch verwenden die Methode PATCH in HTTP-Anfragen, um eine bestehende Ressource zu aktualisieren. Wenn man tiefer in die Implementierung der Befehle eintaucht, wird deutlich, dass in allen der Ansatz strategic-merge patching zur Aktualisierung von Ressourcen verwendet wird, obwohl der Befehl patch auch andere Ansätze nutzen kann (darüber später mehr). Der Ansatz des strategic-merge patching versucht, "alles richtig zu machen" beim Zusammenführen der bereitgestellten Spezifikation mit der bestehenden Spezifikation. Genauer gesagt versucht er, sowohl Objekte als auch Arrays zu kombinieren, was bedeutet, dass Änderungen in der Regel additiv sind. Zum Beispiel führt der Befehl patch mit einer neuen Umgebungsvariable in der Spezifikation des Pod-Containers dazu, dass diese Umgebungsvariable zu den bestehenden Umgebungsvariablen hinzugefügt wird, anstatt sie zu überschreiben. Um über diesen Ansatz zu löschen, muss der Parameterwert in der bereitgestellten Spezifikation auf null gesetzt werden. Welche der Befehle kubectl für Aktualisierungen sollten am besten verwendet werden?

Wenn Sie Ihre Ressourcen mit kubectl applyerstellen und verwalten, sollten Sie beim Aktualisieren immer kubectl applyviele verschiedene gemeinsame Schlüsselpaarungen generiert (oder viele solcher Paare aus einem gemeinsamen Seed ableitet). Das ist aber lästig und kubectl verwaltete Konfiguration und erkennt korrekt angeforderte Änderungen von Anwendung zu Anwendung. Der Vorteil bei der ständigen Verwendung von apply liegt darin, dass er die zuvor angewandte Spezifikation verfolgt und somit weiß, wann Spezifikationsattribute und Array-Elemente ausdrücklich entfernt werden. Dies ermöglicht die Verwendung apply um Eigenschaften und Elemente eines Arrays zu entfernen, während normale strategische Zusammenführungen nicht funktionieren. Die Befehle edit und patch aktualisieren keine Anmerkungen, die kubectl apply verwendet werden, um ihre Änderungen zu verfolgen, daher sind alle Änderungen, die über die Kubernetes-API nachverfolgt und vorgenommen werden, aber über die Befehle edit und patch, für nachfolgende Befehle unsichtbar apply, das heißt apply entfernt sie nicht, selbst wenn sie nicht in der Eingabespezifikation erscheinen für apply (In der Dokumentation steht, dass edit und patch Aktualisierungen der Anmerkungen vorgenommen werden, die apply, aber in der Praxis – nein).

Wenn Sie den Befehl nicht verwenden apply, können Sie verwenden als edit, als auch patch, indem Sie den Befehl wählen, der am besten zum Änderungen passt. Beim Hinzufügen und Ändern der Eigenschaften der Spezifikation sind beide Ansätze etwa gleich. Beim Entfernen von Eigenschaften der Spezifikation oder Elementen eines Arrays edit verhält es sich wie ein einmaliger Lauf apply, einschließlich der Verfolgung, wie die Spezifikation vor und nach ihrer Bearbeitung war, sodass Sie explizit Eigenschaften und Elemente eines Arrays aus der Ressource entfernen können. Sie müssen den Wert der Eigenschaft in der Spezifikation für patch, auf null setzen, um ihn aus der Ressource zu entfernen. Das Entfernen eines Array-Elements mit strategischer Zusammenführungs-Patching ist komplexer, da die Verwendung von Zusammenführungsanweisungen erforderlich ist. Siehe andere Ansätze zur Aktualisierung unten, um geeignetere Alternativen auszuwählen.

Um in der Client-Bibliothek Methoden zur Aktualisierung zu implementieren, die sich ähnlich wie die oben genannten Befehle verhalten, kubectl, sollten Sie im Anfrage die content-type in application/strategic-merge-patch+json. Wenn Sie Eigenschaften in der Spezifikation entfernen möchten, müssen Sie deren Werte explizit auf null setzen, ähnlich wie kubectl patch. Wenn Sie Array-Elemente entfernen müssen, sollten Sie die Zusammenführungsanweisungen in die Aktualisierungsspezifikation einfügen oder einen anderen Ansatz für die Aktualisierungen verwenden.

Weitere Ansätze zur Aktualisierung

In Kubernetes werden zwei weitere Ansätze zur Aktualisierung unterstützt: JSON-Merge-Patch und JSON-Patch. Der JSON Merge Patch-Ansatz akzeptiert eine teilweise Spezifikation von Kubernetes als Eingabewerte und unterstützt das Zusammenführen von Objekten ähnlich dem Ansatz des strategic-merge Patching. Der Unterschied zwischen ihnen besteht darin, dass er nur die Ersetzung von Arrays unterstützt, einschließlich des Container-Arrays in der Pod-Spezifikation. Das bedeutet, dass Sie beim Einsatz von JSON Merge Patch vollständige Spezifikationen für alle Container bereitstellen müssen, falls Sie eine Eigenschaft eines Containers ändern möchten. Daher ist dieser Ansatz nützlich, um Elemente aus einem Array in der Spezifikation zu entfernen. In der Kommandozeile können Sie JSON Merge Patch mit kubectl patch --type=merge. Bei der Arbeit mit der Kubernetes-API sollte die Anfragemethode verwendet werden PATCH und eingestellt werden content-type in application/merge-patch+json.

Der JSON Patch-Ansatz verwendet anstelle einer teilweisen Ressourcenspezifikation eine Bereitstellung von Änderungen, die Sie an der Ressource vornehmen möchten, in Form eines Arrays, wobei jedes Array-Element eine Beschreibung der Änderung darstellt, die an der Ressource vorgenommen wird. Dieser Ansatz ist eine flexiblere und leistungsstärkere Möglichkeit, die vorgenommenen Änderungen auszudrücken, da die Liste der Änderungen in einem separaten, nicht Kubernetes-Format vorliegt, anstatt eine teilweise Ressourcenspezifikation zu senden. In kubectl können Sie JSON Patch verwenden, indem Sie kubectl patch --type=json. Bei Verwendung der Kubernetes-API funktioniert dieser Ansatz mit der Anfragemethode PATCH und eingestellt werden content-type in application/json-patch+json.

Brauchen Sie Verlässlichkeit – verwenden Sie Replace

In einigen Fällen ist es notwendig, sicherzustellen, dass zwischen dem Lesen der Ressource und deren Aktualisierung keine Änderungen an der Ressource vorgenommen werden. Mit anderen Worten, es ist zu gewährleisten, dass alle Änderungen atomar sind. In diesem Fall sollte zum Aktualisieren von Ressourcen replace. Zum Beispiel, wenn es ein ConfigMap mit einem Zähler gibt, der von mehreren Quellen aktualisiert wird, sollte sichergestellt werden, dass zwei Quellen den Zähler nicht gleichzeitig aktualisieren, was zu Verlusten bei den Aktualisierungen führen könnte. Stellen Sie sich zur Veranschaulichung folgendes Szenario vor, in dem der Ansatz patch:

  • A und B den aktuellen Zustand der Ressource von der API abrufen
  • Jeder von ihnen aktualisiert lokal die Spezifikation und erhöht den Zähler um eins und fügt entsprechend 'A' oder 'B' in die Notiz 'updated-by' ein.
  • A aktualisiert die Ressource etwas schneller
  • B aktualisiert die Ressource

Das Update A wurde verloren. Letzte Operation. patch gewinnt, der Zähler erhöht sich um eins anstelle von zwei, und der Wert des Hinweises "updated-by" endet mit "B" und enthält nicht "A". Lassen Sie uns das oben Genannte mit dem vergleichen, was passiert, wenn Updates mit der Methode ausgeführt werden replace:

  • A und B den aktuellen Zustand der Ressource von der API abrufen
  • Jeder von ihnen aktualisiert lokal die Spezifikation und erhöht den Zähler um eins und fügt entsprechend 'A' oder 'B' in die Notiz 'updated-by' ein.
  • A aktualisiert die Ressource etwas schneller
  • B versucht, die Ressource zu aktualisieren, aber das Update wird von der API abgelehnt, weil die Version der Ressource in der Spezifikation replace nicht mit der aktuellen Version der Ressource in Kubernetes übereinstimmt, da die Version der Ressource bei der Ausführung der Ersetzungsoperation seitens A erhöht wurde.

In dem oben genannten Fall muss B die Ressource erneut abrufen, die Änderungen am neuen Zustand vornehmen und es erneut versuchen replace. Infolgedessen wird der Zähler um zwei erhöht, und der Hinweis "updated-by" wird "AB" am Ende enthalten.

Das oben genannte Beispiel setzt voraus, dass bei der Ausführung von replace eine vollständige Ersetzung der gesamten Ressource erfolgt. Die für replace, verwendete Spezifikation muss vollständig sein, nicht teilweise oder in Teilen wie in apply, sondern vollständig, einschließlich der Hinzufügung resourceVersion zu den Metadaten der Spezifikation. Wenn Sie nicht resourceVersion einfügen oder die von Ihnen angegebene Version nicht aktuell ist, wird die Ersetzung abgelehnt. Daher ist der beste Ansatz zur Nutzung replace – die Ressource abzurufen, sie zu aktualisieren und sie sofort zu ersetzen. Mit kubectlkann es so aussehen:

$ kubectl get deployment my-deployment -o json 
    | jq '.spec.template.spec.containers[0].env[1].value = "neuer Wert"' 
    | kubectl replace -f -

Es ist jedoch zu beachten, dass die folgenden zwei Befehle, die nacheinander ausgeführt werden, erfolgreich ausgeführt werden, da deployment.yaml nicht die Eigenschaft .metadata.resourceVersion

$ kubectl create -f deployment.yaml
$ kubectl replace -f deployment.yaml

Es scheint, als widerspreche dies dem, was oben gesagt wurde, d.h. "Hinzufügen von resourceVersion zu den Metadaten der Spezifikation". Ist es falsch, so zu behaupten? Nein, das ist es nicht, denn wenn kubectl feststellt, dass Sie nicht angegeben haben resourceVersion, wird er es aus der Ressource lesen und zu der von Ihnen angegebenen Spezifikation hinzufügen und erst dann replace. Da dies potenziell gefährlich ist, wenn man sich auf Atomarität verlässt, funktioniert diese Magie vollständig auf der Seite kubectl, sollten Sie sich nicht darauf verlassen, wenn Sie Client-Bibliotheken verwenden, die mit der API arbeiten. In diesem Fall müssen Sie die aktuelle Spezifikation der Ressource lesen, sie aktualisieren und dann einen PUT Antrag stellen.

Ein Patch kann nicht durchgeführt werden – es wird ersetzt.

Manchmal müssen Änderungen vorgenommen werden, die nicht über die API verarbeitet werden können. In solchen Fällen kann man den Ressourcenwechsel erzwingen, indem man sie löscht und neu erstellt. Dies geschieht mit Hilfe von kubectl replace --force. Der Befehl entfernt sofort die Ressourcen und erstellt sie anschließend mit der bereitgestellten Spezifikation neu. Es gibt keinen "erzwungenen Ersetzungs"-Handler in der API, und um dies über die API zu tun, sind zwei Schritte erforderlich. Zunächst muss die Ressource gelöscht werden, indem man für sie gracePeriodSeconds auf null (0) und propagationPolicy auf „Background“ einstellt und dann die Ressource mit der gewünschten Spezifikation neu erstellt.

Achtung: Dieser Ansatz ist potenziell gefährlich und kann zu einem undefinierten Zustand führen.

Apply auf der Serverseite

Wie oben erwähnt, arbeiten die Kubernetes-Entwickler an der Implementierung von Logik apply aus kubectl in der Kubernetes-API. Die Logik apply ist in Kubernetes 1.18 über kubectl apply --server-side oder über die API unter Verwendung der Methode PATCH c content-type application/apply-patch+YAML.

Hinweis: JSON ist ebenfalls valides YAML, sodass die Spezifikation auch im JSON-Format gesendet werden kann, selbst wenn content-type schrittweise gestartet application/apply-patch+yaml.

Darüber hinaus wird die Logik kubectl allen über die API zugänglich, apply verfolgt die Verantwortlichen für die Felder in der Spezifikation auf der Serverseite, wodurch ein sicherer Mehrbenutzerzugriff für die konfliktfreie Bearbeitung ermöglicht wird. Anders ausgedrückt, wenn apply auf der Serverseite breiter verbreitet wird, entsteht eine universelle, sichere Schnittstelle zur Ressourcenverwaltung für verschiedene Clients, wie zum Beispiel kubectl, Pulumi oder Terraform, GitOps sowie eigene Skripte, die Client-Bibliotheken verwenden.

Ergebnisse

Ich hoffe, dass dieser kurze Überblick über verschiedene Möglichkeiten zum Aktualisieren von Ressourcen in Clustern für Sie hilfreich war. Es ist wichtig zu wissen, dass es nicht nur ein "apply" gegen "replace" gibt, denn man kann eine Ressource auch mit apply, edit, patch oder replace aktualisieren. Grundsätzlich hat jeder Ansatz seinen Anwendungsbereich. Für atomare Änderungen ist replace vorzuziehen, andernfalls sollte man ein strategic-merge patch über apply verwenden. Im schlimmsten Fall hoffe ich, dass Sie verstanden haben, dass man Google oder StackOverflow bei der Suche nach "kubernetes apply vs replace" nicht blind vertrauen sollte. Zumindest bis dieser Artikel die aktuelle Antwort ersetzt hat.

Richtiger Vergleich von Kubernetes Apply, Replace und Patch

Quelle: habr.com

60GB SSD 8Gb DDR4