Zum Inhalt springen

GitOps mit Argo CD

Auftrag

«Unser Webshop soll im Frühling laufen», schreibt das Veloatelier Matte. «Wir möchten Änderungen selbst vorschlagen können, ohne dass jemand von euch nachts auf einem Server Befehle eintippt.»

Lernziele

Nach diesem Kapitel kannst du:

  • Die vier Prinzipien von GitOps erklären
  • Begründen, warum der Git-Server ausserhalb des Clusters läuft, den er steuert
  • Gitea auf dem Front-end betreiben und ein Repo mit Zugriff nur zum Lesen freigeben
  • Argo CD installieren und mit Applications den Webshop aus Git ausrollen
  • Sync und Self-Heal an einem Beispiel zeigen und erklären, was Prune löscht

Mit OpenTofu beschreibst du die VMs in Code, ausführen musst du ihn aber selbst. Bei GitOps übernimmt das eine Software im System. Die Arbeitsgruppe OpenGitOps der CNCF fasst GitOps in vier Prinzipien zusammen.

PrinzipBedeutung
DeklarativDas System ist als Soll-Zustand beschrieben.
Versioniert und unveränderlichDer Soll-Zustand liegt in einem Speicher, der jede Version behält, meist Git.
Automatisch geholtSoftware im System holt sich den Soll-Zustand selbst ab.
Laufend abgeglichenDieselbe Software vergleicht Soll und Ist ständig und gleicht Abweichungen an.

Der Unterschied zum klassischen Ausrollen liegt in der Richtung. Bei einem Push-Ansatz meldet sich eine Pipeline oder ein Mensch mit Rechten am Cluster an und spielt Änderungen ein. Bei GitOps braucht ausser dem Agent im Cluster niemand diese Rechte. Wer etwas ändern will, ändert Git. Die Geschichte von Git ist zugleich das Protokoll aller Änderungen.

Der Kreislauf von GitOps Das Plattform-Team schickt eine Änderung mit git push an Gitea auf dem Front-end oder öffnet einen Pull Request, der nach der Prüfung zusammengeführt wird. Argo CD im k3s-Cluster holt das Repo wolkenwerk/platform jede Minute mit Leserechten. Es vergleicht den Soll-Zustand aus Git mit dem Ist-Zustand und wendet die Unterschiede an, etwa beim Webshop im Namespace velo. Ändert jemand den Cluster von Hand mit kubectl scale, setzt Self-Heal die Änderung zurück. Achte auf die Richtung der Pfeile. Argo CD holt sich den Soll-Zustand selbst, niemand schickt ihn in den Cluster. FRONT-END Port 3000 K3S-CLUSTER git push Merge holt jede Minute nur lesen wendet an Ist-Zustand Self-Heal setzt zurück Plattform-Team Pull Request prüfen, annehmen Gitea wolkenwerk/platform Von Hand kubectl scale Webshop Namespace velo Argo CD vergleicht Soll und Ist

Argo CD, der Agent dieses Kapitels, braucht ein Git-Repo, das er aus dem Cluster erreicht. Das Plattform-Team betreibt dafür ein eigenes Gitea. Gitea ist ein schlanker Git-Server mit Weboberfläche, Benutzern, Organisationen und Pull Requests, unter der Lizenz MIT.

Gitea läuft auf dem Front-end, ausserhalb des Clusters. Den Cluster baust du spätestens in Kapitel 6.2 für etcd neu auf, im Zusatzauftrag A von Kapitel 4.6 schon früher. Läge der Soll-Zustand in einem Gitea im selben Cluster, wäre er nach tofu destroy weg. Der neue Cluster wüsste dann nicht, was er ausrollen soll. Der Soll-Zustand, aus dem ein Cluster neu entsteht, gehört deshalb nicht in diesen Cluster. Das Front-end hat genug Speicher und läuft ohnehin, weil OpenNebula es braucht.

install-gitea.sh
#!/bin/bash
# Installs Gitea on the front-end as a systemd service with SQLite, the Git server of chapter 4.4.
# Gitea runs outside the k3s cluster, so that rebuilding the cluster keeps the desired state.
#
# sudo ~/cc/platform/gitea/install-gitea.sh
#
# The admin "platform" gets a random password, it is stored in ~/cc-lab/gitea/platform.pass of the calling user.
set -euo pipefail
VERSION=${GITEA_VERSION:-28.3.0}
BIN=/usr/local/bin/gitea
# shellcheck source=/dev/null
source /etc/cc-lab.env
OWNER=${SUDO_USER:?run with sudo as your own user}
OWNER_HOME=$(getent passwd "$OWNER" | cut -d: -f6)
# Binary with its published checksum
url="https://dl.gitea.com/gitea/$VERSION/gitea-$VERSION-linux-amd64"
tmp=$(mktemp -d)
curl -fsSL -o "$tmp/gitea" "$url"
echo "$(curl -fsSL "$url.sha256" | cut -d' ' -f1) $tmp/gitea" | sha256sum -c -
install -m 0755 "$tmp/gitea" "$BIN"
rm -rf "$tmp"
# A system user without login owns the data
id git >/dev/null 2>&1 || adduser --system --shell /bin/bash --group --disabled-password --home /home/git git
install -d -o git -g git -m 0750 /var/lib/gitea /var/lib/gitea/custom /var/lib/gitea/data /var/lib/gitea/log
install -d -o root -g git -m 0770 /etc/gitea
# Configuration without the web installer, HTTP on port 3000 of the front-end, no self-registration
if [ ! -f /etc/gitea/app.ini ]; then
cat > /etc/gitea/app.ini <<INI
APP_NAME = Wolkenwerk Git
RUN_USER = git
WORK_PATH = /var/lib/gitea
[server]
HTTP_PORT = 3000
ROOT_URL = http://$CC_FE_IP:3000/
DISABLE_SSH = true
[database]
DB_TYPE = sqlite3
PATH = /var/lib/gitea/data/gitea.db
[security]
INSTALL_LOCK = true
SECRET_KEY = $($BIN generate secret SECRET_KEY)
INTERNAL_TOKEN = $($BIN generate secret INTERNAL_TOKEN)
[oauth2]
JWT_SECRET = $($BIN generate secret JWT_SECRET)
[service]
DISABLE_REGISTRATION = true
REQUIRE_SIGNIN_VIEW = true
[repository]
DEFAULT_BRANCH = main
[log]
MODE = file
LEVEL = Info
INI
chown root:git /etc/gitea/app.ini
chmod 0640 /etc/gitea/app.ini
fi
cat > /etc/systemd/system/gitea.service <<UNIT
[Unit]
Description=Gitea
After=network.target
[Service]
User=git
Group=git
WorkingDirectory=/var/lib/gitea
ExecStart=$BIN web --config /etc/gitea/app.ini
Restart=always
Environment=USER=git HOME=/home/git GITEA_WORK_DIR=/var/lib/gitea
[Install]
WantedBy=multi-user.target
UNIT
systemctl daemon-reload
systemctl enable --now gitea
until curl -fs -o /dev/null http://localhost:3000/api/healthz; do sleep 2; done
# Admin of the platform team, created once
pass_file="$OWNER_HOME/cc-lab/gitea/platform.pass"
if [ ! -f "$pass_file" ]; then
install -d -o "$OWNER" -g "$OWNER" -m 0700 "$OWNER_HOME/cc-lab" "$OWNER_HOME/cc-lab/gitea"
pass=$(openssl rand -hex 16)
sudo -u git "$BIN" --config /etc/gitea/app.ini admin user create --admin --username platform \
--email platform@wolkenwerk.example --password "$pass" --must-change-password=false
install -o "$OWNER" -g "$OWNER" -m 0600 /dev/null "$pass_file"
echo "$pass" > "$pass_file"
fi
echo "Gitea $VERSION runs on http://$CC_FE_IP:3000/, user platform, password in $pass_file"
Terminal-Fenster
sudo ~/cc/platform/gitea/install-gitea.sh

Das Skript prüft die Prüfsumme des Programms, legt den Systembenutzer git an und schreibt die Konfiguration selbst, damit kein Installationsassistent im Browser nötig ist. Gitea speichert seine Daten in SQLite und läuft als Dienst von systemd. Die Dokumentation von Gitea 28 erklärt diese Schritte unter Installation from binary. Niemand kann sich selbst ein Konto anlegen. Ohne Anmeldung sieht man nichts. Im Browser des Clients (auf smartlearn die Kali-VM, mit eigenem Hypervisor dein PC) erreichst du Gitea unter Port 3000 des Front-ends. Du meldest dich als platform mit dem Passwort aus ~/cc-lab/gitea/platform.pass an.

Ein zweites Skript legt die Organisation wolkenwerk, das private Repo platform und zwei Tokens an.

make-repo.sh
#!/bin/bash
# Creates the organisation wolkenwerk and its private repo platform in Gitea, and a token that may only read it.
#
# ~/cc/platform/gitea/make-repo.sh
#
# The token for Argo CD is stored in ~/cc-lab/gitea/argocd.token, the token of the platform team in platform.token.
set -euo pipefail
# shellcheck source=/dev/null
source /etc/cc-lab.env
API="http://$CC_FE_IP:3000/api/v1"
DIR=~/cc-lab/gitea
AUTH="platform:$(cat "$DIR/platform.pass")"
api() {
curl -fsS -u "$AUTH" -H "Content-Type: application/json" "$@"
}
# Organisation and repo, both only once
api -o /dev/null "$API/orgs/wolkenwerk" 2>/dev/null ||
api -o /dev/null -X POST "$API/orgs" -d '{"username": "wolkenwerk", "visibility": "private"}'
api -o /dev/null "$API/repos/wolkenwerk/platform" 2>/dev/null ||
api -o /dev/null -X POST "$API/orgs/wolkenwerk/repos" -d '{"name": "platform", "private": true, "default_branch": "main"}'
# Tokens are shown only once, so they are written right away
token() {
local name=$1 scopes=$2 file="$DIR/$1.token"
[ -f "$file" ] && return
install -m 0600 /dev/null "$file"
api -X POST "$API/users/platform/tokens" -d "{\"name\": \"$name\", \"scopes\": $scopes}" |
python3 -c 'import json, sys; print(json.load(sys.stdin)["sha1"])' > "$file"
}
token platform '["write:repository", "write:organization"]'
token argocd '["read:repository"]'
echo "Repo http://$CC_FE_IP:3000/wolkenwerk/platform.git, tokens in $DIR"
Terminal-Fenster
~/cc/platform/gitea/make-repo.sh
TokenRechteWer es nutzt
platform.tokenRepos und Organisation schreibendu, beim Push
argocd.tokenRepos lesenArgo CD

Argo CD bekommt nur Leserechte. Gelangt sein Token an die falsche Stelle, kann damit niemand den Soll-Zustand ändern. Wie Tokens mit ihren Rechten über die API entstehen, beschreibt die Anleitung zur API von Gitea 28.

In ~/platform liegt seit Kapitel 4.2 die Vorlage. Die Dateien für Argo CD enthalten den Platzhalter FRONTEND, den du durch die Adresse deines Front-ends ersetzt. Danach machst du daraus ein Git-Repo und schickst es an Gitea.

Terminal-Fenster
source /etc/cc-lab.env
cd ~/platform
sed -i "s#http://FRONTEND:3000#http://$CC_FE_IP:3000#" root.yaml apps/*.yaml
git init -b main
git config user.name "Plattform-Team"
git config user.email "platform@wolkenwerk.example"
git config credential.helper store
printf 'http://platform:%s@%s:3000\n' "$(cat ~/cc-lab/gitea/platform.token)" "$CC_FE_IP" > ~/.git-credentials
chmod 600 ~/.git-credentials
git add .
git status
git commit -m "Plattform von Wolkenwerk Bern"
git remote add origin "http://$CC_FE_IP:3000/wolkenwerk/platform.git"
git push -u origin main

Vor dem Commit zeigt git status keinen State von OpenTofu, weil .gitignore ihn draussen hält. Nach dem Push zeigt Gitea die Ordner tofu, apps, webshop, templates sowie members.

Der Webshop des Veloateliers ist ein kleines Programm in Python, das nur die Standardbibliothek nutzt. Es liegt im Kursrepo unter case/webshop/.

PfadAntwort
/die Seite des Shops mit den Velos
/api/productsdie Produkte als JSON
/healthzok, für die Proben von Kubernetes
/metricsAnfragen und Antwortzeiten im Format von Prometheus, Thema von Teil 5

Die CI des Kursrepos baut daraus ein Image und legt es in der Registry von GitHub ab, ghcr.io/ndum/cc/webshop:1.0.0. Das Image beruht auf dem offiziellen Image python:3.14-slim und läuft als Benutzer ohne Root-Rechte.

Containerfile
FROM docker.io/library/python:3.14-slim
LABEL org.opencontainers.image.title="webshop" \
org.opencontainers.image.description="Web shop of Veloatelier Matte for the course CC" \
org.opencontainers.image.source="https://github.com/ndum/cc" \
org.opencontainers.image.licenses="MIT"
# tini runs as the first process and passes signals on. Python is then an ordinary process and stops
# on SIGTERM even while it is still starting, before the shop has set its own handler
RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends tini \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /app
COPY app.py load.py ./
# The shop runs as an unprivileged user, Kubernetes checks this with runAsNonRoot
USER 10001
ENV PYTHONUNBUFFERED=1 PORT=8080
EXPOSE 8080
ENTRYPOINT ["/usr/bin/tini", "--"]
CMD ["python3", "/app/app.py"]

Im Plattform-Repo beschreiben drei Dateien den Shop im Cluster.

deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: webshop
labels:
app: webshop
spec:
replicas: 2
selector:
matchLabels:
app: webshop
template:
metadata:
labels:
app: webshop
spec:
securityContext:
runAsNonRoot: true
containers:
- name: webshop
image: ghcr.io/ndum/cc/webshop:1.0.0
imagePullPolicy: IfNotPresent
ports:
- name: http
containerPort: 8080
env:
- name: SHOP_NAME
value: Veloatelier Matte
resources:
requests:
cpu: 50m
memory: 64Mi
limits:
cpu: 500m
memory: 128Mi
readinessProbe:
httpGet:
path: /healthz
port: http
timeoutSeconds: 3
livenessProbe:
httpGet:
path: /healthz
port: http
# A busy pod may need longer than the default second, a failed liveness check restarts it
timeoutSeconds: 3

Das Deployment hält zwei Pods am Laufen und gibt ihnen Requests und Limits für CPU und Speicher. Die Proben fragen /healthz ab. Ein Pod bekommt erst Anfragen, wenn die Readiness-Probe antwortet. Bleibt die Liveness-Probe aus, startet Kubernetes ihn neu. Der Service webshop gibt den Pods einen festen Namen. Über den Ingress leitet Traefik Anfragen auf Port 80 der beiden VMs an den Service weiter.

Argo CD ist ein Projekt der CNCF und läuft als Satz von Controllern im Cluster. Er liest Repos, rendert die Manifeste, auch mit Kustomize oder Helm, vergleicht sie mit dem Cluster und gleicht ab. Eine Application sagt ihm, welcher Ordner welches Repos in welchen Namespace gehört.

Ein Skript installiert Argo CD mit Helm, hinterlegt das Token für Gitea und wendet die erste Application an. Das Chart argo-cd in der Version 10.11.6 bringt Argo CD 3.5 mit. Seine Werte erklärt das README des Charts.

bootstrap-argocd.sh
#!/bin/bash
# Installs Argo CD in a new k3s cluster and connects it to the platform repo in Gitea, chapter 4.4.
# After this step Argo CD builds everything else from Git.
#
# ~/cc/platform/bootstrap-argocd.sh
set -euo pipefail
CHART_VERSION=${ARGOCD_CHART_VERSION:-10.11.6}
# shellcheck source=/dev/null
source /etc/cc-lab.env
REPO="http://$CC_FE_IP:3000/wolkenwerk/platform.git"
HERE=$(dirname "$(readlink -f "$0")")
helm repo add argo https://argoproj.github.io/argo-helm >/dev/null
helm repo update argo >/dev/null
helm upgrade --install argocd argo/argo-cd --version "$CHART_VERSION" \
--namespace argocd --create-namespace -f "$HERE/argocd/values.yaml" --wait --timeout 10m
# Read access to the repo, the token comes without a line break, otherwise Gitea refuses it
kubectl -n argocd create secret generic repo-platform \
--from-literal=type=git \
--from-literal=url="$REPO" \
--from-literal=username=platform \
--from-literal=password="$(tr -d '\n' < ~/cc-lab/gitea/argocd.token)" \
--dry-run=client -o yaml |
kubectl label --local -f - argocd.argoproj.io/secret-type=repository -o yaml |
kubectl apply -f -
kubectl apply -f ~/platform/root.yaml
# The password goes to a file instead of the terminal, so it does not end up in a screenshot
(umask 077; kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath='{.data.password}' | base64 -d > ~/cc-lab/argocd-admin.pass)
echo "Argo CD runs, UI on http://$CC_K3S_SERVER_IP:30080/, user admin, password in ~/cc-lab/argocd-admin.pass"
values.yaml
# Values of the Helm chart argo-cd for the lab, chapter 4.4
# Login with the admin of Argo CD only, no single sign-on and no notifications
dex:
enabled: false
notifications:
enabled: false
configs:
params:
# The lab has no certificate, the UI answers over HTTP
server.insecure: true
cm:
# Check Git every minute instead of every three minutes, so that changes show up quickly in class
timeout.reconciliation: 60s
server:
service:
type: NodePort
nodePortHttp: 30080
nodePortHttps: 30443
Terminal-Fenster
~/cc/platform/bootstrap-argocd.sh
kubectl -n argocd get pods
kubectl -n argocd get applications

Das Token geht als Secret mit dem Label argocd.argoproj.io/secret-type=repository an Argo CD. Im Lab scheiterte ein erster Versuch mit kubectl create secret --from-file. Das Secret enthielt den Zeilenumbruch am Ende der Datei, worauf Gitea mit «authentication required» antwortete. Das Skript entfernt den Zeilenumbruch darum vorher.

Die erste Application heisst root. Sie zeigt auf den Ordner apps im Repo, in dem weitere Applications liegen.

root.yaml
# The root application, it deploys every application in apps/ of the platform repo, apply it once with kubectl
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: root
namespace: argocd
spec:
project: default
source:
repoURL: http://FRONTEND:3000/wolkenwerk/platform.git
targetRevision: main
path: apps
destination:
server: https://kubernetes.default.svc
namespace: argocd
syncPolicy:
automated:
prune: true
selfHeal: true
webshop.yaml
# The web shop of Veloatelier Matte in the namespace velo
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: webshop
namespace: argocd
spec:
project: default
source:
repoURL: http://FRONTEND:3000/wolkenwerk/platform.git
targetRevision: main
path: webshop
destination:
server: https://kubernetes.default.svc
namespace: velo
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true

Argo CD rollt also zuerst root aus und findet darin die Application webshop, die den Ordner webshop in den Namespace velo bringt. Für eine neue Anwendung legst du nur eine Datei in apps an, kubectl brauchst du dafür nicht mehr. Dieses Muster heisst App of Apps. Die Dokumentation von Argo CD 3.5 beschreibt es unter Cluster Bootstrapping.

App of Apps im Plattform-Repo Die Application root wendet das Skript beim Bootstrap einmal an, sie zeigt auf den Ordner apps. Darin liegen das ApplicationSet members aus Kapitel 4.5 und die Application webshop, die den Ordner webshop in den Namespace velo bringt. Das ApplicationSet erzeugt für jeden Ordner unter members eine eigene Application, etwa member-velo und member-gemeinde. Achte darauf, dass nur root von Hand in den Cluster kommt, alles Weitere entsteht aus Dateien in Git. BOOTSTRAP einmal ORDNER apps/ ERZEUGT pro Ordner root Application apps/ webshop Application webshop/ → velo members ApplicationSet members/* member-velo Application members/velo member-gemeinde Application members/gemeinde
EinstellungWirkung
automatedArgo CD gleicht ohne Klick ab, sobald Git sich ändert
prune: truewas aus Git verschwindet, löscht Argo CD auch im Cluster
selfHeal: truewas jemand im Cluster von Hand ändert, setzt Argo CD auf den Stand von Git zurück
CreateNamespace=trueArgo CD legt den Namespace an, wenn er fehlt

Automatischen Abgleich, Prune und Self-Heal beschreibt die Dokumentation von Argo CD 3.5 unter Automated Sync Policy. Im Lab waren alle Applications 5 bis 15 Sekunden nach dem Bootstrap Synced und Healthy. Argo CD fragt Gitea hier jede Minute ab, das legt timeout.reconciliation in den Werten fest. Ohne diese Einstellung wären es drei Minuten.

In der Weboberfläche zeigt Argo CD jede Application als Baum ihrer Objekte.

Die Application webshop in Argo CD. Oben stehen Healthy und Synced mit dem Commit von main, darunter der Baum. Von der Application führen Linien zum Service, zum Deployment und zum Ingress, das Deployment führt über sein ReplicaSet zu den zwei Pods. Herz und Haken zeigen bei jedem Objekt Zustand und Abgleich.

Der Cluster liegt im internen Netz cc-net, das nur vom Front-end aus erreichbar ist. Damit der Browser des Clients Argo CD und den Webshop öffnen kann, leitest du im Terminal des Clients zwei Ports durch SSH weiter. Die Adressen stehen in deinen Parametern. Das Handbuch von ssh beschreibt die Option -L.

Terminal-Fenster
ssh -L 8080:<adresse-des-servers>:30080 -L 8081:<adresse-des-servers>:80 mmuster-fe

Solange die Verbindung offen ist, zeigt http://localhost:8080/ die Oberfläche von Argo CD und http://localhost:8081/ den Webshop. In Argo CD meldest du dich als admin mit dem Passwort aus ~/cc-lab/argocd-admin.pass an, das Skript hat es dort abgelegt. Gitea erreichst du direkt unter Port 3000 des Front-ends, bis die Firewall aus Kapitel 6.1 den Port von aussen sperrt.

Self-Heal siehst du am schnellsten, wenn du den Cluster an Git vorbei änderst.

Terminal-Fenster
kubectl -n velo scale deployment webshop --replicas=4
kubectl -n velo get deployment webshop
kubectl -n velo delete deployment webshop
kubectl -n velo get deployment webshop

Im Lab stand das Deployment beim ersten Versuch nach etwa einer Sekunde wieder auf zwei Pods. Die zwei zusätzlichen Pods waren nach zwei Sekunden beendet. Das gelöschte Deployment war nach einer bis drei Sekunden zurück und seine Pods liefen wenige Sekunden später. Nach dem Signal zum Beenden beantwortet ein Pod des Webshops noch zwei Sekunden lang Anfragen. So kann Traefik ihn aus seiner Liste nehmen, bevor er sich beendet. Wiederholst du den Versuch, wartet Argo CD jedes Mal etwas länger, damit sich Self-Heal und ein Mensch nicht endlos abwechseln. Im Lab kam das Deployment beim dritten Versuch erst nach 15 Sekunden zurück. Eine Änderung über Git dauert länger, weil Argo CD erst bei der nächsten Abfrage davon erfährt.

Terminal-Fenster
cd ~/platform
sed -i 's/replicas: 2/replicas: 3/' webshop/deployment.yaml
git commit -am "Webshop mit drei Pods für die Saison"
git push

Im Lab lief der dritte Pod 40 Sekunden bis gut zwei Minuten nach dem Push, denn Argo CD fragt jede Minute ab und verschiebt die Abfrage um einen zufälligen Anteil. Ein Webhook von Gitea an Argo CD würde den Abgleich sofort auslösen. Für das Lab genügt die Abfrage jede Minute.

Aufgabe Lab

Installiere Gitea, lege das Repo an, rolle Argo CD aus und öffne Webshop und Argo CD im Browser des Clients. Ändere die Zahl der Pods über Git.

Lösung

kubectl -n argocd get applications zeigt root und webshop sowie member-velo als Synced und Healthy. member-velo kommt schon mit dem ersten Push, Kapitel 4.5 erklärt es. Der Webshop zeigt die Velos und unten den Namen des Pods, der geantwortet hat. Nach einem Push mit replicas: 3 zeigt kubectl -n velo get pods nach spätestens gut zwei Minuten drei Pods. In Argo CD erscheint der neue Commit bei der Application.

Aufgabe Zurück zum alten Stand

Die neue Version mit drei Pods war ein Fehler. Wie stellst du den alten Stand wieder her, ohne kubectl zu benutzen?

Lösung

git revert HEAD legt einen Commit an, der die letzte Änderung rückgängig macht, git push schickt ihn an Gitea. Argo CD gleicht bei der nächsten Abfrage ab und entfernt den dritten Pod. Der Fehler und seine Korrektur bleiben in der Geschichte von Git sichtbar.

Aufgabe Rechte

Wer braucht nach diesem Kapitel noch Schreibrechte auf den Cluster und was bedeutet das für die kubeconfig auf dem Front-end?

Lösung

Für den Betrieb nur noch Argo CD, alles andere geht über Git. Die kubeconfig mit vollen Rechten brauchst du zum Bootstrap, für die Fehlersuche und im Notfall. In einem Team würde man sie wenigen Personen geben und für den Alltag Konten mit Leserechten anlegen.

Texte und Abbildungen stehen unter CC BY-SA 4.0 und der Code unter der MIT-Lizenz. Die Quellen liegen auf GitHub.