Keep application configuration decoupled from Helm chart values
If you use Helm chart to manage your applications on Kubernetes, don’t embed parameters from your application configuration in the values of your Helm chart. Doing so means you'll have to update the values.yaml schema every time you add or modify a field in your application configuration.
Instead, follow this approach:
- Define sensible defaults in your application configuration, and allow overrides via the configuration file
This is the most important step. A set of sensible defaults that suit most users in the beginning is necessary. Users won’t have to tweak too many settings just to have an initial installation of the Helm chart, thus having a better user experience.
For example, if you have the following configuration in a Golang application:
1type Config struct {
2 Port int `yaml:"port"`
3 LogLevel string `yaml:"logLevel"`
4}
Create a variable with the default configuration:
1var DefaultAppConfig = AppConfig{
2 Port: 8080,
3 LogLevel: "INFO",
4}
When initializing the application, set the application configuration to the default variable. Afterwards, read the configuration file, check which fields of the configuration struct are set in the configuration file and overwrite the default values set previously in the application configuration. This can be easily done by using a library such as Viper.
- Create a single field in the values file. Let’s call it
appConfig
. It must be empty by default:
1# Default values for ..
2# This is a YAML-formatted file.
3# Declare variables to be passed into your templates.
4
5replicaCount: 1
6
7image:
8 repository: nginx
9 pullPolicy: IfNotPresent
10 # Overrides the image tag whose default is the chart appVersion.
11 tag: ""
12
13imagePullSecrets: []
14nameOverride: ""
15fullnameOverride: ""
16
17# Application configuration. It should be empty by default.
18appConfig: {}
- Use this field to fill a ConfigMap in the Helm chart templates:
1apiVersion: v1
2kind: ConfigMap
3metadata:
4 name: {{ include "app.configMapName" . }}
5 namespace: {{ .Release.Namespace }}
6 labels:
7 {{- include "app.labels" . | nindent 4 }}
8 {{- with .Values.configAnnotations }}
9 annotations:
10 {{- toYaml . | nindent 4 }}
11 {{- end }}
12data:
13 config.yaml: |
14 {{- toYaml .Values.appConfig | nindent 4 }}
- Mount the ConfigMap in your application.
1apiVersion: apps/v1
2kind: Deployment
3metadata:
4 name: { { include "app.fullname" . } }
5 labels: { { - include "app.labels" . | nindent 4 } }
6spec:
7 # [...]
8 template:
9 metadata:
10 annotations:
11 # Add the checksum so the deployment can pick up updates to the ConfigMap.
12 checksum/config:
13 {
14 {
15 include (print $.Template.BasePath "/configmap.yaml") . | sha256sum,
16 },
17 }
18 # [...]
19 spec:
20 # Create a volume containing the file.
21 volumes:
22 - name: app-config
23 configMap:
24 name: { { include "app.configMapName" . } }
25 optional: false
26 containers:
27 - name: { { .Chart.Name } }
28 securityContext: { { - toYaml .Values.securityContext | nindent 12 } }
29 image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
30 imagePullPolicy: { { .Values.image.pullPolicy } }
31 command: ["/app", "--config", "/etc/app/config.yaml"]
32 ports:
33 - name: http
34 containerPort: { { .Values.service.port } }
35 protocol: TCP
36 # Mount the volume containing the file in the application container.
37 volumeMounts:
38 - name: app-config
39 mountPath: /etc/app/config
40 readOnly: true
41 # [...]
Make sure to include the checksum annotation. On every ConfigMap change, the checksum is computed and the annotation will change. The change will trigger a restart of the application which will pick up the new version of the ConfigMap. If using the helm create
command to initialize the Helm chart, a ConfigMap will be created that will include this annotation.
By following this recipe, a user can easily start with the default configuration of the application and set fields in the appConfig
field of the values of the Helm chart whenever they feel the need. Whenever you add a new configuration parameter to your app which is optional or has a default value, it will be supported as is for the Helm chart too, you just have to increase the application version in the Helm chart. And you don't bloat the values yaml file.