If you’re a Docker/Compose user new to Kubernetes, you might have noticed that you can’t use environment variables in your manifest files. This might not be a huge deal to most, especially since there are other (better) ways of managing environment variables in Kubernetes, but to someone getting started it can be a pain.
For example, say we have an Nginx container that we’re migrating from Docker Compose to Kubernetes. We want to change this container’s port number depending on whether we’re running it in production or in development. Our docker-compose.yml
looks like this:
version: '3.7' services: nginx: build: images/nginx hostname: nginx restart: always ports: - "${NGINX_PORT:-443}:443"
Here, ${NGINX_PORT:-443}
will use the environment variable NGINX_PORT
if it’s set, and if not, it’ll default to 443. As a convenience, if we set this environment variable using an .env
file, Docker Compose will automatically read this file when it runs. This makes it easy for us to make two different .env files – one for each environment – and easily swap them out whenever we call docker-compose
.
Unfortunately, if we migrate our container over to Kubernetes, this system no longer works. We’d need to use some other method of storing configuration metadata, like a ConfigMap. But let’s say you’re in a rush, and you just want to get the container working, best practices be damned. In that case, there is a way to use environment variables in Kubernetes manifests, and that’s with envsubst.
envsubst
envsubst is a command-line program that substitutes environment variables into standard input, then spits out a copy with the variables replaced. This is perfect for Kubernetes, as it gives us an exact copy of our original manifest only with any environment variables replaced. This way, we can keep our .env file until we have a more Kubernetes-friendly way of managing these variables and secrets.
First, we need to source our .env file to load the variables into our environment:
source. env
Next, we run envsubst, hand it our manifest file, then pipe the output to kubectl. Our manifest file is called nginx.yml and looks like this:
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:latest
ports:
- containerPort: 443
- hostPort: ${NGINX_PORT:-443}:443
If we handed this to kubectl, it would complain about the hostPort not being set. But if we pass it off to envsubst, we get this as the output:
$ envsubst < nginx.yml
...
ports:
- containerPort: 443
- hostPort: 443:443
There we go, a fully formatted manifest file with our environment variable set. Now, we can pass this directly to kubectl:
$ envsubst < nginx.yml | kubectl apply -f -
And our Nginx container gets deployed! If we want to change our .env file, we just run source <new env file>
and re-run envsubst. If you want to make this even easier, I’ve created a script you can use. You can call this script just like you would kubectl
. This script sources any .env file in the current directory. If you’re running kubectl apply
or kubectl delete
, it calls envsubst
to swap out your environment variables, then it passes the code on to the real kubectl
. Save this code to a text file, mark it as executable, and run it just like you would kubectl
:
#!/bin/bash
ENV_FILE=.env
source $ENV_FILE
if [[ "$1" == "apply" ]] || [[ "$1" == "delete" ]]; then
envsubst < $3 | kubectl $1 $2 -
else
kubectl "$@"
fi
Again, this isn’t a best practice, and I wouldn’t recommend using this method in production. But if you’re coming from Docker Compose, you’re still learning Kubernetes, and you just want to get up and running, I hope this helps!