設定寫死在 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:dev 和 myapp:prod 兩個 Image?多一個 staging 就是三個。多個地區就是六個。荒謬。
問題 2:改個設定就要重 build
只是把 LOG_LEVEL 從 info 改成 debug 看一下日誌 → 改 Dockerfile → push → deploy。走完整套 CI/CD pipeline。
問題 3:敏感資料外洩風險
如果有密碼寫在 Image 裡,只要有人能 docker pull,docker 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.conf、my.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_LEVEL → APP_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
| Docker | Kubernetes |
|---|---|
-e KEY=value | env / envFrom + ConfigMap |
--env-file .env | envFrom: configMapRef |
-v ./nginx.conf:/etc/nginx/... | Volume + 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 課,按學習路徑順序排)