In traditional systems, environment variables play an important role, but not always a crucial one. Some applications make more use of environment variables than others. Some prefer configuration files over environment variables. However, when it comes to Kubernetes, environment variables are more important than you might think. It’s partially due to the way containers work in general and partially due to the specifics of Kubernetes. In this post, you’ll learn all about environment variables in Kubernetes.
Traditionally, environment variables are dynamic key-value variables that are accessible to any process running on the system.
The Basics
Let’s start with the basics. What are environment variables and why do they exist? Traditionally, environment variables are dynamic key-value variables that are accessible to any process running on the system. The operating system itself will set many environment variables that help running processes understand the specifics of the system. Thanks to this, software developers can include logic in their software that makes the programs adjustable to a specific operating system.
Environment variables also hold a lot of important information about the user, things like username, preferred language, user home directory path and many other useful bits of information.
User-Defined Environment Variables
Dawid Ziolkowski
Dawid has 10 years of experience as a network/systems engineer at the beginning, DevOps in between, cloud native engineer recently. He has worked for an IT outsourcing company, a research institute, telco, a hosting company and a consultancy company, so hes gathered a lot of knowledge from different perspectives. Nowadays, hes helping companies move to cloud and/or redesign their infrastructure for a more cloud native approach.
As a user, you can easily create and access your own environment variables. On Unix-based systems, you can do that by executing the export
command followed by the name of your variable and its value.
So, for example, to create an environment variable called myvar
with a value of 10, you need to execute the following:
export myvar=10
You can then access the value of your variable using a dollar sign followed by your variable name.
In our case, to print (using Linux command echo
) the value of our variable, we can execute the following:
echo $myvar
And if you want to print all the environment variables, you can execute either printenv
or env
commands.
All of this applies to applications running in your pods too.
Environment Variables in Kubernetes
The basic principle of environment variables in Kubernetes is the same. However, Kubernetes uses environment variables quite extensively and for a few different things. Therefore, it’s good to understand the role environment variables play in Kubernetes. That’s especially true if you want to migrate an existing application that doesn’t use environment variables that much. You can still create your pods without any environment variables. But if you ignore environment variables in Kubernetes completely, you may lose some of the value of the more powerful Kubernetes features.
Before we move any further, you also need to know that it’s generally good practice when developing microservices to provide configuration to your Docker containers as environment variables whenever possible. This way you can make your Docker image more generic and possibly reuse the same image for different purposes. With that being said, let’s see how you can inject some environment variables into your Kubernetes pods.
The main use case for environment variables in Kubernetes is similar to the one from traditional software development.
Configuration for Your Pods
The main use case for environment variables in Kubernetes is similar to the one from traditional software development. That is to provide information about the environment to your software. This information is usually used to alter or adapt the way software works to the specifics of the environment. The definition may seem vague, so let me give you an example. Instead of having separate Docker images for your development and production environments, you can use the same image but run your application in a development or production mode based on an environment variable.
In Kubernetes, environment variables are scoped to a container, and there are three main ways of adding them. Let’s break them down.
Direct
The first option is the most straightforward. You can simply specify environment variables directly in your deployment definition with an env
keyword:
apiVersion: apps/v1
kind: Deployment
metadata:
name: example-app
spec:
replicas: 1
selector:
matchLabels:
app: example-app
template:
metadata:
labels:
app: example-app
spec:
containers:
– name: example-app-dev
image: [yourimage]
env:
– name: ENVIRONMENT
value: “development”
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
—–
apiVersion: apps/v1
kind: Deployment
metadata:
name: example–app
spec:
replicas: 1
selector:
matchLabels:
app: example–app
template:
metadata:
labels:
app: example–app
spec:
containers:
– name: example–app–dev
image: [yourimage]
<strong>env:</strong>
– <strong>name: ENVIRONMENT</strong>
<strong> value: “development”</strong> |
As soon as your application is instructed to read the value of an environment variable called ENVIRONMENT
, you can use it directly to run your application in the desired mode.
To run the same application in a production mode, you can simply reuse the same deployment definition. You’ll only need to change the environment variable value, and optionally a name of the pod:
apiVersion: apps/v1
kind: Deployment
(…)
containers:
– name: example-app-prod
image: [yourimage]
env:
– name: ENVIRONMENT
value: “production”
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
—–
apiVersion: apps/v1
kind: Deployment
(...)
containers:
– name: example–app–prod
image: [yourimage]
<strong>env:</strong>
– <strong>name: ENVIRONMENT</strong>
<strong> value: “production”</strong> |
Here’s another example: Imagine that you have a web application that needs to download a product catalog to be served to users. This catalog may differ in a few ways, for example, by a country, month or supplier. This is a perfect use case for an environment variable. Instead of creating many different versions of your application to accommodate different download options, your application can remain generic. Which catalog it has to download will be determined by the value of some specific environment variable.
Secrets
Another way of providing environment variables to your application is by passing them from Kubernetes secrets. You may guess that this is a good option when you need to pass some sensitive information like passwords or tokens. This way you don’t specify the value of the environment variable directly in the deployment as we did before. Instead, you instruct Kubernetes to take the value of a specified secret object and use it as a value of an environment variable for your pod.
For example, if you have a Kubernetes secret like this:
kind: Secret
metadata:
name: secret_data
type: Opaque
stringData:
username: “example”
password: “supersecretpassword”
and you want to pass the password as an environment variable to your pod, you can reference it in the deployment definition as follows:
—
apiVersion: apps/v1
kind: Deployment
(…)
containers:
– name: example-app-prod
image: [yourimage]
env:
# Inject variables from a Kuberentes secret
– name: secret_variables
valueFrom:
secretKeyRef:
name: secret_data
key: password
In your pod, you will then be able to access the actual password (supersecretpassword) by accessing an environment variable called secret_variable. For example, in Python you could do it like this:
import os
PASSWORD = os.environ.get[‘secret_variable’]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
apiVersion: v1
kind: Secret
metadata:
name: secret_data
type: Opaque
stringData:
username: “example”
password: “supersecretpassword”
and you want to pass the password as an environment variable to your pod, you can reference it in the deployment definition as follows:
—–
apiVersion: apps/v1
kind: Deployment
(...)
containers:
– name: example–app–prod
image: [yourimage]
env:
# Inject variables from a Kuberentes secret
– name: secret_variables
valueFrom:
secretKeyRef:
name: secret_data
key: password
In your pod, you will then be able to access the actual password (supersecretpassword) by accessing an environment variable called secret_variable. For example, in Python you could do it like this:
import os
PASSWORD = os.environ.get[‘secret_variable’] |
As you can see, in our example we have username
and password
defined in Kubernetes secret, but we are only passing the password
value to the pod. If you want to pass all the secrets from a Kubernetes secret without specifying each key, you can use secretRef
instead of secretKeyRef
. This way, you only need to specify the Kubernetes secret object name, and all the values from it will be automatically loaded as environment variables:
apiVersion: apps/v1
kind: Deployment
(…)
containers:
– name: example-app-prod
image: [yourimage]
env:
# Inject variables from a Kuberentes secret
– name: secret_variables
valueFrom:
secretRef:
name: secret_data
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
—–
apiVersion: apps/v1
kind: Deployment
(...)
containers:
– name: example–app–prod
image: [yourimage]
env:
# Inject variables from a Kuberentes secret
– name: secret_variables
valueFrom:
secretRef:
name: secret_data |
ConfigMaps
Another way of injecting environment variables into your pods is by using values from ConfigMaps. For example, if you have ConfigMap like this:
kind: ConfigMap
metadata:
name: config-data
data:
environment: “dev”
timezone: “UTC”
apiVersion: v1
kind: ConfigMap
metadata:
name: config–data
data:
environment: “dev”
timezone: “UTC” |
and you want to load both environment
and timezone
as environment variables into your pod, you can add the following valueFrom
definition to your deployment:
apiVersion: apps/v1
kind: Deployment
(…)
containers:
– name: example-app-prod
image: [yourimage]
env:
# Inject variables from a Kuberentes ConfigMap
– name: config_variables
valueFrom:
configMapRef:
name: config-data
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
—–
apiVersion: apps/v1
kind: Deployment
(...)
containers:
– name: example–app–prod
image: [yourimage]
env:
# Inject variables from a Kuberentes ConfigMap
– name: config_variables
valueFrom:
configMapRef:
name: config–data |
In your pod, you’ll then be able to see both environment variables as defined in your ConfigMap:
HOSTNAME=5ad4e9e78e57
environment=dev
timezone=UTC
# env
HOSTNAME=5ad4e9e78e57
environment=dev
timezone=UTC |
As with secrets, if you don’t want to load all values from a ConfigMap, you can define specific keys instead by changing configMapRef
to configMapKeyRef
.
The main difference between passing environment variables from ConfigMaps and specifying them directly as in the first example is the fact that here the environment variable life cycle is separated from the pod life cycle. This means you can update the value of your variable independently from the running pod. Or, to put it differently, you’ll need to restart the pod yourself in order to load the new value of the environment variables into the pod. On the other hand, when you specify environment variables directly in the deployment, every change to the variables will automatically trigger pod restart.
Summary
Environment variables play an important role in Kubernetes. You can use them not only to provide basic information about the operating system to your application, but also as the main configuration mechanism for your pods or for passing sensitive information. It’s not uncommon in Kubernetes to extract as much configuration as possible as info ConfigMaps and environment variables to keep your Docker images as generic as possible. As you can see, even something simple like environment variables have a few options in Kubernetes. If you want to learn more, Release founding engineer Regis Wilson wrote about why Kubernetes is hard and what you can do about it.
The New Stack is a wholly owned subsidiary of Insight Partners, an investor in the following companies mentioned in this article: Docker.
Feature image via Pixabay