Phần 6 — Configuration: ConfigMap, Secret và projected volumes
Ý kiến
0
Chưa có ý kiến nào. Hãy là người đầu tiên chia sẻ!
Chưa có ý kiến nào. Hãy là người đầu tiên chia sẻ!
Phần cuối series K8s: Cluster API (CAPI) quản lý cluster bằng K8s API, FinOps với OpenCost + Karpenter + right-sizing, AI/ML workloads (GPU, Kubeflow, KServe, vLLM, Argo Workflows, gang scheduling), Edge K8s và WebAssembly.
Multi-tenancy K8s (soft: namespace+RBAC+Quota, HNC, Capsule, vCluster; hard: cluster riêng) và multi-cluster: management cluster, GitOps ApplicationSet, Cluster API, Karmada, Crossplane, observability liên cluster.
Service Mesh chuyên sâu cho K8s: Istio (sidecar + ambient), Linkerd (proxy Rust nhẹ), Cilium Service Mesh (eBPF sidecarless) — mTLS, VirtualService, AuthorizationPolicy, canary, mirror, multi-cluster, đo overhead, khi không nên dùng mesh.
Series Kubernetes Toàn Tập — 13 phần:
Phần 6 — Configuration ← bạn đang đọc
Twelve-Factor App nói: config phải tách khỏi code. Trên K8s nguyên tắc đó được implement qua ConfigMap (config non-sensitive) và Secret (password, token, cert). Phần này đi qua mọi cách inject chúng vào Pod, các pitfall thường gặp, và cách quản lý secret an toàn cho production.
Có 4 cách phổ biến:
# 1. Từ literal
kubectl create configmap app-config \
--from-literal=LOG_LEVEL=info \
--from-literal=ENV=production
# 2. Từ file
kubectl create configmap app-config \
--from-file=application.yaml
# 3. Từ thư mục — mỗi file thành 1 key
kubectl create configmap nginx-conf \
--from-file=./nginx-config/
# 4. YAML khai báo
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
data:
LOG_LEVEL: info
ENV: production
application.yaml: |
server:
port: 8080
db:
host: postgres
pool: 10
EOF
Có 3 cách: env var đơn lẻ, envFrom (toàn bộ key), volume mount.
apiVersion: v1
kind: Pod
metadata:
name: app
spec:
containers:
- name: app
image: myapp:1.0
env:
# (a) Env var đơn lẻ
- name: LOG_LEVEL
valueFrom:
configMapKeyRef:
name: app-config
key: LOG_LEVEL
envFrom:
# (b) Toàn bộ key thành env var
- configMapRef:
name: app-config
volumeMounts:
# (c) Mount thành file
- name: config
mountPath: /etc/app
readOnly: true
volumes:
- name: config
configMap:
name: app-config
items:
- key: application.yaml
path: app.yaml # rename file khi mount
| Cách inject | Update khi ConfigMap đổi? |
|---|---|
Env var (env/envFrom) | KHÔNG — phải restart pod |
| Volume mount | CÓ — kubelet refresh file sau 60–90s |
| SubPath mount | KHÔNG — file snapshot lúc mount |
App của bạn vẫn phải hỗ trợ hot reload (watch file, signal SIGHUP) nếu muốn ăn config mới mà không restart. Cách an toàn nhất là restart Deployment khi đổi config:
kubectl rollout restart deployment/api
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config-v1
immutable: true
data:
...
K8s không cho phép edit. Lợi ích: hiệu năng (kube-apiserver không phải watch update) + an toàn (không vô tình overwrite). Workflow: tạo ConfigMap mới có version trong tên, update Deployment trỏ sang.
Secret giống ConfigMap về API nhưng:
Lưu base64 (không phải encryption — chỉ binary-safe).
Có nhiều type: Opaque, kubernetes.io/tls, kubernetes.io/dockerconfigjson, kubernetes.io/service-account-token, …
kubelet cố không log giá trị, không mount lên tmpfs (RAM).
etcd có thể bật encryption at rest.
# Generic / Opaque
kubectl create secret generic db-creds \
--from-literal=username=postgres \
--from-literal=password='S3cur3!P@ss'
# TLS
kubectl create secret tls api-tls \
--cert=tls.crt --key=tls.key
# Docker registry
kubectl create secret docker-registry regcred \
--docker-server=registry.example.com \
--docker-username=ci \
--docker-password=...
--docker-email=ci@example.com
apiVersion: v1
kind: Pod
metadata:
name: app
spec:
imagePullSecrets:
- name: regcred
containers:
- name: app
image: registry.example.com/myapp:1.0
env:
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: db-creds
key: password
volumeMounts:
- name: tls
mountPath: /etc/tls
readOnly: true
volumes:
- name: tls
secret:
secretName: api-tls
defaultMode: 0400
Quan trọng cần hiểu: Secret mặc định lưu trong etcd base64, không phải mã hoá. Ai có quyền đọc Secret object trong cluster = có password.
Để tăng cường:
Encryption at rest cho etcd — qua EncryptionConfiguration ở kube-apiserver. AES-CBC, AES-GCM, hoặc KMS provider.
RBAC nghiêm ngặt — chỉ SA cần mới được get secrets.
Audit log bật để biết ai access secret.
External secrets manager — Vault, AWS Secrets Manager, GCP Secret Manager, Azure Key Vault. Inject vào K8s qua External Secrets Operator hoặc CSI Secret Store.
Cách hiện đại để đồng bộ secret từ Vault/cloud sang K8s:
apiVersion: external-secrets.io/v1beta1
kind: SecretStore
metadata:
name: aws-sm
spec:
provider:
aws:
service: SecretsManager
region: us-east-1
auth:
jwt:
serviceAccountRef:
name: external-secrets-sa
---
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: db-creds
spec:
refreshInterval: 1h
secretStoreRef:
name: aws-sm
kind: SecretStore
target:
name: db-creds
data:
- secretKey: password
remoteRef:
key: prod/db
property: password
ESO tạo và đồng bộ Secret K8s từ AWS Secrets Manager. Pod consume như Secret bình thường — nhưng nguồn sự thật ở Vault/cloud, có audit log, rotation.
Nếu bạn dùng GitOps (Argo CD, Flux), không thể commit Secret thường. Hai cách phổ biến:
Bitnami SealedSecret — controller có private key, encrypt thành SealedSecret object có thể commit Git. Khi apply vào cluster, controller decrypt thành Secret thật.
SOPS (Mozilla) — encrypt YAML với KMS/PGP, Flux Kustomize tự decrypt khi sync.
Cho phép mount ConfigMap, Secret, downwardAPI, ServiceAccountToken vào cùng 1 thư mục:
volumes:
- name: all-in-one
projected:
sources:
- configMap:
name: app-config
items:
- key: application.yaml
path: app.yaml
- secret:
name: db-creds
items:
- key: password
path: db/password
- downwardAPI:
items:
- path: pod-name
fieldRef:
fieldPath: metadata.name
- serviceAccountToken:
path: token
audience: vault
expirationSeconds: 3600
Quan trọng: serviceAccountToken projected là cách K8s 1.20+ inject SA token short-lived, có audience — không còn token vĩnh viễn trong Secret như trước.
Đôi khi container cần biết tên pod, node, label, request CPU/RAM — không cần gọi API server. downwardAPI inject info đó:
env:
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
- name: NODE_NAME
valueFrom:
fieldRef:
fieldPath: spec.nodeName
- name: MEMORY_LIMIT
valueFrom:
resourceFieldRef:
containerName: app
resource: limits.memory
divisor: 1Mi
App như JVM nên đọc MEMORY_LIMIT để set heap đúng — đừng phụ thuộc cgroup detect (Java cũ tính sai).
Một app chạy dev/staging/prod thường khác config. Vài cách phổ biến:
base/
deployment.yaml
configmap.yaml
kustomization.yaml
overlays/
staging/
kustomization.yaml
configmap.yaml # patch giá trị riêng staging
prod/
kustomization.yaml
configmap.yaml
# overlays/prod/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../base
patches:
- path: configmap.yaml
target:
kind: ConfigMap
name: app-config
Kustomize đi kèm kubectl apply -k — không cần cài thêm.
Helm chart có file values.yaml default, override bằng values-prod.yaml:
helm upgrade --install app ./chart -f values-prod.yaml
Argo CD app point đến chart, chèn valuesFrom file trong Git. Vận hành phổ biến nhất hôm nay.
Đừng nhồi binary lớn vào ConfigMap. ConfigMap giới hạn 1MiB (mặc định etcd object). Image, model… để object storage.
Immutable ConfigMap/Secret + version trong tên cho rollout an toàn:
app-config-2026-05-11-abc → next deploy → app-config-2026-05-11-xyz
Restart deployment khi đổi config quan trọng. Volume tự refresh nhưng app phải support; env không refresh.
Không commit Secret YAML vào Git. Dùng ESO/SealedSecret/SOPS.
Bật encryption at rest cho etcd. Mất 5 phút config, đỡ thảm hoạ.
RBAC cho Secret: developer chỉ được tạo Secret trong namespace của họ, không get mọi namespace.
imagePullSecrets nên gán cho ServiceAccount (gán 1 lần) thay vì khai báo trong từng Pod:
kubectl patch serviceaccount default \
-p '{"imagePullSecrets": [{"name": "regcred"}]}'
SA token: dùng projected token có audience, không tin token vĩnh viễn cũ.
downwardAPI cho identity tốt hơn parse hostname.
# Xem nội dung
kubectl get configmap app-config -o yaml
kubectl get secret db-creds -o jsonpath='{.data.password}' | base64 -d
# Pod thấy gì thực sự?
kubectl exec -it <pod> -- env | grep LOG_LEVEL
kubectl exec -it <pod> -- cat /etc/app/app.yaml
# Secret type lạ?
kubectl get secret db-creds -o jsonpath='{.type}'
# Watch update real-time trên volume
kubectl exec -it <pod> -- watch -n 5 ls -lL /etc/app/
ConfigMap: config non-sensitive, 3 cách inject (env, envFrom, volume).
Secret: nhạy cảm — base64, KHÔNG mã hoá. Bật encryption at rest + RBAC + dùng external secrets cho production.
Projected volume: gộp nhiều source + SA token short-lived.
downwardAPI: pod tự biết identity và resource limit.
Immutable + version trong tên cho rollout an toàn.
GitOps + ESO/SealedSecret/SOPS thay vì commit secret thường.
Trong Phần 7, ta sẽ đi sâu security: RBAC, ServiceAccount, Pod SecurityContext và Pod Security Standards.
← Phần 5 | Phần 7: Security — RBAC, ServiceAccount, SecurityContext →