Es ist etwas passiert,
worauf wir (und nicht nur wir) gewartet haben:
werf , unser Open Source-Dienstprogramm zum
Erstellen von Anwendungen und zum Bereitstellen auf Kubernetes, unterstützt jetzt das Anwenden von Änderungen mithilfe von 3-Wege-Merge-Patches! Darüber hinaus wurde es möglich, vorhandene K8-Ressourcen in Helm-Releases zu übernehmen, ohne diese Ressourcen neu zu erstellen.

Wenn es sehr kurz ist, setzen Sie
WERF_THREE_WAY_MERGE=enabled
- wir erhalten die Bereitstellung "wie in
kubectl apply
", kompatibel mit bestehenden Installationen auf Helm 2 und sogar ein bisschen mehr.
Aber fangen wir mit der Theorie an: Was sind 3-Wege-Merge-Patches im Allgemeinen, wie sind die Menschen mit ihrer Generation dazu gekommen, und warum sind sie in CI / CD-Prozessen mit Kubernetes-basierter Infrastruktur wichtig? Und danach - mal sehen, was 3-Wege-Merge in werf ist, welche Modi standardmäßig verwendet werden und wie man es verwaltet.
Was ist ein 3-Wege-Merge-Patch?
Beginnen wir also mit der Aufgabe, die in den YAML-Manifesten in Kubernetes beschriebenen Ressourcen bereitzustellen.
Um mit Ressourcen zu arbeiten, bietet die Kubernetes-API die folgenden grundlegenden Vorgänge: Erstellen, Patchen, Ersetzen und Löschen. Es wird davon ausgegangen, dass mit ihrer Hilfe ein bequemer kontinuierlicher Rollout von Ressourcen in den Cluster erstellt werden muss. Wie?
Imperative Kubectl-Teams
Der erste Ansatz zum Verwalten von Objekten in Kubernetes besteht darin, die zwingenden Kubectl-Befehle zum Erstellen, Ändern und Löschen dieser Objekte zu verwenden. Einfach ausgedrückt:
Ein solcher Ansatz mag auf den ersten Blick zweckmäßig erscheinen. Es gibt jedoch Probleme:
- Es ist schwer zu automatisieren .
- Wie spiegelt sich die Konfiguration in Git wider ? Wie überprüfe ich Änderungen an einem Cluster?
- Wie kann die Reproduzierbarkeit der Konfiguration beim Neustart sichergestellt werden?
- ...
Es ist klar, dass dieser Ansatz nicht gut zum Speichern der Anwendung und der Infrastruktur als Code (IaC oder sogar
GitOps als modernere Option, die im Kubernetes-Ökosystem an Beliebtheit gewinnt) mit dem Code
passt . Daher wurden diese Teams in kubectl nicht weiterentwickelt.
Vorgänge erstellen, abrufen, ersetzen und löschen
Mit der primären
Erstellung ist alles einfach: Wir senden das Manifest an die
create
von kube api und die Ressource wird erstellt. Die YAML-Darstellung des Manifests kann in Git gespeichert werden. Zum Erstellen verwenden Sie den
kubectl create -f manifest.yaml
.
Das Löschen ist ebenfalls einfach: Wir ersetzen das gleiche
manifest.yaml
von Git durch den
kubectl delete -f manifest.yaml
.
Mit der
replace
können Sie die Ressourcenkonfiguration vollständig durch eine neue ersetzen, ohne die Ressource neu erstellen zu müssen. Dies bedeutet, dass es logisch ist, die aktuelle Version mit der Operation
get
anzufordern, zu ändern und mit der Operation
replace
aktualisieren, bevor eine Änderung an einer Ressource vorgenommen wird. Die optimistische Sperrung ist in kube apiserver integriert. Wenn sich das Objekt nach dem Abrufvorgang geändert hat, schlägt der
replace
fehl.
Um die Konfiguration in Git zu speichern und mit replace zu aktualisieren, müssen Sie einen
get
Vorgang ausführen, die Konfiguration von Git mit dem, was wir haben, halten und
replace
ausführen. Normalerweise können Sie in kubectl nur den Befehl
kubectl replace -f manifest.yaml
, wobei
manifest.yaml
das vollständig vorbereitete (in unserem Fall angefügte) Manifest ist, das installiert werden muss. Es stellt sich heraus, dass der Benutzer Zusammenführungsmanifeste implementieren muss, aber dies ist keine triviale Angelegenheit ...
Es ist auch erwähnenswert, dass, obwohl
manifest.yaml
in Git gespeichert ist, wir nicht im Voraus wissen können, ob wir ein Objekt erstellen oder aktualisieren müssen - dies sollte von der Anwendersoftware durchgeführt werden.
Fazit:
Können wir ein kontinuierliches Rollout nur mit create, replace und delete erstellen, um sicherzustellen, dass die Infrastrukturkonfiguration zusammen mit dem Code und einer praktischen CI / CD in Git gespeichert wird?
Grundsätzlich können wir ... Dazu müssen wir
die Zusammenführungsoperation der Manifeste und eine Art Bindung
implementieren, die :
- prüft, ob ein Objekt im Cluster vorhanden ist,
- führt die anfängliche Erstellung der Ressource durch,
- aktualisiert oder löscht es.
Beim Aktualisieren müssen Sie berücksichtigen, dass sich die
Ressource seit dem letzten
Abruf möglicherweise geändert hat
get
und den Fall des optimistischen Sperrens automatisch behandeln. Versuchen Sie wiederholt, eine Aktualisierung durchzuführen.
Warum jedoch das Rad neu erfinden, wenn kube-apiserver eine andere Möglichkeit zum Aktualisieren von Ressourcen bietet: den
patch
Vorgang, mit dem einige der vom Benutzer beschriebenen Probleme behoben werden?
Patch
Also kamen wir zu den Patches.
Patches sind die Hauptmethode, um Änderungen an vorhandenen Objekten in Kubernetes anzuwenden. Der
patch
Vorgang funktioniert folgendermaßen:
- Der Benutzer von kube-apiserver muss den Patch im JSON-Format senden und das Objekt angeben.
- und apiserver selbst wird sich mit dem aktuellen Zustand des Objekts befassen und es in die gewünschte Form bringen.
Ein optimistisches Sperren ist in diesem Fall nicht erforderlich. Diese Operation ist aussagekräftiger als das Ersetzen, obwohl es auf den ersten Blick andersherum erscheinen mag.
Auf diese Weise:
- Mit der Operation create
create
wir ein Objekt aus dem Manifest von Git. - using
delete
- Löschen, wenn das Objekt nicht mehr benötigt wird, - using
patch
- Wir modifizieren das Objekt und bringen es in die in Git beschriebene Form.
Dazu müssen Sie jedoch den
richtigen Patch erstellen!
Wie Patches in Helm 2 funktionieren: 2-Wege-Zusammenführung
Bei der ersten Installation einer Version führt Helm einen
create
für Diagrammressourcen durch.
Beim Aktualisieren der Helm-Version für jede Ressource:
- Zählt den Patch zwischen der Version der Ressource aus dem vorherigen Diagramm und der aktuellen Version des Diagramms.
- wendet diesen Patch an.
Wir werden einen solchen Patch als
2-Wege-Merge-Patch bezeichnen , da zwei Manifeste an seiner Erstellung beteiligt sind:
- Ressourcenmanifest aus vorheriger Version,
- Das Manifest der Ressource aus der aktuellen Ressource.
Beim Löschen wird der
delete
in kube apiserver für Ressourcen aufgerufen, die in der vorherigen Version deklariert, in der aktuellen Version jedoch nicht deklariert wurden.
Der Ansatz mit 2-Wege-Merge-Patch hat ein Problem: Er führt zu einer
Desynchronisierung des tatsächlichen Zustands der Ressource im Cluster und des Manifests in Git .
Ein Beispiel für ein Problem
- In Git wird ein Manifest in dem Diagramm gespeichert, in dem das Feld Deployment-
image
den ubuntu:18.04
Wert ubuntu:18.04
. - Der Benutzer hat durch
kubectl edit
den Wert dieses Feldes in ubuntu:19.04
geändert. - Wenn Sie das Diagramm erneut bereitstellen, generiert Helm keinen Patch , da das
image
in der vorherigen Version der Version und im aktuellen Diagramm identisch sind. - Nach der wiederholten Bereitstellung von
image
bleibt ubuntu:19.04
, obwohl ubuntu:18.04
in der Tabelle ubuntu:18.04
ist.
Wir haben Desync und Deklarativität verloren.
Was ist eine synchronisierte Ressource?
Im Allgemeinen ist es unmöglich, eine
vollständige Übereinstimmung zwischen einem Ressourcenmanifest in einem laufenden Cluster und einem Manifest von Git zu erhalten. Da im realen Manifest möglicherweise Dienstanmerkungen / -beschriftungen, zusätzliche Container und andere Daten vorhanden sind, die von einigen Controllern dynamisch zur Ressource hinzugefügt und daraus gelöscht wurden. Wir können und wollen diese Daten nicht in Git speichern. Wir möchten jedoch, dass beim Rollout die Felder, die wir explizit in Git angegeben haben, entsprechende Werte annehmen.
Es stellt sich heraus, dass diese allgemeine
Regel einer synchronisierten Ressource gilt : Wenn Sie eine Ressource bereitstellen, können Sie nur die Felder ändern oder löschen, die explizit im Manifest von Git angegeben sind (oder in der vorherigen Version registriert wurden, jetzt aber gelöscht werden).
3-Wege-Merge-Patch
Die Hauptidee des
3-Wege-Merge-Patches : Wir generieren einen Patch zwischen der zuletzt angewendeten Version des Manifests von Git und der Zielversion des Manifests von Git unter Berücksichtigung der aktuellen Version des Manifests aus dem Arbeitscluster. Der endgültige Patch muss der synchronisierten Ressourcenregel entsprechen:
- Neue Felder, die der Zielversion hinzugefügt wurden, werden mit dem Patch hinzugefügt.
- Zuvor vorhandene Felder in der zuletzt angewendeten Version und nicht im Zielfeld vorhandene Felder werden mit dem Patch zurückgesetzt.
- Felder in der aktuellen Version des Objekts, die sich von der Zielversion des Manifests unterscheiden, werden mit dem Patch aktualisiert.
Nach diesem Prinzip werden
kubectl apply
Patches generiert:
- Die zuletzt angewendete Version des Manifests wird in der Annotation des Objekts selbst gespeichert.
- Ziel - aus der angegebenen YAML-Datei entnommen,
- current - aus einem Arbeitscluster.
Nachdem wir die Theorie herausgefunden haben, ist es Zeit, Ihnen zu erzählen, was wir bei werf gemacht haben.
Änderungen auf werf anwenden
Früher verwendete werf wie Helm 2 2-Wege-Merge-Patches.
Patch reparieren
Um zu einer neuen Art von Patches zu wechseln - 3-Wege-Merge - haben wir als ersten Schritt die sogenannten
Repair-Patches eingeführt .
Bei der Bereitstellung wird der standardmäßige 2-Wege-Merge-Patch verwendet, aber werf generiert zusätzlich einen Patch, der den tatsächlichen Status der Ressource mit dem in Git geschriebenen synchronisiert (ein solcher Patch wird mit derselben synchronisierten Ressourcenregel wie oben beschrieben erstellt).
Bei einer Rassynchronisierung erhält der Benutzer am Ende der Bereitstellung eine WARNUNG mit der entsprechenden Meldung und dem entsprechenden Patch, die angewendet werden muss, um die Ressource in ein synchronisiertes Formular zu bringen. Auch dieser Patch ist in einer speziellen Annotation
werf.io/repair-patch
. Es wird davon ausgegangen, dass der Benutzer diesen Patch selbst mit den Händen anwendet: werf wird ihn grundsätzlich nicht anwenden.
Das Generieren von Reparatur-Patches ist eine temporäre Maßnahme, mit der Sie die Erstellung von Patches nach dem Prinzip der 3-Wege-Zusammenführung testen können, diese Patches jedoch nicht automatisch anwenden können. Momentan ist diese Betriebsart standardmäßig aktiviert.
3-Wege-Merge-Patch nur für Neuerscheinungen
Ab dem 1. Dezember 2019 verwenden Beta- und Alpha-Versionen von werf
standardmäßig vollwertige 3-Wege-Merge-Patches, um Änderungen nur für neue Helm-Versionen anzuwenden, die über werf eingeführt wurden. In bestehenden Releases wird weiterhin der 2-Wege-Merge + Repair-Patch-Ansatz verwendet.
Sie können diesen Betriebsmodus explizit
WERF_THREE_WAY_MERGE_MODE=onlyNewReleases
jetzt
WERF_THREE_WAY_MERGE_MODE=onlyNewReleases
.
Hinweis : Die Funktion erschien in werf über mehrere Releases: Im Alphakanal wurde sie ab Version 1.0.5-alpha.19 und im Betakanal mit Version 1.0.4-beta.20 bereitgestellt .3-Wege-Merge-Patch für alle Releases
Ab dem 15. Dezember 2019 verwenden die Beta- und Alpha-Versionen von werf standardmäßig vollwertige Patches für die 3-Wege-Zusammenführung, um Änderungen für alle Releases zu übernehmen.
Diese Betriebsart kann explizit
WERF_THREE_WAY_MERGE_MODE=enabled
indem
WERF_THREE_WAY_MERGE_MODE=enabled
jetzt
WERF_THREE_WAY_MERGE_MODE=enabled
.
Was ist mit Ressourcen für die automatische Skalierung zu tun?
Kubernetes bietet zwei Arten der automatischen Skalierung: HPA (horizontal) und VPA (vertikal).
Horizontal wählt automatisch die Anzahl der Replikate aus, vertikal die Anzahl der Ressourcen. Sowohl die Anzahl der Replikate als auch die Ressourcenanforderungen werden im Ressourcenmanifest angegeben (siehe
spec.replicas
oder
spec.containers[].resources.limits.cpu
,
spec.containers[].resources.limits.memory
und
andere ).
Problem: Wenn ein Benutzer eine Ressource im Diagramm so konfiguriert, dass bestimmte Werte für Ressourcen oder Replikate angezeigt werden und für diese Ressource automatische Skalierer aktiviert sind, werden diese Werte bei jeder Bereitstellung auf die Werte zurückgesetzt, die im Diagrammmanifest angegeben sind.
Es gibt zwei Lösungen für das Problem. Für den Anfang ist es am besten, die explizite Angabe von Autoscale-Werten im Diagrammmanifest zu verwerfen. Wenn diese Option aus irgendeinem Grund nicht passt (z. B. weil es bequem ist, die anfänglichen Ressourcenlimits und die Anzahl der Replikate im Diagramm festzulegen), bietet werf die folgenden Anmerkungen an:
werf.io/set-replicas-only-on-creation=true
werf.io/set-resources-only-on-creation=true
Wenn eine solche Anmerkung vorhanden ist, setzt werf die entsprechenden Werte nicht bei jeder Bereitstellung zurück, sondern legt sie nur beim erstmaligen Erstellen der Ressource fest.
Weitere Informationen finden Sie in der Projektdokumentation für
HPA und
VPA .
Verweigern Sie die Verwendung eines 3-Wege-Patches
Der Benutzer kann weiterhin die Verwendung neuer Patches in werf unter Verwendung der Umgebungsvariablen
WERF_THREE_WAY_MERGE_MODE=disabled
.
Ab dem 1. März 2020 funktioniert dieses Verbot jedoch nicht mehr und es können nur noch 3-Wege-Merge-Patches verwendet werden.
Übernahme von Ressourcen in werf
Durch die Beherrschung der Methode zum Anwenden von Änderungen in 3-Wege-Merge-Patches konnten wir eine Funktion wie die Übernahme von Ressourcen, die im Cluster in der Helm-Version vorhanden sind, sofort implementieren.
Helm 2 hat ein Problem: Sie können keine Ressource zu einem bereits im Cluster vorhandenen Diagrammmanifest hinzufügen, ohne diese Ressource von Grund auf neu zu erstellen (siehe
# 6031 ,
# 3275 ). Wir haben werf gelehrt, vorhandene Ressourcen in einer Version zu akzeptieren. Dazu müssen Sie in einem laufenden Cluster eine Anmerkung für die aktuelle Version der Ressource
kubectl edit
(z. B. mit
kubectl edit
):
"werf.io/allow-adoption-by-release": RELEASE_NAME
Nun muss die Ressource in der Tabelle beschrieben werden und bei der nächsten Bereitstellung durch die werf-Version der Version mit dem entsprechenden Namen wird die vorhandene Ressource in diese Version aufgenommen und bleibt unter ihrer Kontrolle. Darüber hinaus wird werf beim Akzeptieren der Ressource zur Freigabe den aktuellen Status der Ressource aus dem Arbeitscluster mit denselben 3-Wege-Merge-Patches und der synchronisierten Ressourcenregel in den im Diagramm beschriebenen Status versetzen.
Hinweis : Die Einstellung von WERF_THREE_WAY_MERGE_MODE
wirkt sich nicht auf die Übernahme von Ressourcen aus. Im Falle einer Übernahme wird immer ein 3-Wege-Merge-Patch verwendet.Details finden Sie in der
Dokumentation .
Schlussfolgerungen und Zukunftspläne
Ich hoffe, dass nach diesem Artikel klarer wurde, was 3-Wege-Merge-Patches sind und warum sie zu ihnen gekommen sind. Aus praktischer Sicht war die Implementierung des werf-Projekts ein weiterer Schritt zur Verbesserung des helmartigen Einsatzes. Jetzt können Sie die Probleme bei der Konfigurationssynchronisierung vergessen, die bei der Verwendung von Helm 2 häufig auftraten. Gleichzeitig wurde eine neue nützliche Funktion für die Übernahme von Kubernetes-Ressourcen hinzugefügt, die bereits in die Helm-Version hochgeladen wurden.
Es gibt immer noch einige Probleme und Schwierigkeiten bei der helmartigen Bereitstellung, z. B. die Verwendung von Go-Vorlagen, und wir werden sie weiterhin lösen.
Informationen zu Ressourcenaktualisierungsmethoden und zur Übernahme finden Sie auch auf
dieser Dokumentationsseite .
Helm 3
Ein besonderer Hinweis verdient die kürzlich veröffentlichte neue Hauptversion von Helm - v3 -, die ebenfalls 3-Wege-Merge-Patches verwendet und Tiller beseitigt. Die neue Version von Helm erfordert die
Migration vorhandener Installationen, um sie in ein neues Release-Speicherformat zu konvertieren.
Werf seinerseits hat nun die Verwendung von Tiller beseitigt, auf 3-Wege-Zusammenführung umgestellt und
viel mehr hinzugefügt, wobei die Kompatibilität mit vorhandenen Installationen auf Helm 2 erhalten bleibt (es sind keine Migrationsskripte erforderlich). Bis werf auf Helm 3 umgestellt wird, verlieren werf-Benutzer daher nicht die Hauptvorteile von Helm 3 gegenüber Helm 2 (sie existieren auch in werf).
Die Umstellung auf die Helm 3-Codebasis ist jedoch unumgänglich und wird in naher Zukunft erfolgen. Vermutlich handelt es sich um werf 1.1 oder werf 1.2 (die Hauptversion von werf ist derzeit 1.0; weitere Informationen zum werf-Versionsgerät finden Sie
hier ). Während dieser Zeit hat Helm 3 Zeit, sich zu stabilisieren.
PS
Lesen Sie auch in unserem Blog: