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:

  1. Encryption at rest: cấu hình kube-apiserver encrypt Secret trong etcd (EncryptionConfiguration).
  2. RBAC: giới hạn ai được get/list secret.
  3. Sealed Secrets: encrypt secret trong Git, chỉ cluster decrypt được.
  4. 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ạnhEnv variableVolume 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íchCầ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.