06 Mai 2020

Flux - GitOps-Operator von Weaveworks

Kategorien: HowTos
Tags: Container Flux Git GitOps Kubernetes

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:

flux-cd-diagram

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.*.*
  • 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.

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!

Kategorien: HowTos
Tags: Container Flux Git GitOps Kubernetes


Beitrag teilen: