Have you ever wondered about the ways in which you can manage environment variables when working with Kubernetes? If so, you have come to the right place!
This article will take you through how to set up and use environment variables in your Kubernetes cluster. I will first create a cluster with Civo, demonstrate how to set environment variables from ConfigMaps, share environment variables securely using Secrets, and finally show you how to use a tool called Doppler to securely store your environment variables.
There are many ways in which you can set up and use environment variables, this tutorial will go through some best practices.
Prerequisites
- Verified Civo account
- Civo CLI installed
- kubectl installed
- Basic understanding of Kubernetes
Creating a Kubernetes Cluster on Civo
In this section, we will be going through setting up a simple Kubernetes cluster with 2 nodes with Civo. We will use these clusters throughout this tutorial, that is why it is important you follow through.
We will need to get the API key in other to connect the CLI to our Civo account. You can get the API key by going to your Civo account dashboard and clicking on the Settings→Profile→Security then copy the API key you see there.
Attach your API Key to the Civo command-line client by running civo apikey save <name_your_API_key> <paste_your_API_key_here>
. Now, to ensure that the CLI uses the API Key you just attached, run civo apikey current <name_your_API_key>
.
We are now ready to create the cluster. Create the cluster with Civo by running the following command. You can check what each flag is doing by running civo kubernetes create --help
and perhaps add/remove flag(s) if needed.
civo kubernetes create <name-your-cluster> --size "g4s.kube.small" --nodes 2 --wait --save --merge --region LON1
Note: <name-your-cluster>
is env_tutorial
in my case.
To switch the current context to the cluster we just created, run kubectl config use-context env_tutorial
. You can now run kubectl get nodes
to see all the nodes available in the cluster.
Setting Environment variables from ConfigMaps
ConfigMap is a Kubernetes object used for storing non-confidential environment variables in key-value pairs. When you store your environment variable in ConfigMap you will be able to access them in pods, command line queries and also volumes.
Before going into ConfigMaps I’d like to show you how you can state your environment variables directly in the deployment file and access it.
To begin, create a new file with name deployment.yaml
and paste the following code. The code below is just a hello-world deployment that takes in some arbitrary environment variables.
apiVersion: apps/v1
kind: Deployment
metadata:
name: hello-world
spec:
replicas: 1
selector:
matchLabels:
app: hello-world
template:
metadata:
labels:
app: hello-world
spec:
containers:
- name: hello-world
image: civo/docker-hello-world:latest
env:
- name: HOST_NAME
value: "server1.com"
- name: PORT_NUMBER
value: "9001"
ports:
- containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
name: hello-world
spec:
selector:
app: hello-world
ports:
- protocol: TCP
port: 80
targetPort: 8080
type: ClusterIP
Now open your terminal and navigate to the directory where you created the file and run the following command to apply our deployment.
kubectl apply -f deployment.yaml
Next run kubectl get pods
to display the pods you have available. Now, copy the name of one of the pods of the hello-world
, then run kubectl exec -it <name-of-pod> -- /bin/sh
and you will be taken inside the shell of the pod. You can run the following command to print the environment variables available.
printenv | sort
Now, you should see some environment variables, including the ones that were stated in the deployment file, namely HOST_NAME
and PORT_NUMBER
. Your list will look something like:
The disadvantage of doing this is older pods cannot access the environment variables in newer pods, so you may have to state one environment variable repeatedly. This is where ConfigMaps come in.
How to Use ConfigMaps
To set ConfigMaps, create a file with the name my-config.txt (the name is arbitrary) and paste the text below.
HOST_NAME="server2.com"
APP_PORT_NUMBER="3000"
You can now create the configmap by running the command below. You will add the file you want to generate the configmap from with the --from-file=
flag.
kubectl create configmap random --from-file=my-config.txt
Once this has been created, you can see the details of your configmap by running kubectl get configmap random -o yaml
.
Next, create a new deployment file with name configmap-deployment.yaml then paste the code below.
apiVersion: apps/v1
kind: Deployment
metadata:
name: hello-world-configmaps
spec:
replicas: 1
selector:
matchLabels:
app: hello-world-configmaps
template:
metadata:
labels:
app: hello-world-configmaps
spec:
containers:
- name: hello-world
image: civo/docker-hello-world:latest
envFrom:
- configMapRef: #used to state the configmap you need.
name: random
ports:
- containerPort: 8080
You can see that we specify that we are taking our environment variables from a configMap called random
in the section that starts with envFrom:
.
Apply the deployment with kubectl apply -f configmap-deployment.yaml
.
Now, get the name of the pod you just created like you did previously, then run the following command to get the environment variables in the configmap directly from the pod.
kubectl exec -it <name-of-pod> -- /bin/sh -c "printenv | sort"
This should output the environment variables straight onto your terminal.
How to Use ConfigMaps in Volumes
Create a new file with name configmaps-volumes.yaml and paste the code below. To access ConfigMap in volumes you have to name the volume, then have a .spec.volumes[].configMap.name
field set to reference your ConfigMap object that you have created already (in our case it’s random
). When you set the mountPath for the volumes, the environment variables will be pasted there in a file.
apiVersion: apps/v1
kind: Deployment
metadata:
name: hello-world-configmaps-volumes
spec:
replicas: 1
selector:
matchLabels:
app: hello-world-configmaps-volumes
template:
metadata:
labels:
app: hello-world-configmaps-volumes
spec:
volumes:
- name: configmap-volume
configMap:
name: random # name of configmap created earlier
containers:
- name: hello-world
image: civo/docker-hello-world:latest
ports:
- containerPort: 8080
volumeMounts:
- name: configmap-volume
mountPath: "/etc/configmap-volume" # destination inside the Pod where the volume is mounted. This is where you will find the environment variables
Now apply this deployment (kubectl apply -f configmaps-volumes.yaml
) and like we did in the first example, execute a shell on the pod that was just created using kubectl exec -it <name-of-pod> -- /bin/sh
. Now, run ls /etc/configmap-volume
and you will see your environment variables in a file:
Using Secrets to share Environment Variables Securely
When working directly with pods, you may need to use sensitive environment variables like API keys and passwords. These need to be stored securely. This is where Secrets come in. A secret is a Kubernetes object used to store sensitive data like API keys, tokens and passwords.
The most direct way you can create a secret is by running a command like the one below, which takes input you give and creates the secret based on it.
kubectl create secret generic <name-of-secret> --from-literal=USERID=admin --from-literal=PASSWORD='pssWd@!70T5n%'
Now you can run the command below to confirm that the secret has been created. As you can see the details of the secrets are concealed from view.
kubectl describe secret <name-of-secret>
The code sample below shows how you can use the environment variables from secrets.
apiVersion: apps/v1
kind: Deployment
metadata:
name: hello-world-secrets
spec:
replicas: 1
selector:
matchLabels:
app: hello-world-secrets
template:
metadata:
labels:
app: hello-world-secrets
spec:
containers:
- name: hello-world
image: civo/docker-hello-world:latest
envFrom:
- secretRef:
name: login-detail #name of secret
ports:
- containerPort: 8080
The environment variable is encoded in base64 and can be decoded by running the command below.
echo $(kubectl get secret <name-of-secret> --template={{.data.USERID}} | base64 --decode )
How to use Doppler to Store Environment Variables for Kubernetes
There are external tools that help manage and synchronise environment variables across applications. Doppler is an example of such a tool that is used to keep sensitive environment variables and app configurations. Here I will go into what Doppler is and the benefits of using it to secure your environment variables.
Also, with Doppler, you have a log of changes in environment variables in case a change is made that breaks the software you can always revert to older values.
To get started, install the Operator using Helm or kubectl.
You will need a service token to get your secrets from Doppler. Access the service token by creating an account and a project on the Doppler site, then select a config from your dashboard.
Next, click on the ACCESS tab then click on the Generate button and give it a name. You can click on the SECRETS tab to add your environment variables. Copy the service token you get from this page.
Next, generate your Doppler token secret. This is a Kubernetes secret containing the Service Token for the config to synchronise in your Kubernetes application. Generate your Doppler token secret by running the following command:
kubectl create secret generic doppler-token-secret --namespace doppler-operator-system --from-literal=serviceToken=<paste-service-token>
Now you need to create a custom resource definition (DopplerSecret
) to continuously synchronize secret updates in Doppler to its managed Kubernetes secrets. You can do this by creating a new .yaml
file (secrets_dopplersecrets.yaml
) and pasting the code below. Then apply the file to your cluster like you did in the previous sections with kubectl apply -f secrets_dopplersecrets.yaml
.
apiVersion: secrets.doppler.com/v1alpha1
kind: DopplerSecret
metadata:
name: dopplersecret-test
namespace: doppler-operator-system # Doppler Service token namespace
spec:
tokenSecret:
name: doppler-token-secret # Kubernetes service token secret
managedSecret: # Kubernetes managed secret (will be created if does not exist)
name: doppler-test-secret
namespace: default # Should match the namespace of deployments that will use the secret
Now create a new .yaml file for deployment (doppler_deployment.yaml
), paste the code below and then apply this to your cluster as above.
apiVersion: apps/v1
kind: Deployment
metadata:
name: doppler-test-deployment-envfrom
annotations:
secrets.doppler.com/reload: 'true'
spec:
replicas: 2
selector:
matchLabels:
app: doppler-test
template:
metadata:
labels:
app: doppler-test
spec:
containers:
- name: doppler-test
image: alpine
command:
- /bin/sh
- -c
# Print all non-Kubernetes environment variables
- apk add --no-cache tini > /dev/null 2>&1 &&
echo "### This is a simple deployment running with this env:" &&
printenv | grep -v KUBERNETES_ &&
tini -s tail -f /dev/null # Pretend to be a long-running process
imagePullPolicy: Always
envFrom:
- secretRef:
name: doppler-test-secret # Kubernetes secret name
After you apply this deployment, you can access the environment variables you save on Doppler by running kubectl logs <name-of-pod>
. You can test them changing by updating their values on the Doppler site and monitoring the logs in the container pod.
Conclusion
In this article, we have been able to go through how to set up and use environment variables in your Kubernetes cluster. We went from creating a cluster with Civo, demonstrated how to set environment variables from ConfigMaps, store sensitive environment variables securely, and finally showed you how to use Doppler as an example of an external tool to securely store and manage your environment variables.
Hopefully with this information, you will be able to confidently start working with environment variables in your Kubernetes application.