Deploying Helm Charts using ArgoCD and Helmfile

Deploying Helm Charts with ArgoCD using Helmfile is not supported out of the box. In this article, I will show how to extend ArgoCD using config management plugins to support Helmfile.

Deploying Helm Charts using ArgoCD and Helmfile
Photo by Loik Marras / Unsplash

ArgoCD provides four patterns for deploying existing Helm Charts to a Kubernetes cluster. One approach that is not supported out of the box is to use a Helmfile. In this article, I will show how to extend the ArgoCD repository server using the Config Management Plugin feature to support Helmfile.

What is ArgoCD?

Argo CD is an open-source continuous delivery (CD) tool designed for deploying and managing applications in Kubernetes clusters. It follows the GitOps approach, meaning that the desired state of the application and the configuration details are specified in a Git repository. Argo CD then continuously monitors the repository for changes and synchronizes the state of the deployed applications with the desired state specified in the Git repository.

Supported approaches to deploy Helm Charts

At the time of writing, ArgoCD supports these four approaches:

  1. ArgoCD application pointing to a Helm Chart in a Helm repository
  2. ArgoCD application pointing to a Helm Chart in a Git repository
  3. ArgoCD application pointing to a Helm Dependency in a Git repository
  4. ArgoCD application pointing to a Kustomize folder in a Git repository
💡
I have written another article that explains these four approaches in more detail. Check it out here.

What is Helmfile?

Helmfile is a declarative tool for deploying and managing Helm charts in Kubernetes clusters. It provides a higher-level abstraction and simplifies the management of Helm releases by allowing you to define and version your releases in a YAML file called the "Helmfile." Helmfile is built on top of Helm, which is a popular package manager for Kubernetes.

Take a look at the following example of a Helmfile:

repositories:
  - name: christianhuth
    url: https://charts.christianhuth.de

releases:
  - name: popeye
    namespace: security
    chart: christianhuth/popeye
    values:
      - cronJob:
          outputFormat: "prometheus"

As you can see you can define a list of Helm repositories, that contain Helm Charts, and a list of releases, that represent the installation of a Helm Chart in a particular namespace and with a defined configuration.

💡
Of course, there are way more configuration options in Helmfile, which you can find in the official documentation.

A given helmfile.yaml can be installed with the command helmfile apply.

Setting up the Config Management Plugin for ArgoCD

There are two ways to install a Config Management Plugin:

  • Sidecar plugin: This is a good option for a more complex plugin that would clutter the Argo CD ConfigMap. A copy of the repository is sent to the sidecar container as a tarball and processed individually per application.
  • ConfigMap plugin: The repo-server container will run your plugin's commands. But be aware that this method is deprecated and will be removed in a future version.

I highly recommend using the Sidecar plugin mechanism as it gives you more features and flexibility and is future proof.

At a higher level, we need to do the following things:

  • Add a Sidecar container that runs the helmfile binary
  • Create the Config Management Plugin configuration that uses the helmfile binary
  • Include this configuration in the Sidecar container using a ConfigMap

Depending on how you install and manage your ArgoCD installation, the way you implement these actions will differ. I will show you how to do this using the official ArgoCD Helm Chart. However, this approach can be adapted to suit your installation.

Create the Config Management Plugin configuration

In our configuration, we will use the discover section. With this option, ArgoCD will automatically use the plugin whenever it finds a helmfile.yaml in the source repository. If we do not define this section we would need to add a plugin section to each ArgoCD application that should use the Helmfile plugin.

configs:
  cmp:
    create: true
    plugins:
      helmfile:
        allowConcurrency: true
        discover:
          fileName: helmfile.yaml
        generate:
          command:
            - bash
            - "-c"
            - |
              if [[ -v ENV_NAME ]]; then
                helmfile -n "$ARGOCD_APP_NAMESPACE" -e $ENV_NAME template --include-crds -q
              elif [[ -v ARGOCD_ENV_ENV_NAME ]]; then
                helmfile -n "$ARGOCD_APP_NAMESPACE" -e "$ARGOCD_ENV_ENV_NAME" template --include-crds -q
              else
                helmfile -n "$ARGOCD_APP_NAMESPACE" template --include-crds -q
              fi
        lockRepo: false

Add a Sidecar container that runs the helmfile binary

For helmfile to work we need the helmfile binary, the helm binary, and the Helm Diff plugin installed in the container. Luckily for us, we can use the already existing image hosted in the Google Container Registry at ghcr.io/helmfile/helmfile.

repoServer:
  extraContainers:
    - name: helmfile
      image: ghcr.io/helmfile/helmfile:v0.157.0
      # Entrypoint should be Argo CD lightweight CMP server i.e. argocd-cmp-server
      command: ["/var/run/argocd/argocd-cmp-server"]
      env:
        - name: HELM_CACHE_HOME
          value: /tmp/helm/cache
        - name: HELM_CONFIG_HOME
          value: /tmp/helm/config
        - name: HELMFILE_CACHE_HOME
          value: /tmp/helmfile/cache
        - name: HELMFILE_TEMPDIR
          value: /tmp/helmfile/tmp
      securityContext:
        runAsNonRoot: true
        runAsUser: 999
      volumeMounts:
        - mountPath: /var/run/argocd
          name: var-files
        - mountPath: /home/argocd/cmp-server/plugins
          name: plugins
        # Register helmfile plugin into sidecar
        - mountPath: /home/argocd/cmp-server/config/plugin.yaml
          subPath: helmfile.yaml
          name: argocd-cmp-cm
        # Starting with v2.4, do NOT mount the same tmp volume as the repo-server container. The filesystem separation helps mitigate path traversal attacks.
        - mountPath: /tmp
          name: cmp-tmp
  volumes:
    - name: argocd-cmp-cm
      configMap:
        name: argocd-cmp-cm
    - name: helmfile-tmp
      emptyDir: {}

Setting up the Git repository

Now, all we need to do is create a Git repository containing the Helmfile:

repositories:
  - name: bitnami
    url: https://charts.bitnami.com/bitnami

releases:
  - name: postgresql
    namespace: default
    chart: bitnami/postgresql
    values:
      - auth:
          username: my-user
          password: my-password

And an ArgoCD application pointing to that Git repository:

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: helmfile-example
spec:
  project: default
  source:
    repoURL: https://github.com/christianhuth/blog-code.git
    targetRevision: main
    path: deploying-helm-charts-using-argocd-and-helmfile
  destination:
    server: https://kubernetes.default.svc
    namespace: default
💡
You can find the source code of this article on my GitHub profile as well: https://github.com/christianhuth/blog-code.

Et voilá

You should now have a new application in ArgoCD that uses Helmfile for deploying Helm Charts: