Hardcoding configuration in containers is bad. Environment variables are better but still limited. ConfigMaps and Secrets are Kubernetes' native solution for configuration management. ConfigMaps store non-sensitive data. Secrets store sensitive data like passwords and API keys. Both can be mounted as files or injected as environment variables.
1. Learning Objectives
By the end of this lesson, you will be able to:
- Create ConfigMaps from literal values, files, and directories
- Inject ConfigMap data as environment variables
- Mount ConfigMaps as volumes
- Create and manage Secrets for sensitive data
- Use Secrets as environment variables or mounted files
- Update configurations without rebuilding images
2. Why This Matters
Real-world scenario: Your application needs different database hosts, API endpoints, and feature flags for dev, staging, and production. Instead of rebuilding images for each environment, ConfigMaps let you inject configuration at runtime.
3. Core Concepts
Creating ConfigMaps
# Create from literal values
kubectl create configmap app-config \
--from-literal=APP_ENV=production \
--from-literal=LOG_LEVEL=info \
--from-literal=MAX_CONNECTIONS=100
# Create from file
kubectl create configmap app-config --from-file=./app.conf
kubectl create configmap app-config --from-file=config=./app.conf
# Create from directory (all files in directory)
kubectl create configmap app-config --from-file=./configs/
# Create from env file
kubectl create configmap app-config --from-env-file=.env
# Create from YAML definition
cat << EOF | kubectl apply -f -
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
data:
APP_ENV: production
LOG_LEVEL: info
MAX_CONNECTIONS: "100"
database.conf: |
host: postgres-service
port: 5432
name: mydb
nginx.conf: |
server {
listen 80;
location / {
proxy_pass http://app:3000;
}
}
EOF
# View ConfigMaps
kubectl get configmaps
kubectl describe configmap app-config
kubectl get configmap app-config -o yaml
Creating ConfigMaps
Using ConfigMaps as Environment Variables
# deployment-with-configmap-env.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: app-deployment
spec:
replicas: 3
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
spec:
containers:
- name: app
image: myapp:latest
env:
# Single value from ConfigMap
- name: APP_ENV
valueFrom:
configMapKeyRef:
name: app-config
key: APP_ENV
- name: LOG_LEVEL
valueFrom:
configMapKeyRef:
name: app-config
key: LOG_LEVEL
# All values from ConfigMap
envFrom:
- configMapRef:
name: app-config
- configMapRef:
name: database-config
ports:
- containerPort: 8080
Injecting ConfigMaps as environment variables
Using ConfigMaps as Volumes
# deployment-with-configmap-volume.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
replicas: 2
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:alpine
volumeMounts:
- name: nginx-config
mountPath: /etc/nginx/conf.d
readOnly: true
- name: app-config
mountPath: /app/config
readOnly: true
volumes:
- name: nginx-config
configMap:
name: nginx-config
- name: app-config
configMap:
name: app-config
# Mount specific items
items:
- key: database.conf
path: database.yaml
- key: app.properties
path: application.properties
Mounting ConfigMaps as volumes
Secrets (Sensitive Data)
# Create secrets
# From literal
kubectl create secret generic db-secret \
--from-literal=username=admin \
--from-literal=password=Sup3rSecret!
# From file
kubectl create secret generic tls-secret \
--from-file=tls.crt=./server.crt \
--from-file=tls.key=./server.key
# From env file
kubectl create secret generic app-secrets --from-env-file=.secrets
# From YAML (base64 encoded)
cat << EOF | kubectl apply -f -
apiVersion: v1
kind: Secret
metadata:
name: db-secret
type: Opaque
data:
username: YWRtaW4= # echo -n 'admin' | base64
password: U3VwM3JTZWNyZXQh # echo -n 'Sup3rSecret!' | base64
EOF
# Encode/decode base64
echo -n 'myvalue' | base64
echo 'bXl2YWx1ZQ==' | base64 -d
# View secrets
kubectl get secrets
kubectl describe secret db-secret
kubectl get secret db-secret -o yaml
kubectl get secret db-secret -o jsonpath='{.data.password}' | base64 -d
Creating and managing Secrets
# deployment-with-secrets.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: app-with-secrets
spec:
replicas: 2
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
spec:
containers:
- name: app
image: myapp:latest
env:
# Secret as environment variable
- name: DB_USERNAME
valueFrom:
secretKeyRef:
name: db-secret
key: username
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: db-secret
key: password
envFrom:
- secretRef:
name: app-secrets
# Secret mounted as volume
volumeMounts:
- name: tls-certs
mountPath: /etc/ssl/certs
readOnly: true
volumes:
- name: tls-certs
secret:
secretName: tls-secret
defaultMode: 0400 # Read-only for security
Using Secrets in deployments
Secret Types
# Opaque - Generic secret (default)
apiVersion: v1
kind: Secret
metadata:
name: generic-secret
type: Opaque
data:
key: dmFsdWU=
# kubernetes.io/service-account-token
# Automatically created for service accounts
# kubernetes.io/dockercfg / kubernetes.io/dockerconfigjson
# For private container registry authentication
apiVersion: v1
kind: Secret
metadata:
name: regcred
type: kubernetes.io/dockerconfigjson
data:
.dockerconfigjson: eyJhdXRocyI6eyJodHRwczovL2luZGV4LmRvY2tlci5pby92MS8iOnsiYXV0aCI6Ik1HVXpaak5rTkdZdFkyUTJZeTAwTTJKa0xXRmtOV1l0WkRjeU1tWmlNV1ZqT1RjPSJ9fX0=
# kubernetes.io/tls - TLS certificates
apiVersion: v1
kind: Secret
metadata:
name: tls-secret
type: kubernetes.io/tls
data:
tls.crt: LS0tLS1CRUdJTiBDRV...
tls.key: LS0tLS1CRUdJTiBSU0Eg...
# Use in Ingress
# spec:
# tls:
# - hosts:
# - example.com
# secretName: tls-secret
# bootstrap.kubernetes.io/token - Bootstrap tokens
# Used for node bootstrapping
Secret types
4. Complete Project: Configuration Management
# complete-config.yaml
---
# Database ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
name: database-config
data:
DB_HOST: postgres-service
DB_PORT: "5432"
DB_NAME: myapp
DB_POOL_SIZE: "10"
postgresql.conf: |
max_connections = 100
shared_buffers = 128MB
effective_cache_size = 384MB
---
# Application ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
data:
APP_ENV: production
LOG_LEVEL: INFO
REDIS_HOST: redis-service
REDIS_PORT: "6379"
CACHE_TTL: "300"
FEATURE_FLAGS: |
new_dashboard: true
beta_features: false
analytics: true
application.yml: |
server:
port: 8080
compression:
enabled: true
spring:
datasource:
url: jdbc:postgresql://postgres-service:5432/myapp
hikari:
maximum-pool-size: 10
---
# Database Secret
apiVersion: v1
kind: Secret
metadata:
name: db-secret
type: Opaque
data:
DB_USER: cG9zdGdyZXM= # postgres
DB_PASSWORD: c3VwZXJzZWNyZXQ= # supersecret
---
# API Secret
apiVersion: v1
kind: Secret
metadata:
name: api-secrets
type: Opaque
data:
API_KEY: YWJjMTIzNDU2Nzg5MGFiY2RlZg==
JWT_SECRET: eHl6Nzg5MGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6
---
# TLS Secret for Ingress
apiVersion: v1
kind: Secret
metadata:
name: tls-secret
type: kubernetes.io/tls
data:
tls.crt: LS0tLS1CRUdJTiBDRV...
tls.key: LS0tLS1CRUdJTiBSU0Eg...
---
# Application Deployment using all config
apiVersion: apps/v1
kind: Deployment
metadata:
name: app-deployment
spec:
replicas: 3
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
spec:
containers:
- name: app
image: myapp:latest
ports:
- containerPort: 8080
# Environment variables from ConfigMaps
envFrom:
- configMapRef:
name: database-config
- configMapRef:
name: app-config
# Environment variables from Secrets
env:
- name: DB_USER
valueFrom:
secretKeyRef:
name: db-secret
key: DB_USER
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: db-secret
key: DB_PASSWORD
- name: API_KEY
valueFrom:
secretKeyRef:
name: api-secrets
key: API_KEY
# Config files mounted as volumes
volumeMounts:
- name: app-config-volume
mountPath: /app/config
readOnly: true
- name: postgres-config
mountPath: /etc/postgresql
readOnly: true
volumes:
- name: app-config-volume
configMap:
name: app-config
- name: postgres-config
configMap:
name: database-config
---
# Ingress with TLS
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: main-ingress
spec:
tls:
- hosts:
- app.example.com
secretName: tls-secret
rules:
- host: app.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: app-service
port:
number: 80
# Apply all
kubectl apply -f complete-config.yaml
# Verify configuration
kubectl get configmaps
kubectl get secrets
kubectl describe pod -l app=myapp
kubectl exec deployment/app-deployment -- env | grep -E "DB_|APP_|REDIS"
kubectl exec deployment/app-deployment -- cat /app/config/application.yml
Complete configuration management with ConfigMaps and Secrets
5. Common Errors & Solutions
6. Summary Checklist
7. Next Steps
Next lesson: Kubernetes Lesson 6.5: Persistent Volumes (PVCs)
Comments (0)
This is exactly what I needed! The initContainer approach solved our migration issues completely. Thanks for the detailed guide!
ReplyLeave a Comment