前言 應用程式除了程式碼本身,還需要配置資訊(如資料庫連線設定)、敏感資訊(如密碼)以及資料持久化。Kubernetes 提供了完整的解決方案:
ConfigMap :管理非敏感配置
Secret :管理敏感資訊
Volume :提供資料儲存
本篇將深入探討這些資源的使用方式和最佳實踐。
ConfigMap:配置管理 為什麼需要 ConfigMap? 在容器化之前,我們可能這樣管理配置:
配置寫在程式碼裡(❌ 難以修改)
使用環境變數(✅ 但不易管理)
使用配置檔案(✅ 但需要掛載)
ConfigMap 統一解決這些問題 ,將配置與容器映像檔分離。
graph LR
CM[ConfigMap] -->|環境變數| POD1[Pod]
CM -->|掛載檔案| POD2[Pod]
CM -->|命令列參數| POD3[Pod]
創建 ConfigMap 方法一:從文字值創建 1 2 3 4 kubectl create configmap app-config \ --from-literal=DATABASE_HOST=db.example.com \ --from-literal=DATABASE_PORT=5432 \ --from-literal=LOG_LEVEL=info
方法二:從檔案創建 1 2 3 4 5 6 7 8 kubectl create configmap nginx-config --from-file=nginx.conf kubectl create configmap all-configs --from-file=./config-dir/ kubectl create configmap app-config --from-file=settings=config.yaml
方法三:使用 YAML 定義 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 apiVersion: v1 kind: ConfigMap metadata: name: app-config data: DATABASE_HOST: "db.example.com" DATABASE_PORT: "5432" LOG_LEVEL: "info" app.properties: | server.port=8080 spring.profiles.active=production logging.level.root=INFO nginx.conf: | server { listen 80; server_name localhost; location / { root /usr/share/nginx/html; } }
使用 ConfigMap 方式一:作為環境變數 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 apiVersion: v1 kind: Pod metadata: name: app-pod spec: containers: - name: app image: my-app:latest env: - name: DB_HOST valueFrom: configMapKeyRef: name: app-config key: DATABASE_HOST envFrom: - configMapRef: name: app-config
方式二:作為檔案掛載 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 apiVersion: v1 kind: Pod metadata: name: nginx-pod spec: containers: - name: nginx image: nginx:alpine volumeMounts: - name: config-volume mountPath: /etc/nginx/conf.d readOnly: true volumes: - name: config-volume configMap: name: nginx-config items: - key: nginx.conf path: default.conf
方式三:作為命令列參數 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 apiVersion: v1 kind: Pod metadata: name: app-pod spec: containers: - name: app image: my-app:latest command: ["./app" ] args: ["--log-level=$(LOG_LEVEL)" , "--port=$(PORT)" ] env: - name: LOG_LEVEL valueFrom: configMapKeyRef: name: app-config key: LOG_LEVEL - name: PORT valueFrom: configMapKeyRef: name: app-config key: PORT
ConfigMap 熱更新 環境變數 :Pod 必須重啟才能讀取新值掛載檔案 :自動更新(約 1 分鐘內),但應用程式需要自己偵測變化
1 2 3 4 5 volumeMounts: - name: config mountPath: /app/config.yaml subPath: config.yaml
Secret:敏感資訊管理 ConfigMap vs Secret
特性
ConfigMap
Secret
用途
非敏感配置
敏感資訊
儲存方式
明文
Base64 編碼
記憶體存放
否
是(tmpfs)
大小限制
1MiB
1MiB
注意 :Secret 的 Base64 不是加密,只是編碼。生產環境應啟用 etcd 加密。
Secret 類型
類型
用途
Opaque
預設,任意用戶定義資料
kubernetes.io/basic-auth
基本認證
kubernetes.io/ssh-auth
SSH 私鑰
kubernetes.io/tls
TLS 憑證
kubernetes.io/dockerconfigjson
Docker Registry 認證
創建 Secret 方法一:命令列創建 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 kubectl create secret generic db-secret \ --from-literal=username=admin \ --from-literal=password=supersecret kubectl create secret generic tls-secret \ --from-file=tls.crt=./cert.pem \ --from-file=tls.key=./key.pem kubectl create secret docker-registry regcred \ --docker-server=https://index.docker.io/v1/ \ --docker-username=myuser \ --docker-password=mypassword \ [email protected] kubectl create secret tls my-tls \ --cert=path/to/cert.pem \ --key=path/to/key.pem
方法二:YAML 定義 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 apiVersion: v1 kind: Secret metadata: name: db-secret type: Opaque data: username: YWRtaW4= password: c3VwZXJzZWNyZXQ= --- apiVersion: v1 kind: Secret metadata: name: db-secret-v2 type: Opaque stringData: username: admin password: supersecret
1 2 3 echo -n "admin" | base64 echo "YWRtaW4=" | base64 -d
使用 Secret 與 ConfigMap 類似,可以作為環境變數或檔案掛載:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 apiVersion: v1 kind: Pod metadata: name: app-pod spec: containers: - name: app image: my-app:latest env: - name: DB_USERNAME valueFrom: secretKeyRef: name: db-secret key: username - name: DB_PASSWORD valueFrom: secretKeyRef: name: db-secret key: password volumeMounts: - name: secret-volume mountPath: /etc/secrets readOnly: true volumes: - name: secret-volume secret: secretName: db-secret defaultMode: 0400
拉取私有映像檔 1 2 3 4 5 6 7 8 9 10 apiVersion: v1 kind: Pod metadata: name: private-app spec: containers: - name: app image: myregistry.io/private-app:latest imagePullSecrets: - name: regcred
Secret 最佳實踐
不要將 Secret 提交到 Git
使用外部密鑰管理 :AWS Secrets Manager、HashiCorp Vault
啟用 etcd 加密 :在叢集層級加密儲存
限制存取權限 :使用 RBAC 控制誰可以讀取 Secret
定期輪換 :定期更換密碼和憑證
Volume:資料存儲 為什麼需要 Volume? 容器的檔案系統是臨時的 :
容器重啟後,檔案會消失
同一 Pod 內的容器需要共享檔案
資料庫等應用需要持久化資料
Volume 類型概覽 graph TD
V[Volume 類型] --> T[臨時儲存]
V --> P[持久儲存]
V --> S[特殊用途]
T --> E[emptyDir]
T --> CM[configMap]
T --> SEC[secret]
P --> HP[hostPath]
P --> PVC[persistentVolumeClaim]
P --> NFS[nfs]
P --> CLOUD[雲端儲存]
S --> DOWN[downwardAPI]
S --> PROJ[projected]
emptyDir:臨時共享儲存 Pod 內容器間共享資料,Pod 刪除後資料消失。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 apiVersion: v1 kind: Pod metadata: name: shared-data-pod spec: containers: - name: writer image: busybox command: ["/bin/sh" , "-c" ] args: ["echo 'Hello' > /data/message.txt && sleep 3600" ] volumeMounts: - name: shared-data mountPath: /data - name: reader image: busybox command: ["/bin/sh" , "-c" ] args: ["cat /data/message.txt && sleep 3600" ] volumeMounts: - name: shared-data mountPath: /data volumes: - name: shared-data emptyDir: {}
hostPath:掛載主機目錄 將節點上的檔案或目錄掛載到 Pod。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 apiVersion: v1 kind: Pod metadata: name: hostpath-pod spec: containers: - name: app image: my-app:latest volumeMounts: - name: host-logs mountPath: /var/log/myapp volumes: - name: host-logs hostPath: path: /var/log/pods type: DirectoryOrCreate
hostPath type
說明
""
不檢查
DirectoryOrCreate
目錄不存在則創建
Directory
目錄必須存在
FileOrCreate
檔案不存在則創建
File
檔案必須存在
⚠️ 安全警告 :hostPath 可存取節點檔案系統,生產環境應謹慎使用。
持久化儲存:PV 與 PVC 概念說明 graph LR
ADMIN[管理員] -->|創建| PV[PersistentVolume<br/>實際儲存]
DEV[開發者] -->|創建| PVC[PersistentVolumeClaim<br/>資源請求]
PVC -->|綁定| PV
POD[Pod] -->|使用| PVC
PersistentVolume(PV) :叢集中的一塊儲存,由管理員創建
PersistentVolumeClaim(PVC) :對儲存的請求,由開發者創建
StorageClass :動態供應儲存的模板
PersistentVolume 範例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 apiVersion: v1 kind: PersistentVolume metadata: name: pv-storage spec: capacity: storage: 10Gi accessModes: - ReadWriteOnce persistentVolumeReclaimPolicy: Retain storageClassName: manual hostPath: path: /mnt/data
存取模式
模式
縮寫
說明
ReadWriteOnce
RWO
單一節點讀寫
ReadOnlyMany
ROX
多節點唯讀
ReadWriteMany
RWX
多節點讀寫
ReadWriteOncePod
RWOP
單一 Pod 讀寫
回收策略
策略
說明
Retain
PVC 刪除後保留 PV,需手動處理
Delete
PVC 刪除後自動刪除 PV
Recycle
清除資料後重新可用(已棄用)
PersistentVolumeClaim 範例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 apiVersion: v1 kind: PersistentVolumeClaim metadata: name: pvc-storage spec: accessModes: - ReadWriteOnce resources: requests: storage: 5Gi storageClassName: manual
在 Pod 中使用 PVC 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 apiVersion: v1 kind: Pod metadata: name: pvc-pod spec: containers: - name: app image: my-app:latest volumeMounts: - name: storage mountPath: /data volumes: - name: storage persistentVolumeClaim: claimName: pvc-storage
StorageClass:動態供應 不用預先創建 PV,由 StorageClass 自動創建。
1 2 3 4 5 6 7 8 9 10 11 12 apiVersion: storage.k8s.io/v1 kind: StorageClass metadata: name: fast-storage provisioner: kubernetes.io/aws-ebs parameters: type: gp3 iopsPerGB: "10" fsType: ext4 reclaimPolicy: Delete allowVolumeExpansion: true volumeBindingMode: WaitForFirstConsumer
1 2 3 4 5 6 7 8 9 10 11 12 apiVersion: v1 kind: PersistentVolumeClaim metadata: name: dynamic-pvc spec: accessModes: - ReadWriteOnce resources: requests: storage: 20Gi storageClassName: fast-storage
常見雲端 StorageClass
雲端
Provisioner
說明
AWS
ebs.csi.aws.com
EBS 儲存
GCP
pd.csi.storage.gke.io
Persistent Disk
Azure
disk.csi.azure.com
Azure Disk
實戰範例:有狀態應用部署 部署 PostgreSQL 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 apiVersion: storage.k8s.io/v1 kind: StorageClass metadata: name: local-storage provisioner: kubernetes.io/no-provisioner volumeBindingMode: WaitForFirstConsumer --- apiVersion: v1 kind: PersistentVolume metadata: name: postgres-pv spec: capacity: storage: 10Gi accessModes: - ReadWriteOnce persistentVolumeReclaimPolicy: Retain storageClassName: local-storage hostPath: path: /data/postgres --- apiVersion: v1 kind: PersistentVolumeClaim metadata: name: postgres-pvc spec: accessModes: - ReadWriteOnce resources: requests: storage: 10Gi storageClassName: local-storage --- apiVersion: v1 kind: Secret metadata: name: postgres-secret type: Opaque stringData: POSTGRES_PASSWORD: supersecretpassword --- apiVersion: v1 kind: ConfigMap metadata: name: postgres-initdb data: init.sql: | CREATE TABLE IF NOT EXISTS users ( id SERIAL PRIMARY KEY, name VARCHAR(100) NOT NULL, email VARCHAR(100) UNIQUE NOT NULL ); --- apiVersion: apps/v1 kind: Deployment metadata: name: postgres spec: replicas: 1 selector: matchLabels: app: postgres template: metadata: labels: app: postgres spec: containers: - name: postgres image: postgres:15-alpine ports: - containerPort: 5432 env: - name: POSTGRES_DB value: myapp - name: POSTGRES_USER value: postgres - name: POSTGRES_PASSWORD valueFrom: secretKeyRef: name: postgres-secret key: POSTGRES_PASSWORD volumeMounts: - name: postgres-data mountPath: /var/lib/postgresql/data - name: initdb mountPath: /docker-entrypoint-initdb.d resources: requests: cpu: "250m" memory: "256Mi" limits: cpu: "500m" memory: "512Mi" readinessProbe: exec: command: ["pg_isready" , "-U" , "postgres" ] initialDelaySeconds: 5 periodSeconds: 10 volumes: - name: postgres-data persistentVolumeClaim: claimName: postgres-pvc - name: initdb configMap: name: postgres-initdb --- apiVersion: v1 kind: Service metadata: name: postgres spec: selector: app: postgres ports: - port: 5432
本章重點回顧 ConfigMap
用途 :管理非敏感的配置資訊
使用方式 :環境變數、檔案掛載、命令列參數
熱更新 :檔案掛載可自動更新(但需應用程式配合)
Secret
用途 :管理敏感資訊(密碼、憑證)
編碼 :使用 Base64,不是加密
最佳實踐 :不提交 Git、啟用 etcd 加密、使用外部密鑰管理
Volume
臨時儲存 :emptyDir(Pod 內共享)
主機儲存 :hostPath(掛載節點目錄)
持久儲存 :PV + PVC + StorageClass
PV/PVC 流程 graph LR
A[創建 StorageClass] --> B[創建 PVC]
B --> C[動態創建 PV]
C --> D[PVC 綁定 PV]
D --> E[Pod 使用 PVC]
下一篇預告 在下一篇文章中,我們將探討 資源管理與自動擴展 :
資源請求與限制(Requests/Limits)
Horizontal Pod Autoscaler (HPA)
Pod 調度策略
探針機制(Liveness/Readiness/Startup)
系列文章導覽
Part 1:入門篇 - 認識 K8s 與核心架構
Part 2:基礎篇 - Pod、Deployment 與 Service
Part 3:網路篇 - 服務發現與流量管理
Part 4:配置與存儲篇 - ConfigMap、Secret 與 Volume (本篇)
Part 5:進階篇 - 資源管理與自動擴展
Part 6:實戰篇 - 部署完整微服務應用
參考資源