An Introduction to Environment Variables
Environment variables are a common way for developers to move application and infrastructure configuration into an external source outside of application code. A common reason to do this is to enable easier switching between environments. If you keep the key configuration, you need to move your application between a development and testing machine separate from the code; your application is more portable.
When an application runs on a machine, the variable becomes part of the environment a process runs in. Typically you can set these variables directly from a terminal, from a configuration file in a home directory, or using other tools.
For example, to set a variable in your command line, run the following:
<p> CODE: https://gist.github.com/ChrisChinchilla/ca38616c7a617985fc1c7077cd6e7726.js </p>
And echo it back out to the screen:
<p> CODE: https://gist.github.com/ChrisChinchilla/c6395d3c9ce956886f44c06b5279ec74.js </p>
Environment Variables and Kubernetes
A Pod is the basic execution unit of a Kubernetes application and represents processes running on your cluster.
When you create a Pod (with a Deployment, StatefulSet, or other means), you set environment variables for the containers that run in the Pod, which Kubernetes then passes to the application(s) inside the Pods.
There are two ways to define environment variables with Kubernetes: by setting them directly in a configuration file, from an external configuration file, using variables, or a secrets file.
This tutorial shows both options, and uses the Humanitec getting started application used in previous tutorials. The backend deployment spec sections use environment variables to send database access details to the backend service, and details of the backend to the front end service.
Using name and value Pairs
In the Deployment this consists of the env field and an array of EnvVars (which represents an environment variable in a container).
Each EnVar further consists of a name for the variable, and a value (a string).
<p> CODE: https://gist.github.com/ChrisChinchilla/94116daf27e4affadab6ef909a1724d3.js </p>
Using a configMap
Another method for defining environment variables is using configMaps. You can use configMaps for more than environment variables, but they primarily help you decouple configuration from image content and the infrastructure that supports that content.
The Postgres StatefulSet container spec section uses environment variables to configure the default Postgres database. Instead of using in-line variables, it uses envFrom to populate them from the configMapRef named postgres-config.
<p> CODE: https://gist.github.com/ChrisChinchilla/d011b29d2d73b042cece42a4909bdc9d.js </p>
And here’s the configMap, the majority of which is metadata, and the data section contains the variables passed back to the StatefulSet:
<p> CODE: https://gist.github.com/ChrisChinchilla/ada88ea93b3660f69351f632ddd0bf05.js </p>
For more flexibility and decoupling from Kubernetes, you can also use external configuration files in the format VAR=VAL, and create configMaps from them by using a kubectl command like the below:
kubectl create configmap postgres-config --from-env-file=postgres-config.properties
The postgres-config.properties file contains the below:
<p> CODE: https://gist.github.com/ChrisChinchilla/6aeda63e0e32ba6b8c7da07bfe06bef9.js </p>
Read more about configMaps in the Kubernetes documentation.
Using Kubernetes Variables
Instead of using the value field to set a value directly, you can instead use valueFrom that lets you set the value of environment variables to the value of Pod fields and container resources in your Kubernetes cluster.
From other Fields
The values for fields are limited to Pod fields from its metadata, name, service account name, and IP addresses, but the list may grow in the future.
The frontend deployment uses a label selector to set PRODUCT_BE_SERVER_URL environment variable to the frontend application requires. Using fieldRef, you could instead use the current IP address of the Pod running a container and set the environment variable with that.
<p> CODE: https://gist.github.com/ChrisChinchilla/13459c50eb8db3a17b60e5afbe1a304a.js </p>
Using other values follow a similar pattern, using metadata.*, status.*, spec.* to access the value you want.
The values for resources are limited to container CPU, memory, and storage limits and requests, but the list may grow in the future.
While it works, the spec for the example application backend deployment doesn’t set any resource limits on containers, which is generally bad Kubernetes practice, especially when running in production.
By using resourceFieldRef, the code example below from the backend deployment sets a MEMORY_LIMIT environment variable to the container that it, and the applications running in it, can use.
<p> CODE: https://gist.github.com/ChrisChinchilla/5d111502611294fe392923e879bf4b1e.js </p>
The other values follow a similar pattern, using limits.* to access the value you want.
The other common use for environment variables is storing secrets for sensitive information, such as passwords and access keys. These work in a similar way to configMaps (and you could use either for similar purposes), but secrets imply a certain level of security and privacy needed, and Kubernetes requires you to encode the values.
Using a Secret instead of a configMap for the Postgres user details is a more secure method in production, so let’s convert part of the Postgres configMap to secrets.
Create the following postgres-secrets.yml, as this is a secret, you need to base64 encode each data value:
<p> CODE: https://gist.github.com/ChrisChinchilla/0e0fc32d051ca5ab11f16a73d50ab815.js </p>
You can also use stringData instead of data and instead use unencoded values, Kubernetes then encodes the values for you when you create or update the secret. This is useful when your deployment process generates configuration, and you want to set the value at this stage.
Change the Postgres StatefulSet to the following:
<p> CODE: https://gist.github.com/ChrisChinchilla/8f332725f9e9b20c019c4c881c9f5f85.js </p>
The code above still uses the configMap for getting the database location but uses the separate Secret for the user account.
Find more details about Secrets in the Kubernetes documentation.
How Humanitec can help
After an introduction to environment variables by Christoph Richter, we have provided some hands-on examples in this article. As you see, managing environment variables can be pretty complex, especially when you need to handle multiple environments in parallel. Stay tuned and read in our next article, how Humanitec can help manage environment variables in a more efficient way.
Do you have more questions about environment variables? Humanitec's DevOps experts are happy to answer your questions during a free webinar!