1. Overview
Deployments in Kubernetes offer declarative management of the entire lifecycle of applications. So, we can configure various requirements for the desired state of a deployment, such as replica count, container images, labels, and so on, using a JSON or YAML manifest file.
In this tutorial, we’ll explore multiple ways for passing environment variable(s) to Kubernetes deployment.
2. Understanding the Scenario
Let’s start by defining an environment variable REPLICAS that we intend to pass to a Kubernetes deployment:
$ export REPLICAS=3
Now, let’s write a deployment.yaml manifest file to configure a deployment that runs an nginx server inside the container:
$ cat deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: frontend-nginx-deployment
spec:
replicas: $REPLICAS
selector:
matchLabels:
app: dashboard
environment: production
template:
metadata:
labels:
app: dashboard
environment: production
spec:
containers:
- name: frontend-nginx
image: nginx
We must note that we’re trying to use the value of the REPLICAS environment variable in the replicas field in the deployment’s spec.
Lastly, let’s see if we can create a deployment using the deployment.yaml manifest:
$ kubectl apply -f deployment.yaml
Error from server (BadRequest): error when creating "deployment.yaml": Deployment in version "v1" cannot be handled as a Deployment: json: cannot unmarshal string into Go struct field DeploymentSpec.spec.replicas of type int32
Unfortunately, our attempt to pass the REPLICAS environment variable to the deployment failed. Further, we can infer from the error message that $REPLICAS didn’t translate into its integer value. As a result, Kubernetes was unable to parse the string $REPLICAS because it expects an integer value for the replicas field.
In the following sections, we’ll reuse this manifest file with minor modifications to solve the use case of passing an environment variable to the Kubernetes deployment.
3. Using envsubst
We can use the envsubst utility to preprocess the deployment.yaml file and replace the $REPLICAS text with its corresponding value:
$ envsubst < deployment.yaml | grep -i replicas
replicas: 3
So, let’s get go ahead and pass the preprocessed manifest file directly to the kubectl apply command:
$ envsubst < deployment.yaml | kubectl apply -f -
deployment.apps/nginx-deployment created
We must note that we passed – as an argument to the kubectl command so that it reads the manifest from stdin instead of a file.
Finally, let’s verify that frontend-nginx-deployment is running three replicas:
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
frontend-nginx-deployment-7f8459fff4-2zmp2 1/1 Running 0 10s
frontend-nginx-deployment-7f8459fff4-mfdc7 1/1 Running 0 10s
frontend-nginx-deployment-7f8459fff4-trz2n 1/1 Running 0 10s
Fantastic! It looks like we’ve got this one right.
4. Using Text Substitution
In this section, we will use some of the popular text-editing tools in Linux to preprocess the deployment.yaml file. Like earlier, our goal will be to replace the $REPLICAS string with its corresponding value and pass it on to the Kubernetes deployment.
4.1. Using sed
sed is one of the most popular text editing utilities in Linux. So, let’s see how we can use it to preprocess the deployment.yaml file, and then apply the deployment configuration:
$ sed "s/\$REPLICAS/$REPLICAS/" deployment.yaml | kubectl apply -f -
deployment.apps/nginx-deployment created
We need to enclose the substitution(s) command within double quotes so that the $REPLICAS replacement text evaluates to its corresponding value. Otherwise, sed treats it as a literal string if we use single quotes.
Further, let’s also verify that pods are up and running:
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
frontend-nginx-deployment-7f8459fff4-4s784 1/1 Running 0 10s
frontend-nginx-deployment-7f8459fff4-cdr58 1/1 Running 0 10s
frontend-nginx-deployment-7f8459fff4-djmql 1/1 Running 0 10s
Great! The result matches our expectations.
4.2. Using awk
Similar to sed, we can use awk to substitute the $REPLICAS text in the deployment.yaml file with its value. For this purpose, we can use the gsub() substitution method and access the value of the $REPLICAS environment variable through the ENVIRON associative array.
So, let’s go ahead and preprocess the deployment.yaml file using awk, and then apply the resulting configuration using the kubectl utility:
$ awk '{gsub(/\$REPLICAS/, ENVIRON["REPLICAS"]); print}' deployment.yaml | kubectl apply -f -
deployment.apps/nginx-deployment created
Further, we can verify that three replicas are up:
$ kubectl get pods
# similar output as earlier
4.3. Using Shell Parameter Substitution
Another way to preprocess the deployment.yaml file is by using shell parameter substitution. For this purpose, let’s start by initializing the manifest_var variable with the content from the deployment.yaml file:
$ manifest_var=$(<deployment.yaml)
Now, by using variable substitution, let’s replace the $REPLICAS text in the manifest_var variable with its corresponding value from the environment variable. Further, we can pass the output to the kubectl apply command for creating the deployment:
$ echo "${manifest_var//\$REPLICAS/$REPLICAS}" | kubectl apply -f -
deployment.apps/nginx-deployment created
We must realize that this approach depends on the support of parameter substitution from the shell. Although most modern shells, such as Bash, Zsh, and so on, support this, the feature may be missing or partially supported in some older shells.
4.4. Using yq
Since we’re working with a YAML file, yq would be an excellent option to preprocess the deployment.yaml manifest file. So, let’s see how we can use the eval command to replace the .spec.replicas field with an environment variable and then pass it down to Kubernetes deployment:
$ yq eval '.spec.replicas = env(REPLICAS)' deployment.yaml | kubectl apply -f -
deployment.apps/nginx-deployment created
We’ve successfully passed down the REPLICAS environment variable to the nginx-deployment. However, it’s important to note that we used the env() function to retrieve the value from the environment.
4.5. Using jq
Earlier, we used the yq utility to preprocess the manifest file when available in YAML format. Now, let’s learn how to use the jq utility to preprocess the manifest file in JSON format.
First, let’s look at the deployment configuration in JSON format present in the deployment.json file:
$ cat deployment.json
{
"apiVersion": "apps/v1",
"kind": "Deployment",
"metadata": {
"name": "frontend-nginx-deployment"
},
"spec": {
"replicas": "$REPLICAS",
"selector": {
"matchLabels": {
"app": "dashboard",
"environment": "production"
}
},
"template": {
"metadata": {
"labels": {
"app": "dashboard",
"environment": "production"
}
},
"spec": {
"containers": [
{
"name": "frontend-nginx",
"image": "nginx"
}
]
}
}
}
}
We can observe that manifest uses $REPLICAS to specify the value for the spec.replicas field.
Now, let’s use the jq command with the –argjson option to pass $REPLICAS as an internal variable named replicas. Further, we can replace the spec.replicas field with the value of the replicas variable before applying the Kubernetes deployment configuration:
$ jq --argjson replicas "$REPLICAS" '.spec.replicas |= $replicas' deployment.json \
| kubectl apply -f -
deployment.apps/frontend-nginx-deployment created
It looks like we’ve nailed this one too!
5. Using helm
Helm is widely used for managing deployments on a Kubernetes cluster. In this section, let’s learn how to use the helm command to pass down an environment variable to a Kubernetes deployment.
First, let’s start by using the helm create command to create the frontend-nginx helm chart:
$ helm create frontend-nginx
Further, let’s use the exa command to peek into the structure of the frontend-nginx directory:
$ exa -d --tree frontend-nginx
frontend-nginx
├── Chart.yaml
├── charts
├── templates
│ ├── _helpers.tpl
│ ├── deployment.yaml
│ ├── hpa.yaml
│ ├── ingress.yaml
│ ├── NOTES.txt
│ ├── service.yaml
│ ├── serviceaccount.yaml
│ └── tests
│ └── test-connection.yaml
└── values.yaml
Next, let’s replace the frontend-nginx/templates/deployment.yaml manifest with our existing deployment.yaml file:
$ cat deployment.yaml > frontend-nginx/templates/deployment.yaml
Furthermore, let’s also replace the line in frontend-nginx/templates/deployment.yaml file that contains the replicas field with the templatized definition:
$ cat frontend-nginx/templates/deployment.yaml | grep -i replicas
replicas: {{ .Values.replicas | default 1 }}
It’s worth noting that the value of the replicas field for deployment is derived from the replicas field available in the values.yaml file. If the field isn’t set in values.yaml, then it’ll use a default value of 1.
Now, we’re ready to create the deployment using the helm install command. Further, we can use the –set option to set the value of the replicas field as the REPLICAS environment variable:
$ helm install frontend-nginx frontend-nginx --set replicas=$REPLICAS
Lastly, let’s verify that the value of replicas is picked correctly:
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
frontend-nginx-deployment-7f8459fff4-pv7xv 1/1 Running 0 16m
frontend-nginx-deployment-7f8459fff4-v5bnj 1/1 Running 0 16m
frontend-nginx-deployment-7f8459fff4-wqrsf 1/1 Running 0 16m
As expected, there are three replicas available for frontend-nginx-deployment.
6. Using ansible-playbook
In this section, we’ll learn how to use Ansible to pass the REPLICAS environment variable to a Kubernetes deployment.
First, let’s convert our deployment.yaml manifest file into a Jinja2 template file by using {{ replicas }} as a template variable for the replicas field:
$ cat deployment.yaml.j2
apiVersion: apps/v1
kind: Deployment
metadata:
name: frontend-nginx-deployment
spec:
replicas: {{ replicas }}
selector:
matchLabels:
app: dashboard
environment: production
template:
metadata:
labels:
app: dashboard
environment: production
spec:
containers:
- name: frontend-nginx
image: nginx
Next, let’s write the deploy.yaml Ansible playbook that executes two tasks. Firstly, it renders the template variables from the deployment.yaml.j2 file into a new file located at /tmp/deployment.yaml, and then it uses this rendered manifest file for creating the Kubernetes deployment:
$ cat deploy.yaml
---
- name: Deploy Kubernetes app with variable number of replicas
hosts: localhost
become: true
vars:
replicas: "{{ lookup('env', 'REPLICAS') | default(1) }}"
tasks:
- name: Render Kubernetes manifest template
template:
src: deployment.yaml.j2
dest: /tmp/deployment.yaml
mode: '0644'
- name: Apply Kubernetes manifest
command: kubectl apply -f /tmp/deployment.yaml
It’s important to note that we’ve used the lookup() function to retrieve the value of the REPLICAS environment variable.
Moving on, let’s see this in action by executing the deploy.yaml playbook with the ansible-playbook command:
$ ansible-playbook deploy.yaml
Lastly, let’s verify that our approach worked as expected:
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
frontend-nginx-deployment-7f8459fff4-k2vk5 1/1 Running 0 13m
frontend-nginx-deployment-7f8459fff4-kfgj2 1/1 Running 0 13m
frontend-nginx-deployment-7f8459fff4-tnbjp 1/1 Running 0 13m
7. Conclusion
In this article, we explored various techniques to pass an environment variable to a Kubernetes deployment. Further, we solved this use case with interesting utilities, such as envsubst, Bash parameter substitution, helm, and the ansible-playbook command.
Additionally, we used different text substitution tools, such as sed, awk, jq, and yq, to preprocess the deployment.yaml file containing an environment variable. Then, we applied the processed manifest file for passing the environment variable to the Kubernetes deployment.