Kubernetes equivalent of env-file in Docker

63,443

Solution 1

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

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

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

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

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

Share:
63,443

Related videos on Youtube

Johan
Author by

Johan

Architecture, domain driven design, large-scale systems, devops, simplicity. Strives to find pragmatic solutions to hard problems. Active open source contributor and founder of several popular open source projects such as PowerMock, REST Assured and Awaitility. Frequent speaker at user groups, companies and conferences such as Öredev, Jazoon, Devoxx and JFokus. Blogs at https://code.haleby.se

Updated on March 08, 2022

Comments

  • Johan
    Johan about 2 years

    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
    
    1. If this is not possible, what is the suggested approach?
    • alltej
      alltej about 4 years
      I am also looking for something like this. I don't want to create a Secret or ConfigMap resource because this is just temporary and use for testing. I have limited permissions in the k8s cluster. I maybe be able to create a Secret resource but I won't be able to delete them once it is already created.
  • Tim Hockin
    Tim Hockin over 8 years
    Ideally you should be able to define a Secret (or the eventual config objects we will have) and have that injected as env vars. Unfortunately that work is not done yet, so I am voting for this.
  • ant31
    ant31 over 8 years
    If you're using ansible, we have a common role to deploy on kubernetes: github.com/ansibl8s/k8s-common. Then it's very easy to prepare new applications, see exemples how to use it in other repo: github.com/ansibl8s
  • Paul Morie
    Paul Morie over 8 years
    I am hoping that we will do secrets in env vars for 1.2
  • luebken
    luebken about 8 years
    Note that there is a proposal for templates: github.com/kubernetes/kubernetes/blob/master/docs/proposals/‌​…
  • jävi
    jävi almost 8 years
    This is my current approach, however I have 3 different pods using the same list of secrets exposed as EnvVars. Is it possible to define them once and expose them to the 3 pods?
  • Pixel Elephant
    Pixel Elephant almost 8 years
    Not that I know of.
  • holms
    holms over 7 years
    what should I do if i want to use kubectl-run to pass 20 env variables??? why then don't do 12factor easier??
  • AndrewMcLagan
    AndrewMcLagan over 7 years
    that would be so great... seems like allot of boilerplate to get the env vars into containers. @PixelElephant
  • Bruce Zu
    Bruce Zu over 7 years
    Can this work. At least in my case it does not work. Please see my issue github.com/kubernetes/kubernetes/issues/38858 . Will you please have a look to see if I am wrong at some part. Thank you!
  • aronchick
    aronchick over 7 years
    This is no longer correct. The higher voted answer (below) is the correct answer.
  • aronchick
    aronchick over 7 years
    @jävi Do you mean replication controllers? Regardless, there's nothing binding a secret/config map to a single pod/RC/Deployment. It's just defined in the manifest as above, and can be mounted to as many things as you would like.
  • Pixel Elephant
    Pixel Elephant over 7 years
    @aronchick I believe they are looking for this feature: github.com/kubernetes/kubernetes/issues/26299 which looks like it will be landing soon. I'll update the answer once the feature is in a released version of Kubernetes.
  • Artem Dolobanko
    Artem Dolobanko almost 7 years
    Can you share Kubernetes documentation on this?
  • Or Duan
    Or Duan almost 7 years
    @ArtemDolobanko Edited, keep in mind this is still new and lakes of docs, you can find many discussions on Github's issues tracker if you want more details.
  • linuxbandit
    linuxbandit about 5 years
    I think it's important to mention that the values MUST be base64 encoded, in the definition of the Secret. I just tried this with my learning cluster and I get error unless the values are all base64 encoded
  • Alex
    Alex about 5 years
    Correct syntax is in the answer below, please fix in your answer, otherwise it generates error. You need a dash before secretRef and two spaces before name.
  • Muzi Jack
    Muzi Jack over 4 years
    @Or Duan how would i pass a version number to docker image using env
  • Muzi Jack
    Muzi Jack over 4 years
    @Pixel Elephant, how would i use env for docker image so i don't have to update deployment.yaml every time i need to increment a version
  • Muzi Jack
    Muzi Jack over 4 years
    how would i use env for docker image so i don't have to update deployment.yaml every time i need to increment a version
  • Muzi Jack
    Muzi Jack over 4 years
    how would i use env for docker image so i don't have to update deployment.yaml every time i need to increment a version
  • Tara Prasad Gurung
    Tara Prasad Gurung over 4 years
    what if we have to mount that text file to some location and the app will create the env automatically from there
  • David
    David almost 4 years
    Should this be --from-env-file? Using --from-file results in one key (named after the input file) with the content of the file. Using --from-env-file expands the keys inside the file into the secret. See this Google documentation for more.
  • srth12
    srth12 about 2 years
    Referencing '/var/www/html/.env' is creating a .env folder instead of the filename for me.
  • Cyril I
    Cyril I almost 2 years
    stackoverflow.com/questions/72343649/… my question also is related to env variable . Please help me to sort this out
  • Cyril I
    Cyril I almost 2 years
    stackoverflow.com/questions/72343649/… … my question also is related to env variable . Please help me to sort this out