Kubernetes ConfigMap 與 Secret 管理

前言

在 Kubernetes 環境中,應用程式的配置管理是一個重要的課題。我們經常需要將配置資訊與容器映像檔分離,以實現更靈活的部署和管理。Kubernetes 提供了 ConfigMap 和 Secret 兩種資源來處理不同類型的配置資料。本文將深入介紹這兩種資源的建立、使用方式以及最佳實務。

ConfigMap 簡介

ConfigMap 是 Kubernetes 中用於儲存非機密配置資料的物件。它可以儲存環境變數、設定檔內容或其他配置資訊,讓我們能夠將配置與容器映像檔解耦。

ConfigMap 的優點

  • 配置與映像檔分離:無需重新建構映像檔即可更改配置
  • 環境區隔:不同環境(開發、測試、生產)可使用不同的 ConfigMap
  • 集中管理:配置資訊集中在 Kubernetes 中管理
  • 版本控制:可透過 GitOps 進行版本控制

ConfigMap 建立與使用

使用 kubectl 建立 ConfigMap

從字面值建立

1
2
3
4
5
6
7
8
# 建立包含單一鍵值對的 ConfigMap
kubectl create configmap app-config --from-literal=APP_ENV=production

# 建立包含多個鍵值對的 ConfigMap
kubectl create configmap app-config \
  --from-literal=APP_ENV=production \
  --from-literal=LOG_LEVEL=info \
  --from-literal=MAX_CONNECTIONS=100

從檔案建立

1
2
3
4
5
6
7
8
# 從單一檔案建立 ConfigMap
kubectl create configmap nginx-config --from-file=nginx.conf

# 從目錄建立 ConfigMap(目錄中所有檔案都會被包含)
kubectl create configmap config-files --from-file=./config/

# 指定鍵名從檔案建立
kubectl create configmap app-config --from-file=app.properties=./application.properties

使用 YAML 定義 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
apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
  namespace: default
data:
  # 簡單的鍵值對
  APP_ENV: "production"
  LOG_LEVEL: "info"
  MAX_CONNECTIONS: "100"

  # 多行配置檔內容
  app.properties: |
    server.port=8080
    server.host=0.0.0.0
    database.pool.size=10    

  # JSON 格式配置
  config.json: |
    {
      "apiVersion": "v1",
      "features": {
        "enableCache": true,
        "enableMetrics": true
      }
    }    
1
2
3
4
5
# 套用 ConfigMap 定義
kubectl apply -f configmap.yaml

# 查看 ConfigMap
kubectl get configmap app-config -o yaml

Secret 類型與建立

Secret 用於儲存敏感資訊,如密碼、OAuth Token、SSH 金鑰等。與 ConfigMap 不同,Secret 中的資料會以 Base64 編碼儲存,並且 Kubernetes 提供了額外的存取控制機制。

Secret 類型

Kubernetes 支援多種 Secret 類型:

類型用途說明
Opaque通用型 Secret,預設類型
kubernetes.io/service-account-tokenServiceAccount Token
kubernetes.io/dockerconfigjsonDocker Registry 認證資訊
kubernetes.io/basic-auth基本認證憑證
kubernetes.io/ssh-authSSH 認證憑證
kubernetes.io/tlsTLS 憑證

建立 Opaque Secret

使用 kubectl 建立

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# 從字面值建立 Secret
kubectl create secret generic db-credentials \
  --from-literal=username=admin \
  --from-literal=password=secretpassword123

# 從檔案建立 Secret
kubectl create secret generic ssh-key --from-file=id_rsa=./ssh/id_rsa

# 查看 Secret(Base64 編碼)
kubectl get secret db-credentials -o yaml

使用 YAML 定義 Secret

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
apiVersion: v1
kind: Secret
metadata:
  name: db-credentials
  namespace: default
type: Opaque
# 使用 stringData 可直接寫入明文(Kubernetes 會自動編碼)
stringData:
  username: admin
  password: secretpassword123
  connection-string: "postgresql://admin:secretpassword123@db:5432/mydb"

或使用預先編碼的 data 欄位:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
apiVersion: v1
kind: Secret
metadata:
  name: db-credentials
type: Opaque
data:
  # 值需為 Base64 編碼
  # echo -n 'admin' | base64 -> YWRtaW4=
  username: YWRtaW4=
  # echo -n 'secretpassword123' | base64 -> c2VjcmV0cGFzc3dvcmQxMjM=
  password: c2VjcmV0cGFzc3dvcmQxMjM=

建立 TLS Secret

1
2
3
4
# 從憑證檔案建立 TLS Secret
kubectl create secret tls tls-secret \
  --cert=./tls.crt \
  --key=./tls.key
1
2
3
4
5
6
7
8
apiVersion: v1
kind: Secret
metadata:
  name: tls-secret
type: kubernetes.io/tls
data:
  tls.crt: <base64-encoded-cert>
  tls.key: <base64-encoded-key>

建立 Docker Registry Secret

1
2
3
4
5
6
# 建立 Docker Registry 認證 Secret
kubectl create secret docker-registry regcred \
  --docker-server=https://registry.example.com \
  --docker-username=user \
  --docker-password=password \
  --docker-email=user@example.com

掛載方式

ConfigMap 和 Secret 可以透過兩種主要方式提供給 Pod:Volume 掛載和環境變數注入。

Volume 掛載

將 ConfigMap 掛載為 Volume

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
apiVersion: v1
kind: Pod
metadata:
  name: app-pod
spec:
  containers:
  - name: app
    image: myapp:1.0
    volumeMounts:
    - name: config-volume
      mountPath: /etc/config
      readOnly: true
  volumes:
  - name: config-volume
    configMap:
      name: app-config
      # 可選:指定掛載的項目
      items:
      - key: app.properties
        path: application.properties
      - key: config.json
        path: config.json

將 Secret 掛載為 Volume

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
apiVersion: v1
kind: Pod
metadata:
  name: app-pod
spec:
  containers:
  - name: app
    image: myapp:1.0
    volumeMounts:
    - name: secret-volume
      mountPath: /etc/secrets
      readOnly: true
  volumes:
  - name: secret-volume
    secret:
      secretName: db-credentials
      # 設定檔案權限(選填)
      defaultMode: 0400

掛載特定項目到指定路徑

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
apiVersion: v1
kind: Pod
metadata:
  name: nginx-pod
spec:
  containers:
  - name: nginx
    image: nginx:1.24
    volumeMounts:
    - name: nginx-config
      mountPath: /etc/nginx/nginx.conf
      subPath: nginx.conf
    - name: tls-certs
      mountPath: /etc/nginx/ssl
      readOnly: true
  volumes:
  - name: nginx-config
    configMap:
      name: nginx-config
  - name: tls-certs
    secret:
      secretName: tls-secret

環境變數注入

從 ConfigMap 注入環境變數

注入單一鍵值

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
apiVersion: v1
kind: Pod
metadata:
  name: app-pod
spec:
  containers:
  - name: app
    image: myapp:1.0
    env:
    - name: APP_ENVIRONMENT
      valueFrom:
        configMapKeyRef:
          name: app-config
          key: APP_ENV
    - name: LOG_LEVEL
      valueFrom:
        configMapKeyRef:
          name: app-config
          key: LOG_LEVEL

注入整個 ConfigMap

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
apiVersion: v1
kind: Pod
metadata:
  name: app-pod
spec:
  containers:
  - name: app
    image: myapp:1.0
    envFrom:
    - configMapRef:
        name: app-config
      # 可選:為所有鍵加上前綴
      prefix: CONFIG_

從 Secret 注入環境變數

注入單一鍵值

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
apiVersion: v1
kind: Pod
metadata:
  name: app-pod
spec:
  containers:
  - name: app
    image: myapp:1.0
    env:
    - name: DB_USERNAME
      valueFrom:
        secretKeyRef:
          name: db-credentials
          key: username
    - name: DB_PASSWORD
      valueFrom:
        secretKeyRef:
          name: db-credentials
          key: password

注入整個 Secret

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
apiVersion: v1
kind: Pod
metadata:
  name: app-pod
spec:
  containers:
  - name: app
    image: myapp:1.0
    envFrom:
    - secretRef:
        name: db-credentials

完整範例:Deployment 配置

 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
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-app
spec:
  replicas: 3
  selector:
    matchLabels:
      app: web-app
  template:
    metadata:
      labels:
        app: web-app
    spec:
      containers:
      - name: web-app
        image: myapp:1.0
        ports:
        - containerPort: 8080
        # 環境變數注入
        envFrom:
        - configMapRef:
            name: app-config
        env:
        - name: DB_PASSWORD
          valueFrom:
            secretKeyRef:
              name: db-credentials
              key: password
        # Volume 掛載
        volumeMounts:
        - name: config-files
          mountPath: /app/config
        - name: tls-certs
          mountPath: /app/certs
          readOnly: true
      volumes:
      - name: config-files
        configMap:
          name: app-config
      - name: tls-certs
        secret:
          secretName: tls-secret

最佳實務

1. Secret 安全性管理

啟用 Encryption at Rest

在 API Server 中啟用 Secret 加密:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# /etc/kubernetes/encryption-config.yaml
apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
  - resources:
      - secrets
    providers:
      - aescbc:
          keys:
            - name: key1
              secret: <base64-encoded-32-byte-key>
      - identity: {}

使用 RBAC 控制存取

 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: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: secret-reader
  namespace: production
rules:
- apiGroups: [""]
  resources: ["secrets"]
  resourceNames: ["db-credentials"]
  verbs: ["get"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: read-secrets
  namespace: production
subjects:
- kind: ServiceAccount
  name: app-service-account
  namespace: production
roleRef:
  kind: Role
  name: secret-reader
  apiGroup: rbac.authorization.k8s.io

2. 使用外部 Secret 管理工具

考慮整合外部密鑰管理系統:

  • HashiCorp Vault:企業級密鑰管理
  • AWS Secrets Manager:AWS 原生密鑰服務
  • Azure Key Vault:Azure 原生密鑰服務
  • External Secrets Operator:同步外部密鑰到 Kubernetes
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
# External Secrets Operator 範例
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: db-credentials
spec:
  refreshInterval: 1h
  secretStoreRef:
    name: vault-backend
    kind: SecretStore
  target:
    name: db-credentials
  data:
  - secretKey: username
    remoteRef:
      key: database/credentials
      property: username
  - secretKey: password
    remoteRef:
      key: database/credentials
      property: password

3. ConfigMap 和 Secret 更新策略

使用 Immutable ConfigMap/Secret

1
2
3
4
5
6
7
apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config-v1
data:
  APP_ENV: production
immutable: true

不可變的 ConfigMap/Secret 優點:

  • 防止意外修改
  • 提升叢集效能(kubelet 不需要持續監控變更)
  • 明確的版本控制

觸發 Pod 重新部署

ConfigMap/Secret 更新時,需要重啟 Pod 才能載入新配置。可透過以下方式:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# 在 Deployment 中加入 annotation
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-app
spec:
  template:
    metadata:
      annotations:
        # 更新此值以觸發重新部署
        configmap-version: "v2"

或使用工具如 Reloader 自動監控變更並重啟 Pod。

4. 命名與組織規範

1
2
3
4
5
6
7
# 建議的命名規範
<application>-<type>-<environment>

# 範例
webapp-config-prod
webapp-secrets-prod
nginx-config-staging

5. 避免在 Git 中儲存明文 Secret

  • 使用 Sealed Secrets 加密後再提交
  • 使用 SOPS 加密 YAML 檔案
  • 使用 GitOps 工具的 Secret 管理功能
1
2
# 使用 kubeseal 建立 Sealed Secret
kubeseal --format yaml < secret.yaml > sealed-secret.yaml

6. 定期輪換 Secret

建立 Secret 輪換機制:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# 建立新版本的 Secret
kubectl create secret generic db-credentials-v2 \
  --from-literal=username=admin \
  --from-literal=password=newpassword456

# 更新 Deployment 使用新 Secret
kubectl set env deployment/web-app --from=secret/db-credentials-v2

# 確認應用正常後,刪除舊 Secret
kubectl delete secret db-credentials-v1

常見問題排解

查看 ConfigMap/Secret 內容

1
2
3
4
5
6
7
8
9
# 查看 ConfigMap
kubectl describe configmap app-config
kubectl get configmap app-config -o yaml

# 查看 Secret(Base64 編碼)
kubectl get secret db-credentials -o yaml

# 解碼 Secret 值
kubectl get secret db-credentials -o jsonpath='{.data.password}' | base64 -d

驗證 Pod 中的配置

1
2
3
4
5
6
# 檢查環境變數
kubectl exec -it <pod-name> -- env | grep APP

# 檢查掛載的檔案
kubectl exec -it <pod-name> -- ls -la /etc/config
kubectl exec -it <pod-name> -- cat /etc/config/app.properties

總結

本文介紹了 Kubernetes ConfigMap 與 Secret 的管理方式:

  1. ConfigMap 建立:從字面值、檔案或 YAML 定義建立配置
  2. Secret 類型:Opaque、TLS、Docker Registry 等不同類型的 Secret
  3. 掛載方式:Volume 掛載和環境變數注入兩種主要方式
  4. 最佳實務:安全性管理、更新策略、命名規範等建議

正確管理 ConfigMap 和 Secret 是維護 Kubernetes 應用程式的重要技能。建議根據應用需求選擇適合的配置方式,並遵循安全最佳實務來保護敏感資訊。

參考資源

comments powered by Disqus
Built with Hugo
Theme Stack designed by Jimmy