ConfigMap và Secret: Tách Cấu Hình Khỏi Image
Một image tốt là một image bất biến và dùng được mọi nơi: cùng một image nginx hay cùng một image app của bạn phải chạy được ở máy dev, ở staging, ở production. Điều đó chỉ khả thi nếu cấu hình (URL database, cờ tính năng, mật khẩu) không nằm trong image mà được tiêm vào lúc chạy. Kubernetes cho hai công cụ: ConfigMap cho cấu hình thường, Secret cho dữ liệu nhạy cảm.
Vì sao không nhét cấu hình vào image
Nếu bạn build mật khẩu DB hay URL backend thẳng vào image, bạn sẽ phải build lại image cho mỗi môi trường — đánh mất ý nghĩa của container. Tệ hơn, mật khẩu nằm trong image là rủi ro bảo mật (ai pull image cũng đọc được). Nguyên tắc (theo tinh thần 12-factor app): tách cấu hình ra khỏi code. Trong Kubernetes, ConfigMap và Secret là nơi chứa cấu hình tách rời đó.
image (bất biến) + ConfigMap/Secret (theo môi trường) = pod chạy
nginx:1.27 APP_MODE=production
DB_PASSWORD=...
ConfigMap: cấu hình không nhạy cảm
kubectl create configmap app-config \
--from-literal=APP_COLOR=blue \
--from-literal=APP_MODE=production
configmap/app-config created
ConfigMap chỉ là một túi cặp key=value. Ngoài --from-literal, bạn tạo được từ file (--from-file=config.properties) hoặc khai báo bằng YAML — cách hợp để lưu vào Git.
Secret: dữ liệu nhạy cảm
kubectl create secret generic app-secret \
--from-literal=DB_PASSWORD='s3cr3t-p@ss'
secret/app-secret created
Secret nhìn giống ConfigMap, nhưng dành riêng cho mật khẩu, token, khoá. Kubernetes đối xử với Secret cẩn trọng hơn (không in giá trị ra log mặc định, có thể giới hạn truy cập). Nhưng có một điều bạn nhất định phải biết.
Cảnh báo: Secret chỉ là base64, KHÔNG phải mã hóa
Đây là hiểu lầm phổ biến và nguy hiểm nhất về Kubernetes. Xem giá trị Secret vừa tạo:
kubectl get secret app-secret -o jsonpath='{.data.DB_PASSWORD}'
czNjcjN0LXBAc3M=
Trông như "đã được bảo vệ". Nhưng base64 là mã hoá thuận nghịch ai cũng giải được, không phải mật mã:
kubectl get secret app-secret -o jsonpath='{.data.DB_PASSWORD}' | base64 -d
s3cr3t-p@ss
Mật khẩu hiện nguyên hình. Vậy nên:
- Đừng commit Secret YAML vào Git như thể nó an toàn — base64 không che được gì.
- Mặc định Secret lưu trong etcd ở dạng không mã hoá. Production cần bật encryption at rest cho etcd, và/hoặc dùng giải pháp ngoài như Sealed Secrets, External Secrets Operator, hay vault của nhà cung cấp cloud.
- Giá trị của "Secret" so với "ConfigMap" nằm ở cách Kubernetes đối xử và phân quyền (RBAC, không log), chứ bản thân nó không mã hoá nội dung.
Nhớ kỹ điều này thì bạn tránh được một lỗ hổng kinh điển.
Inject vào pod: hai cách
ConfigMap và Secret vô dụng nếu pod không đọc được. Có hai cách tiêm: thành biến môi trường, hoặc mount thành file. Pod sau dùng cả hai:
apiVersion: v1
kind: Pod
metadata:
name: config-demo
spec:
containers:
- name: app
image: busybox:1.36
command: ["sh", "-c", "echo COLOR=$APP_COLOR MODE=$APP_MODE; cat /etc/secret/DB_PASSWORD; sleep 3600"]
env: # cách 1: ConfigMap → biến môi trường
- name: APP_COLOR
valueFrom:
configMapKeyRef: { name: app-config, key: APP_COLOR }
- name: APP_MODE
valueFrom:
configMapKeyRef: { name: app-config, key: APP_MODE }
volumeMounts: # cách 2: Secret → file
- name: secret-vol
mountPath: /etc/secret
readOnly: true
volumes:
- name: secret-vol
secret:
secretName: app-secret
kubectl apply -f pod-config.yaml
kubectl logs config-demo
COLOR=blue MODE=production
s3cr3t-p@ss
Thành công cả hai đường:
- Biến môi trường (
env.valueFrom.configMapKeyRef): giá trị ConfigMap thành$APP_COLOR,$APP_MODEtrong container. Hợp cho cấu hình đơn giản. (Tiêm cả ConfigMap một lần bằngenvFrom.) - Mount thành file (
volumes.secret+volumeMounts): mỗi key của Secret thành một file trong/etc/secret/. Hợp cho file cấu hình, chứng chỉ TLS, hoặc khi không muốn secret lộ qua biến môi trường (env dễ bị in ra log hơn).
Khi nào dùng cách nào? Cấu hình ngắn gọn → env. File cấu hình/chứng chỉ, hoặc secret cần kín đáo hơn → mount file. Một điểm tiện của mount: nếu cập nhật ConfigMap, file mount sẽ tự cập nhật theo (env thì không — phải dựng lại pod).
Tổng kết
Tách cấu hình khỏi image để một image chạy được mọi môi trường — đó là việc của ConfigMap (cấu hình thường) và Secret (dữ liệu nhạy cảm). Cả hai inject vào pod theo hai cách: biến môi trường (configMapKeyRef/secretKeyRef, hoặc envFrom) hoặc mount thành file (qua volumes). Điều tối quan trọng: Secret chỉ là base64, không phải mã hoá — ai có quyền đọc là thấy giá trị; production phải bật encryption at rest cho etcd và/hoặc dùng giải pháp secret chuyên dụng, và đừng commit Secret YAML như thể nó an toàn.
Cấu hình đã tách rời, nhưng dữ liệu trong pod vẫn bay hơi cùng pod — pod chết là mất sạch. Bài 8: Volumes, PV và PVC — cách lưu trữ dữ liệu bền vững, sống lâu hơn vòng đời pod.