Was ist „GitOps“?
Der Begriff „GitOps“ wird verwendet, wenn zur Einrichtung und Wartung von Infrastruktur und Applikationen eines oder mehrere Code-Repositories (meist Git) als Grundlage dienen. In diesen werden z.B. die Kubernetes-Yamls verwaltet die auf dem einen oder anderen Weg in Kubernetes deployed werden sollen.
Ein anderes Beispiel wären Ansible-Playbooks (kubespray, ansible-ceph), die ebenfalls über ein Git-Repository versioniert werden können. So ist jederzeit ersichtlich, wer welche Änderung zu welchem Zeitpunkt durchgeführt hat. Alte Stände können somit ohne Aufwand wiederhergestellt werden.
Gerade im Kubernetes-Umfeld gibt es zu den Repositories häufig noch eine Pipeline, die die hinterlegten Daten verarbeitet und deployed.
Als Alternative bieten sich hier natürlich Operatoren direkt in Kubernetes an, die die Aufgaben einer separaten Pipeline übernehmen und speziell für die Wartung von Kubernetes-Deployments konzipiert sind. Hierzu zählen Lösungen wie z.B. Flux oder auch Argo CD.
Das Projekt
Flux (oder auch FluxCD) wird primär von Weaveworks (für das CNI-Plugin bekannt) entwickelt. Ursprünglich sollte Flux als Service-Routing-Tool für Container entwickelt werden, was jedoch recht schnell verworfen wurde.
Am 22.08.2017 wurde Flux in der Version 1.0 veröffentlicht. Seitdem werden im Abstand von 1-2 Monaten neue Versionen released. Der Quellcode ist wie üblich auf Github hinterlegt, die Website des Projekts findet sich unter https://fluxcd.io/.
Das Projekt besteht aus zwei Hauptkomponenten, dem fluxd-Daemon, der im Kubernetes-Cluster deployed wird, und dem fluxctl-CLI, die z.B. lokal verwendet werden kann, um den fluxd zu steuern bzw. Flux im Cluster zu installieren. Beide Komponenten sind in golang implementiert.
Funktionsweise
Dankenswerterweise stellt Flux ein übersichtliches Schaubild zur Verfügung, das die Abläufe illustriert:
Via fluxctl wird der fluxd+memcache in einem Namespace im Cluster deployed und für ein Git-Repository konfiguriert. Dieses wird dann in regelmäßigen Abständen gepulled und die im Repository enthaltenen Yaml-Files im Cluster deployed.
Zusätzlich bietet Flux die Möglichkeit, automatisiert Images, die in Containern benutzt werden, zu aktualisieren sobald eine neue Version bereitsteht.
Die Details zu den einzelnen Funktionen folgen in einem späteren Abschnitt.
Installation
Die Installation von Flux ist in der Dokumentation entsprechend beschrieben. Vor der Installation sollte man sich jedoch die Einschränkungen genauer anschauen.
So kann Flux z.B. aktuell pro Instanz nur ein einzelnes Git-Repository nutzen. Ausserdem kann es im Vergleich zu den Hosts oder Kubernetes selbst, Unterschiede in der Möglichkeit der Namensauflösung geben, da Flux selbst in einem Container läuft. So können z.B. keine Image-Metadaten ausgelesen werden wenn eine private Registry genutzt wird die auf localhost
lauscht.
Die genauen, einzelnen Schritte sollen hier nicht weiter ausgeführt werden, da diese bereits sehr ausführlich dokumentiert sind.
Wichtig: fluxctl benötigt für die Ausführung zwingend ein konfiguriertes kubectl auf dem Host, auf dem es ausgeführt wird. Da während der Installation neue RBACs für den Serviceaccount von fluxd angelegt werden, was entsprechend weitreichende Berechtigungen benötigt, empfiehlt es sich, diese mit cluster-admin Berechtigungen auszuführen. Es ist auch möglich fluxctl beim Aufruf den kubectl-Context mitzuteilen, welchen er nutzen soll, sollte dieser vom Standardcontext abweichen.
Folgend der Ablauf der Installation auf der Kommandozeile. Der Aufruf zur Installation von Flux wurde dabei der Einfachheit halber in ein kurzes Shellskript eingebettet:
#!/bin/bash
GITUSER=""
GITMAIL=""
GITURL=""
GITPATHS="namespaces,workloads"
FLUXNS="flux"
fluxctl install \
--git-user=${GITUSER} \
--git-email=${GITMAIL} \
--git-url=${GITURL} \
--git-path=${GITPATHS} \
--namespace=${FLUXNS} | kubectl apply -f -
$ kubectl create ns flux
namespace/flux created
$ ./install_flux.sh
deployment.apps/flux created
secret/flux-git-deploy created
deployment.apps/memcached created
service/memcached created
serviceaccount/flux created
clusterrole.rbac.authorization.k8s.io/flux created
clusterrolebinding.rbac.authorization.k8s.io/flux created
$ kubectl get all -n flux
NAME READY STATUS RESTARTS AGE
pod/flux-676dcdd787-tbqcw 1/1 Running 0 119s
pod/memcached-86bdf9f56b-8kj46 1/1 Running 0 119s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/memcached ClusterIP 10.106.85.32 <none> 11211/TCP 119s
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/flux 1/1 1 1 119s
deployment.apps/memcached 1/1 1 1 119s
NAME DESIRED CURRENT READY AGE
replicaset.apps/flux-676dcdd787 1 1 1 119s
replicaset.apps/memcached-86bdf9f56b 1 1 1 119s
Da Flux sich im Standard mittels SSH am Git-Repository authentifiziert, muss der Public-Key von Flux noch hinterlegt werden.
Diesen kann man sich via fluxctl mit folgendem Befehl ausgeben lassen:
$ fluxctl identity --k8s-fwd-ns flux
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDP3mVuuDYto+lhuxRbZFrAV0jurOgNwxzFnWxHVKP9EZpnUOK0FUPiiTnMvCQK1PrGSHmXoXEgQvx9UU+XgEStOvr3xlU2AjkvFwaH+2MbSrT0HEV/2Nib2YVyeM+4T65BY7E+pkqrSQg86Q3WN7x3d1peMJQI4BeMmsaddPI7dpvy+unxTe0cdS/D6IjH6em9pTKwaWx+h9kp8BpQBI+OTFoL6dEp+ITZi+bCkk43R9zVhw21SroutTvPuIgdDCIn178JgfsJanA9jAn7woGY8D1+cc6R9jlkcnOR5IVQdOl9CC2EQUgvdZu3cyeW5xZvUwxY+10apZrV9tzHyRA9vqyobHn3sixJc8g3fD8ppVe2xBz9aFFUTEKJJbpNnVyhDXx4Q+nUEUVa17sGjUj05Q3UFTS/YW/YVUJYG4+SYIqxX0PZnvZ6gk94EFa37lWsE0Sud4Z6qnn6yMJD0yP0dAJDd6xLG7/sJGJCalvnhsDP1gVwWLJHtYjEjZXkSHk= root@flux-676dcdd787-tbqcw
Es gibt aber auch die Möglichkeit, einen eigenen, spezifischen Key zu hinterlegen. Dies ist hier genauer erläutert.
Ohne zusätzliche Konfiguration wir das Git-Repository aller 5 Minuten gesynced. Allerdings kann man einen Sync auch manuell auslösen:
$ fluxctl sync --k8s-fwd-ns flux
Synchronizing with ssh://git@gitlab.com/XXX
Revision of master to apply is f27a086
Waiting for f27a086 to be applied ...
Done.
Damit ist Flux erfolgreich installiert und zumindest grundlegend konfiguriert. Ein Beispieldeployment um die Funktionalität initial zu testen findet sich auch auf Github.
Nutzung/Konfiguration
Die Konfiguration des fluxd läuft weniger über den Daemon selbst, als mehr über z.B. Annotations an den Deployments.
Bevor damit begonnen werden kann, sollte noch der Begriff des Workloads erklärt werden. Dieser steht im Kontext von Flux für jede Clusterresource, die in irgendeiner Form Container mittels versionierten Images erstellt. Das wäre in Kubernetes neben Deployments z.B. auch Daemonsets, Statefulsets oder (Cron-)Jobs.
Diese können via dem CLI-Tool fluxctl gesteuert bzw. konfiguriert werden.
Generell werden die Workloads über Annotations gesteuert bzw. geben diese fluxd Anweisungen wie mit ihnen zu verfahren ist.
Ein Beispiel aus dem aktuellen Testcluster (Erläuterungen dazu folgen später):
$ kubectl get replicaset.apps/podinfo-7599d75df -n testspace -o yaml | head -12
apiVersion: apps/v1
kind: ReplicaSet
metadata:
annotations:
deployment.kubernetes.io/desired-replicas: "2"
deployment.kubernetes.io/max-replicas: "3"
deployment.kubernetes.io/revision: "1"
fluxcd.io/automated: "true"
fluxcd.io/sync-checksum: 1845334e4ef3156c6a84a2866206bebb626fac6c
fluxcd.io/tag.init: regex:^3.10.*
fluxcd.io/tag.podinfod: semver:~3.1
creationTimestamp: "2020-03-27T09:05:32Z"
Auflisten der Workloads
Eine Liste der Workloads kann man sich mittels fluxctl list-workloads
ausgeben lassen. Dabei werden jedoch nur die Workloads im Namespace des fluxd-Container angezeigt. Um sich die Workloads in allen Namespaces anzeigen zu lassen kann hier -a
angehangen werden. Für einen bestimmten Namespace steht -n $NS
zu Verfügung wobei $NS der Name des Namespaces ist.
Folgend ein Beispiel aus dem Testsetup:
$ fluxctl list-workloads --k8s-fwd-ns flux -a
WORKLOAD CONTAINER IMAGE RELEASE POLICY
testspace:deployment/podinfo podinfod stefanprodan/podinfo:3.1.5 ready automated
init alpine:3.10
flux:deployment/flux flux docker.io/fluxcd/flux:1.18.0 ready
flux:deployment/memcached memcached memcached:1.5.20 ready
kube-system:daemonset/kube-proxy kube-proxy k8s.gcr.io/kube-proxy:v1.17.3 ready
kube-system:deployment/coredns coredns k8s.gcr.io/coredns:1.6.5 ready
$ fluxctl list-workloads --k8s-fwd-ns flux -n testspace
WORKLOAD CONTAINER IMAGE RELEASE POLICY
testspace:deployment/podinfo podinfod stefanprodan/podinfo:3.1.5 ready automated
init alpine:3.10
Um sich alle Instanzen des Containers podinfod
anzeigen zu lassen kann folgendes verwendet werden:
$ fluxctl list-workloads --k8s-fwd-ns flux -a -c podinfod
WORKLOAD CONTAINER IMAGE RELEASE POLICY
testspace:deployment/podinfo podinfod stefanprodan/podinfo:3.1.5 ready automated
init alpine:3.10
Da flux nicht im default-Namespace deployed wurde muss entsprechend immer mit --k8s-fwd-ns flux
der Namespace mit angegeben werden, in dem die entsprechende fluxd-Instanz zu finden ist.
Details zu den Workload-Images
Flux scannt neben dem Git-Repository auch die Container-Registry aus dem die Images deployed werden nach allen verfügbaren Versionen. Damit kann man sich auch alle Versionen der Images anzeigen lassen.
Hierfür muss man fluxctl nur zu list-images
mitteilen welchen Workload in welchem Namespace man genauer untersuchen möchte.
$ fluxctl list-images --k8s-fwd-ns flux -n testspace --workload deployment/podinfo
WORKLOAD CONTAINER IMAGE CREATED
testspace:deployment/podinfo podinfod stefanprodan/podinfo
| 9715769 27 Oct 18 08:52 UTC
| 7181351 25 Mar 19 10:13 UTC
| 2192219 18 Aug 18 11:24 UTC
| 3.2.1 24 Mar 20 12:05 UTC
| 3.2.0 24 Mar 20 10:19 UTC
'-> 3.1.5 21 Jan 20 11:40 UTC
3.1.4 06 Nov 19 22:21 UTC
3.1.3 06 Nov 19 22:16 UTC
3.1.2 06 Nov 19 22:02 UTC
3.1.1 06 Nov 19 22:00 UTC
init alpine
| 3 23 Mar 20 21:19 UTC
| 3.11 23 Mar 20 21:19 UTC
| 3.11.5 23 Mar 20 21:19 UTC
| latest 23 Mar 20 21:19 UTC
| 20200319 20 Mar 20 03:25 UTC
| edge 20 Mar 20 03:25 UTC
| 3.8 23 Jan 20 16:53 UTC
| 3.8.5 23 Jan 20 16:53 UTC
| 3.9 23 Jan 20 16:53 UTC
| 3.9.5 23 Jan 20 16:53 UTC
'-> 3.10 23 Jan 20 16:53 UTC
Dies zeigt alle verfügbaren Versionen für alle Container im Workload. Hier werden standardmäßig die aktuellsten 10 Versionen + die aktuell Laufende angezeigt. Dies kann man mittels dem Parameter -l
anpassen (0 bedeutet dabei alle).
Releasen eines Workloads
Um manuell eine Version eines Images zu deployen gibt es die release
Funktion von fluxctl, die automatisch das neueste Image für den Container deployed (sofern nicht durch Konfiguration eingeschränkt).
Da im Deployment unseres Demosystems das automatische Update aktiviert ist, hier ein Beispiel aus der Dokumentation:
$ fluxctl release --workload=default:deployment/helloworld --user=phil --message="New version" --update-all-images
Submitting release ...
Commit pushed: 7dc025c
Applied 7dc025c61fdbbfc2c32f792ad61e6ff52cf0590a
WORKLOAD STATUS UPDATES
default:deployment/helloworld success helloworld: quay.io/weaveworks/helloworld:master-a000001 -> master-9a16ff945b9e
Automatische Releases
Um automatisch immer die neueste Version im Workload deployen zu lassen kann das Automation-Feature aktiviert werden.
Dies kann entweder in den Workload-Yamls im Git-Repository mittels der Annotation fluxcd.io/automated: "true"
geschehen oder im Nachgang durch das fluxctl-Kommando
fluxctl automate --workload=$Workload
.
Die Automatisierung kann entweder via der Annotation fluxcd.io/automated: "false"
oder der CLI mit fluxctl deautomate --workload=$workload
deaktiviert werden.
Wird eine neue Version eines Container in der Registry released welcher auch Teil eines Workloads ist, so wird bei aktivierter Automatisierung automatisch auch das Deployment des Workloads angepasst und die neue Version deployed.
Diese Änderung wird dann (in der Standardkonfiguration) auch in das Git-Repository gepushed. In der History äußert sich dies durch Einträge wie:
$ git log workloads/podinfo-dep.yaml
commit f27a08637d69d391bc639b62dfc5353b70a9e2e6 (HEAD -> master, origin/master)
Author: Author
Date: Mon Mar 23 11:39:14 2020 +0000
Auto-release stefanprodan/podinfo:3.1.5
commit 9db210a8066141bd5600f399078a79c2ab462b0b
Author: Author
Date: Mon Mar 23 11:39:01 2020 +0000
Auto-release alpine:3.10
Welche Versionen automatisch deployed werden kann natürlich ebenso konfiguriert werden, um z.B.: sicher zu stellen dass man innerhalb einer Major-Version verbleibt.
Dafür muss am Workload entsprechend die Version gepinned werden. In diesen ist beschrieben welche Tags die Images haben müssen, um deployed zu werden.
Das Schema dabei ist fluxcd.io/tag.<container-name>: <filter-type>:<filter-value>
oder filter.fluxcd.io/<container-name>: <filter-type>:<filter-value>
.
Als Filtertypen können folgende Optionen gesetzt werden:
- glob
- Einfache Wildcards mit
*
- z.B.
fluxcd.io/tag.podinfod: glob:master-v1.*.*
- Einfache Wildcards mit
- semver
- „Semantic Versioning“. Die Tags müssen also in das Schema X.Y.Z. passen, und es kann ein Filter darauf angegeben werden.
- z.B:
fluxcd.io/tag.podinfod: semver:~3.1
um alle Images der Version 3.1.X für das automatische Deployment freizugeben
- regexp / regex
- Reguläre Ausrdrücke, z.B.
fluxcd.io/tag.init: regex:^3.10.*
- Wie üblich ist zu beachten, dass um den gesamten Tag abzudecken der regex auch mit
^$
eingefasst werden muss.
- Reguläre Ausrdrücke, z.B.
Diese Annotations können entweder auch in den yamls der Workloads hinterlegt werden oder nachträglich via z.B. fluxctl policy --workload=default:deployment/helloworld --tag-all='glob:master-v1.*.*'
gesetzt werden.
Eine umfassende Dokumentation dazu findet sich hier.
Rollback
Es ist auch soweit problemlos möglich auf ein älteres Release zurück zu wechseln, insofern dieses in die ggf. gesetzten Filter-Tags passt.
Hierzu muss zuerst die Automation abgeschaltet werden. Anschließend kann mit z.B. folgendem fluxctl release
ein bestimmtes Release deployed werden:
$ fluxctl release --workload=default:deployment/helloworld --update-image=quay.io/weaveworks/helloworld:master-a000001
Hier ist natürlich bei Stateful-Applications darauf zu achten, das keine Inkompatibilitäten mit Drittkomponenten auftreten.
Locking/Unlocking
Der Vollständigkeit halber sei noch erwähnt, dass man Workloads auch generell via fluxctl lock --workload=deployment/helloworld
für automatische Deployments jeder Art sperren kann. Ein entsperren erfolgt dann entsprechend mit fluxctl unlock --workload=deployment/helloworld
.
Generierung/Anpassung von Workloads
Flux hat eine Unterstützung für Kustomize bereits von Haus aus eingebaut, womit es möglich ist in begrenztem Maße Workloads mittels Yaml-Overlays dynamisch anzupassen.
Da die Behandlung dieses Themas jedoch den Rahmen des Artikels sprengen würde sei hier nur die Dokumentation verlinkt.
Bei Interesse können wird dies jedoch gern in einem separaten Artikel genauer ausführen.
Bei Fragen zum Einsatz von Flux stehen wir Ihnen natürlich gerne zur Verfügung. Sprechen Sie uns an!