/mid Week: Team „ArgoCD“

Im Rahmen der /mid Week 2020 haben wir uns mit dem Tool ArgoCD beschäftigt. Ziel war es das Tool mit seinen Features und Limitierungen besser kennen zu lernen, und eine konkrete Build-Pipeline von A-Z umzusetzen.

GitOps

Nach dem GitOps Prinzip wird der Zustand einer Applikation und der darunterliegenden Infrastruktur komplett deklarativ beschrieben. Diese Definitionen, auch Manifests genannt, werden in Git Repositories versioniert gespeichert. Diese Repositories dienen als single source of truth. Ein Tool wie ArgoCD ist dafür verantwortlich, diese Manifests mit dem live-state auf einer Container Plattform kontinuierlich zu synchronisieren. Werden die GitOps Prinzipien streng eingehalten, wird der Zustand einer Applikation durch einen Git Commit Hash eindeutig definiert, und kann dementsprechend einfach ausgerollt werden. Die Verwendung von GitOps und Git Repositories als single source of truth bietet einige Vorteile. Folgend die wichtigsten aufgelistet:

  • Die Definition der Manifests erfolgt auf eine deklarative Art und ein Tool sorgt für den Abgleich zwischen den desired- und live-Manifests. Die Differenzen zwischen den gewünschten Konfigurationen und den effektiv applizierten Manifests ist jederzeit einfach ersichtlich.

  • Rollbacks auf ältere Versionen sind mit git revert oder via des eingesetzten GitOps Tool einfach möglich (vorausgesetzt die Applikation unterstützt dies auch)

  • Manuelle Anpassungen direkt auf der Container Plattform werden sofort ersichtlich und können automatisiert auch überschrieben werden (self-healing)

  • Die git Commit History ist zugleich ein detailliertes Audit Log

  • Die Entwickler beschreiben die Infrastruktur in bereits bekannten Formaten und Tools wie yaml und Git.

ArgoCD

ArgoCD ist ein Teil des Argo Projektes und unter der Cloud Native Computing Foundation (CNCF) angegliedert. Das Projekt ist knapp dreijährig, komplett OpenSource und primär in Go implementiert.

Wie der Name schon verrät kümmert sich ArgoCD um den continuous delivery-Aspekt von CI/CD. Die continuous integration wird durch ein CI-Tool wie GitLab CI/CD, Jenkins, Tekton oder GitHub Actions wahrgenommen. Der Core von ArgoCD besteht aus einem Kubernetes-Controller, welcher kontinuierlich den live-State mit dem desired-State vergleicht. Der live-State wird dabei von der Kubernetes-API abgegriffen, und der desired-State ist in Form von Manifests in YAML oder JSON in einem Git Repository persistiert. ArgoCD hilft dabei auf Abweichungen der States hinzuweisen, die Abweichungen darzustellen oder auch autonom den desired-State wiederherzustellen.

Im Frühling 2020 wurde ArgoCD im ThoughtWorks Tech Radar als Trial gelistet. Dies zeugt in den meisten Fällen von einer produktions-reifen Maturität des Tools, was wir in diesem Fall auch bestätigen können.

ArgoCD wird auf einer Kubernetes-basierten Container Plattform deployed und betrieben. Es ist möglich mehrere Kubernetes und OpenShift Cluster an eine ArgoCD Instanz anzubinden.

Die detaillierten Features von ArgoCD sind auf der Feature List zu finden.

Integration in Dev-Pipeline

Neben Know-How Aufbau haben wir uns als Ziel für die /mid Week den Aufbau einer durchgängigen CI/CD Pipeline für eine konkrete Applikation vorgenommen. Es handelt sich um eine Webapplikation welche auf Quarkus basiert und eine PostgreSQL Datenbank als Persistenz-Layer verwendet.

Der CI-Teil wurde durch eine Jenkins Pipeline und einem OpenShift Build umgesetzt. Die Applikation wird mittels Apache Maven gebaut, in ein Image gepackt und das neue Image-Tag im Deployment Manifest entsprechend nachgeführt. Sobald die Manifests erfolgreich in’s Git Git Repository eingecheckt wurden, ist die Jenkins Pipeline mit dem Status succeeded abgeschlossen.

Das Deployment (CD) mittels ArgoCD erfolgt anschliessend asynchron entkoppelt von der Jenkins Pipeline. ArgoCD stellt eine Abweichung zwischen dem desired- und dem live-State fest. Dies geschieht entweder mittels Gitlab Webhook sofort beim Commit in das Git Repository oder beim nächsten Polling durch ArgoCD. Wenn das Auto-Sync-Feature aktiviert ist, wird die Änderung durch ArgoCD automatisch auf die Container Plattform appliziert.

Eine synchrone Integration von ArgoCD in die Jenkins Pipeline ist falls gewünscht auch möglich. Dadurch kann erreicht werden, dass die CI-Pipeline failed, wenn das Deployment via ArgoCD nicht erfolgreich war. Um dies zu erreichen müsste der Sync in ArgoCD entweder über die REST-API oder mithilfe des CLI Tools explizit getriggert werden.

 
Figure 1. Big Picture der End-to-end Pipeline

ArgoCD kann via CLI Tool oder über das Webinterface bedient werden. Sämtliche Konfigurationen und Definitionen persistiert ArgoCD den cloud-native-way als Custom Ressources. Das Webinterface bietet einen schlanke und intuitive Übersicht der verwalteten Applikationen und deren Sync Status.

Figure 2. App Overview in ArgoCD

Abweichungen zwischen den desired- und den live-Manifests werden im UI dargestellt. Es wird pro Applikation auf einen Blick klar welche Differenzen vorliegen.

 

Figure 3. Config Drift in ArgoCD

Weitere Details der CI Jenkins Pipeline findest du im Appendix am Ende des Beitrages.

Erkenntnisse

Wir und auch einige unserer Kunden verwenden in einzelnen Projekten ArgoCD erfolgreich in produktiven Environments. Unsere Erfahrungen mit dem Tool sind soweit durchwegs positiv. Wir haben ein paar wenige „Unschönheiten“ entdeckt, welche jedoch ohne grösseren Aufwand umgangen konnten. Folgend die Erkenntnisse welche wir während den drei Tagen festgestellt haben:

  • Der deklarative Approach von ArgoCD ist mächtig. Es kann eine Menge an boilerplate Code vermieden werden, wie dieser in imperativen Pipelines nötig ist.

  • Die Dokumentation ist teilweise sehr (zu?) kurz gehalten. Es ist nicht in jedem Fall auf Anhieb ersichtlich, wie ein spezifisches Problem gelöst werden kann.

  • In der Community via Slack wird einem in den meisten Fällen schnell geholfen.

  • Das User Interface ist intuitiv, übersichtlich und bringt definitiv einen Mehrwert. Insbesondere der Vergleich zwischen den desired- und den live-Manifests ist für jedermann verständlich.

  • Will man anch strikten GitOps Prinzipien fahren, sind die OpenShift ImageStreams aufwändig einzusetzen. Diese werden typischerweise über das CLI Tool oc oder über einen Controller aktualisiert, und eignen sich nur bedingt in einem Git Repository zu führen. Als Alternative bietet sich hier die vanilla Kubernetes Ressource Deployment an, und in dieser direkt Images zu referenzieren.

  • Ignore Paths innerhalb von Manifests können nur als JsonPatches und nicht als JsonPaths abgebildet werden. Dies limitiert die Selektionsmöglichkeiten drastisch. Bspw. muss bei einer Liste auf den expliziten Index zugegriffen werden. Für den Support von JsonPath existiert bereits ein Feature Request.

  • Default values welches durch OpenShift in live Manifest geschrieben werden sollten auch in Git gepflegt werden. Ansonsten fällt eine Applikation nach einem Sync gleich wieder in den OutOfSync-Status. successThreshold: 1 oder terminationMessagePolicy: File sind solche Attribute.

  • Wir wollten aktiv notifiziert werden, sobald ArgoCD Config Drifts zwischen den desired- und live-Manifests feststellt. Da wir bereits einen Prometheus/AlertManager Stack im Einsatz haben, wollen wir auch diesen verwenden, und nicht ein zusätzliches Tool wie ArgoCD Notifications enablen. ArgoCD exposed standardmässig Prometheus Metriken. Health Status, Sync Status und Sync History sind einige davon. Somit gestaltete sich die Integration in unseren favorisierten Monitoring/Alerting-Stack als einfache . Sobald ArgoCD Config Drifts festgestellt hat, werden wir über Rocket.Chat notifiziert.

  • In der verwendeten Version (1.7.9) werden PostSync hooks unter Umständen zu früh ausgeführt, wenn diese in Kombination mit Sync Waves verwendet werden: Issue 4669 und Issue 771.

  • ArgoCD ist gegenüber die Verwendung von Secrets agnostisch. Diese Verantworlichkeit wird an das verwendete Templating Tool wie bspw. Kustomize oder Helm delegiert. Generell kann gesagt werden, dass ein Secret Management mit einem Tool wie bspw. Vault nicht den GitOps Prinzipien entspricht, da die Secrets erst zur Runtime injected resp. aufgelöst, und so nicht Bestandteil eines Git Commits sind. Jedoch können solche Lösungen via Custom Tooling trotzdem in ArgoCD integriert werden.

  • Sync Waves: Die Deploymentreihenfolge verschiedener voneinander abhängigen Komponenten kann mittels Waves konfiguriert werden. Je höher der Wert einer Wave, desto später wird diese Ressource während eines Sync-Cycles appliziert. Damit die Konfiguration auch greift, muss die Annotoation innerhalb der Ressource korrekt platziert werden (Die offizielle Doku ist diesbezüglich nicht sehr präzise). Hier beispielsweise in einer OpenShift DeploymentConfig:

spec:
  template:
    metadata:
      annotations:
        argocd.argoproj.io/sync-wave: '1'

Vergleich ArgoCD vs. Flux

Beide Tools konzentrieren sich auf Continous Deployment. Das Continous Integration (Erstellen/Verwalten und Testen von Images) muss bei beiden Tools durch andere Tools wie (Jenkins/Tekton o.ä) abgedeckt werden.

Multitenancy / Multicluster

ArgoCD bietet für Multitenancy/Multicluster eine bessere Unterstützung als FluxCD. FluxCD fährt den Ansatz mehrere Instanzen in unterschiedlichen Namespaces/Clustern zu starten, um die Berechtigungenmechanismen von Kubernetes zu verwenden. In ArgoCD hingegen kann man mehrere Repositories anhängen und zusätzliche Clusters integrieren.

GUI

Die oss Variante von FluxCD bietet keine grafische Benutzeroberfläche an. Hier kommt ArgoCD mit einer state of the Art Benutzeroberfläche daher und zeigt dynamisch ob Live-/Target-State synchron sind. Etwas verwirrlich kann es sein, wenn auf dem GUI noch immer Objekte angezeigt werden, welche bereits nicht mehr aktiv sind. Hier lohnt es sich die Filterfunktion entsprechend zu bemühen.

Manifest support

ArgoCD und Flux unterstützen beide gegenwärtig folgende Kubernetes Manifeste: – kustomize – helm

ArgoCD unterstützt zusätzlich noch – ksonnet – jsonnet – Verzeichnis von raw yaml/json Manifests – Custom Configuration Management Tool, welches als Plugin installiert wurde

Appendix

Folgend die relevanten Snippets aus der verwendeten Jenkins Pipeline:

Build app

Builden der Applikation mittels Apache Maven:

 

stage('Build app') {
    steps {
        withMaven() {
            sh './mvnw clean package'
        }
    }
}

Build des Images
Das Image wird mittels OpenShift binary Source-to-Image builds gebaut. Die aus der vorherigen Stage mit Maven gebauten Artefakte werden als Input-Directory für den S2I Build mitgegeben. Als Image Tag wird einfachheitshalber der Git-Commit Hash verwendet. Ein Semantic Versioning ist hier zu bevorzugen.

 

stage ('Build image') {
    steps {
        script {
            openshift.withCluster("openshift-1") {
                openshift.withCredentials("token") {
                    openshift.withProject("$PROJECT_NAME") {
                        docker.withRegistry("https://registry.puzzle.ch", "registry-argocd-mid-week") {
                            openshift.startBuild("bc/java-s2i-kustomize-pipeline-quickstart", '--follow', '--wait', '--from-dir=./target/image')
                            docker.image("$APP_IMAGE:latest").pull()
                            docker.image("$APP_IMAGE:latest").tag("$COMMIT_HASH")
                            docker.image("$APP_IMAGE:$COMMIT_HASH").push("$COMMIT_HASH")
                        }
                    }
                }
            }
        }
    }
}
Image Promotion für dev

Für die Image Promotion auf eine bestimmte Stage wird das Image-Tag des Deployments im Manifest entsprechend angepasst, und anschliessend in das Git Repository eingecheckt. kustomize bietet hierfür mittels kustomize edit set image eine komfortable Option ohne dass die Manifests mit Tools wie jq oder yq umständlich geparst werden müssen.

 

stage ('Bump image for dev') {
    steps {
        script {
            sh 'git remote set-url origin https://$GIT_CREDENTIALS@gitlab.puzzle.ch/pitc_toolchain/java-s2i-kustomize-pipeline-quickstart.git'
            sh 'git checkout argocd-mid-week && git pull origin argocd-mid-week'
            sh "cd openshift/bundle/dev && kustomize edit set image $APP_IMAGE=$APP_IMAGE:$COMMIT_HASH"
            sh 'git add openshift/bundle/dev/kustomization.yaml'
            sh "git commit -m '[JENKINS] Bumps dev image version to $COMMIT_HASH' || true"
            sh 'git push'
        }
    }
}
Kommentare sind geschlossen.