iT邦幫忙

2026 iThome 鐵人賽

DAY 11
0

昨天學了 ConfigMap,把設定從 Deployment YAML 裡抽出來。但 ConfigMap 有一個明確的限制:資料是明文儲存的,不適合放密碼、API Key 這類敏感資料。

今天學 Secret,處理這類不能明文存放的資訊,同時也把昨天暫時放在 ConfigMap 的 DATABASE_URL(含密碼)移出去,改用 Secret 儲存。


為什麼不能用 ConfigMap 放敏感資料

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 是什麼

Secret 跟 ConfigMap 很像,也是 key-value 的儲存,差別在於:

  • 資料以 base64 編碼儲存(不是加密,只是編碼)
  • 可以透過 RBAC 更精細地控制誰能讀取
  • K8s 在傳輸和儲存時可以額外加密(需要額外設定)
  • Pod 讀取 Secret 時,K8s 會自動解碼還原成原始值

Secret 有幾種類型,最常用的是 Opaque,也就是通用的 key-value 儲存。


建立 Secret 的兩種方式

方式一:用 kubectl 指令建立(推薦)

kubectl create secret generic todo-api-secret \
  --from-literal=DATABASE_URL=mysql+pymysql://user:password@mysql-service:3306/tododb

用指令建立的好處是密碼不會出現在 YAML 檔案裡,不會不小心被 commit 進 git。

方式二:用 YAML 建立

如果要用 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,但任何人都能解碼回原始值,等同於明文洩漏。(生產環境怎麼安全地處理這個問題,可參考下面的延伸筆記)


把 Secret 注入 Pod

跟 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 確認敏感資料有正確以原始值注入。

Step1: 建立 Secret

kubectl create secret generic todo-api-secret \
  --from-literal=DATABASE_URL=mysql+pymysql://user:password@mysql-service:3306/tododb
  1. 確認建立成功:
kubectl get secrets

https://ithelp.ithome.com.tw/upload/images/20260921/20183863uyF7P0JBul.png

  1. 看 Secret 的內容(value 是 base64 編碼):
kubectl get secret todo-api-secret -o yaml

https://ithelp.ithome.com.tw/upload/images/20260921/20183863qIzZ2RiLbL.png

  1. 解碼確認:
echo "bXlzcWwrcHlteXNxbDovL3VzZXI6cGFzc3dvcmRAbXlzcWwtc2VydmljZTozMzA2L3RvZG9kYg==" | base64 --decode

https://ithelp.ithome.com.tw/upload/images/20260921/20183863uR5VLSVmbX.png

Step2: 更新 todo-api-deployment.yaml 使用 Secret

apiVersion: 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

Step3: 確認環境變數有正確注入

kubectl exec -it <pod-name> -- env | grep DATABASE_URL

Secret 注入進來的值是已解碼的原始值,不是 base64:

https://ithelp.ithome.com.tw/upload/images/20260921/20183863QXPbFvAfHn.png


延伸筆記:Secret 其實沒有你想的那麼安全

很多人以為 Secret 是加密的,但其實不是。

  • base64 是編碼,不是加密。 任何人都能解碼回原始值,沒有任何保護效果。

  • etcd 預設是明文儲存 Secret 的。 etcd 是整個 Cluster 的狀態資料庫,如果有人能直接讀取 etcd,所有 Secret 都能看到。生產環境應該開啟 etcd 的加密功能(Encryption at Rest)。

  • YAML 不能 commit 進 git。 如果用 YAML 建立 Secret,這份 YAML 本身就是安全風險,不能進版本控制。

生產環境的常見解法:

  • Sealed Secrets:把 Secret 加密成 SealedSecret,加密後的 YAML 才能安全地 commit 進 git,只有 Cluster 裡的 controller 能解密
  • HashiCorp Vault:獨立的 Secret 管理服務,K8s 在需要的時候動態去 Vault 拿 Secret,不把 Secret 存在 etcd 裡
  • 雲端 KMS:AWS Secrets Manager、GCP Secret Manager,讓雲端平台管理 Secret 的生命週期

小結

今天學了 Secret,處理不能明文儲存的敏感資料:

  • Secret 以 base64 編碼儲存,提供基本的存取控制隔離
  • 建立方式:優先用 kubectl create secret 指令,避免密碼出現在 YAML 檔案裡
  • 注入方式與 ConfigMap 相同:envFrom、valueFrom、掛載成檔案
  • ConfigMap 放一般設定、Secret 放敏感資料,DATABASE_URL 這類含密碼的設定要放 Secret

明天會學 StatefulSet,說明為什麼資料庫這類有狀態的服務不能用 Deployment 跑,以及 K8s 怎麼處理這個問題 !


上一篇
Day 10|ConfigMap
下一篇
Day 12|StatefulSet
系列文
從零學 K8s|30 天核心概念 × 實作,新手也能真正掌握 Kubernetes 共 15 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言