ConfigMap and Secret Solutions
SummaryStep-by-step solutions for Exercises 1 and 2: creating...
Step-by-step solutions for Exercises 1 and 2: creating...
Step-by-step solutions for Exercises 1 and 2: creating a ConfigMap from a file with dual consumption via volume mount and valueFrom environment variable, and creating a TLS Secret mounted into an nginx container with read-only verification.
ConfigMap and Secret Solutions
Solution: Exercise 1 — ConfigMap as Environment Variables and Volume Mount
This exercise requires creating a ConfigMap from a file, consuming it as both a volume mount and a specific environment variable, and demonstrating update propagation behavior.
Step 1: Create the Configuration File
cat <<EOF > app.properties
db.host=postgres.prod.svc
db.port=5432
cache.ttl=300
EOF
Step 2: Create the ConfigMap
kubectl create configmap app-settings --from-file=app.properties
Verify:
kubectl get configmap app-settings -o yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: app-settings
data:
app.properties: |
db.host=postgres.prod.svc
db.port=5432
cache.ttl=300
The entire file is stored as a single key app.properties. The value is the file content.
Note on the valueFrom requirement: The exercise asks for db.host to be injected as an environment variable via valueFrom. Since the ConfigMap was created with --from-file, the key is app.properties (the whole file), not db.host. To use valueFrom with specific keys, we need a second ConfigMap created from literals, or we create the ConfigMap with --from-env-file. The best approach: create an additional ConfigMap from literals for the env var, or use --from-env-file.
Let’s use --from-env-file for the environment variable and --from-file for the volume mount. We can create one ConfigMap that serves both purposes by creating it from both sources, or create two ConfigMaps. The cleanest approach is a single ConfigMap with individual keys plus the file:
# Delete the previous ConfigMap
kubectl delete configmap app-settings
# Create with both individual keys and the file
kubectl create configmap app-settings \
--from-literal=db.host=postgres.prod.svc \
--from-literal=db.port=5432 \
--from-literal=cache.ttl=300 \
--from-file=app.properties
This ConfigMap contains four keys: db.host, db.port, cache.ttl (individual keys for env vars) and app.properties (the entire file, for volume mount).
Step 3: Create the Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: config-app
spec:
replicas: 2
selector:
matchLabels:
app: config-app
template:
metadata:
labels:
app: config-app
spec:
volumes:
- name: config-volume
configMap:
name: app-settings
items:
- key: app.properties
path: app.properties
containers:
- name: app
image: busybox
command: ["sh", "-c", "sleep 3600"]
env:
- name: DATABASE_HOST
valueFrom:
configMapKeyRef:
name: app-settings
key: db.host
volumeMounts:
- name: config-volume
mountPath: /etc/config
readOnly: true
Key decisions in this manifest:
- Volume mount: Uses
itemsto mount only theapp.propertieskey, projecting it as the file/etc/config/app.properties. - Environment variable: Uses
valueFromwithconfigMapKeyRefto inject thedb.hostkey asDATABASE_HOST. - readOnly: Prevents accidental writes to the ConfigMap mount.
Apply:
kubectl apply -f config-app.yaml
Step 4: Verify
Wait for all replicas to be ready:
kubectl rollout status deployment/config-app
Verify the volume mount:
POD=$(kubectl get pods -l app=config-app -o jsonpath='{.items[0].metadata.name}')
kubectl exec $POD -- cat /etc/config/app.properties
Expected output:
db.host=postgres.prod.svc
db.port=5432
cache.ttl=300
Verify the environment variable:
kubectl exec $POD -- env | grep DATABASE_HOST
Expected output:
DATABASE_HOST=postgres.prod.svc
Step 5: Demonstrate Update Propagation
Update the ConfigMap:
kubectl edit configmap app-settings
# Change db.host to postgres.staging.svc in both the literal key and the app.properties content
Or use a patch:
kubectl create configmap app-settings \
--from-literal=db.host=postgres.staging.svc \
--from-literal=db.port=5432 \
--from-literal=cache.ttl=300 \
--from-file=app.properties \
--dry-run=client -o yaml | kubectl apply -f -
Wait 60–90 seconds, then check:
# Volume mount — should show updated value
kubectl exec $POD -- cat /etc/config/app.properties
# Environment variable — should still show old value
kubectl exec $POD -- env | grep DATABASE_HOST
The volume-mounted file reflects the updated ConfigMap. The environment variable retains the original value (set at container startup). This confirms the behavior described in Chapter 16: volume mounts are dynamic, environment variables are static.
Troubleshooting
Volume mount shows stale data after 2+ minutes: Verify the ConfigMap was actually updated with kubectl get configmap app-settings -o yaml. If using subPath, note that subPath mounts never auto-update.
Pod fails to start with CreateContainerConfigError: Check that the ConfigMap exists in the same namespace as the Pod. A typo in configMapRef or configMapKeyRef causes this error.
Environment variable is empty: Verify the key name matches exactly. db.host has a dot — make sure the ConfigMap key matches the configMapKeyRef.key field.
Solution: Exercise 2 — TLS Secret with Nginx
This exercise requires creating a TLS Secret from certificate/key files and mounting it into an nginx container.
Step 1: Generate the Certificate
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
-keyout tls.key -out tls.crt \
-subj "/CN=myapp.example.com"
This generates two files:
tls.crt— the self-signed certificatetls.key— the private key
Step 2: Create the TLS Secret
kubectl create secret tls nginx-tls --cert=tls.crt --key=tls.key
Verify:
kubectl get secret nginx-tls -o yaml
apiVersion: v1
kind: Secret
metadata:
name: nginx-tls
type: kubernetes.io/tls
data:
tls.crt: LS0tLS1CRUdJTi...
tls.key: LS0tLS1CRUdJTi...
The Secret type is kubernetes.io/tls and contains exactly two keys: tls.crt and tls.key.
Step 3: Create the Pod
apiVersion: v1
kind: Pod
metadata:
name: secure-nginx
spec:
volumes:
- name: tls-volume
secret:
secretName: nginx-tls
containers:
- name: nginx
image: nginx:1.25
ports:
- containerPort: 443
volumeMounts:
- name: tls-volume
mountPath: /etc/nginx/ssl
readOnly: true
Apply:
kubectl apply -f secure-nginx.yaml
Step 4: Verify
Wait for the Pod to be ready:
kubectl wait --for=condition=ready pod/secure-nginx --timeout=30s
Verify the TLS files are mounted:
kubectl exec secure-nginx -- ls -la /etc/nginx/ssl/
Expected output:
total 0
drwxrwxrwt 3 root root 120 ... .
drwxr-xr-x 3 root root 28 ... ..
lrwxrwxrwx 1 root root 14 ... tls.crt -> ..data/tls.crt
lrwxrwxrwx 1 root root 14 ... tls.key -> ..data/tls.key
Both tls.crt and tls.key are present as symbolic links (this is how Kubernetes manages volume-mounted Secrets for atomic updates).
Verify the certificate content:
kubectl exec secure-nginx -- cat /etc/nginx/ssl/tls.crt | openssl x509 -noout -subject
Expected output:
subject=CN = myapp.example.com
Verify the mount is read-only:
kubectl exec secure-nginx -- touch /etc/nginx/ssl/test
Expected output:
touch: /etc/nginx/ssl/test: Read-only file system
Troubleshooting
error: failed to read certificate: The certificate or key file is malformed. Regenerate using the openssl command above. Ensure you are not accidentally base64-encoding the files before passing them to kubectl create secret tls.
Pod stuck in ContainerCreating: Run kubectl describe pod secure-nginx and check the Events section. A common issue is a missing Secret (typo in secretName).
Files not visible in /etc/nginx/ssl/: Verify the mountPath and volumeMounts.name match the volume name in spec.volumes. A name mismatch silently skips the mount.