Kubernetes ConfigMap 與 Secret 配置管理

根據 12-Factor App 的原則,應用程式的 設定 (Config) 應該與 程式碼 (Code) 分離。 在 Kubernetes 中,我們使用 ConfigMap 儲存一般設定,使用 Secret 儲存敏感資料(如密碼、金鑰)。

ConfigMap (一般配置)

ConfigMap 用於儲存非機密的 key-value 鍵值對。

建立 ConfigMap

可以用宣告式 YAML 或指令式建立。

A. 指令式 (Imperative):

# 從 literal 建立
kubectl create configmap game-config \
  --from-literal=player_lives=3 \
  --from-literal=level=easy

# 從檔案建立 (檔名作為 key,內容作為 value)
kubectl create configmap game-config-file --from-file=./config/game.properties

B. 宣告式 YAML (Declarative):

game-config.yaml:

apiVersion: v1
kind: ConfigMap
metadata:
  name: game-config
data:
  player_lives: '3'
  level: 'easy'
  # 也可以放整個設定檔內容
  ui.properties: |
    color.good=purple
    color.bad=yellow
    allow.textmode=true

建立或更新 ConfigMap:

kubectl apply -f game-config.yaml

刪除 ConfigMap:

kubectl delete -f game-config.yaml
# 或指定名稱刪除
kubectl delete configmap game-config

在 Pod 中使用 ConfigMap

方式 A: 注入為特定環境變數 (valueFrom)

spec:
  containers:
    - name: game
      image: my-game
      env:
        - name: PLAYER_LIVES
          valueFrom:
            configMapKeyRef:
              name: game-config
              key: player_lives

方式 B: 注入所有 Key 為環境變數 (envFrom)

這會將 ConfigMap 中所有的 key 直接轉為環境變數 (例如 PLAYER_LIVES=3, LEVEL=easy)。

spec:
  containers:
    - name: game
      image: my-game
      envFrom:
        - configMapRef:
            name: game-config

方式 C: 掛載為檔案 (Volume Mount)

這會將 ConfigMap 中的每個 key 變成一個檔案。適合設定檔 (config file)。

spec:
  volumes:
    - name: config-volume
      configMap:
        name: game-config
  containers:
    - name: game
      volumeMounts:
        - name: config-volume
          mountPath: /etc/config # 檔案會出現在這裡

ConfigMap 更新與熱重載 (Hot Reload)

這是一個常見的面試題與實務坑點:

  • 環境變數 (Environment Variables): Pod 啟動時讀取一次就固定了。如果修改了 ConfigMap,Pod 不會自動更新。必須重啟 Pod (刪除 Pod 讓 Deployment 重建) 才能生效。
  • 掛載檔案 (Volume Mount): Kubernetes 會定期同步 ConfigMap 的變更有已掛載的 Volume。應用程式會看到檔案內容改變(通常有延遲,約 1 分鐘內)。前提是應用程式要有邏輯去監聽或重新讀取設定檔。

Secret (敏感資料)

Secret 與 ConfigMap 類似,但專門用於存放敏感資訊。Secret 預設會以 Base64 編碼儲存。

注意: Base64 只是編碼 (Encoding),不是加密 (Encryption)。任何拿到 Base64 字串的人都可以輕易解碼。所以 Secret 依賴於 Kubernetes 的 RBAC 權限控管來保護。

建立 Secret

A. 指令式:

kubectl create secret generic db-user-pass \
  --from-literal=username=admin \
  --from-literal=password=123456

B. 宣告式 YAML:

資料必須先手動轉 Base64。

echo -n 'admin' | base64  # YWRtaW4=
echo -n '123456' | base64 # MTIzNDU2

secret.yaml:

apiVersion: v1
kind: Secret
metadata:
  name: db-user-pass
type: Opaque # 預設類型,表示任意資料
data:
  username: YWRtaW4=
  password: MTIzNDU2

建立 Secret:

kubectl apply -f secret.yaml

刪除 Secret:

kubectl delete -f secret.yaml
# 或指定名稱刪除
kubectl delete secret db-user-pass

在 Pod 中使用 Secret

用法與 ConfigMap 幾乎完全相同 (env, envFrom, volume),只是關鍵字改為 secret 相關。

env:
  - name: DB_PASSWORD
    valueFrom:
      secretKeyRef:
        name: db-user-pass
        key: password

特殊類型的 Secret

除了預設的 Opaque,還有幾種常用類型:

  • kubernetes.io/dockerconfigjson: 用於拉取 Private Registry 影像 (Private Image Pull Secret)。
  • kubernetes.io/tls: 用於存放 TLS/SSL 憑證 (tls.crt, tls.key),常用於 Ingress HTTPS 設定。

建立 TLS Secret 範例:

kubectl create secret tls my-tls-secret --cert=path/to/cert.crt --key=path/to/key.key

常用操作指令 (Cheat Sheet)

這裡整理了管理 ConfigMap (cm) 與 Secret 的常用 kubectl 指令。

查詢與檢視

# 列出所有 ConfigMap
kubectl get cm

# 列出所有 Secret
kubectl get secret

# 查看詳細資訊 (除錯用)
kubectl describe cm game-config
kubectl describe secret db-user-pass

# 查看 YAML 內容 (會顯示 Base64 編碼後的 Secret)
kubectl get secret db-user-pass -o yaml

解碼 Secret (Decoding)

當你在 debug 時想看 Secret 裡面的真實內容,可以使用以下技巧:

# 取得 Secret 並用 jsonpath 提取特定欄位,再用 base64 解碼
kubectl get secret db-user-pass -o jsonpath='{.data.password}' | base64 --decode

# 或是直接安裝好用的插件 (如果有的話),例如 ksd (kubernetes-secret-decode)

編輯與刪除

# 直接編輯 (類似 vim 編輯遠端資源,存檔後即時更新 Kubernetes 物件)
kubectl edit cm game-config
kubectl edit secret db-user-pass

# 刪除
kubectl delete cm game-config
kubectl delete secret db-user-pass

安全性注意事項

  1. Etcd 加密: 預設 Secret 在 Etcd 資料庫中是明文儲存的。建議開啟 Kubernetes 的 Encryption at Rest 功能,確保資料落地時是加密的。
  2. RBAC 權限: 嚴格限制誰可以 getwatch Secret。
  3. GitOps 風險: 含有真實 Secret 的 YAML 絕對不能直接 commit 到 Git Repo。

總結

透過 ConfigMap 與 Secret,我們可以讓同一個 Image 在不同環境 (Dev/Test/Prod) 讀取不同的配置,實現真正的 Build once, Deploy anywhere