December 16, 2022

Nicholas Morey

Argo CD Build Environment Examples

In many cases, the location or name of an application's deployment will influence its configuration. Consider that an app will deploy to multiple environments with slight differences (e.g., the same Helm chart deployed with different values files).

Depending on the tool, the location or name of the application will need to be duplicated throughout the configuration. This is bad practice because this configuration value could be updated in one location but forgotten in another. Instead, configurations should be kept DRY (“don't repeat yourself”). A single source of truth prevents consistency errors.

Argo CD is a popular tool for implementing GitOps principles for application deployment and lifecycle management in Kubernetes. If you are new to Argo CD, check out our “Argo 101” post.

BLOG POST Argo CD Architecture Redesigned Find out how the Akuity Platform delivers a more secure and scalable Argo CD

With Argo CD Applications, I can reuse some of the values from the specification in the configuration by taking advantage of the variables provided in the Build Environment.

I'll illustrate three scenarios where I have used this:

  1. Using the Application Name in an Ingress.
  2. Selecting a Helm values file based on the Application namespace.
  3. Setting the targetRevision, repoURL, and destination.name in an App of Apps.

Then I'll briefly touch on the next evolution of templating Applications, ApplicationSets.

Use-case 1: Using the Application Name in an Ingress

When using Helm, I have access to the {{ .Release.name }} in the Templates, but what if I want to use the release name in a value of the Helm chart? I can't set a value to a variable, so I must set it as a parameter. This means duplicating the information.

helm install my-release ./my-chart -p ingress.host=my-release.my-domain.com

If I maintain the chart, it's easy to update the manifests in templates/ to use the {{ .Release.name }}. However, it's not that simple if it's a third-party Helm chart. I may get lucky that the chart is in an open-source repo where I can submit a pull request to suggest an update to get the release name added to the manifests. But it may take weeks to get the attention of the maintainers to get a review or approval, if ever. This is where I can take advantage of the Build Environment variables in Argo CD Applications.

Let's say that my cluster has an instance of Grafana, and the ingress includes the application name in the FQDN (Full Qualified Domain Name) to differentiate it under the cluster domain. With the Build Environment, I can use the $ARGOCD_APP_NAME variable in the ingress.hostname parameter of the Helm chart.

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: grafana
  namespace: argocd
spec:
  destination:
    name: in-cluster
    namespace: front-end
  project: default
  source:
    repoURL: https://charts.bitnami.com/bitnami
    targetRevision: 8.2.20
    chart: grafana
    helm:
      parameters:
        - name: 'ingress.hostname'
          value: $ARGOCD_APP_NAME.my-cluster.company.com

In this example, the ingress for the application would be grafana.my-cluster.company.com (note the grafana subdomain which is the Application name).

Use-case 2: Selecting a Helm Values file based on the Application Namespace

A typical pattern when using Helm charts is to have separate values files for different instances. For example, I will have the typical values.yaml file, which contains the defaults for the chart. Then the values-dev.yaml and values-prod.yaml files contain the slight differences required to deploy them into those environments (dev and prod).

templates/
Chart.yaml
values.yaml         # default values
values-dev.yaml     # application instance values
values-prod.yaml

Following this example, the namespaces in the cluster correspond to the suffix used on the values files (i.e., values-dev.yaml is for the dev namespace). The Build Environment provides the Application's namespace as a variable ($ARGOCD_APP_NAMESPACE). This allows the Application to dynamically set it for the source.helm.valueFiles parameter.

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: springboot
  namespace: argocd
spec:
  destination:
    name: in-cluster
    namespace: dev
  project: default
  source:
    repoURL: https://github.com/morey-tech/argocd-example-apps.git
    targetRevision: main
    path: helm-springboot
    helm:
      valueFiles:
        - “values-$ARGOCD_APP_NAMESPACE.yaml”

Use-case 3: Setting the targetRevision and repoURL in an App of Apps

This is personally my favourite use case for the Build Environment. When I first tried it out, it was very satisfying to deploy. The scenario is based on a Helm chart for an App of Apps with Application templates where the chart's values are used to populate the targetRevision, repoURL, and destination.name of the spec.

templates/
  applications.yaml
Chart.yaml
values.yaml
# templates/applications.yaml
{{- range .Values.applications }}
---
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: {{ . }}
  namespace: {{ $.Values.namespace }}
  # finalizers:
  # - resources-finalizer.argocd.argoproj.io
spec:
  destination:
    namespace: {{ . }}
    name: {{ $.Values.spec.destination.name }}
  project: default
  source:
    path: {{ . }}
    repoURL: {{ $.Values.spec.source.repoURL }}
    targetRevision: {{ $.Values.spec.source.targetRevision }}
  syncPolicy:
    automated: {}
    syncOptions:
    - CreateNamespace=true
{{- end }}

In the parent Application of the App of Apps, I can use the Build Environment variables to populate the parameters of the Helm chart.

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: production
  namespace: argocd
  finalizers:
    - resources-finalizer.argocd.argoproj.io
spec:
  destination:
    name: in-cluster
  project: default
  source:
    path: apps
    repoURL: https://github.com/morey-tech/argocd-example-apps
    targetRevision: main
    helm:
      parameters:
        - name: spec.destination.name
          value: $ARGOCD_APP_NAME
        - name: spec.source.repoURL
          value: $ARGOCD_APP_SOURCE_REPO_URL
        - name: spec.source.targetRevision
          value: $ARGOCD_APP_SOURCE_TARGET_REVISION

Here, the Helm chart will template Application manifests and, using the Build Environment, populate the Helm values corresponding to the destination name, repo URL, and target revision of the child Applications.

I find this interesting because if I want to deploy the same set of applications to a different environment, I can simply deploy another instance of that App of Apps application and change the app name to that new cluster destination name. That way, I have a parent app named after each application destination cluster, and all child applications automatically follow.

Also, suppose I want to change the target revision for all applications in an environment. In that case, I can change the target revision of the parent application, which means I can set what an environment is tracking for my GitOps repo in one place.

ApplicationSets

Of course, I should mention that I can also accomplish this with ApplicationSets. They provide a much more powerful set of templating functionality that can render entire Application specs using a variety of generators. For example, an arbitrary list of cluster names that correspond to a path in the source and cluster URLs for the destination data.

apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
  name: guestbook
spec:
  generators:
    - list:
        elements:
          - cluster: engineering-dev
            url: https://1.2.3.4
          - cluster: engineering-prod
            url: https://2.4.6.8
          - cluster: finance-preprod
            url: https://9.8.7.6
  template:
    metadata:
      name: '{{cluster}}-guestbook'
    spec:
      project: my-project
      source:
        repoURL: https://github.com/infra-team/cluster-deployments.git
        targetRevision: HEAD
        path: guestbook/{{cluster}}
      destination:
        server: '{{url}}'
        namespace: guestbook

Conclusion

Whether it's wanting to include the application name in an FQDN, or dynamically set values file for my Helm chart based on the name, or wanting to control an app of apps based on application metadata, the Build Environment can help solve all of these problems.

I hope you find this blog post interesting. If you want to learn more about Argo CD, please get in touch with me (Nicholas Morey) on the CNCF Slack. You can find me in the #argo-* channels, and don't hesitate to send me a direct message.

WHITEPAPER GitOps Best Practices Optimize your GitOps Practices Updated for 2024

Argo CD Build Environment Examples

Get Started

Try the Akuity Platform free for 30 days.
No credit card required.

Man and woman throwing ball between themselves

Get Started

Try the Akuity Platform free for 30 days.
No credit card required.

Man and woman throwing ball between themselves

Get Started

Try the Akuity Platform free for 30 days.
No credit card required.

Man and woman throwing ball between themselves