Cấu hình vào pod bằng bốn cách, và chúng hành xử khác nhau khi bạn sửa. Bài này đo sự khác nhau đó.
Bốn cách nạp
env:
- {name: TU_ENV, valueFrom: {configMapKeyRef: {name: cfg, key: gia_tri}}}
- {name: TU_SECRET, valueFrom: {secretKeyRef: {name: sec, key: mat_khau}}}
volumeMounts:
- {name: v1, mountPath: /cau-hinh}
- {name: v2, mountPath: /bi-mat}
env TU_ENV = phien_ban_1
env TU_SECRET = bi_mat_1
tệp /cau-hinh: app.conf gia_tri
nội dung = phien_ban_1
tệp /bi-mat: mat_khau -> bi_mat_1
Cả bốn đều chạy. Khác biệt lộ ra khi sửa.
Sửa ConfigMap: một cái cập nhật, một cái không
Đổi giá trị từ phien_ban_1 sang phien_ban_2:
| Biến môi trường | Tệp gắn | |
|---|---|---|
| sau 5 s | phien_ban_1 |
phien_ban_1 |
| sau 20 s | phien_ban_1 |
phien_ban_1 |
| sau 40 s | phien_ban_1 |
phien_ban_1 |
| sau 70 s | phien_ban_1 |
phien_ban_2 |
Đo lại chính xác hơn: tệp gắn cập nhật sau 65 giây.
Biến môi trường không bao giờ đổi. Nó được đóng băng lúc container khởi động — đó là cách biến môi trường hoạt động ở mức hệ điều hành, không phải giới hạn của Kubernetes.
Con số 65 giây đến từ chu kỳ đồng bộ của kubelet cộng thời gian sống của bộ nhớ đệm. Nó không cấu hình được ở mức pod.
Cập nhật tệp là nguyên tử
drwxr-xr-x ..2026_08_31_14_25_23.1911276327
lrwxrwxrwx ..data -> ..2026_08_31_14_25_23.1911276327
lrwxrwxrwx app.conf -> ..data/app.conf
Mỗi tệp bạn thấy là một liên kết mềm trỏ vào ..data, và ..data lại là liên kết trỏ vào một thư mục mang dấu thời gian.
Khi ConfigMap đổi, kubelet ghi một thư mục mới rồi đổi liên kết ..data sang nó bằng một thao tác duy nhất. Ứng dụng không bao giờ đọc được trạng thái nửa vời — không có khoảnh khắc nào tệp A đã mới còn tệp B còn cũ.
Đây là chi tiết đáng biết vì nó ảnh hưởng tới cách theo dõi tệp: inotify trên chính tệp app.conf sẽ không bắt được thay đổi, vì bản thân liên kết đó không đổi. Phải theo dõi thư mục, hoặc theo dõi ..data.
Secret: base64 không phải mã hoá
giá trị trong API: YmlfbWF0XzE=
giải mã base64: bi_mat_1
Ai đọc được Secret là đọc được mật khẩu. Base64 chỉ để chứa được byte nhị phân trong YAML.
Khác biệt thật so với ConfigMap chỉ có hai điều:
RBAC tách riêng — bạn cấp quyền đọc secrets riêng khỏi configmaps, nên phân quyền chặt hơn được.
Gắn bằng tmpfs — đo được df trả về tmpfs, nghĩa là nội dung nằm trong RAM và không chạm đĩa node. Node bị lấy đĩa ra đọc thì Secret không có ở đó.
Muốn mã hoá thật thì phải bật encryption at rest ở apiserver (EncryptionConfiguration), hoặc dùng hệ thống quản lý bí mật bên ngoài (Vault, cloud KMS) với CSI driver.
Mặc định, Secret trong etcd là base64 thuần — và phần 45 đã đo etcd chỉ là một tệp trên đĩa control plane.
Hệ quả thực dụng
Cần cấu hình đổi nóng → gắn thành tệp, và ứng dụng phải theo dõi tệp đó. Chấp nhận độ trễ tới ~65 giây.
Dùng biến môi trường → chấp nhận phải khởi động lại pod mới có hiệu lực:
kubectl rollout restart deployment/<ten>
Đổi ConfigMap mà quên rollout restart là lỗi rất hay gặp: thay đổi nằm trong etcd, kubectl get cm cho thấy giá trị mới, và ứng dụng vẫn chạy giá trị cũ. Không lỗi, không cảnh báo.
Cách chặn: đặt hàm băm nội dung ConfigMap vào annotation của pod template. Khi ConfigMap đổi, hàm băm đổi, template đổi, và Deployment tự cuốn chiếu:
template:
metadata:
annotations:
checksum/config: "{{ sha256sum cau-hinh }}"
Helm và Kustomize đều có cách làm việc này sẵn.
immutable: true
apiVersion: v1
kind: ConfigMap
metadata: {name: cfg}
immutable: true
data: {...}
ConfigMap bất biến không sửa được — muốn đổi thì tạo cái mới với tên khác và trỏ pod sang đó.
Nghe bất tiện, nhưng nó có hai lợi ích thật:
kubeletngừng theo dõi nó, giảm tải lênapiserver. Với cụm hàng nghìn pod, đây là khác biệt đo được.- Không còn chuyện sửa nhầm ConfigMap đang chạy và làm hỏng thứ gì đó 65 giây sau, ở một nơi không ai liên hệ được với thao tác vừa rồi.
Với cấu hình gắn với phiên bản ứng dụng, bất biến là lựa chọn đúng.
Ba giới hạn
1 MiB mỗi object (phần 45). Không nhét tệp lớn vào ConfigMap.
ConfigMap và Secret phải cùng namespace với pod. Không tham chiếu chéo namespace được — muốn dùng chung thì phải sao chép, và đó là việc của công cụ như reflector.
Pod không khởi động được nếu ConfigMap chưa tồn tại. Nó nằm ở ContainerCreating với event configmap "cfg" not found. Đặt optional: true nếu muốn nó khởi động dù thiếu.
Thử ba mươi giây
Kiểm ứng dụng của bạn có nhận được thay đổi cấu hình không:
kubectl get deployment <ten> -o jsonpath='{.spec.template.spec.containers[*].env[*].valueFrom.configMapKeyRef.name}'
Có tên nào hiện ra nghĩa là phần cấu hình đó đi qua biến môi trường — và sửa ConfigMap sẽ không có tác dụng cho tới khi bạn khởi động lại pod. Nếu quy trình triển khai của bạn không có bước đó, thay đổi cấu hình đang lặng lẽ không tới nơi.
Phần sau đo Volume và PVC: bốn loại lưu trữ, chênh nhau gần năm lần tốc độ ghi.