2

We're using Helm to deploy our app to K8s. In 4 different deployment files (one for each service) and a job file for migrations, we have to have an identical set of env variables. Whenever we need to add a new one, we need to add it to all 5 files. Is there a way to share these so new env vars only need to be added once and all 5 files will pick them up (and also cannot ever be out of sync)?

Here's an example of a deployment file (with potentially sensitive values redacted).

apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ include "helm-chart.fullname" . }}-celery
  labels:
    app.kubernetes.io/name: {{ include "helm-chart.name" . }}-celery
    helm.sh/chart: {{ include "helm-chart.chart" . }}
    app.kubernetes.io/instance: {{ .Release.Name }}-celery
    app.kubernetes.io/managed-by: {{ .Release.Service }}
    app.kubernetes.io/component: worker-celery
spec:
  replicas: {{ .Values.replicaCountCelery }}
  selector:
    matchLabels:
      app.kubernetes.io/name: {{ include "helm-chart.fullname" . }}-celery
      app.kubernetes.io/instance: {{ .Release.Name }}-celery
  template:
    metadata:
      labels:
        app.kubernetes.io/name: {{ include "helm-chart.fullname" . }}-celery
        app.kubernetes.io/instance: {{ .Release.Name }}-celery
    spec:
      imagePullSecrets:
        - name: {{ .Values.imagePullSecretsName }}
      containers:
        - name: {{ .Chart.Name }}-celery
          image: "{{ .Values.appImage.repository }}:{{ .Values.imageTag }}"
          imagePullPolicy: {{ .Values.appImage.pullPolicy }}
          command: ["celery"]
          args: [REDACTED]
          env:
            - name: DJANGO_DEBUG
              value: "{{ .Values.djangoDebug }}"
            - name: DATABASE_NAME
              value: "{{ .Values.databaseName }}"
            - name: DATABASE_USER
              valueFrom:
                secretKeyRef:
                  name: [REDACTED]
                  key: [REDACTED]
            - name: DATABASE_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: [REDACTED]
                  key: [REDACTED]
            - name: DATABASE_HOST
              value: "myapp-haproxy.{{ .Release.Namespace }}.svc.cluster.local"
            - name: MEMCACHED_HOST
              value: "myapp-memcached.{{ .Release.Namespace }}.svc.cluster.local"
            - name: SENDGRID_USER
              valueFrom:
                secretKeyRef:
                  name: [REDACTED]
                  key: [REDACTED]
            - name: SENDGRID_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: [REDACTED]
                  key: [REDACTED]
            - name: STRIPE_LIVE_PUBLIC_KEY
              valueFrom:
                secretKeyRef:
                  name: [REDACTED]
                  key: [REDACTED]
            - name: STRIPE_LIVE_SECRET_KEY
              valueFrom:
                secretKeyRef:
                  name: [REDACTED]
                  key: [REDACTED]
            - name: OBJECT_STORE_ENDPOINT_URL
              value: [REDACTED]
            - name: OBJECT_STORE_REGION_NAME
              value: [REDACTED]
            - name: OBJECT_STORE_KEY_ID
              valueFrom:
                secretKeyRef:
                  name: [REDACTED]
                  key: [REDACTED]
            - name: OBJECT_STORE_ACCESS_KEY
              valueFrom:
                secretKeyRef:
                  name: [REDACTED]
                  key: [REDACTED]
            - name: OBJECT_STORE_CDN_URL
              value: [REDACTED]
            - name: QUICKBOOKS_CLIENT_ID
              valueFrom:
                secretKeyRef:
                  name: [REDACTED]
                  key: [REDACTED]
            - name: QUICKBOOKS_CLIENT_SECRET
              valueFrom:
                secretKeyRef:
                  name: [REDACTED]
                  key: [REDACTED]
            - name: QUICKBOOKS_ENVIRONMENT
              value: production
            - name: XERO_CONSUMER_KEY
              valueFrom:
                secretKeyRef:
                  name: [REDACTED]
                  key: [REDACTED]
            - name: XERO_CONSUMER_SECRET
              valueFrom:
                secretKeyRef:
                  name: [REDACTED]
                  key: [REDACTED]
            - name: SAGE_CLIENT_ID
              valueFrom:
                secretKeyRef:
                  name: [REDACTED]
                  key: [REDACTED]
            - name: SAGE_CLIENT_SECRET
              valueFrom:
                secretKeyRef:
                  name: [REDACTED]
                  key: [REDACTED]
            - name: ACCOUNTANCY_REDIRECT_URI_PREFIX
              value: [REDACTED]
          resources:
            {{- toYaml .Values.celeryResources | nindent 12 }}
      {{- with .Values.nodeSelector }}
      nodeSelector:
        {{- toYaml . | nindent 8 }}
      {{- end }}
    {{- with .Values.affinity }}
      affinity:
        {{- toYaml . | nindent 8 }}
    {{- end }}
    {{- with .Values.tolerations }}
      tolerations:
        {{- toYaml . | nindent 8 }}
    {{- end }}

I'm not sure if it adds any complications, but you can see that some are using variables from values.yaml (such as {{ .Values.djangoDebug }}), some are referring to Kubernetes secrets and some use the {{ .Release.Namespace }} variable.

Additionally, the indentation required is the same for the 4 deployment files but different for the job file.

I'm trying to share a bunch of env values but also be able to optionally add some extras to some files.

I hope that makes sense? And thanks in advance for your help.

Luke Cousins
  • 377
  • 1
  • 3
  • 18

1 Answers1

2

If I understand you correctly the thing you need is a ConfigMap.

Many applications require configuration via some combination of config files, command line arguments, and environment variables. These configuration artifacts should be decoupled from image content in order to keep containerized applications portable. The ConfigMap API resource provides mechanisms to inject containers with configuration data while keeping containers agnostic of Kubernetes. ConfigMap can be used to store fine-grained information like individual properties or coarse-grained information like entire config files or JSON blobs.

Basicly you create a ConfigMap and setup a proper key:value. After that you use the created ConfigMap to declare it's values as environment in your Deployments.

Here you can find the official example:


Create a ConfigMap containing multiple key-value pairs.

apiVersion: v1
kind: ConfigMap
metadata:
  name: special-config
  namespace: default
data:
  SPECIAL_LEVEL: very
  SPECIAL_TYPE: charm

Use envFrom to define all of the ConfigMap’s data as container environment variables. The key from the ConfigMap becomes the environment variable name in the Pod.

apiVersion: v1
kind: Pod
metadata:
  name: dapi-test-pod
spec:
  containers:
    - name: test-container
      image: k8s.gcr.io/busybox
      command: [ "/bin/sh", "-c", "env" ]
      envFrom:
      - configMapRef:
          name: special-config
  restartPolicy: Never

Now, the Pod’s output includes environment variables SPECIAL_LEVEL=very and SPECIAL_TYPE=charm


Adjust to your needs and please let me know if that helped.

  • Thanks, this is really helpful. I didn't realise you could use a `ConfigMap` in `envFrom`. Do you know how I can include the secret values in the `ConfigMap`? – Luke Cousins Oct 15 '19 at 11:03
  • 1
    There is a different purpose for ConfigMaps and Secrets. Take a look [here](https://github.com/kubernetes/kubernetes/issues/79224). Especially [this](https://github.com/kubernetes/kubernetes/issues/79224#issuecomment-533942608) comment answers your question. – Wytrzymały Wiktor Oct 15 '19 at 12:16
  • Thanks, I really appreciate your help. – Luke Cousins Oct 15 '19 at 14:42