Sau bài này bạn làm được: inject config vào pod đúng cách (env vs volume); hiểu secret không thực sự “mã hoá”; biết khi nào config thay đổi cần restart pod.
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.
Tóm tắt
- 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.