Kubernetes equivalent of env-file in Docker

ConfigurationDockerEnvironment VariablesConfiguration FilesKubernetes

Configuration Problem Overview


Background:

Currently we're using Docker and Docker Compose for our services. We have externalized the configuration for different environments into files that define environment variables read by the application. For example a prod.env file:

ENV_VAR_ONE=Something Prod
ENV_VAR_TWO=Something else Prod

and a test.env file:

ENV_VAR_ONE=Something Test
ENV_VAR_TWO=Something else Test

Thus we can simply use the prod.env or test.env file when starting the container:

docker run --env-file prod.env <image>

Our application then picks up its configuration based on the environment variables defined in prod.env.

Questions:

  1. Is there a way to provide environment variables from a file in Kubernetes (for example when defining a pod) instead of hardcoding them like this:

    apiVersion: v1
    kind: Pod
    metadata:
    labels:
    context: docker-k8s-lab
    name: mysql-pod
    name: mysql-pod
    spec:

    containers:

    env: - name: MYSQL_USER value: mysql - name: MYSQL_PASSWORD value: mysql - name: MYSQL_DATABASE value: sample - name: MYSQL_ROOT_PASSWORD value: supersecret image: "mysql:latest" name: mysql ports: - containerPort: 3306

  2. If this is not possible, what is the suggested approach?

Configuration Solutions


Solution 1 - Configuration

You can populate a container's environment variables through the use of Secrets or ConfigMaps. Use Secrets when the data you are working with is sensitive (e.g. passwords), and ConfigMaps when it is not.

In your Pod definition specify that the container should pull values from a Secret:

apiVersion: v1
kind: Pod
metadata: 
  labels: 
    context: docker-k8s-lab
    name: mysql-pod
  name: mysql-pod
spec: 
  containers:
  - image: "mysql:latest"
    name: mysql
    ports: 
    - containerPort: 3306
    envFrom:
      - secretRef:
         name: mysql-secret

Note that this syntax is only available in Kubernetes 1.6 or later. On an earlier version of Kubernetes you will have to specify each value manually, e.g.:

env: 
- name: MYSQL_USER
  valueFrom:
    secretKeyRef:
      name: mysql-secret
      key: MYSQL_USER

(Note that env take an array as value)

And repeating for every value.

Whichever approach you use, you can now define two different Secrets, one for production and one for dev.

dev-secret.yaml:

apiVersion: v1
kind: Secret
metadata:
  name: mysql-secret
type: Opaque
data:
  MYSQL_USER: bXlzcWwK
  MYSQL_PASSWORD: bXlzcWwK
  MYSQL_DATABASE: c2FtcGxlCg==
  MYSQL_ROOT_PASSWORD: c3VwZXJzZWNyZXQK

prod-secret.yaml:

apiVersion: v1
kind: Secret
metadata:
  name: mysql-secret
type: Opaque
data:
  MYSQL_USER: am9obgo=
  MYSQL_PASSWORD: c2VjdXJlCg==
  MYSQL_DATABASE: cHJvZC1kYgo=
  MYSQL_ROOT_PASSWORD: cm9vdHkK

And deploy the correct secret to the correct Kubernetes cluster:

kubectl config use-context dev
kubectl create -f dev-secret.yaml

kubectl config use-context prod
kubectl create -f prod-secret.yaml

Now whenever a Pod starts it will populate its environment variables from the values specified in the Secret.

Solution 2 - Configuration

A new update for Kubernetes(v1.6) allows what you asked for(years ago).

You can now use the envFrom like this in your yaml file:

  containers:
  - name: django
    image: image/name
    envFrom:
      - secretRef:
         name: prod-secrets

Where development-secrets is your secret, you can create it by:

kubectl create secret generic prod-secrets --from-env-file=prod/env.txt`

Where the txt file content is a key-value:

DB_USER=username_here
DB_PASSWORD=password_here

The docs are still lakes of examples, I had to search really hard on those places:

Note: there's a difference between --from-file and --from-env-file when creating secret as described in the comments below.

Solution 3 - Configuration

When defining a pod for Kubernetes using a YAML file, there's no direct way to specify a different file containing environment variables for a container. The Kubernetes project says they will improve this area in the future (see Kubernetes docs).

In the meantime, I suggest using a provisioning tool and making the pod YAML a template. For example, using Ansible your pod YAML file would look like:

file my-pod.yaml.template:

apiVersion: v1
kind: Pod
...
spec:
  containers:
  ...
    env:
    - name: MYSQL_ROOT_PASSWORD
      value: {{ mysql_root_pasword }}
    ...

Then your Ansible playbook can specify the variable mysql_root_password somewhere convenient, and substitute it when creating the resource, for example:

file my-playbook.yaml:

- hosts: my_hosts
  vars_files: 
  - my-env-vars-{{ deploy_to }}.yaml
  tasks:
  - name: create pod YAML from template
    template: src=my-pod.yaml.template dst=my-pod.yaml
  - name: create pod in Kubernetes
    command: kubectl create -f my-pod.yaml

file my-env-vars-prod.yaml:

mysql_root_password: supersecret

file my-env-vars-test.yaml:

mysql_root_password: notsosecret
    

Now you create the pod resource by running, for example:

ansible-playbook -e deploy=test my-playbook.yaml

Solution 4 - Configuration

This works for me:

file env-secret.yaml

apiVersion: v1
kind: Secret
metadata:
  name: env-secret
type: Opaque
stringData:
  .env: |-
    APP_NAME=Laravel
    APP_ENV=local

and into the deployment.yaml or pod.yaml

spec:
  ...
        volumeMounts:
        - name: foo
          mountPath: "/var/www/html/.env"
          subPath: .env
      volumes:
      - name: foo
        secret:
          secretName: env-secret
````

Solution 5 - Configuration

I smashed my head aupon tyhis for 2 hours now. I found in the docs a very simple solution to minimize my (and hopefully your) pain.

  • Keep env.prod, env.dev as you have them.

  • Use a oneliner script to import those into yaml:

    kubectl create configmap my-dev-config --from-env-file=env.dev

    kubectl create configmap my-prod-config --from-env-file=env.prod

You can see the result (for instant gratification):

# You can also save this to disk
kubectl get configmap my-dev-config -o yaml

As a rubyist, I personally find this solution the DRYest as you have a single point to maintain (the ENV bash file, which is compatible with Python/Ruby libraries, ..) and then you YAMLize it in a single execution.

Note that you need to keep your ENV file clean (I have a lot of comments which prevented this to work so had to prepend a cat config.original | egrep -v "^#" | tee config.cleaned) but this doen't change the complexity substantially.

It's all documented here

Solution 6 - Configuration

This is an old question but it has a lot of viewers so I add my answer. The best way to separate the configuration from K8s implementation is using Helm. Each Helm package can have a values.yaml file and we can easily use those values in the Helm chart. If we have a multi-component topology we can create an umbrella Helm package and the parent values package also can overwrite the children values files.

Solution 7 - Configuration

This is an old question but let me describe my answer for future beginner.

You can use kustomize configMapGenerator.

configMapGenerator:
  - name: example
    env: dev.env

and refer this configMap/example in pod definition

Solution 8 - Configuration

You can reference K8S values by specifying them in your container as environment variables.

let your deployment be mongo.yml as follows:

--
kind: Deployment
   --
      --
      containers:
        --
        env:
        - name: DB_URL
          valueFrom:
            configMapKeyRef:
              name: mongo-config
              key: mongo-url
        - name: MONGO_INITDB_ROOT_PASSWORD
          valueFrom:
            secretKeyRef:
              name: mongo-secret
              key: mongo-password  

Where mongo-secret is used for senective data, e.g.: passwords or certificates

apiVersion: v1
kind: Secret
metadata:
  name: mongo-secret
type: Opaque
data:
  mongo-user: bW9uZ291c2Vy
  mongo-password: bW9uZ29wYXNzd29yZA==

and mongo-config is used for non-sensitive data

apiVersion: v1
kind: ConfigMap
metadata:
  name: mongo-config
data:
  mongo-url: mongo-service

Solution 9 - Configuration

You could try this steps:

This command will put your file prod.env in the secrets.

kubectl create secret generic env-prod --from-file=prod.env=prod.env

Then, you could reference this file in your deployment.yaml like

apiVersion: apps/v1
kind: Deployment
metadata:
  name: api-identity
  labels:
    app.kubernetes.io/name: api-identity
spec:
  replicas: 1
  selector:
    matchLabels:
      app.kubernetes.io/name: api-identity
  template:
    metadata:
      labels:
        app.kubernetes.io/name: api-identity
    spec:
      imagePullSecrets:
        - name: docker-registry-credential
      containers:
        - name: api-identity
          image: "api-identity:test"
          imagePullPolicy: IfNotPresent
          ports:
            - name: http
              containerPort: 8005
              protocol: TCP
          volumeMounts:
          - name: env-file-vol
            mountPath: "/var/www/html/.env"
            subPath: .env
      volumes:
      - name: env-file-vol
        secret:
          secretName: env-prod

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionJohanView Question on Stackoverflow
Solution 1 - ConfigurationPixel ElephantView Answer on Stackoverflow
Solution 2 - ConfigurationOr DuanView Answer on Stackoverflow
Solution 3 - ConfigurationSpencer BrownView Answer on Stackoverflow
Solution 4 - ConfigurationmadwyattView Answer on Stackoverflow
Solution 5 - ConfigurationRiccardoView Answer on Stackoverflow
Solution 6 - ConfigurationAVarfView Answer on Stackoverflow
Solution 7 - ConfigurationsgshView Answer on Stackoverflow
Solution 8 - ConfigurationMostafa WaelView Answer on Stackoverflow
Solution 9 - Configurationsrth12View Answer on Stackoverflow