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.
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:
targetRevision
, repoURL
, and destination.name
in an App of Apps.Then I'll briefly touch on the next evolution of templating Applications, ApplicationSets.
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).
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”
targetRevision
and repoURL
in an App of AppsThis 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.
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
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.
The Akuity Platform has been updated once again with new features and improvements. Here’s a quick summary of what has been added and how it can boost your…...
September 05, 2024Akuity was created with the mission to make engineers more productive by empowering them to get the most out of Kubernetes. To achieve this, we’ve created the…...
July 25, 2024Kargo v0.8.0 is here! We are thrilled to announce the latest release of Kargo, the revolutionary GitOps promotion tool that eliminates the need for bespoke…...