Articles Today I Learnt FM

Difference between env and envFrom in Kubernetes deployments

- 3 minutes read

When deploying software on Kubernetes, it is common to inject configuration parameters using environment variables. It is one of the ideas that Heroku advocated for so long, with the 12 factor app manifesto, and it is now common practice. On Kubernetes there are several ways to do that.

Let’s take this Deployment as reference


> k get deployment coreworker -o yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: coreworker
spec:
...
  template:
    metadata:
      annotations:
        env.coreworker/checksum: cce0fe17
    spec:
      containers:
      - args:
        - worker
        command:
        - celery
        env:
        - name: DD_ENV
          value: test
        - name: DD_SERVICE
          value: core
        - name: DJANGO_CONFIGURATION
          value: Worker
        - name: POD_IP
          valueFrom:
            fieldRef:
              apiVersion: v1
              fieldPath: status.podIP
        - name: HOSTNAME
          valueFrom:
            fieldRef:
              apiVersion: v1
              fieldPath: metadata.name
        envFrom:
        - configMapRef:
            name: coreworker-environment
        - secretRef:
            name: coreworker-environment
        image: django:62613
        name: django

You can see the two distinct methods above: env and envFrom. They both help you achieve the same thing, which is make data available to the application, but from a Kubernetes perspective, they are very different.

env sections

This is the most obvious way to inject configuration data. It is very easy to spot, and very easy to verify what is injected. Any change to their value will cause a manifest change, which in turn causes K8s to rotate the replicasets, which restarts the app with the new value.

This way to specify variables is also the easiest way to use the Downward API, used to inject dynamic data from the cluster into the deployment. Here you can see POD_IP passed to the application, which contains the IP that the networking layer assigned to the pod.

In cases in which your app requires a lot of configuration data, this can get unwieldy. This method does not work well for long sets of variables.

envFrom sections

These sections are used to refer to environment variables that are either specified in a ConfigMap or a Secret manifest. The manifest above references both a ConfigMap (used more liberally for key/value pairs) and a Secret.


> k get cm coreworker-environment -o yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: coreworker-environment
data:
  ENVIRONMENT: test
  DB_URL: databases.company/name
  LOGGING: "1"
  ...

The Secret would have the same structure.

The advantage of using external references, besides being better for long list of variables, is that, being a separate K8s resource, different RBAC rules can apply. You may want to be more liberal about Deployment read access, but stricter on reading the content of a Secret manifest, limiting that to only a few people in the business.

The disadvantage of external references is that, when updated, they do not propagate changes automatically, or trigger a restart of the pod. To force this, our Deployment manifest needs something that varies in the YAML.

One of the conventions that Helm encourages is to include a configuration checksum as annotation in the deployment. It is a way of telling Kubernetes that any configuration change needs to trigger an application restart. Another way could be to dynamically change the ConfigMap name, appending a random suffix.

Kubernetes does not offer a standard way to solve this (more details here), and each deployment tool ended up solving this problem in a different way.