I am implementing CI/CD and GitOps repository and currently deciding the approach of how/if separating environment variables to separate (non yaml indented) files.
I am using monorepo, ArgoCD and Helm files stored directly in Git. The common helm chart is a base which can render all my microservices by setting specific values for each of them.
I have the following directory structure:
.
├── apps
│ ├── .base
│ │ ├── app1
│ │ └── app2
│ ├── .charts
│ │ └── base-app
│ │ ├── Chart.yaml
│ │ ├── templates
│ │ │ ├── _helpers.tpl
│ │ │ ├── configmap.yaml
│ │ │ ├── cronjob.yaml
│ │ │ ├── deployment.yaml
│ │ │ └── service.yaml
│ │ └── values.yaml
│ ├── dev
│ │ ├── app1
│ │ │ │── env.properties
│ │ │ └── values.yam
│ │ └── app2
│ ├── stg
│ │ ├── app1
│ │ └── app2
│ └── pprod
│ │ ├── app1
│ │ └── app2
├── argocd
│ ├── appsets
│ ├── dev-apps.yaml
│ ├── stg-apps.yaml
│ └── prod-apps.yaml
├── bootstrap.yaml
and my ArgoCd ApplicationSet which is generating apps using git generator
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: dev-apps
spec:
goTemplate: true
goTemplateOptions: ["missingkey=error"]
generators:
- git:
repoURL: https://gitlab.com/<hidden>/gitops.git
revision: HEAD
directories:
- path: apps/dev/*
template:
metadata:
name: '{{.path.basename}}-dev'
spec:
project: dev
source:
repoURL: https://gitlab.com/<hidden>/gitops.git
targetRevision: HEAD
path: test
helm:
ignoreMissingValueFiles: true # allow missing value files
valueFiles:
- '/apps/.base/{{.path.basename}}/values.yaml'
- '/{{.path.path}}/values.yaml'
destination:
name: in-cluster
namespace: dev
syncPolicy:
automated:
prune: true
allowEmpty: false
selfHeal: true
syncOptions:
- CreateNamespace=true
This way I can pass every values.yaml file to any app.
I would like to improve this and put the environment variables in separate files (e.g. as seen in dev/app1/env.properties). But this way I cannot reference those files via helm template {{ .Files.Get }} and use them to generate ConfigMaps, etc.
I found a solution which is the following:
- put the common base chart which is now stored in apps/.charts/base-chart directly on the root of the repo
- pass the relative path (from where the Chart.yaml is) to a variable in Values via Appset Git generator
- put a filename setting in Values.yaml
- in the helm template (e.g. ConfigMap) render the contents of the file if the filename variable is set
for example:
...
helm:
ignoreMissingValueFiles: true # allow missing value files
valueFiles:
- '/apps/.base/{{.path.basename}}/values.yaml'
- '/{{.path.path}}/values.yaml'
parameters:
- name: "configPath"
value: 'apps/{{.path.basename}}'
...
set the config in values.yaml
environment.configsFile=env.properties
and then use the path in configPath and filename in configsFile to produce a full path to the file and use it in a configmap template:
{{- if .Values.environment.configsFile }}
---
apiVersion: v1
kind: ConfigMap
metadata:
name: sample-cfg
namespace: {{ template "application.namespace" . }}
data:
{{- .Files.Get ( printf "%s/%s" .Values.configPath .Values.environment.configsFile ) | nindent 2 }}
{{- end }}
And the final structure
.
├── apps
│ ├── .base
│ │ ├── app1
│ │ └── app2
│ ├── dev
│ │ ├── app1
│ │ │ │── env.properties
│ │ │ └── values.yam
│ │ └── app2
│ ├── stg
│ │ ├── app1
│ │ └── app2
│ └── pprod
│ │ ├── app1
│ │ └── app2
├── argocd
│ ├── appsets
│ ├── dev-apps.yaml
│ ├── stg-apps.yaml
│ └── prod-apps.yaml
├── bootstrap.yaml
├── Chart.yaml
├── templates
│ ├── _helpers.tpl
│ ├── configmap.yaml
│ ├── cronjob.yaml
│ ├── deployment.yaml
│ └── service.yaml
└── values.yaml
How are you managing this? I would be happy to hear your opinion and get maybe some better ideas.