主線

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. 你先建立 ConfigMap 或 Secret 這種 Kubernetes API object。
  2. Pod spec 裡引用它們,例如用 envFrom、valueFrom,或掛成 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

能 list 或 watch 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 會換,但資料不應該跟著消失。

技術校對參考: