iT邦幫忙

2025 iThome 鐵人賽

DAY 9
0
DevOps

30 天挑戰 CKAD 認證!菜鳥的 Kubernetes 學習日記系列 第 9

【Day09】配置檔案不寫死!ConfigMap 與 Secret 管理應用程式配置

  • 分享至 

  • xImage
  •  

gh

前情提要

昨天我們看到如何用 ResourceQuotaLimitRange 來管理 Kubernetes Cluster 的資源配額。透過 ResourceQuota 控制整個 Namespace 的總資源使用量,用 LimitRange 限制單一容器的資源上下限,並為沒有設定資源的 Pod 自動分配預設值。我們也實際測試了當超過配額限制時會發生什麼,看到 Kubernetes 如何嚴格執行這些限制。

接下來我們看到如果把這些資訊都寫死在設定檔裡,不僅不安全,也很難維護。每次改個設定就要重新打包映像檔,實在太麻煩了。想想看這些實際情境:

  • 資料庫連線字串、API Keys 等資料不要 Hard Code 寫死在程式碼裡
  • 不同環境(開發、測試、生產)需要不同的設定參數
  • 應用程式的設定檔需要動態調整

所以我們今天要來看看 ConfigMapSecrets

ConfigMap 和 Secret 是什麼?

ConfigMap

ConfigMap 允許將設定檔 (配置文件) 從容器映像中解耦 (decouple),也就是分開管理,從而增強容器應用的可移值性。

將應用程式的程式碼與像是應用程式設定、環境變數等等內容分開。方便我們管理,且可以掛載到不同 Pod 內使用,達到「高內聚、低耦合」的設計目標。

高內聚,低耦合 (high cohesion、low coupling) 的意思是,物件的程式碼應該要有很高的比率只和物件內其他有關的程式碼有關聯,而對外部的程式碼,物件或元件等的關聯度要愈低愈好 (最佳的狀態是零耦合)。

Secrets

Secrets 則是專門用來存放敏感資料的,比如密碼、API Keys、Token 等等。雖然它跟 ConfigMap 很像,但 Secrets 會用 base64 編碼來存放資料。Secrets 也支援當作環境變數注入到 Pod,也可以當作檔案掛載到指定的目錄。

  • 超級重要 ‼️
    • Secrets 只是用 base64 編碼,不是真正的加密,任何人都可以輕易解碼。如果真的要用 Secret 儲存敏感資訊,不要把 Secrets 的 YAML 檔案推到 GitHub 等公開儲存庫。並且同個 Namespace 裡能建立 Pod 的人都能存取到 Secrets。
  • 最佳實踐 👍
    • 考慮啟用 ETCD 的靜態加密,並且使用 RBAC 嚴格限制誰能存取 Secret。
    • 考慮使用第三方 Secrets 管理工具(如 AWS、GCP Secrets Manager、HashiCorp Vault)

實戰 🔥

ConfigMap

首先看 ConfigMap,我們可以用命令式指令 --from-literal 建立:

kubectl create configmap mydb-env-literal --from-literal=ACCOUNT=ITHOME2025 --from-literal=PASSWORD=K8sDevOps --from-literal=TZ="Asia/Taipei"

gh

接下來將變數儲存到檔案中,用命令式從檔案匯入。有兩種方式:--from-file--from-env-file

ACCOUNT=ITHOME2025
PASSWORD=K8sDevOps
TZ="Asia/Taipei"

使用 --from-env-file 建立:

kubectl create configmap mydb-env --from-env-file=mydb_env

可以看到會以 Key-Value 的方式存進 ConfigMap 物件,與用 --from-literal 的結果一樣:

gh

使用 --from-file 建立:

kubectl create configmap mydb-file --from-file=mydb_env

這種方式會將整個檔案內容當作一個值存進去:

gh

兩種方式差異很大,連 DATA 的數量都不一樣。如果要當作環境變數掛載到 Pod,需要用 Key-Value 的方式才能正確掛載:

gh

接下來將 ConfigMap 掛載到 Deployment 裡。為了因應 CKAD 考試情境,我們先用 dry run 建立 template:

kubectl create deployment nginx --image nginx --dry-run=client -o yaml > nginx-dep.yaml

這樣可以在不建立任何物件的情況下,拿到結構相對完整的 YAML 檔:

gh

修改 YAML 檔,將 ConfigMap 掛載為環境變數:

apiVersion: apps/v1
kind: Deployment
metadata:
  creationTimestamp: null
  labels:
    app: nginx
  name: nginx
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
  strategy: {}
  template:
    metadata:
      creationTimestamp: null
      labels:
        app: nginx
    spec:
      containers:
      - image: nginx
        name: nginx
        resources: {}
        env: 
        - name: ACCOUNT
          valueFrom:
            configMapKeyRef:
              key: ACCOUNT
              name: mydb-env
        - name: PASSWORD
          valueFrom:
            configMapKeyRef:
              key: PASSWORD
              name: mydb-env
        - name: TZ
          valueFrom:
            configMapKeyRef:
              key: TZ
              name: mydb-env
status: {}

部署後驗證環境變數是否成功掛載:

kubectl exec -it your-pod -- env|grep -E "ACCOUNT|PASSWORD|TZ"

gh

當然也可以用 YAML 聲明式建立 ConfigMap:

apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
data:
  APP_MODE: dev

gh

除了一個個設定環境變數,也可以用 envFrom 直接掛載整個 ConfigMap:

apiVersion: apps/v1
kind: Deployment
metadata:
  creationTimestamp: null
  labels:
    app: nginx
  name: nginx-envfrom
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
  strategy: {}
  template:
    metadata:
      creationTimestamp: null
      labels:
        app: nginx
    spec:
      containers:
      - image: nginx
        name: nginx
        resources: {}
        envFrom:
          - configMapRef:
              name: app-config
status: {}

會跟上面一樣成功掛載到 Deployment 裡面。

gh

Secrets

Secret 的使用方式跟 ConfigMap 幾乎一模一樣,差別只在 kind 變成 Secret。

# from literal
kubectl create secret generic app-secret --from-literal=PASSWORD=password

# mydb_env
PASSWORD=password

# from env file
kubectl create secret app-secret --from-env-file=mydb_env

YAML 檔聲明式:

apiVersion: v1
kind: Secret
metadata:
  name: app-secret
data:
  PASSWORD: password

首先看 Secret 支援的三種類型:

kubectl create secret --help

可以看到他提過三種主要類型,各自針對不同使用場景:

  • generic(通用型):用來存放一般敏感資料,如密碼、API Keys
  • tls(TLS 憑證型):專門存放 TLS/SSL 憑證和私鑰
  • docker-registry(Docker Registry 認證型):存放私有 Registry 登入憑證

gh

我們使用 generic 類型建立:

kubectl create secret generic app-secret --from-literal=PASSWORD=password

建立完成後,可以看到 Secret 的內容:

kubectl get secrets app-secret -o yaml

取得 yaml 檔內容之後,然後把我們要的資料拿出來做 base64 的解碼就可以看到原始資料的樣子了。

gh

甚至我們只用一行指令就可以直接解碼:

kubectl get secrets app-secret -o yaml|grep PASSWORD|awk '{print $2}' | base64 --decode

這告訴我們 Kubernetes 原生的 Secrets 真的不適合拿來存放機敏資料!

總結

今天我們學會了 ConfigMap 和 Secret 如何幫助我們管理應用程式的配置資料。透過 ConfigMap,我們可以將應用程式的運行參數從程式碼中抽離出來,讓程式更靈活地在不同環境中重複使用。而 Secret 提供了集中管理敏感資訊的方式,雖然它本質上只是 base64 編碼,但搭配適當的權限控制和安全措施,仍然比寫死在程式碼裡安全得多。

不過我們也看到了 Kubernetes 原生 Secret 的限制,在實際的生產環境中,還是搭配 RBAC 權限管控、etcd 靜態加密,或者使用第三方 Secret 管理工具來增強安全性。

到目前為止,我們已經學會了如何建立和管理 Pod、控制副本數量、分配資源、管理配置資料等等。但還有一個重要問題:這些 Pod 要如何對外提供服務?其他 Pod 或外部使用者要怎麼存取這些應用程式?

明天我們要看看 Service,看看 Kubernetes 如何讓我們的應用程式真正能被其他人使用!

下一篇文章:服務要曝光:Service 入門實戰


上一篇
【Day08】叢集資源怎麼分?ResourceQuota 和 LimitRange 配額管理實戰
系列文
30 天挑戰 CKAD 認證!菜鳥的 Kubernetes 學習日記9
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言