昨天學了 ConfigMap,把設定從 Deployment YAML 裡抽出來。但 ConfigMap 有一個明確的限制:資料是明文儲存的,不適合放密碼、API Key 這類敏感資料。
今天學 Secret,處理這類不能明文存放的資訊,同時也把昨天暫時放在 ConfigMap 的 DATABASE_URL(含密碼)移出去,改用 Secret 儲存。
kubectl get configmap todo-api-config -o yaml
輸出裡的 data 欄位是完全明文的,任何有權限執行這個指令的人都能直接看到所有內容。
昨天的 ConfigMap 裡有這一行:
data:
DATABASE_URL: "mysql+pymysql://user:password@mysql-service:3306/tododb"
DATABASE_URL 裡面包含了資料庫密碼,這樣放在 ConfigMap 是不安全的。今天就來把它移出去。
Secret 跟 ConfigMap 很像,也是 key-value 的儲存,差別在於:
Secret 有幾種類型,最常用的是 Opaque,也就是通用的 key-value 儲存。
kubectl create secret generic todo-api-secret \
--from-literal=DATABASE_URL=mysql+pymysql://user:password@mysql-service:3306/tododb
用指令建立的好處是密碼不會出現在 YAML 檔案裡,不會不小心被 commit 進 git。
如果要用 YAML,value 必須先手動 base64 編碼:
echo -n "mysql+pymysql://user:password@mysql-service:3306/tododb" | base64
# bXlzcWwrcHltc3FsOi8vdXNlcjpwYXNzd29yZEBteXNxbC1zZXJ2aWNlOjMzMDYvdG9kb2Ri
apiVersion: v1
kind: Secret
metadata:
name: todo-api-secret
type: Opaque
data:
DATABASE_URL: bXlzcWwrcHltc3FsOi8vdXNlcjpwYXNzd29yZEBteXNxbC1zZXJ2aWNlOjMzMDYvdG9kb2Ri
用 YAML 建立 Secret 要特別注意:這份 YAML 如果 commit 進 git,雖然是 base64,但任何人都能解碼回原始值,等同於明文洩漏。(生產環境怎麼安全地處理這個問題,可參考下面的延伸筆記)
跟 ConfigMap 的注入方式完全一樣,支援 envFrom、valueFrom、掛載成檔案三種方式。
注入整個 Secret 作為環境變數:
envFrom:
- secretRef:
name: todo-api-secret
只取特定的 key:
env:
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: todo-api-secret
key: DATABASE_URL
掛載成檔案:
volumeMounts:
- name: secret-volume
mountPath: /app/secrets
readOnly: true
volumes:
- name: secret-volume
secret:
secretName: todo-api-secret
今天目標:用 kubectl 指令建立 Secret 儲存 DATABASE_URL,更新 Deployment 同時從 ConfigMap 和 Secret 注入環境變數,並進入 Pod 確認敏感資料有正確以原始值注入。
kubectl create secret generic todo-api-secret \
--from-literal=DATABASE_URL=mysql+pymysql://user:password@mysql-service:3306/tododb
kubectl get secrets

kubectl get secret todo-api-secret -o yaml

echo "bXlzcWwrcHlteXNxbDovL3VzZXI6cGFzc3dvcmRAbXlzcWwtc2VydmljZTozMzA2L3RvZG9kYg==" | base64 --decode

todo-api-deployment.yaml 使用 SecretapiVersion: apps/v1
kind: Deployment
metadata:
name: todo-api
spec:
replicas: 2
selector:
matchLabels:
app: todo-api
template:
metadata:
labels:
app: todo-api
tier: backend
spec:
containers:
- name: api
image: yourname/todo-app-api:v1.0.0
ports:
- containerPort: 8000
envFrom:
- configMapRef:
name: todo-api-config # 一般設定從 ConfigMap 來
- secretRef:
name: todo-api-secret # 敏感資料從 Secret 來
kubectl apply -f todo-api-deployment.yaml
kubectl exec -it <pod-name> -- env | grep DATABASE_URL
Secret 注入進來的值是已解碼的原始值,不是 base64:

很多人以為 Secret 是加密的,但其實不是。
base64 是編碼,不是加密。 任何人都能解碼回原始值,沒有任何保護效果。
etcd 預設是明文儲存 Secret 的。 etcd 是整個 Cluster 的狀態資料庫,如果有人能直接讀取 etcd,所有 Secret 都能看到。生產環境應該開啟 etcd 的加密功能(Encryption at Rest)。
YAML 不能 commit 進 git。 如果用 YAML 建立 Secret,這份 YAML 本身就是安全風險,不能進版本控制。
生產環境的常見解法:
今天學了 Secret,處理不能明文儲存的敏感資料:
kubectl create secret 指令,避免密碼出現在 YAML 檔案裡明天會學 StatefulSet,說明為什麼資料庫這類有狀態的服務不能用 Deployment 跑,以及 K8s 怎麼處理這個問題 !