主線

KAI K8s之路 05|ConfigMap / Secret 是什麼?不要把配置和鑰匙烤進 image

Probe 讓 workload 回報狀態,但同一份程式在不同環境會吃不同設定。這篇把 ConfigMap 和 Secret 拆成外部注入的設定層:image 放程式,ConfigMap 放普通設定,Secret 放敏感資料,但 Secret 不是保險箱。

中文 主線 第 5 篇
先抓住

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 物件。

KAI notebook style diagram showing a clean container image receiving non-sensitive settings from ConfigMap and sensitive keys from Secret, with labels app code, normal config, and credentials.
Image 放程式;ConfigMap 放普通設定;Secret 放敏感資料。三個東西混在一起,後面每一次部署都會變難查。

先不要把 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=production
  • LOG_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「這個環境允許你拿哪把鑰匙」。

KAI notebook style flow showing ConfigMap and Secret as Kubernetes API objects injected into a Pod through environment variables or mounted files, with a note that env values need restart while mounted files update eventually.
ConfigMap / Secret 不是塞進 image,而是由 Pod spec 引用,再由 Kubernetes 在啟動時注入成環境變數或檔案。

Kubernetes 實際在做什麼

這裡給一個夠用的版本。

  1. 你先建立 ConfigMapSecret 這種 Kubernetes API object。
  2. Pod spec 裡引用它們,例如用 envFromvalueFrom,或掛成 volume。
  3. kubelet 在啟動 container 時,把這些資料注入到 container 裡。
  4. app 自己讀 environment variable 或檔案,Kubernetes 不會替 app 理解設定內容。
  5. 如果後來更新 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

listwatch Secrets 的權限很敏感。

有些人只想讓 workload 讀一把鑰匙,結果給了一整個 namespace 的鑰匙櫃。

5) 忘記 app 需要 reload

就算 volume 內的設定檔更新了,app 也不一定會重新讀。

Kubernetes 能把資料送到檔案系統,但不能保證你的程式會重新理解它。

我會怎麼 inspect

如果有人說「改了設定但服務沒變」,我會先問三件事:

  1. 這個設定是 env 還是 volume?
  2. Pod 有沒有真的重啟或 rollout?
  3. 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 失敗
KAI notebook style troubleshooting path for configuration changes: check ConfigMap or Secret object, Pod reference, env or volume injection, rollout, and app reload without printing secret values.
排查設定沒生效時,不要急著重 build image。先查 object、引用、注入方式、rollout,以及 app 是否真的 reload。

一句話版本:

設定沒生效時,先查注入路徑,再查 image。

我對 ConfigMap / Secret 的記法

我不會把這章記成「Kubernetes 有兩種放設定的資源」。

那太像背名詞。

我會這樣記:

ConfigMap / Secret 是 image 和環境之間的插槽。

image 代表「這個程式是什麼」。

ConfigMap / Secret 代表「這次部署要怎麼跑、拿哪把鑰匙」。

把插槽分出來,才會有同一份 image 跑多個環境的可能。

把插槽混回 image,Kubernetes 就會退化成一個很複雜的打包器。

這篇先記住三件事

  1. image 放程式,不放環境決策和秘密
  2. ConfigMap 放普通設定,Secret 放敏感資料,但 Secret 仍需要 RBAC 和加密邊界
  3. env 注入通常要重啟才會變;volume projection 會 eventually update,但 app 也要懂 reload

下一篇會接著看:Volume / PersistentVolume 是什麼?Pod 會換,但資料不應該跟著消失。

技術校對參考: