Bỏ qua để đến nội dung
Knowledge Hub

Config trên Kubernetes: Secret, restart, rotation, feature flag, drift Trung cấp

Thời gian đọc ước tính: 7 phút

Trang này trả lời: config đi vào Pod thế nào, khi đổi thì cần làm gì để nó thật sự được áp dụng, và cách đổi secret, flag mà không gây sự cố.

spec:
containers:
- name: api
image: registry.example.com/payment-api@sha256:... # cùng digest mọi môi trường
envFrom:
- configMapRef:
name: payment-api-config # DATABASE_POOL_MAX, PAYMENT_API_BASE_URL, LOG_LEVEL
env:
- name: PAYMENT_API_SECRET_KEY
valueFrom:
secretKeyRef:
name: payment-api-secrets
key: PAYMENT_API_SECRET_KEY

Những điều docs Kubernetes nói mà hay bị bỏ qua:

Sự thật Hệ quả
ConfigMap “does not provide secrecy or encryption”; tối đa 1 MiB Không để secret trong ConfigMap
Giá trị trong Secret là base64, “obscures them but does NOT provide any useful level of confidentiality” base64 không phải mã hoá
Mặc định Secret lưu không mã hoá trong etcd Phải bật encryption at rest, RBAC least-privilege
Ai được tạo Pod trong namespace thì đọc được mọi Secret của namespace đó Phân namespace theo ranh giới quyền
ConfigMap dùng làm env var “are not updated automatically and require a pod restart” Sửa ConfigMap xong, Pod đang chạy vẫn dùng giá trị cũ
Volume mount được cập nhật dần (kubelet sync + cache); mount bằng subPath thì không File thì đổi được khi chạy, nhưng app phải tự đọc lại

Deployment chỉ rollout khi Pod template (.spec.template) đổi. Sửa ConfigMap không đổi template, nên không tự rollout.

Đang tải diagram…

Cách làm phổ biến để template đổi theo config:

  • Helm: annotation chứa checksum của config.
  • Kustomize: configMapGenerator thêm hash vào tên ConfigMap.
  • Thủ công: kubectl rollout restart sau khi sửa.

Kubernetes không tự rollback. Pod mới exit vì config sai thì Deployment báo ProgressDeadlineExceeded sau progressDeadlineSeconds (mặc định 600) và dừng ở đó.

Với maxUnavailable mặc định 25%, một phần Pod cũ có thể đã bị gỡ trước khi Pod mới fail. Cấu hình readiness đúng cách ở Health probes.

Restart khi config đổi (mặc định)

  • Config bất biến trong suốt đời process; code không cần xử lý “giá trị đổi giữa request”.
  • Rollout đi qua readiness và graceful shutdown; config sai thì Pod mới không ready, Pod cũ vẫn chạy.
  • Đổi config có lịch sử như một lần deploy.

Hot reload trong process

  • Mọi thay đổi phải qua pool, client, cache đã khởi tạo từ giá trị cũ; dễ có trạng thái nửa cũ nửa mới.
  • Config sai được nạp vào Pod đang phục vụ, không có readiness nào chặn.
  • Chỉ đáng làm cho một nhóm nhỏ giá trị: log level, secret xoay vòng, flag.

Default: restart. Giá trị cần đổi khi đang chạy thường là feature flag, nên đưa vào flag service thay vì tự viết hot reload. Ngoại lệ chính đáng là secret rotation.

Secret sẽ phải đổi: định kỳ, hoặc khi bị lộ. Đọc secret một lần lúc start thì cách duy nhất để đổi là restart. Điều đó ổn nếu quy trình có giai đoạn chuyển tiếp:

  1. Tạo credential mới ở provider, giữ credential cũ còn hiệu lực.
  2. Cập nhật Secret, rollout restart. Pod mới dùng key mới, Pod cũ vẫn dùng key cũ hợp lệ.
  3. Khi mọi Pod đã chạy key mới, revoke key cũ.

Với key do mình kiểm tra (webhook signing secret, JWT signing key), app phải chấp nhận hai giá trị cùng lúc trong giai đoạn chuyển:

  • WEBHOOK_SIGNING_SECRETS=new,old, thử lần lượt.
  • Hoặc JWT có kid để chọn key.

Thiếu móc này, rotation biến thành sự cố có kế hoạch.

Muốn đổi không restart: mount Secret thành file (không subPath), đọc lại khi file đổi hoặc khi nhận 401 từ provider. Xử lý khi secret bị lộ xem Secure coding.

Pete Hodgson (“Feature Toggles” trên martinfowler.com) chia flag thành bốn loại, khác nhau ở tuổi thọ và độ động:

Loại Dùng cho Tuổi thọ Quyết định
Release Merge code chưa xong vào main, bật khi sẵn sàng Ngắn, “transitionary by nature” Gần như tĩnh
Experiment A/B test Đủ lâu để có số liệu Theo từng request/user
Ops Kill switch, giảm tải tính năng phụ Thường ngắn; một số kill switch sống lâu Phải đổi rất nhanh
Permissioning Tính năng cho gói premium, beta user Có thể rất dài Theo từng request

Đang tải diagram…

Bài gốc nói flag là “inventory which comes with a carrying cost”. Trong thực tế:

  • Mỗi flag có owner và ngày hết hạn khi tạo. Release flag quá hạn là ticket, không phải trang trí.
  • Có thể đặt giới hạn số flag đang tồn tại, như bài gốc gợi ý, để ép việc dọn dẹp.
  • Kill switch (ops flag sống lâu) là ngoại lệ có chủ đích: tắt recommendation khi DB quá tải. Kiểm tra định kỳ rằng tắt nó vẫn chạy được.
  • Flag đọc lúc runtime nên có giá trị fallback khi flag service không trả lời. Fallback của tính năng mới là false.

Env var FEATURE_X_ENABLED vẫn hợp lý cho release flag tĩnh. Khi cần bật theo user hoặc tắt trong vài giây, chuyển sang flag service.

12-factor nêu ba khoảng cách giữa dev và prod: thời gian, con người, công cụ. Với config, drift thường trông như sau:

  • Staging có key mà production thiếu. Phát hiện lúc deploy production.
  • Ai đó kubectl edit ConfigMap production lúc 2h sáng, không ai ghi lại. Lần apply manifest sau ghi đè mất.
  • Local dùng SQLite, production dùng PostgreSQL. 12-factor gọi thẳng: “resists the urge to use different backing services”.

Cách chặn:

  1. Config (trừ secret) nằm trong git, apply qua CI hoặc GitOps. Không sửa tay trên cluster.
  2. Schema trong loadConfig là nguồn sự thật duy nhất. CI kiểm tra mỗi môi trường có đủ key của schema.
  3. Local chạy cùng loại DB, broker, cache với production (container), dù nhỏ hơn.
  4. Đổi tên hay xoá key theo kiểu expand–contract: đọc cả key mới và cũ trong một release, cập nhật mọi môi trường, rồi mới bỏ key cũ.

Tiếp theo: Lỗi hay gặp.