4.2 Argo CD

GitOps with Argo CD.

Introduction to GitOps

Argo CD is a declarative, GitOps continuous delivery tool for Kubernetes.

Argo CD follows the GitOps pattern of using Git repositories as the source of truth for defining the desired application state. Kubernetes manifests can be specified in several ways:

  • kustomize applications
  • helm charts
  • ksonnet applications
  • jsonnet files
  • Plain directory of YAML/json manifests
  • Any custom config management tool configured as a config management plugin

Argo CD automates the deployment of the desired application states in the specified target environments. Application deployments can track updates to branches, tags, or pinned to a specific version of manifests at a Git commit. See tracking strategies for additional details about the different tracking strategies available.

For a quick 10 minute overview of Argo CD, check out the demo presented to the Sig Apps community meeting:

Task 4.2.1: Getting started

Login within the Web IDE

You can access Argo CD via Web UI (URL is provided by your teacher) or using the CLI. The Argo CD CLI Tool is already installed on the web IDE.

Since the sso login does not work inside the Web IDE for various reasons, your teacher will provide a generic local Argo CD account hannelore without any number.

argocd login argocd.techlab.openshift.ch --grpc-web --username hannelore

Task 4.2.2: Add Resources to a Git repository

As we are proceeding from now on according to the GitOps principle we need to push all existing resources located in <workspace>/*.yaml into a new Git repository. All the cli commands in this chapter must be executed in the terminal of the provided Web IDE.

Create an empty Git repository in Gitea. Visit https://gitea.techlab.openshift.ch/ with your browser and register a new account with your personal username and a password that you can remember ;)

Register new User in Gitea

Login with the new user and create a new Git repository with the Name gitops-resources.

The URL of the newly created Git repository will look like https://gitea.techlab.openshift.ch/<username>/gitops-resources.git

Git repository created

Change directory to the workspace where the yaml resources of the previous labs are located: cd <workspace>

Ensure that the LAB_USER environment variable is set.

echo $LAB_USER

If the result is empty, set the LAB_USER environment variable.

command hint
export LAB_USER=<username>

Configure your workspace folder to be a Git repository

git init

Configure the Git Client and verify the output

git config user.name "$LAB_USER"
git config user.email "foo@bar.org"
git config --local --list
git config --global credential.helper store

Now add the resource definitions to your personal Git repository and push them to remote. Use the password you entered when creating your Gitea user.

After the Git push command a password input field will apear at the top of the Web IDE. You need to enter your Gitea password there.

git add --all
git commit -m "Initial commit of resource definitions"
git remote add origin https://$LAB_USER@gitea.techlab.openshift.ch/$LAB_USER/gitops-resources.git
git push -u origin master

After a successful push you should see the following output

Enumerating objects: 15, done.
Counting objects: 100% (15/15), done.
Delta compression using up to 4 threads
Compressing objects: 100% (15/15), done.
Writing objects: 100% (15/15), 4.02 KiB | 4.02 MiB/s, done.
Total 15 (delta 1), reused 0 (delta 0)
remote: . Processing 1 references
remote: Processed 1 references in total
To https://gitea.techlab.openshift.ch/<username>/gitops-resources.git
 * [new branch]      master -> master

Go back to the webinterface of Gitea and inspect the structure and files in your personal Git repository: https://gitea.techlab.openshift.ch/<username>/gitops-resources

Task 4.2.3: Deploying the resources with Argo CD

Now we want to deploy the resources of the previous labs with Argo CD to demonstrate how Argo CD works.

Change to your main Project.

oc project $LAB_USER

To deploy the resources using the Argo CD CLI use the following command:

argocd app create argo-$LAB_USER --repo https://gitea.techlab.openshift.ch/$LAB_USER/gitops-resources.git --path '.' --dest-server https://kubernetes.default.svc --dest-namespace $LAB_USER

Expected output: application 'argo-<username>' created

Once the application is created, you can view its status:

argocd app get argo-$LAB_USER
Name:               argo-<username>
Project:            default
Server:             https://kubernetes.default.svc
Namespace:          <username>
URL:                https://argocd.techlab.openshift.ch/applications/argo-<username>
Repo:               https://gitea.techlab.openshift.ch/<username>/gitops-resources.git
Target:
Path:               .
SyncWindow:         Sync Allowed
Sync Policy:        <none>
Sync Status:        OutOfSync from  (891cabc)
Health Status:      Missing

GROUP                  KIND                   NAMESPACE   NAME                   STATUS     HEALTH   HOOK  MESSAGE
                       ConfigMap              <username>  consumer-config        OutOfSync
                       PersistentVolumeClaim  <username>  pipeline-workspace     OutOfSync  Healthy
                       Service                <username>  data-consumer          OutOfSync  Healthy
                       Service                <username>  data-producer          OutOfSync  Healthy
apps                   Deployment             <username>  data-consumer          OutOfSync  Healthy
apps                   Deployment             <username>  data-producer          OutOfSync  Healthy
build.openshift.io     BuildConfig            <username>  data-producer          OutOfSync
image.openshift.io     ImageStream            <username>  data-producer          OutOfSync
kafka.strimzi.io       Kafka                  <username>  amm-techlab            OutOfSync
kafka.strimzi.io       KafkaTopic             <username>  manual                 OutOfSync
route.openshift.io     Route                  <username>  data-consumer          OutOfSync
route.openshift.io     Route                  <username>  data-producer          OutOfSync
tekton.dev             Pipeline               <username>  build-and-deploy       OutOfSync
tekton.dev             Task                   <username>  apply-manifests        OutOfSync
template.openshift.io  Template               <username>  pipeline-run-template  OutOfSync  Missing

The application status is initially in OutOfSync state. To sync (deploy) the resource manifests, run:

argocd app sync argo-$LAB_USER

This command retrieves the manifests from the git repository and performs a kubectl apply on them. Because all our manifests have been deployed manually before, no new rollout of them will be triggered on OpenShift. But from now on, all resources are managed by Argo CD. Congrats, the first step in direction GitOps! :)

Check the Argo CD UI to browse the application and their components: https://argocd.techlab.openshift.ch

Argo CD App overview

Application Tree

Or use the CLI to check the state of the Argo CD application:

argocd app get argo-$LAB_USER

which gives you an output similar to this:

Name:               argo-<username>
Project:            default
Server:             https://kubernetes.default.svc
Namespace:          <username>
URL:                https://argocd.techlab.openshift.ch/applications/argo-<username>
Repo:               https://gitea.techlab.openshift.ch/<username>/gitops-resources.git
Target:
Path:               .
SyncWindow:         Sync Allowed
Sync Policy:        <none>
Sync Status:        Synced to  (891cabc)
Health Status:      Healthy

GROUP                  KIND                   NAMESPACE    NAME                   STATUS  HEALTH   HOOK  MESSAGE
                       ConfigMap              <username>  consumer-config        Synced                 configmap/consumer-config configured
                       PersistentVolumeClaim  <username>  pipeline-workspace     Synced  Healthy        persistentvolumeclaim/pipeline-workspace configured
                       Service                <username>  data-consumer          Synced  Healthy        service/data-consumer configured
                       Service                <username>  data-producer          Synced  Healthy        service/data-producer configured
apps                   Deployment             <username>  data-producer          Synced  Healthy        deployment.apps/data-producer configured
apps                   Deployment             <username>  data-consumer          Synced  Healthy        deployment.apps/data-consumer configured
kafka.strimzi.io       Kafka                  <username>  amm-techlab            Synced                 kafka.kafka.strimzi.io/amm-techlab configured
tekton.dev             Task                   <username>  apply-manifests        Synced                 task.tekton.dev/apply-manifests configured
tekton.dev             Pipeline               <username>  build-and-deploy       Synced                 pipeline.tekton.dev/build-and-deploy configured
route.openshift.io     Route                  <username>  data-consumer          Synced                 route.route.openshift.io/data-consumer configured
build.openshift.io     BuildConfig            <username>  data-producer          Synced                 buildconfig.build.openshift.io/data-producer configured
route.openshift.io     Route                  <username>  data-producer          Synced                 route.route.openshift.io/data-producer configured
image.openshift.io     ImageStream            <username>  data-producer          Synced                 imagestream.image.openshift.io/data-producer configured
kafka.strimzi.io       KafkaTopic             <username>  manual                 Synced                 kafkatopic.kafka.strimzi.io/manual configured
template.openshift.io  Template               <username>  pipeline-run-template  Synced                 template.template.openshift.io/pipeline-run-template created

Task 4.2.4: Automated Sync Policy and Diff

When there is a new commit in your Git repository, the Argo CD application becomes OutOfSync. Let’s assume we want to scale up our producer of the previous lab from 1 to 2 replicas. We will change this in the Deployment.

Change the number of replicas in your file <workspace>/producer.yaml.

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: data-producer
    application: amm-techlab
  name: data-producer
spec:
  replicas: 2
  selector:
    matchLabels:
      deployment: data-producer
  strategy:
    type: Recreate
...

Commit the changes and push them to the remote:

git add . && git commit -m 'Scaled up to 2 replicas' && git push

Don’t forget to interactively provide your personal Git password. After a successful push you should see a message similar to the following lines:

[master 18daed3] Scaled up to 2 replicas
 1 file changed, 1 insertion(+), 1 deletion(-)
Enumerating objects: 7, done.
Counting objects: 100% (7/7), done.
Delta compression using up to 4 threads
Compressing objects: 100% (4/4), done.
Writing objects: 100% (4/4), 372 bytes | 372.00 KiB/s, done.
Total 4 (delta 2), reused 0 (delta 0)
remote: . Processing 1 references
remote: Processed 1 references in total
To https://gitea.techlab.openshift.ch/<username>/gitops-resources.git
   fe4e2b6..18daed3  master -> master

Check the state of the resources by cli:

argocd app get argo-$LAB_USER --refresh

The parameter --refresh triggers an update against the Git repository. Out of the box Git will be polled by Argo CD. To use a synchronous workflow you can use webhooks in Git. These will trigger a synchronization in Argo CD on every push to the repository.

You will see that the data-producer is OutOfSync:

...
GROUP               KIND         NAMESPACE    NAME        STATUS     HEALTH   HOOK  MESSAGE
                       ConfigMap              <username>  consumer-config        Synced                    configmap/consumer-config configured
                       PersistentVolumeClaim  <username>  pipeline-workspace     Synced     Healthy        persistentvolumeclaim/pipeline-workspace configured
                       Service                <username>  data-consumer          Synced     Healthy        service/data-consumer configured
                       Service                <username>  data-producer          Synced     Healthy        service/data-producer configured
apps                   Deployment             <username>  data-producer          OutOfSync  Healthy        deployment.apps/data-producer configured
apps                   Deployment             <username>  data-consumer          Synced     Healthy        deployment.apps/data-consumer configured
...

When an application is OutOfSync then your deployed ’live state’ is no longer the same as the ’target state’ which is represented by the resource manifests in the Git repository. You can inspect the differences between live and target state by cli:

argocd app diff argo-$LAB_USER

which should give you an output similar to:

===== apps/Deployment <username>/data-producer ======
155c155
<   replicas: 1
---
>   replicas: 2

Now open the web console of Argo CD and go to your application. The deployment data-producer is marked as ‘OutOfSync’:

Application Out-of-Sync

With a click on Deployment > Diff you will see the differences:

Application Differences

Now click Sync on the top left and let the magic happens ;) The producer will be scaled up to 2 replicas and the resources are in Sync again.

Double-check the status by cli

argocd app get argo-$LAB_USER
...
GROUP                  KIND                   NAMESPACE   NAME                   STATUS  HEALTH       HOOK  MESSAGE
                       ConfigMap              <username>  consumer-config        Synced                     configmap/consumer-config unchanged
                       PersistentVolumeClaim  <username>  pipeline-workspace     Synced  Healthy            persistentvolumeclaim/pipeline-workspace unchanged
                       Service                <username>  data-producer          Synced  Healthy            service/data-producer unchanged
                       Service                <username>  data-consumer          Synced  Healthy            service/data-consumer unchanged
apps                   Deployment             <username>  data-consumer          Synced  Healthy            deployment.apps/data-consumer unchanged
apps                   Deployment             <username>  data-producer          Synced  Progressing        deployment.apps/data-producer configured
kafka.strimzi.io       Kafka                  <username>  amm-techlab            Synced                     kafka.kafka.strimzi.io/amm-techlab unchanged
...

Argo CD can automatically sync an application when it detects differences between the desired manifests in Git, and the live state in the cluster. A benefit of automatic sync is that CI/CD pipelines no longer need direct access to the Argo CD API server to perform the deployment. Instead, the pipeline makes a commit and push to the Git repository with the changes to the manifests in the tracking Git repo.

To configure automatic sync run (or use the UI):

argocd app set argo-$LAB_USER --sync-policy automated

From now on Argo CD will automatically synchronize resources every time you commit to the Git repository.

Task 4.2.5: Automatic Self-Healing

By default, changes made to the live cluster will not trigger automatic sync. To enable automatic sync when the live cluster’s state deviates from the state defined in Git, run:

argocd app set argo-$LAB_USER --self-heal

Watch the deployment data-producer in a separate terminal

oc get deployment data-producer -w

Let’s scale our data-producer Deployment and observe whats happening:

oc scale deployment data-producer --replicas=1

Argo CD will immediately scale back the data-producer Deployment to 2 replicas. You will see the desired replicas count in the watched Deployment.

NAME            READY   UP-TO-DATE   AVAILABLE   AGE
data-producer   2/2     2            2           78m
data-producer   2/1     2            2           78m
data-producer   2/1     2            2           78m
data-producer   1/1     1            1           78m
data-producer   1/2     1            1           78m
data-producer   1/2     1            1           78m
data-producer   1/2     1            1           78m
data-producer   1/2     2            1           78m
data-producer   2/2     2            2           78m

Task 4.2.7: Pruning

You probably asked yourself how can I delete deployed resources on the container platform? Argo CD can be configured to delete resources that no longer exist in the Git repository.

First delete the file imageStream.yaml from Git repository and push the changes

git rm imageStream.yaml
git add --all && git commit -m 'Removes ImageStream' && git push

Check the status of the application with

argocd app get argo-$LAB_USER --refresh

You will see that even with auto-sync and self-healing enabled the status is still OutOfSync

GROUP               KIND         NAMESPACE    NAME           STATUS     HEALTH   HOOK  MESSAGE
...
build.openshift.io  BuildConfig  <username>  data-producer  Synced
image.openshift.io  ImageStream  <username>  data-producer  OutOfSync
kafka.strimzi.io    Kafka        <username>  amm-techlab    Synced
...

Now enable the auto pruning explicitly:

argocd app set argo-$LAB_USER --auto-prune

Recheck the status again

argocd app get argo-$LAB_USER --refresh

Now the ImageStream was successfully deleted by Argo CD.

GROUP               KIND         NAMESPACE    NAME           STATUS     HEALTH   HOOK  MESSAGE
...
image.openshift.io  ImageStream  <username>  data-producer  Succeeded  Pruned         pruned
                    Service      <username>  data-producer  Synced     Healthy        service/data-producer unchanged
                    Service      <username>  data-consumer  Synced     Healthy        service/data-consumer unchanged
apps                Deployment   <username>  data-producer  Synced     Healthy        deployment.apps/data-producer unchanged
...

Task 4.2.8: Manage Tekton managed manifest with ArgoCD

In the previous Lab we’ve created our first tekton pipeline. The apply-manifests task applies a set of manifests to the namespace, within a pipeline run. Since we don’t want our manifests to be managed in two different ways (tekton and argocd) for simplicity reasons, we copy the tekton managed manifests to our workspace and push them to our git repository.

Let’s create the <workspace>/data-transformer.yaml resource within our workspace and push it to the git repository.

apiVersion: v1
kind: List
metadata:
  labels:
    application: amm-techlab
items:
  - apiVersion: image.openshift.io/v1
    kind: ImageStream
    metadata:
      labels:
        app: data-transformer
        application: amm-techlab
      name: data-transformer
    spec:
      lookupPolicy:
        local: true

  - apiVersion: apps/v1
    kind: Deployment
    metadata:
      labels:
        app: data-transformer
        application: amm-techlab
      name: data-transformer
    spec:
      replicas: 1
      selector:
        matchLabels:
          deployment: data-transformer
      strategy:
        type: Recreate
      template:
        metadata:
          labels:
            app: data-transformer
            application: amm-techlab
            deployment: data-transformer
        spec:
          containers:
            - image: image-registry.openshift-image-registry.svc:5000/<username>/data-transformer:latest
              imagePullPolicy: Always
              livenessProbe:
                failureThreshold: 5
                httpGet:
                  path: /health
                  port: 8080
                  scheme: HTTP
                initialDelaySeconds: 3
                periodSeconds: 20
                successThreshold: 1
                timeoutSeconds: 15
              readinessProbe:
                failureThreshold: 5
                httpGet:
                  path: /health
                  port: 8080
                  scheme: HTTP
                initialDelaySeconds: 3
                periodSeconds: 20
                successThreshold: 1
                timeoutSeconds: 15
              name: data-transformer
              env:
                - name: kafka.bootstrap.servers
                  value: 'amm-techlab-kafka-bootstrap:9092'
                - name: mp.messaging.incoming.data.connector
                  value: smallrye-kafka
                - name: mp.messaging.incoming.data.topic
                  value: manual
                - name: mp.messaging.incoming.data.value.deserializer
                  value: >-
                    ch.puzzle.quarkustechlab.reactivetransformer.control.SensorMeasurementDeserializer                    
                - name: transformer.jaeger.enabled
                  value: 'false'
              ports:
              - containerPort: 8080
                name: http
                protocol: TCP
              resources:
                limits:
                  cpu: '1'
                  memory: 500Mi
                requests:
                  cpu: 50m
                  memory: 100Mi

  - apiVersion: v1
    kind: Service
    metadata:
      labels:
        app: data-transformer
        application: amm-techlab
      name: data-transformer
    spec:
      ports:
        - name: http
          port: 8080
          protocol: TCP
          targetPort: http
      selector:
        deployment: data-transformer
      sessionAffinity: None
      type: ClusterIP
git add data-transformer.yaml && git commit -m 'Add Transformer Manifest' && git push

Advanced resource management

In this lab we manage our OpenShift resources with plain yaml files. Doing it this way is limited.

As mentioned in the GitOps introduction, ArgoCD supports several tools to define the resources. For more advanced use cases kustomize or helm charts are the preferred tools. They use inheritance or external values files to adapt the yaml resources to the different environments.