8Alerting integrieren

Begriffe und Synonyme

Wir verwenden »Alerting« und »Benachrichtigung« synonym.

In den Deployment-Flüssen im vorherigen Kapitel haben wir mit einem Prüfschritt am Schluss sichergestellt, dass unsere Anwendung stabil und mit der neuesten Version läuft (siehe Abschnitt 7.4.1 auf Seite 218). Wenn wir unseren Prüfschritt im Cluster ausführen, ist er losgelöst von unserer CI-Pipeline und wir können nicht ohne Weiteres über Fehlschläge dieses Schritts benachrichtigt werden. Zusätzlich können unserem GitOps-Operator verschiedenste Probleme begegnen, während er im Cluster tätig ist:

Zusätzlich ist es denkbar, dass wir keinen aktiven Prüfschritt implementiert haben. Für den produktiven Betrieb jedoch ist Alerting grundsätzlich unabdingbar, egal in welcher Form. Daher wollen wir uns genauer anschauen, wie ein GitOps-Operator uns benachrichtigen kann, wenn Fehler auftreten oder Ressourcen in einem ungesunden Zustand sind. In diesem Kapitel befassen wir uns bewusst nur mit Alerting und nicht mit Monitoring oder gar Observability. Wir betrachten konkret folgende Fragestellungen:

  1. Wie kann ich den Gesundheitszustand einer Ressource mittels meines GitOps-Operators feststellen? (Abschnitt 8.1)
  2. Wie kann ich Benachrichtigungen über den Gesundheitszustand aus meinem GitOps-Operator verschicken? (Abschnitt 8.2 auf Seite 239)

Abschließend betrachten wir in Abschnitt 8.3 auf Seite 245, wie wir diese in einer ganzheitlichen Strategie einsetzen können.

8.1Gesundheitszustand feststellen

Beginnen wir damit, wie wir den Gesundheitszustand von Ressourcen feststellen können. Dabei schauen wir zunächst an, was Kubernetes (Abschnitt 8.1.1) und Helm (Abschnitt 8.1.2 auf Seite 235) unabhängig von GitOps an Bordmitteln mitbringen. Dann betrachten wir, wie Flux (Abschnitt 8.1.3 auf Seite 235) und Argo CD (Abschnitt 8.1.4 auf Seite 238) darauf aufbauen.

Ressourcenzustand in Kubernetes als »low hanging fruit«

Den Gesundheitszustand feststellen zu können ist die Grundlage für das Versenden von Benachrichtigungen in Abschnitt 8.2 auf Seite 239. Kubernetes bietet bereits von Haus aus bei jeder Ressource einen automatisch ermittelten Gesundheitszustand an. Am geläufigsten werden die Zustandsübergänge eines Pods sein, der von »Pending« am Anfang seines Schedulings hin zu »Running« wechselt, oder auch die Übergänge eines Deployments, das sich im Zustand »Progressing«, »Complete« oder »Failed« befinden kann. Dieser Gesundheitszustand wird über das Tool kstatus ermittelt.

Dieser Status einer Kubernetes-Ressource kann hilfreich bei der Umsetzung von Alerting sein, weil wir hier nur wenig selbst implementieren müssen und uns stark auf Vorgefertigtes verlassen können. Außerdem bauen manche GitOps-Operatoren wie Flux bei ihrem eigenen Alerting maßgeblich auf den Kubernetes-Zustand von Ressourcen auf, wie er von kstatus gepflegt wird.

Im folgenden Unterabschnitt beschäftigen wir uns genauer damit, wie der Gesundheitszustand einer Kubernetes-Ressource bestimmt wird. Dabei gehen wir teilweise stark in technische Details! Lass dich davon nicht abschrecken: Die wichtigsten Erkenntnisse fassen wir am Ende des Unterabschnitts kurz zusammen.

8.1.1Kubernetes-nativ mit kstatus

kstatus überprüft den Zustand der Angleichung.

Wie trifft Kubernetes selbst eine Aussage darüber, ob eine Ressource gesund ist – und welche Aussagekraft hat diese Feststellung? Kubernetes hat bei seinen Statusüberprüfungen vor allem ein Ziel: sicherzustellen, dass die Angleichung (»Reconciliation«) einer Ressource erfolgreich war. Eine Ressource wird also grundsätzlich immer dann als »gesund« markiert, wenn die Realität im Cluster der Spezifikation im Manifest entspricht.

Intern nutzt Kubernetes das Tool kstatus als Teil der cli-utils1, um den Status einer Ressource festzustellen. Die möglichen Statuswerte, die kstatus definiert, sind folgende sechs:

  1. InProgress: Die Angleichung ist im Gange. Viele Ressourcen beginnen ihr Leben in diesem Zustand; manche hingegen (wie zum Beispiel ConfigMaps) sind direkt Current.
  2. Failed: Die Angleichung ist fehlgeschlagen oder der Fortschritt ist steckengeblieben.
  3. Current: Das Ziel der Angleichung ist erreicht: Die Deklaration im Manifest entspricht vollständig der Realität im Cluster.
  4. Terminating: Die Ressource wird gerade gelöscht. Dieser Status ist nötig für Ressourcen, deren Abriss (beispielsweise über Finalizer) auch abhängige Ressourcen mit löscht, zum Beispiel Namespaces.
  5. NotFound: Die Ressource wurde nicht im Cluster gefunden.
  6. Unknown: ein Fallback-Wert, wenn kstatus den Zustand nicht bestimmen kann

kstatus stützt sich dabei auf die API-Konventionen von Kubernetes2. Laut diesen kann jede Kubernetes-Ressource eine Liste unter .status.conditions exponieren, die verschiedene »Gesundheitsaspekte« der Ressource beschreibt. Wir zeigen ein Beispiel, wie eine solche Liste beziehungsweise Map an Conditions bei einem Deployment aussehen kann:

Listing 8–1
Beispielstatus eines Deployments

1

apiVersion: apps/v1

2

kind: Deployment

3

metadata:

4

name: argocd-server

5

namespace: argocd

6

# ...

7

spec:

8

# ...

9

status:

10

availableReplicas: 1

11

conditions:

12

- lastTransitionTime: "2023-04-04T07:42:23Z"

13

lastUpdateTime: "2023-04-04T07:42:23Z"

14

message: Deployment has minimum availability.

15

reason: MinimumReplicasAvailable

16

status: "True"

17

type: Available

18

- lastTransitionTime: "2023-04-04T07:41:46Z"

19

lastUpdateTime: "2023-04-04T07:42:23Z"

20

message: >-

21

ReplicaSet "argocd-server-64957744c9"

22

has successfully progressed.

23

reason: NewReplicaSetAvailable

24

status: "True"

25

type: Progressing

26

observedGeneration: 2

27

readyReplicas: 1

28

replicas: 1

29

updatedReplicas: 1

kstatus mappt Conditions auf einen Status.

Wir sehen zwei Conditions:

kstatus würde diese Conditions sehr wahrscheinlich auf den Status »Current« abbilden, weil die Conditions darauf hindeuten, dass die Deklaration im Manifest Wirklichkeit geworden ist.

Das Feld .status.conditions ist eine Liste. Der Verdacht könnte naheliegen, dass diese Liste eine Chronologie abbildet, also den zeitlichen Verlauf von Statusänderungen (noch dazu, weil Zeitstempel vermerkt sind).

Hier wird allerdings eben keine Historie abgebildet: In Wirklichkeit ist diese Liste ein Objekt, bei dem der Wert von type der jeweilige Schlüssel eines Unterobjekts ist. Jeder Listeneintrag beschreibt einen unterschiedlichen Gesundheitsaspekt der Ressource, und die Liste in ihrer Gesamtheit drückt den insgesamten Gesundheitszustand der Ressource aus.

Der Parameter .status.conditions.*.status kann die Werte »True«, »False« oder »Unknown« haben. Im obigen Manifest wäre auch folgende Kombination von Status möglich:

Eine solche Kombination würde dem Zustand »InProgress« von kstatus entsprechen.

Zustände via kstatus sind in der Hand der Controller-Entwickelnden.

Da es sich bei den eben beschriebenen Conditions nur um eine Konvention handelt, ist niemand, der CRDs entwickelt, dazu verpflichtet, Conditions in seinen Ressourcen auszuweisen. Es kann also durchaus Ressourcentypen geben, bei denen der entsprechende Controller das Feld .status niemals setzt. Bei allen nativen Kubernetes-Ressourcentypen wie unter anderem Deployments, StatefulSets, Services und Secrets ist diese Konvention aber glücklicherweise bereits erfüllt.

Welche Prüfungen ein Controller konkret durchführt (wenn er überhaupt welche durchführt) und auch welche Condition-Types er anbietet, ist von außen nicht unmittelbar erkenntlich. Wir sind bei kstatus auch nicht in der Lage, eigene Healthchecks für Ressourcentypen oder einzelne Ressourcen zu definieren. Wir müssen uns letztlich darauf verlassen, dass die Implementierung der Healthchecks der Ressourcentypen in unserem Cluster sinnvoll und zuverlässig ist. kstatus benennt dabei selbst einige semantische Herausforderungen bei der Statusüberprüfung wie ungesetzte Conditions bei Controller-Unverfügbarkeit, Uneindeutigkeit und den Status abhängiger Ressourcen3.

Ein einfacher Smoke-Test mit kstatus

kstatus deckt bei guter Konfiguration einfache Szenarien ab.

Beschränken wir uns für einen Moment auf unser simples curl-Beispiel von Abschnitt 7.1.1 auf Seite 197. In diesem Fall wollen wir nur folgende Fragen beantworten:

  1. Läuft unsere Anwendung stabil?
  2. Ist sie von außen per HTTP erreichbar?
  3. Wird die neue Version der Anwendung ausgeführt?

Die erste Frage können wir über den Kubernetes-Status unserer Pods beantworten, indem wir aussagekräftige Probes definieren für Startup, Liveness und Readiness, denn dadurch wird auch der Gesundheitszustand unseres Pod-Controllers (zum Beispiel Deployment, StatefulSet, DaemonSet) korrekt gesetzt. (Diese Probes zu setzen gehört letztlich auch unabhängig von unserem Beispiel zu einer verantwortlichen Pflege der eigenen Kubernetes-Workloads. Wenn wir nämlich beispielsweise keine Readiness-Probe definieren, riskieren wir Downtime während eines Rollouts, weil der Kubernetes-Service dann sehr wahrscheinlich eingehende Requests zu früh auf neue Pods verteilt, bevor sie bereit sind zu antworten.)

Die zweite Frage können wir über den Gesundheitszustand von Service und Ingress beantworten: Wenn der Service gesund ist, sollte DNS innerhalb des Clusters funktionieren. Und wenn der Ingress und Ingress-Controller gesund sind, sollte der Service von außerhalb des Clusters erreichbar sein. Allerdings wird der Status des Ingress nur dann gesetzt, wenn ein Ingress-Controller installiert ist.

Hier wird kstatus im schlechtesten Fall eine falsch positive Meldung abgeben: Wenn keine Conditions vorhanden sind, wird eine Ressource standardmäßig als »Current« markiert. (Einen Ingress-Controller zu installieren gehört allerdings auch zu einer üblichen »Grundhygiene« in einem Kubernetes-Cluster.) An dieser Stelle können wir sehen, dass kstatus sich letztlich nicht gut für das Prüfen von Integrationen eignet, weil immer nur der Status einer einzelnen Ressource, nicht aber der Zustand der Verknüpfung mehrerer Ressourcen geprüft werden kann.

Die dritte Frage können wir in einem GitOps-Szenario nicht in der jeweiligen Situation beantworten, sondern nur grundlegend, und zwar mit »Ja«. Mit einem GitOps-Operator laden wir schließlich ständig das Config-Repo mit dem neuesten Image-Tag und wenden es kontinuierlich an. Somit ist quasi eine der »Grundannahmen unserer Welt«, dass unsere Workloads beziehungsweise deren Manifeste korrekt konfiguriert sind mit der neuesten Anwendungsversion. Nur wenn wir unserem GitOps-Operator grundlegend misstrauen würden, müssten wir an dieser Stelle zusätzliche Mechanismen einbauen.

Jobs können komplexere Tests mit kstatus abbilden.

Wir haben beim Ingress gesehen: Wenn wir uns nur auf die Status-Conditions unserer einzelnen Ressourcen verlassen, können wir uns leicht selbst täuschen – ganz so wie Unit-Tests ohne Integrationstests unvollständig sind. In Listing 7–2 auf Seite 220 haben wir allerdings ein Beispiel dafür gesehen, wie wir mittels eines Jobs einfache Tests innerhalb des Clusters ausführen lassen können. Auch Tools wie Testkube haben wir in jenem Abschnitt kennengelernt, über deren Custom Resources wir auch komplexere Tests abbilden und ausführen können. Mittels des Zustands solcher Jobs oder Testkube-Ressourcen können wir also gewisse Gesundheitsaspekte unserer Anwendung über kstatus abbilden. (Beim Job definieren wir diese Gesundheitsaspekte über Container-Image und -Befehl, bei Testkube über ausführliche Tests.)

Fassen wir noch einmal zusammen:

8.1.2Helm-Hooks

Helm ermöglicht gezieltes Testen mit Hooks.

Helm bietet neben Templating auch ein ausgefeiltes Release-Management an. Chart-Tests in Helm4 folgen einem ähnlichen Prinzip wie die eben referenzierten Jobs, nur sind sie in den Helm-spezifischen Releasezyklus eingebunden mithilfe von Hooks: Alle Ressourcen, die mit der Annotation helm.sh/hook="test" versehen sind, können nach dem Ausrollen eines Helm-Charts mit dem Befehl helm test angewandt werden.

Wenn ein Helm-Test fehlschlägt, wird das Helm-Release selbst nicht als fehlgeschlagen markiert. Das Helm-Release ist zwar keine Kubernetes-Ressource und kann somit auch gar keinen Kubernetes-Status haben, aber auch in der Helm-CLI werden wir mit helm ls sehen, dass ein Release, dessen Tests fehlschlagen, trotzdem als »deployed« angezeigt wird.

Mit einem Chart-Test können wir das Release also nicht als fehlgeschlagen markieren. Wenn wir stattdessen die Hook-Annotation ändern auf helm.sh/hook="post-install,post-upgrade", dann werden die entsprechend annotierten Ressourcen nach jedem Installieren oder Upgrade ausgeführt und das Helm-Release wird entsprechend als fehlgeschlagen oder deployed markiert.

Je nachdem, wie umfangreich und stabil unsere Tests im Helm-Chart sind, können wir je nach Kontext die Hook-Arten unserer Test-Workloads abwägen. Sehr leichtgewichtige Hooks mit reinen Smoke-Tests können durchaus geeignet sein für einen Post-Install- und Post-Upgrade-Hook. Was darüber hinausgeht, ist meist wahrscheinlich besser in einem Test-Hook aufgehoben.

8.1.3Flux

Flux nutzt kstatus.

Flux verlässt sich standardmäßig auf kstatus, und auch alle CRDs von Flux exponieren den Konventionen gemäß Status-Conditions.

Flux mit Kustomize

Wenn wir Flux nutzen und Kustomizations deployen, nutzen wir zwei Custom Resources (siehe Tabelle 7–2 auf Seite 209): eine Quelle (zum Beispiel ein GitRepository) und eine Kustomization als Ziel. Gelingt Flux das Ausrollen der Kustomization, wird die Kustomization-Ressource in den Conditions als gesund markiert.

Wir können bei Flux-Kustomizations auch noch zusätzliche Healthchecks einbauen5. Diese überprüfen den Kubernetes-Status der Ressourcen, die zur Kustomization gehören. Wenn diese es nicht innerhalb eines Timeouts (definiert als beispielsweise .spec.timeout="5m" für 5 Minuten) in einen gesunden Zustand schaffen, wird die Kustomization als fehlgeschlagen markiert. Wir können entweder auf alle Ressourcen warten, die in der Kustomization enthalten sind, indem wir .spec.wait=true setzen, oder stattdessen auf bestimmte (auch externe) Ressourcen warten mit .spec.healthChecks.

In Listing 8–2 zeigen wir als Beispiel eine Kustomization, die bis zu zehn Minuten darauf wartet, dass ein bestimmtes HelmRelease gesund ist. Diese Kustomization würde sich selbst dann als fehlgeschlagen ausweisen, wenn entweder ihre eigenen inkludierten Ressourcen nicht deploybar sind oder wenn das überprüfte HelmRelease innerhalb des Timeouts keinen gesunden Zustand erreicht.

Listing 8–2
Kustomization mit Healthcheck auf ein HelmRelease

1

apiVersion: kustomize.toolkit.fluxcd.io/v1

2

kind: Kustomization

3

metadata:

4

name: webapp

5

spec:

6

interval: 10m

7

prune: true

8

sourceRef:

9

kind: GitRepository

10

name: webapp

11

healthChecks:

12

- apiVersion: helm.toolkit.fluxcd.io/v2beta1

13

kind: HelmRelease

14

name: backend

15

namespace: dev

16

timeout: 5m

Synchrone Test-Jobs mittels mehrerer Kustomizations

Jobs sind die einzige Ressource, für die Flux das Status-Polling von kstatus mit eigenen Anpassungen überschreibt, damit deren Zustand korrekt in den Zustand einer Kustomization einfließt6. Flux bietet ebenso gesonderte Hinweise an für das Verwenden von Kustomizations zusammen mit Jobs, die vor oder nach dem Ausrollen ausgeführt werden sollen7: Man erstellt eine Kustomization für den Kern-Workload und eine zusätzliche Kustomization für den Job. Die Job-Kustomization enthält in diesem Fall eine Ressource wie beispielsweise den Job in Listing 7–2 auf Seite 220.

In Listing 8–3 sehen wir ein Beispiel dafür mit zwei Kustomizations, die zwar gleichzeitig angewandt werden, aber quasi nacheinander starten: Die erste Kustomization enthält den Kern-Workload und wird als Erstes deployt. Sie hat .spec.wait=true gesetzt, damit sie erst dann als gesund markiert wird, wenn alle ihre Ressourcen vollständig konvergiert sind. Die zweite Kustomization enthält einen Job, und ihre Angleichung wird mittels .spec.dependsOn erst nach dem Konvergieren der ersten Kustomization gestartet. Mittels .spec.force=true erzwingen wir das Neuerzeugen des Jobs, wenn sich unveränderliche Parameter ändern.

Listing 8–3
Zwei Kustomizations mit Reihenfolge

1

apiVersion: kustomize.toolkit.fluxcd.io/v1

2

kind: Kustomization

3

metadata:

4

name: webapp

5

spec:

6

sourceRef:

7

kind: GitRepository

8

name: webapp

9

path: "./core/"

10

interval: 5m

11

timeout: 2m

12

prune: true

13

wait: true

14

---

15

apiVersion: kustomize.toolkit.fluxcd.io/v1

16

kind: Kustomization

17

metadata:

18

name: webapp-test

19

spec:

20

dependsOn:

21

- name: webapp

22

sourceRef:

23

kind: GitRepository

24

name: my-app

25

path: "./post-deploy-tests/"

26

interval: 10m

27

timeout: 5m

28

prune: true

29

wait: true

30

force: true

Flux mit Helm

Flux ermöglicht Helm-Hooks.

Wenn wir Helm-Charts mit Flux deployen, haben wir drei Custom Resources (siehe Tabelle 7–2 auf Seite 209): ein HelmRepository und ein HelmChart als Quellen und ein HelmRelease als Ziel. Da Flux die native Helm-Library nutzt, wird der Release-Zustand, den Helm ermittelt, auch in der HelmRelease-Ressource als Kubernetes-Status widergespiegelt. Wenn wir also beispielsweise Jobs mit Hook-Annotationen für Post-Install und Post-Upgrade inkludiert haben wie in Abschnitt 8.1.2 auf Seite 235, dann wird deren Erfolg oder Fehlschlag in den Gesundheitszustand des HelmRelease mit einbezogen.

Auch die Nutzung von Helm-Tests ist möglich8: Durch Setzen von .spec.test.enable=true werden Chart-Tests beim Installieren und Upgraden automatisch mit ausgeführt, und ihr Ergebnis fließt ebenfalls in den Gesundheitszustand des HelmRelease mit ein.

8.1.4Argo CD

Argo CD nutzt eigene Healthchecks in Lua ohne kstatus oder Helm-Hooks.

Argo CD bietet die größte Flexibilität hinsichtlich Healthchecks. Der native Kubernetes-Status von Ressourcen spielt gar keine Rolle, stattdessen hat Argo CD komplett eigene Healthchecks definiert: Ein Ingress wird beispielsweise nur dann als gesund markiert, wenn .status.loadBalancer.ingress mindestens einen Wert für hostname oder IP enthält9. Momentan gibt es für acht der in Kubernetes eingebauten Ressourcentypen einen in Argo CD eigens definierten Healthcheck.

Diese Argo-CD-Healthchecks werden in der Programmiersprache Lua geschrieben. Mithilfe dieser Healthchecks kann man die Statustypen, die eine Ressource in ihren Conditions hat, auf die sechs Argo-CD-Statuswerte abbilden. Für einige Ressourcentypen, die nicht zu den Standard-Kubernetes-Ressourcentypen gehören, wurden solche Healthchecks bereits von der Community beigesteuert. (Die vollständige Liste aller Community-Healthchecks ist in den Resource-Customizations von Argo CD zu finden10.)

Es gab Diskussionen darüber, kstatus in Argo CD zu integrieren11, aber das Thema scheint momentan eher zweitrangig zu sein. Ein eindeutiges Mapping von Argo-CD-Status zu kstatus-Werten ist ebenfalls nicht möglich: Für »Terminating« bei kstatus gibt es kein Äquivalent bei Argo CD, während es für Argo CDs »Suspended« keine Entsprechung bei kstatus gibt.

Im Gegensatz zu Flux gehören auch Helm-Hooks nicht zu Argos Repertoire, weil Argo CD nicht die Helm-Library zum Ausrollen verwendet, sondern nur die Templates als Manifeste rendert und anschließend anwendet.

8.2Benachrichtigungen verschicken

Wie und worüber können unsere GitOps-Operatoren benachrichtigen, wenn sich der Gesundheitszustand von Applications, Kustomizations oder HelmReleases verändert?

Abschnitt 4.9 auf Seite 82 stellt für Flux und Argo CD bereits zwei Möglichkeiten vor: Alerting über Metriken und Notification-Controllers. Unserer Erfahrung nach ist der Start mittels der eventbasierten Notification-Controllers einfacher als die Nutzung von Metriken. Bei Metriken kann man nicht aus einer vorgefertigten Auswahl an Alerts wählen, sondern muss sich auf Basis der vorhandenen Metriken frei überlegen, welche Alerts sinnvoll sind. Dazu muss man dann passende Queries schreiben, was komplex, fehleranfällig und aufwendig sein kann.

Trotzdem kann es insgesamt Sinn ergeben, das Alerting über Metriken zu nutzen. Ein Vorteil davon ist, dass wir damit unser gesamtes Alerting zentral in einem Tool abbilden können. Abb. 4–7 auf Seite 83 zeigt hierfür beispielsweise Grafana oder Prometheus Alertmanager. Und am Ende dieses Kapitels in Abschnitt 8.3 auf Seite 245 gehen wir auf noch ganzheitlichere Herangehensweisen ein.

Im Folgenden nehmen wir jedoch vor allem die Funktionsweise der Notification-Controllers von Flux und Argo CD in den Fokus.

8.2.1Flux

Flux bietet Alerts und Providers.

Flux hat für Benachrichtigungen einen zusätzlichen Ressourcentyp im Gepäck: den Alert12. Mithilfe des Alert bestimmt man, welche Ressourcen man überwachen will und ab welchem Schweregrad eine Benachrichtigung verschickt wird. Zusätzlich zum Alert wird eine weitere Custom Resource definiert, um das Benachrichtigungsziel anzugeben: einen Provider13.

Im Beispiel in Listing 8–4 sehen wir ein Setup, in dem Benachrichtigungen an einen Slack-Kanal verschickt werden. Benachrichtigungen werden nur dann verschickt,

Wenn eine Kustomization aktiv wartet auf alle oder bestimmte Subressourcen (siehe Abschnitt 8.1.3 auf Seite 235), dann wird ein Überschreiten des Timeouts ebenfalls als Error geloggt und als Benachrichtigung verschickt.

Listing 8–4
Ein Alert und ein Provider in Flux

1

apiVersion: notification.toolkit.fluxcd.io/v1beta2

2

kind: Provider

3

metadata:

4

name: slack-bot

5

namespace: flux-system

6

spec:

7

type: slack

8

channel: general

9

address: https://slack.com/api/chat.postMessage

10

secretRef:

11

name: slack-bot-token

12

---

13

apiVersion: notification.toolkit.fluxcd.io/v1beta2

14

kind: Alert

15

metadata:

16

name: slack

17

namespace: flux-system

18

spec:

19

summary: "Cluster addons impacted in us-east-2"

20

providerRef:

21

name: slack-bot

22

eventSeverity: error

23

eventSources:

24

- kind: GitRepository

25

name: '*'

26

- kind: Kustomization

27

name: '*'

Nur wenige Möglichkeiten zur Filterung

Im Alert haben wir folgende Möglichkeiten der Filterung:

  1. .spec.eventSeverity: Hier können wir »info« wählen, um Filtern zu deaktivieren (das heißt, wir bekommen alle Events), oder »error«, um nur auf Fehlern zu benachrichtigen.
  2. .spec.eventSources: Hier können wir festlegen, von welchen Ressourcentypen wir Events verarbeiten wollen.
  3. .spec.exclustionList: Hier können wir eine Liste von Strings beziehungsweise Go-Regexes auflisten, die ausgenommen werden sollen.

Wir können beliebig viele Alert-Ressourcen erstellen, um beispielsweise pro Ressourcentyp und pro Namespace unterschiedliche Severities zu konfigurieren und unterschiedliche Regexes auszuschließen. Jeder Alert kann allerdings nur an einen Provider gebunden werden. Um einen Alert an mehrere Provider zu verschicken, muss man mehrere Alerts erstellen.

In der Praxis haben sich die Filtermöglichkeiten in Flux-Alerts oft als sehr begrenzt erwiesen, sodass es zumindest anfänglich zu einem hohen Aufkommen an verschickten Alerts kommt. Ein Hintergrund mag sicherlich sein, dass Flux-Alerts einfach Kubernetes-native Events der Flux-Ressourcen abgreifen und nur auf deren Message filtern, die ein freier Text inklusive variabler Fehler-Logs sein kann, nicht aber auf deren Reason, die ein nicht variabler Kurzcode für den Event-Typ ist.

Wir stellen auf GitLab einen bewährten Flux-Alert bereit, der auf Errors filtert und bereits einige irrelevante Messages ignoriert14.

Vielfältige Benachrichtigungsdienste

Die knapp 20 Provider-Typen, die Flux anbietet15, lassen sich grob in folgende Kategorien einsortieren:

8.2.2Argo CD

Argo CD Notifications nutzt Trigger, Templates und Subscriptions.

Früher gab es einen separaten Controller namens »Argo CD Notifications«; dieser hat mittlerweile eine Heimat in Argo CD selbst gefunden und ist somit Teil der Kernfunktionalität geworden. Das Grundprinzip besteht aus Triggern, Templates, Notification Services und Subscriptions17 (siehe Listing 8–5 für ein Beispiel):

  1. Ein Trigger wird ausgelöst, wenn bestimmte Ereignisse auftreten. Trigger-Konditionen sind komplett flexibel definierbar und werden in der ConfigMap argocd-notifications-cm festgelegt.
  2. Ein Template enthält den Nachrichteninhalt, in den Variablen hineininterpoliert werden können. Templates werden in derselben ConfigMap wie die Trigger konfiguriert.
  3. Ein Notification Service entspricht einem Flux-Provider und repräsentiert ein Benachrichtigungsziel. Wenn ein Notification Service Zugangsdaten benötigt, werden sie im Secret argocd-notifications-secret hinterlegt.
  4. Mittels einer Subscription werden Trigger auf einer oder allen Applications assoziiert mit einem Ziel (beispielsweise Kanal) bei einem Notification Service. Notification Services und Subscriptions werden teilweise in der ConfigMap, teilweise über Annotations an den zugewiesenen Applications oder AppProjects konfiguriert.

Listing 8–5 zeigt ein Beispiel mit mehreren Manifesten, das Folgendes enthält:

Listing 8–5
Trigger, Template, Secret und Application mit Subscription Annotation in Argo CD

1

apiVersion: v1

2

kind: ConfigMap

3

metadata:

4

name: argocd-notifications-cm

5

data:

6

template.app-sync-failed: |

7

email:

8

subject: >-

9

Failed to sync application

10

{{.app.metadata.name}}.

11

message: >-

12

{{if eq .serviceType "slack"}}:exclamation:{{end}}

13

The sync operation of application

14

{{.app.metadata.name}} has failed!

15

trigger.on-sync-failed: |

16

- description: App sync has failed

17

send:

18

- app-sync-failed

19

when: >-

20

app.status.operationState.phase

21

in ['Error', 'Failed']

22

service.email.gmail:

23

username: $email-username

24

password: $email-password

25

host: smtp.gmail.com

26

subscriptions: |

27

- recipients:

28

- email:gmail

29

triggers:

30

- on-sync-failed

31

---

32

apiVersion: v1

33

kind: Secret

34

metadata:

35

name: argocd-notifications-secret

36

stringData:

37

email-username: EMAIL_USER

38

email-password: PASSWORD

39

---

40

apiVersion: argoproj.io/v1alpha1

41

kind: Application

42

metadata:

43

# ...

44

annotations:

45

notifications.argoproj.io/subscribe.on-sync-failed.slack:

46

channel-name-1;channel-name-2

47

name: backend

48

namespace: argocd

49

spec:

50

# ...

Argo CD ermöglicht komplexe Benachrichtigungen.

Mit Argo CD können wir vielfältigste Anforderungen hinsichtlich Alerting erfüllen, aber wir haben auch eine entsprechende Komplexität zu verwalten. Argo CD liefert nämlich keine Trigger oder Templates in der Standardinstallation aus. Um dennoch zügig starten zu können, bietet Argo CD einen vorkonfigurierten Katalog an Triggers und Templates an18.

Ein praxisnahes Beispiel für den Start mit Notifications in Argo CD bietet der GitOps Playground19. Hier findet die Konfiguration in der values.yaml des Helm-Charts statt, woraus dann die oben erwähnte ConfigMap generiert wird.

Das Beispiel sendet Alerts per E-Mail nur bei einer kleinen, praxiserprobten Auswahl von defaultTriggers. Die Subscription erfolgt pro Team/Mandant mittels Annotation am jeweiligen AppProject20.

Benachrichtigungsdienste vergleichbar mit Flux

Argo CD bietet ähnlich wie Flux knapp 20 Dienste an, die in die gleichen Kategorien wie bei Flux passen. Zehn der Dienste überschneiden sich zwischen beiden Tools. Bei Messenger-Diensten bietet Argo CD etwas mehr, dafür gibt es weniger Auswahl bei Automatisierungszielen und Git-Commit-Status-Updates. Allerdings sind beide letzteren Kategorien fast immer auch über generische Webhooks abdeckbar.

In Abb. 8–1 zeigen wir beispielhaft eine Benachrichtigung aus Argo CD in Mattermost.

image

Abb. 8–1
Benachrichtigung aus Argo CD nach Mattermost über einen erfolgreichen Application Sync

8.3Ganzheitliche Herangehensweise

Wir sind nun in der Lage, mit den Bordmitteln unserer GitOps-Operators den Gesundheitszustand unserer Applications, Kustomizations und HelmReleases zu überwachen und Benachrichtigungen zu erhalten, wenn Ressourcen in einen abnormalen Zustand geraten. Das allein kann aber nur ein erster Baustein sein für eine ganzheitliche Strategie zum Betrieb unserer Anwendungen.

Wir können viele Fragen in diesem Zustand noch nicht beantworten. Dazu gehören unter anderem folgende Themenfelder: