ConfigMap, config ngoài image
Tách config khỏi container image → cùng image chạy ở dev/staging/prod với config khác nhau.
Tạo ConfigMap
# Từ literal
kubectl create configmap app-config \
--from-literal=DB_HOST=postgres \
--from-literal=LOG_LEVEL=info
# Từ file
kubectl create configmap nginx-config --from-file=nginx.conf
# Từ thư mục
kubectl create configmap configs --from-file=./config-dir/
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
data:
DB_HOST: postgres
LOG_LEVEL: info
config.yaml: |
server:
port: 8080
timeout: 30s
Inject qua environment variable
spec:
containers:
- name: app
image: my-app:1.2.0
env:
- name: DB_HOST
valueFrom:
configMapKeyRef:
name: app-config
key: DB_HOST
# Hoặc inject tất cả keys
envFrom:
- configMapRef:
name: app-config
Inject qua volume
spec:
containers:
- name: app
volumeMounts:
- name: config
mountPath: /etc/config
readOnly: true
volumes:
- name: config
configMap:
name: app-config
Kết quả: /etc/config/DB_HOST, /etc/config/LOG_LEVEL, /etc/config/config.yaml, mỗi key thành file.
subPath, mount file đơn lẻ
volumeMounts:
- name: config
mountPath: /app/config.yaml # Chỉ mount 1 file
subPath: config.yaml # Key trong ConfigMap
Cảnh báo: subPath không nhận hot reload khi ConfigMap thay đổi.
Secret, “config nhạy cảm”
Secret giống ConfigMap nhưng cho dữ liệu nhạy cảm: password, API key, TLS cert.
Tạo Secret
kubectl create secret generic db-creds \
--from-literal=DB_PASSWORD=s3cret \
--from-literal=DB_USER=admin
apiVersion: v1
kind: Secret
metadata:
name: db-creds
type: Opaque
data:
DB_PASSWORD: czNjcmV0 # Base64 encoded, KHÔNG phải mã hoá
DB_USER: YWRtaW4=
stringData: # Plaintext (K8s tự encode khi apply)
API_KEY: my-api-key-123
Inject giống ConfigMap
env:
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: db-creds
key: DB_PASSWORD
# Hoặc volume mount
volumes:
- name: creds
secret:
secretName: db-creds
Secret KHÔNG mã hoá
Quan trọng: Secret chỉ là base64 encoded, không encrypt. Ai có quyền get secret trong namespace → đọc được plaintext.
# Decode secret
kubectl get secret db-creds -o jsonpath='{.data.DB_PASSWORD}' | base64 -d
Bảo vệ thực sự cần:
- Encryption at rest: cấu hình kube-apiserver encrypt Secret trong etcd (
EncryptionConfiguration). - RBAC: giới hạn ai được
get/list secret. - Sealed Secrets: encrypt secret trong Git, chỉ cluster decrypt được.
- External Secrets Operator: sync secret từ Vault, AWS Secrets Manager, GCP Secret Manager.
Env vs Volume, khi nào dùng gì
| Khía cạnh | Env variable | Volume mount |
|---|---|---|
| Hot reload | ❌ Cần restart pod | ✅ Tự cập nhật (trừ subPath) |
| Xem trong log | ⚠️ Dễ leak (env, kubectl describe) | Ít rủi ro hơn |
| File config phức tạp | ❌ Chỉ key-value | ✅ File bất kỳ |
| Legacy app đọc env | ✅ Tương thích | Cần app đọc file |
Best practice cho secret: dùng volume mount thay env. Env dễ leak qua docker inspect, process listing, crash dump.
Hot reload
Khi ConfigMap/Secret thay đổi:
- Volume mount (không subPath): kubelet tự cập nhật file trong pod, thời gian tuỳ sync period (mặc định ~1 phút). App cần watch file hoặc reload khi file đổi.
- Env variable: không cập nhật cho đến khi pod restart.
- subPath mount: không cập nhật, kubelet chỉ mount lúc pod start.
# Force restart pod khi config thay đổi
kubectl rollout restart deploy/my-app
# Hoặc dùng hash annotation (Helm pattern)
# Template: checksum/config: {{ include (print .Template.BasePath "/configmap.yaml") . | sha256sum }}
Downward API
Inject metadata về chính pod vào container:
env:
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
- name: POD_IP
valueFrom:
fieldRef:
fieldPath: status.podIP
- name: NODE_NAME
valueFrom:
fieldRef:
fieldPath: spec.nodeName
- name: CPU_REQUEST
valueFrom:
resourceFieldRef:
containerName: app
resource: requests.cpu
- name: MEM_LIMIT
valueFrom:
resourceFieldRef:
containerName: app
resource: limits.memory
Hoặc qua volume:
volumes:
- name: pod-info
downwardAPI:
items:
- path: labels
fieldRef:
fieldPath: metadata.labels
- path: annotations
fieldRef:
fieldPath: metadata.annotations
Use case: logging (thêm pod name/namespace vào structured log), app tự detect resource limits (JVM -XX:MaxRAMPercentage).
Immutable ConfigMap/Secret
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config-v2
immutable: true # Không cho sửa sau khi tạo
Lợi ích: giảm load lên API server (không watch changes), tránh sửa nhầm config production. Đổi config = tạo ConfigMap mới + update Deployment reference.
Lab: đổi config mà pod không nhận
Khi sửa ConfigMap mà app không đổi hành vi, kiểm tra app lấy config bằng env hay volume:
kubectl -n app get deploy api -o yaml | grep -A20 -E 'envFrom|configMapRef|volumeMounts'
kubectl -n app get configmap app-config -o yaml
kubectl -n app exec deploy/api -- printenv | grep APP_
kubectl -n app exec deploy/api -- ls -l /etc/app-config
Env var chỉ được inject lúc container start, nên đổi ConfigMap không tự đổi env trong container đang chạy. Volume mount có thể cập nhật sau một khoảng delay, nhưng nếu dùng subPath thì cũng không hot reload.
Với config production quan trọng, cách ít mơ hồ nhất là version config name (app-config-v3) rồi rollout Deployment có chủ ý.
Điều cần giữ khi vận hành Kubernetes
- ConfigMap cho config không nhạy cảm. Secret cho password/key/cert, nhưng chỉ base64, không encrypt mặc định.
- Volume mount tốt hơn env: hot reload, ít leak, hỗ trợ file phức tạp.
- subPath không hot reload.
- Downward API inject pod metadata vào container.
- Secret cần bảo vệ thêm: encryption at rest, RBAC, hoặc External Secrets Operator.
Câu hỏi hay gặp
ConfigMap thay đổi nhưng app không nhận config mới?
Trả lời: Kiểm tra: (1) dùng env → cần restart pod; (2) dùng subPath → cũng cần restart; (3) dùng volume mount → file cập nhật sau ~1 phút, nhưng app phải đọc lại file (watch hoặc periodic reload). Không có magic reload.
Secret trong Git repo, cách xử lý?
Trả lời: Không commit Secret YAML plaintext vào Git. Dùng: (1) Sealed Secrets, encrypt bằng cluster public key, chỉ cluster decrypt; (2) External Secrets Operator, sync từ Vault/AWS/GCP; (3) SOPS, encrypt file YAML bằng KMS key.
ConfigMap/Secret size limit?
Trả lời: Mỗi ConfigMap/Secret tối đa 1 MiB (etcd limit). Config lớn hơn → chia nhỏ hoặc dùng init container download từ S3/GCS.
Bài tiếp theo (Giai đoạn IV): Volume, PVC và stateful workload, lưu trữ bền vững trên Kubernetes.