Notes on the fridge: ConfigMaps and Secrets
- Published
- Read
- 6 minutes
- Tags
Give the same application different settings in development and production with ConfigMaps and Secrets.
Give the same application different settings in development and production with ConfigMaps and Secrets.
Both flats now have nginx, but they still serve the same welcome page. A real application might need a different API address in development, or credentials for a production database. Baking those into the image would mean rebuilding it whenever a setting changes.
I prefer to keep the image the same and supply the settings separately. Think of a ConfigMap as a note on the fridge: instructions for whoever lives in that flat. Passwords deserve somewhere less public. Kubernetes has a separate resource for those, called a Secret, though the name needs a little unpacking.
Put a note in each flat
Continue with the Docker Desktop cluster from the earlier chapters, on macOS or Windows PowerShell. Chapter four’s scaling exercise had its own temporary namespace. Here, return to the development and production namespaces from chapter three.
kubectl config use-context docker-desktop
kubectl get pod,service,ingress -n development
kubectl get pod,service,ingress -n productionEach should still have a guide-nginx Pod, Service, and Ingress. Keep Traefik running too.
A ConfigMap stores ordinary configuration as named values. Save this as nginx-config-development.yaml:
apiVersion: v1
kind: ConfigMap
metadata:
name: guide-nginx-config
namespace: development
data:
WELCOME_MESSAGE: "Welcome to development. Mind the loose cables."Copy it to nginx-config-production.yaml, changing these values:
| Field | Development | Production |
|---|---|---|
metadata.namespace | development | production |
data.WELCOME_MESSAGE | Welcome to development. Mind the loose cables. | Welcome to production. Please wipe your feet. |
Keep the same resource name in both files, then apply them:
kubectl apply -f nginx-config-development.yaml
kubectl apply -f nginx-config-production.yamlEach namespace now has its own note. Creating it alone does not change nginx; the container still needs to receive and use the value.
Keep the credential separate
An API token or database password belongs in a Secret. For this exercise, use an invented token. No account or external service is needed.
Save this as nginx-secret-development.yaml:
apiVersion: v1
kind: Secret
metadata:
name: guide-nginx-secret
namespace: development
type: Opaque
stringData:
API_TOKEN: "dummy-development-token"Opaque means a general-purpose Secret. stringData lets you supply readable strings; Kubernetes handles their conversion into its data representation.
Copy the file to nginx-secret-production.yaml. Change the namespace to production and the token to dummy-production-token, keeping the resource name.
kubectl apply -f nginx-secret-development.yaml
kubectl apply -f nginx-secret-production.yamlThese files contain dummy values for learning. Keep real credentials out of Git, including credentials written into Secret YAML.
Let the container read its settings
Replace the contents of the shared nginx-pod.yaml with:
apiVersion: v1
kind: Pod
metadata:
name: guide-nginx
labels:
app: guide-nginx
spec:
containers:
- name: nginx
image: nginx:stable
ports:
- containerPort: 80
env:
- name: WELCOME_MESSAGE
valueFrom:
configMapKeyRef:
name: guide-nginx-config
key: WELCOME_MESSAGE
- name: API_TOKEN
valueFrom:
secretKeyRef:
name: guide-nginx-secret
key: API_TOKEN
command: ["/bin/sh", "-c"]
args:
- |
printf '%s\n' "$WELCOME_MESSAGE" > /usr/share/nginx/html/index.html
exec nginx -g 'daemon off;'The env entries create environment variables inside the container. Each reference names a resource and a key within it. Kubernetes looks for those resources in the Pod’s own namespace, so this one Pod file works in both environments.
nginx does not turn an arbitrary environment variable into a web page by itself. The small startup command writes the welcome message into its index file, then starts nginx. The token stays out of that file. An application that calls an API would read the token in its own code; here, the token only demonstrates delivery.
The existing Pods cannot accept these changes in place. Replace them one environment at a time. Each address will briefly stop serving while its only Pod is absent.
kubectl delete pod guide-nginx -n development
kubectl apply -n development -f nginx-pod.yaml
kubectl wait -n development --for=condition=Ready pod/guide-nginx --timeout=120sVisit http://development.localhost. You should see the development message. Then update production:
kubectl delete pod guide-nginx -n production
kubectl apply -n production -f nginx-pod.yaml
kubectl wait -n production --for=condition=Ready pod/guide-nginx --timeout=120sOpen http://production.localhost. It should show the production message. Both still use nginx:stable, and the existing Services and Ingresses continue routing to them.
Check that each container also received a token, without printing its value:
kubectl exec -n development guide-nginx -- sh -c 'printenv API_TOKEN > /dev/null && echo token-present'
kubectl exec -n production guide-nginx -- sh -c 'printenv API_TOKEN > /dev/null && echo token-present'Both commands should print token-present. The check runs inside the container and discards the token’s value instead of displaying it.
If a Pod does not start, run kubectl describe pod guide-nginx -n development, choosing the affected namespace. The Events section can identify a missing ConfigMap, Secret, or key. Check the spelling and namespace before recreating anything.
The Secret still needs a lock
Inspect the dummy Secret:
kubectl get secret guide-nginx-secret -n development -o yamlLook at the top-level data section. With the dummy token from this exercise, it should contain:
data:
API_TOKEN: ZHVtbXktZGV2ZWxvcG1lbnQtdG9rZW4=You may also spot dummy-development-token in plain text under metadata.annotations, inside kubectl.kubernetes.io/last-applied-configuration. The earlier kubectl apply command saved a copy of the input configuration there, including its readable stringData. That is a second copy of the token; the command has not decoded data.API_TOKEN.
Base64 is reversible encoding, not encryption: anyone who can read the encoded value can recover the token. In this exercise, the annotation also exposes it directly.
Kubernetes Secrets are stored unencrypted by default. Production needs encryption at rest and permissions that limit access. Permission to create Pods in a namespace also matters: someone who can create a Pod there can arrange for it to read a Secret.
External secret managers can hold credentials outside Kubernetes and supply them through an integration. That takes additional setup and does not remove the need to control access inside the cluster. For now, keep using the dummy tokens.
Change the note, keep the image
In nginx-config-development.yaml, change WELCOME_MESSAGE to:
WELCOME_MESSAGE: "Development has been tidied up. Mostly."Apply the changed file:
kubectl apply -f nginx-config-development.yamlRefresh development, bypassing the browser cache. It should still show the old message. ConfigMaps supplied as environment variables do not update those variables in an already-running container. This example also writes the page only at startup.
Recreate development to pick up the new value:
kubectl delete pod guide-nginx -n development
kubectl apply -n development -f nginx-pod.yaml
kubectl wait -n development --for=condition=Ready pod/guide-nginx --timeout=120sRefresh both addresses. Development should show the tidier message; production should still ask you to wipe your feet. The image and the shared Pod file stayed the same. Only development’s configuration changed.
The same replacement requirement applies to Secrets delivered through environment variables. File-based delivery behaves differently, so treat this as the rule for the method used here.
Keep both environments running, along with their ConfigMaps and dummy Secrets. Retain the updated nginx-pod.yaml, both configuration files, and both dummy Secret files beside the Service and Ingress files from chapter three.
