主線
KAI K8s之路 05|ConfigMap / Secret 是什麼?不要把配置和鑰匙烤進 image
Probe 讓 workload 回報狀態,但同一份程式在不同環境會吃不同設定。這篇把 ConfigMap 和 Secret 拆成外部注入的設定層:image 放程式,ConfigMap 放普通設定,Secret 放敏感資料,但 Secret 不是保險箱。
Image 應該裝程式,不應該裝環境決策。ConfigMap 和 Secret 是把配置從 image 拆出去、在 Pod 啟動時注入的 Kubernetes 物件。
ConfigMap 放非敏感設定,Secret 放敏感資料;用 env 或 volume 注入,注意 env 不會跟著更新、Secret 也需要 RBAC 和加密邊界。
上一篇我們把 Probe 想清楚了:
Running 不等於 Ready,workload 要把狀態講清楚,Kubernetes 才知道什麼時候該送流量、什麼時候該重啟。
但 workload 不只需要狀態。
同一份程式,到了 dev、staging、production,通常會吃不同的設定、不同的 endpoint、不同的憑證。
如果每次改設定都要重新 build image,那 image 就不只是程式包了,它變成一個混著環境決策和秘密的黑盒。
我會先記這句:
Image 應該裝程式,不應該裝環境決策。ConfigMap 和 Secret 是把配置從 image 拆出去、在 Pod 啟動時注入的 Kubernetes 物件。
先不要把 image 當成行李箱
container image 最適合放這些東西:
- app code
- runtime
- dependency
- 預設但不帶環境身分的檔案
它不適合放:
- production API URL
- feature flag 現場決策
- database password
- token
- 每個 cluster 都不同的設定
如果把這些東西烤進 image,就會出現一個很麻煩的結果:
同一份程式不能自然跑在不同環境。
配置變更本來應該是「換一張設定單」,最後變成「重新做一個行李箱」。
這不是 Kubernetes 想要的工作方式。
用入住酒店想 ConfigMap / Secret
我會用酒店入住來記這章。
你的行李箱像 container image:衣服、充電器、常用工具都在裡面。
但房號、Wi-Fi 資訊、房卡,不應該縫在行李箱上。
你到前台辦入住時,前台會給你當次住宿需要的資訊:
- 普通入住資訊:早餐時間、Wi-Fi 名稱、樓層提示
- 敏感通行能力:房卡、門禁權限
ConfigMap 像普通入住資訊。
Secret 像房卡。
兩者都不是你的行李箱本身,而是在你抵達那個環境時被交給你的 runtime 資料。
ConfigMap 和 Secret 的分工
我不會把 ConfigMap 記成「一堆 key-value」這麼薄。
我會把它記成:
ConfigMap 是 workload 的普通設定層。
例如:
APP_MODE=productionLOG_LEVEL=info- feature flag
- 非敏感 endpoint
- 小型設定檔
Secret 則是:
Secret 是 workload 的敏感資料層,但不是保險箱。
例如:
- database password
- API token
- TLS key
- private registry credential
它們在使用方式上很像:都可以被 Pod 當成 environment variable,也可以被 mount 成檔案。
但語義不同。
ConfigMap 告訴 app「這個環境怎麼運作」。
Secret 告訴 app「這個環境允許你拿哪把鑰匙」。
Kubernetes 實際在做什麼
這裡給一個夠用的版本。
- 你先建立
ConfigMap或Secret這種 Kubernetes API object。 - Pod spec 裡引用它們,例如用
envFrom、valueFrom,或掛成 volume。 kubelet在啟動 container 時,把這些資料注入到 container 裡。- app 自己讀 environment variable 或檔案,Kubernetes 不會替 app 理解設定內容。
- 如果後來更新 ConfigMap / Secret,environment variable 不會在既有 container 裡自動變;volume projection 會 eventually update,但 app 也要懂得重新讀。
這裡最容易踩坑的是第 5 點。
很多人以為「我改了 ConfigMap,Pod 就一定吃到新設定」。
實際上要看你怎麼注入。
用 environment variable 的話,通常要重新啟動 Pod。
用 volume mount 的話,檔案內容可能會被更新,但不是瞬間,而且如果 app 啟動時只讀一次設定,它也不會自己 magically reload。
一個簡化設定範例
先看普通設定:
apiVersion: v1
kind: ConfigMap
metadata:
name: web-config
data:
APP_MODE: production
LOG_LEVEL: info
FEATURE_CHECKOUT: "true"
再看敏感資料。
這裡只放示意值,真實 Secret 不應該提交到 public repo。
apiVersion: v1
kind: Secret
metadata:
name: web-credentials
type: Opaque
stringData:
DATABASE_PASSWORD: "example-only-do-not-commit-real-secret"
Deployment 可以這樣引用:
apiVersion: apps/v1
kind: Deployment
metadata:
name: web
spec:
template:
spec:
containers:
- name: web
image: ghcr.io/example/web:1.0.0
envFrom:
- configMapRef:
name: web-config
env:
- name: DATABASE_PASSWORD
valueFrom:
secretKeyRef:
name: web-credentials
key: DATABASE_PASSWORD
這段 YAML 的重點不是語法。
重點是責任切分:
- image 保持可重用
- ConfigMap 承載普通環境設定
- Secret 承載敏感資料引用
- Deployment 描述這個 workload 需要哪些 runtime 資料
Secret 不是保險箱
這句一定要講清楚。
Secret 比把密碼直接寫在 Pod spec 或 image 裡好,但它不是「放進去就安全」的保險箱。
Kubernetes 官方文件提醒過幾個關鍵點:
- Secret 預設存在 API server 底層資料庫時不一定加密
- 有 API 權限的人可能讀到 Secret
- 有 namespace 內建立 Pod 權限的人,可能透過 Pod 間接使用 Secret
- Secret mount 到 node 時,kubelet 會把資料放在記憶體檔案系統,Pod 刪除後才清掉本地副本
所以 Secret 的安全不是只靠 kind: Secret。
還要靠:
- etcd encryption at rest
- RBAC 最小權限
- 不把 Secret 值印進 log
- 不把真實 Secret YAML commit 到 repo
- 需要時接 external secret manager
一句話:
Secret 是 Kubernetes 對敏感資料的型別和傳遞方式,不是完整的秘密管理策略。
新手最常踩的坑
1) 把 Secret 的 base64 當成加密
很多 Secret manifest 看起來像一串亂碼,是因為 data 欄位使用 base64。
base64 是編碼,不是加密。
不要因為看不懂就覺得安全。
2) 期待 env 注入會自動刷新
用 environment variable 注入的設定,在 container 啟動後就固定了。
改 ConfigMap / Secret 不代表既有 process 的 env 會變。
3) 用 ConfigMap 放敏感資料
如果資料會讓人取得系統權限、外部服務權限、或用戶資料,先把它當 Secret 看。
不要因為「只是測試環境」就塞進 ConfigMap。
4) 對 Secret 給太寬的 RBAC
能 list 或 watch Secrets 的權限很敏感。
有些人只想讓 workload 讀一把鑰匙,結果給了一整個 namespace 的鑰匙櫃。
5) 忘記 app 需要 reload
就算 volume 內的設定檔更新了,app 也不一定會重新讀。
Kubernetes 能把資料送到檔案系統,但不能保證你的程式會重新理解它。
我會怎麼 inspect
如果有人說「改了設定但服務沒變」,我會先問三件事:
- 這個設定是 env 還是 volume?
- Pod 有沒有真的重啟或 rollout?
- app 是啟動時讀一次,還是會 watch / reload?
我會查:
kubectl get configmap -n <ns>
kubectl get secret -n <ns>
kubectl describe deploy <deploy-name> -n <ns>
kubectl describe pod <pod-name> -n <ns>
kubectl rollout status deploy/<deploy-name> -n <ns>
如果要查 Secret,我會避免把值直接 dump 到 terminal、CI log、chat 裡。
只確認:
- Secret object 存不存在
- Deployment / Pod 有沒有引用正確 name 和 key
- mount path 或 env name 是否和 app 期待一致
- 最近一次 rollout 是否在設定變更之後
- Events 裡有沒有 missing key、permission、mount 失敗
一句話版本:
設定沒生效時,先查注入路徑,再查 image。
我對 ConfigMap / Secret 的記法
我不會把這章記成「Kubernetes 有兩種放設定的資源」。
那太像背名詞。
我會這樣記:
ConfigMap / Secret 是 image 和環境之間的插槽。
image 代表「這個程式是什麼」。
ConfigMap / Secret 代表「這次部署要怎麼跑、拿哪把鑰匙」。
把插槽分出來,才會有同一份 image 跑多個環境的可能。
把插槽混回 image,Kubernetes 就會退化成一個很複雜的打包器。
這篇先記住三件事
- image 放程式,不放環境決策和秘密
- ConfigMap 放普通設定,Secret 放敏感資料,但 Secret 仍需要 RBAC 和加密邊界
- env 注入通常要重啟才會變;volume projection 會 eventually update,但 app 也要懂 reload
下一篇會接著看:Volume / PersistentVolume 是什麼?Pod 會換,但資料不應該跟著消失。
技術校對參考: