In a GitOps workflow, YAML is king. The premise of GitOps is declarative infrastructure stored as Kubernetes-native manifest on Git, which can be automatically synchronized using a tool like Argo CD. Most of the time manifests are stored as YAML. Teams implementing GitOps methodologies use this “Infrastructure-as-Code” workflow to automatically synchronize the desired state in Git, with the running state in their Kubernetes cluster.

But what if you and your organization are heavily invested in Helm? In this blog, we will explore how you can take advantage of all of what Argo CD has to offer while still using Helm in your GitOps workflows.

Native Helm Support in Argo CD

The most straightforward way to use Helm in your GitOps workflow is to use the native support built in to Argo CD. Let’s take a look at an example Application manifest used to deploy a Helm chart.

---
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
 name: quarkus-app
 namespace: openshift-gitops
spec:
 destination:
   namespace: demo
   server: https://kubernetes.default.svc
 project: default
 source:
   helm:
     parameters:
      - name: build.enabled
         value: "false"
       - name: deploy.route.tls.enabled
         value: "true"
      - name: image.name
         value: quay.io/ablock/gitops-helm-quarkus
   chart: quarkus
   repoURL: https://redhat-developer.github.com/redhat-helm-charts
   targetRevision: 0.0.3
 syncPolicy:
   automated:
     prune: true
     selfHeal: true
   syncOptions:
   - CreateNamespace=true

This Argo CD Application deploys a sample Quarkus application from the Red Hat Developer Helm Repository. Using an Argo CD Application manifest, you can supply parameters directly in the manifest file. You also can provide the repo and the name of the chart you want to deploy.

Note: Argo CD assumes that you’re using Helm v3 (even if the apiVersion field in the chart is Helm v2), unless v2 is explicitly specified in the Argo CD Application manifest. More information can be found in the official documentation.

Once you’ve applied this manifest, you will see it as “progressing” in the Argo CD UI.

unnamed (1)-2

After some time the sample application will be in sync. If you click on the Application “card”, it’ll take you to the “tree view” of the application.

unnamed (2)-2

Now that the Application is deployed and synced, you can check all the resources the Helm chart created.

$ oc get pod,svc,deploy -n demo
NAME                           READY   STATUS RESTARTS   AGE
pod/quarkus-app-58f475cb86-s9fbp   1/1 Running   0      27m

NAME              TYPE    CLUSTER-IP   EXTERNAL-IP   PORT(S) AGE
service/quarkus-app   ClusterIP   172.30.169.168   <none>    8080/TCP   27m

NAME                      READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/quarkus-app   1/1 1        1       27m

You can interact with your Helm deployment through Argo CD now, via the UI or the CLI. Below is an example of updating one of the values for the Helm Chart. I am setting deploy.replicas to 2 so that I can have two pods for my deployment.

$ argocd app set quarkus-app -p deploy.replicas=2

This will update your Application to now deploy 2 replicas, giving you two Pods.

$ oc get pods -n demo
NAME                       READY   STATUS RESTARTS   AGE
quarkus-app-58f475cb86-gjlmx   1/1 Running   0      27m
quarkus-app-58f475cb86-s9fbp   1/1 Running   0      67

Although this way is supported, it does have some drawbacks. First, this breaks the first rule of GitOps. Everything in Git! There is no reflection of the desired state that is stored in the SCM. Another drawback is that there is a 1:1, Chart to Application, mapping when doing it this way. Meaning that if your application stack is made up of multiple Helm charts, you’ll have to create an Argo CD Application for each chart.

Helm Subchart Deployments

Another way to use Argo CD is to use the Helm Subchart deployment pattern. In this method, the actual Helm values are stored in Git. This has the advantage of having your Helm manifests version controlled and deployed declaratively. Let’s take the same Quarkus example and see how this would look like.

A tree of the directory structure where my manifest would be on Git.

$ tree quarkus-subchart/
quarkus-subchart/
├── Chart.yaml
└── values.yaml

0 directories, 2 files

As you can see, it’s a pretty simple configuration. Let’s take a look at where the magic happens. Taking a look at the Chart.yaml file, you can see the following configuration.

---
apiVersion: v2
name: quarkus-subchart
type: application
version: 1.0.0
appVersion: "1.0.0"
dependencies:
- name: quarkus
 version: 0.0.3
 repository: https://redhat-developer.github.com/redhat-helm-charts

Using this method, you create an “empty” Helm chart (in my case, this empty chart is called quarkus-subchart) and you list out other charts that you have as dependencies. This is why it’s called “Subchart Deployment”. Take note that dependencies is an array. So this has the advantage of being able to specify multiple Helm Charts as one atomic unit.

Taking a look at the values.yaml file, you’ll see where you can specify your parameters. Here you’ll have a section for each Subchart.

---
quarkus:
 build:
   enabled: false
 deploy:
   route:
     tls:
       enabled: true
    replicas: 3
 image:
   name: quay.io/ablock/gitops-helm-quarkus

Using these two files stored in my Git repository; I can now create my Application manifest.

---
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
 name: quarkus-subchart
 namespace: openshift-gitops
spec:
 destination:
   namespace: test
   server: https://kubernetes.default.svc
 project: default
 source:
   path: quarkus-subchart/
   repoURL: https://github.com/christianh814/gitops-examples
   targetRevision: master
 sync:
   comparedTo:
     destination:
       namespace: test
       server: https://kubernetes.default.svc
     source:
       path: quarkus-subchart/
       repoURL: https://github.com/christianh814/gitops-examples
       targetRevision: master
 syncPolicy:
   automated:
     prune: true
     selfHeal: true
   syncOptions:
   - CreateNamespace=true

Notice how this differs from the other manifest. This Application manifest doesn’t include anything about Helm. This is because you’re just syncing the manifests from Git, instead of supplying the values in this file. Applying this manifest, shows this on the Argo CD UI.

Note the icons in the upper left corner. One registers as a “Helm Application” and the other as a “Git Application”, which just means that the values for my Helm chart come from the Git repo. I can verify this by seeing if my quarkus-subchart application deployed 3 replicas (as it was stated in my values.yaml file).

$ oc get pods -n test
NAME                            READY   STATUS RESTARTS   AGE
quarkus-subchart-77448bbc98-5sk6j   1/1 Running   0      22m
quarkus-subchart-77448bbc98-6jxsf   1/1 Running   0      22m
quarkus-subchart-77448bbc98-zddlr   1/1 Running   0      22m

To make a change here, I wouldn’t use the argocd CLI or the UI. Since the manifests are stored in Git, you will have to make a PR to the repository and edit the values.yaml file there. Argo CD will then pick up the changes and make sure they are synced to your cluster.

You can read more about Helm Dependency from the GitHub Repo.

Things To Look Out For

One thing to note is that Helm charts deployed by Argo CD do not register as Helm deployments. If you run the helm ls command, you won’t see anything.

$ helm ls --all-namespaces
NAME    NAMESPACE    REVISION    UPDATED    STATUS    CHART    APP VERSION

Why is this? Quite simply because how Argo CD deploys Helm charts. Argo CD effectively does the equivalent of: helm template . <options> | kubectl apply -f - 

This has the side effect of Argo CD loading in “bare” manifests without the Helm payload information going into the namespace secret. To learn more about how Helm handles deployments read the official documentation about the architecture and differences between v2 and v3.

Conclusion

In this blog we went over different ways you can integrate Helm charts in your GitOps workflow. Using Argo CD, you can unify your applications and Helm deployments into one logical atomic unit.

OpenShift GitOps includes Argo CD, Tekton, and other tools to help you create your GitOps workflows, in a Kubernetes-native way, on OpenShift. Read more about OpenShift GitOps on our announcement blog. To learn more about GitOps in general, please make sure to tune in to GitOps Guide to the Galaxy on OpenShift.TV! We stream live every other Thursday at 3pm Eastern Time. You can catch up on past shows by visiting https://red.ht/gitops.


About the author

Christian Hernandez currently leads the Developer Experience team at Codefresh. He has experience in enterprise architecture, DevOps, tech support, advocacy, software engineering, and management. He's passionate about open source and cloud-native architecture. He is an OpenGitOps Maintainer and an Argo Project Marketing SIG member. His current focus has been on Kubernetes, DevOps, and GitOps practices.

Read full bio