K8s · 設定與儲存 · 第 24 課 · · 9min read

ConfigMap 教學:把設定從 Image 抽出來的正確姿勢

設定寫死在 Image,每次改都要 build 一次。ConfigMap 把設定獨立出來,env 注入或 Volume 掛載兩種用法,這篇全部教完。

#Kubernetes #ConfigMap #設定 #env
章節目錄 · 10

設定寫死在 Image 是不行的

很多人剛接觸 K8s 會這樣寫 Dockerfile:

ENV DB_HOST=192.168.1.50
ENV DB_PORT=3306
ENV LOG_LEVEL=info

build → push → deploy。看起來沒事,但有三個問題:

問題 1:環境不同要建不同 Image

dev 的資料庫 dev-db:3306、prod 的 prod-db:3306。難道要 build myapp:devmyapp:prod 兩個 Image?多一個 staging 就是三個。多個地區就是六個。荒謬。

問題 2:改個設定就要重 build

只是把 LOG_LEVELinfo 改成 debug 看一下日誌 → 改 Dockerfile → push → deploy。走完整套 CI/CD pipeline。

問題 3:敏感資料外洩風險

如果有密碼寫在 Image 裡,只要有人能 docker pulldocker inspect 就看光了。

ConfigMap:把設定獨立出來

Pod env 篇我們用 env: value 把變數寫在 Pod YAML 裡。ConfigMap 把這件事升級:設定獨立成一個資源,多個 Pod 共用。

ConfigMap (app-config)            ┌─ Pod A 用 envFrom 引用
  MESSAGE: Hello from ConfigMap   │
  USERNAME: admin                 ├─ Pod B 用 envFrom 引用
  LOG_LEVEL: info                 │
                                  └─ Pod C 掛成檔案

對照 Docker:docker run -e KEY=value 的升級版,但設定統一管理。

三種建立方式

1. --from-literal(最快)

$ kubectl create configmap app-config \
    --from-literal=MESSAGE="Hello from ConfigMap" \
    --from-literal=USERNAME=admin

適合臨時測試、簡單 key-value。

2. --from-file(整個檔案)

$ kubectl create configmap nginx-conf \
    --from-file=nginx.conf

把整個檔案存進 ConfigMap,key = 檔名、value = 檔案內容。適合 nginx.confmy.cnf 這種設定檔。

3. YAML(最推薦

apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
data:
  MESSAGE: "Hello from ConfigMap"
  USERNAME: "admin"
  LOG_LEVEL: "info"

YAML 可以放 Git,做版本管理 / Code Review / GitOps。正式環境就用這個

兩種注入方式

方式適合改了會自動更新嗎?
環境變數(envFrom / env)key-value 設定❌ 要重啟 Pod
Volume 掛載設定檔(nginx.conf)✅ 30-60 秒自動同步

方式 A:envFrom 一次塞全部

apiVersion: apps/v1
kind: Deployment
metadata:
  name: app-with-config
spec:
  replicas: 1
  selector:
    matchLabels:
      app: frontend
  template:
    metadata:
      labels:
        app: frontend
    spec:
      containers:
        - name: app
          image: yanchen184/k8s-demo-app:latest
          envFrom:
            - configMapRef:
                name: app-config       # ← 整包注入

envFrom 把 ConfigMap 所有 key 一次變成環境變數。比逐一寫 env: valueFrom: configMapKeyRef 簡潔。

方式 B:env.valueFrom(單一 key)

只想拿其中一個 key:

env:
  - name: APP_LOG_LEVEL
    valueFrom:
      configMapKeyRef:
        name: app-config
        key: LOG_LEVEL

可以順便改名(LOG_LEVELAPP_LOG_LEVEL)。

envFrom 的更新行為

跑 demo 看一下:

$ kubectl apply -f configmap-literal.yaml
$ curl http://<NODE-IP>/frontend
Server: 10.42.0.5:80
Message: Hello from ConfigMap
Username: admin

# 改 ConfigMap
$ kubectl edit configmap app-config
# MESSAGE: "Hello Updated"

# 再 curl
$ curl http://<NODE-IP>/frontend
Message: Hello from ConfigMap # ← 還是舊值!

為什麼沒更新? 環境變數是 process 啟動時讀一次就定死了,後來改 ConfigMap 跑著的 process 不會知道。

讓新值生效:

$ kubectl rollout restart deployment/app-with-config
$ curl http://<NODE-IP>/frontend
Message: Hello Updated                # ← 現在對了

口訣:env 注入 = 改完要重啟。

Volume 掛載:適合 nginx.conf

ConfigMap 也能掛成檔案,每個 key 變一個檔案:

apiVersion: v1
kind: ConfigMap
metadata:
  name: nginx-config
data:
  default.conf: |
    server {
      listen 80;
      location /healthz {
        return 200 'OK';
        add_header Content-Type text/plain;
      }
    }
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-custom
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx-custom
  template:
    metadata:
      labels:
        app: nginx-custom
    spec:
      containers:
        - name: nginx
          image: nginx:1.27
          volumeMounts:
            - name: config
              mountPath: /etc/nginx/conf.d   # ← 整個目錄
      volumes:
        - name: config
          configMap:
            name: nginx-config

掛載後容器裡會有 /etc/nginx/conf.d/default.conf,內容就是 ConfigMap 的 default.conf 那段。

熱更新驗證

$ kubectl exec deploy/nginx-custom -- cat /etc/nginx/conf.d/default.conf
# 看到 'OK'

# 改 ConfigMap,把 OK 改成 HEALTHY
$ kubectl edit configmap nginx-config

# 等 30-60 秒
$ kubectl exec deploy/nginx-custom -- cat /etc/nginx/conf.d/default.conf
# 變成 'HEALTHY' — 不用重啟 Pod!

# 但 Nginx process 還用舊設定,要 reload
$ kubectl exec deploy/nginx-custom -- nginx -s reload

重點:Volume 掛載,檔案會自動更新(30-60 秒),但應用程式自己要會 reload(Nginx -s reload、Node.js / Spring Boot 通常做不到,要重啟)。

⚠️ subPath 的坑

如果你只想掛一個檔案而不是整個目錄:

volumeMounts:
  - name: config
    mountPath: /app/config.json
    subPath: config.json           # ← 只掛這個檔

用了 subPath 就不會自動更新。 這是 K8s 已知行為。

取捨:

  • 要熱更新 → 不用 subPath,掛整個目錄
  • 接受重啟 → 用 subPath 不影響周圍其他檔案

對照 Docker

DockerKubernetes
-e KEY=valueenv / envFrom + ConfigMap
--env-file .envenvFrom: configMapRef
-v ./nginx.conf:/etc/nginx/...Volume + ConfigMap 掛載
K8s 結構化更多但邏輯一致:設定獨立、Image 純粹、不同環境用不同 ConfigMap

重點整理

  • 設定永遠不要寫死在 Image,用 ConfigMap
  • 三種建法:--from-literal / --from-file / YAML(推薦)
  • env 注入:簡單、改了要重啟
  • Volume 掛載:自動更新、但 subPath 例外
  • 熱更新後應用程式自己要會 reload
  • ConfigMap 是明文儲存,密碼不能放這

下一步

ConfigMap 解決了一般設定。但密碼、API Key、TLS 憑證怎麼辦?

kubectl get configmap app-config -o yaml 任何人都看光光,這顯然不行。下一篇:Secret + RBAC:K8s 怎麼存密碼才不會洩漏

📅 下一篇Secret + RBAC:K8s 怎麼存密碼才不會洩漏
ConfigMap 不能放密碼,這篇用 Secret + RBAC 把「敏感資料」這層補齊。
📚 完整系列總覽K8s 系列教學首頁(共 40 課,按學習路徑順序排)