為什麼需要 ConfigMap#

實務開發中,最常見的多環境部署莫過於開發(Development)與正式(Production)兩個環境。決定環境差異的,往往是這些設定:

  • 連線的資料庫位址與帳密。
  • API Token、API Key。
  • 資料初始化腳本與其他組態。

把這些設定從程式碼中抽離,可以降低耦合:同一份程式碼只要切換設定檔,就能切換環境。Kubernetes 提供 ConfigMap 讓這件事可以從叢集最頂層方便地向下注入。

ConfigMap 的特性#

  • 一個 ConfigMap 物件可以存放一個或多個設定檔。
  • 切換環境時不必動程式碼,只要切換對應的 ConfigMap。
  • 所有設定統一存放於 Kubernetes 中,便於查看與管理。

建立 ConfigMap 的幾種方式#

1. 用 kubectl 從檔案匯入#

先準備一個 SQL 初始化腳本:

-- initdb.sql
DROP TABLE IF EXISTS posts CASCADE;

CREATE TABLE posts
(
    id             BIGSERIAL PRIMARY KEY,
    uuid           VARCHAR(36)  NOT NULL UNIQUE,
    user_id        NUMERIC      NOT NULL,
    title          VARCHAR(255) NOT NULL,
    content        TEXT         NOT NULL,
    comments_count NUMERIC               DEFAULT 0,
    created_at     TIMESTAMP    NOT NULL DEFAULT NOW(),
    updated_at     TIMESTAMP    NOT NULL DEFAULT NOW(),
    deleted_at     TIMESTAMP    NULL
);

把整個檔案做成 ConfigMap:

kubectl create configmap pg-initdb --from-file=initdb.sql

kubectl createkubectl apply 的差異:前者明確告訴 Kubernetes 要建立一個資源;後者通常搭配 yaml 設定檔,描述「資源最終應該長什麼樣」。

確認結果:

kubectl describe configmap pg-initdb

2. 用指令直接建立 key-value#

kubectl create configmap pg-connect \
  --from-literal=host=127.0.0.1 \
  --from-literal=port=5432

可以一次塞多組 key-value,使用 kubectl describe 即可看到結果:

Data
====
host:
----
127.0.0.1
port:
----
5432

3. 用 yaml 把整個檔案內容寫進來#

# initdb-configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: initdb-yaml
  labels:
    app: db
data:
  initdb.sql: |
    DROP TABLE IF EXISTS posts CASCADE;
    CREATE TABLE posts (
        id          BIGSERIAL PRIMARY KEY,
        uuid        VARCHAR(36) NOT NULL UNIQUE,
        -- ... 其餘欄位省略,與前面 initdb.sql 相同
    );
kubectl apply -f initdb-configmap.yaml

4. 用 yaml 建立 key-value#

# initdb-kv.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: initdb-kv-yaml
  labels:
    app: db
data:
  PG_USER: postgres
  PG_PASSWORD: postgres

在 Pod 中使用 ConfigMap#

下面範例同時示範兩種典型的注入方式:

  • valueFrom.configMapKeyRef 把某個 key 注入為環境變數。
  • volumes.configMap 把整個 ConfigMap 當作檔案掛載到容器內。
# pg-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: db
  labels:
    app: db
spec:
  containers:
    - name: db
      image: postgres:12.4-alpine
      env:
        # 使用 configmap 的 key-value 做為值傳入
        - name: POSTGRES_USER
          valueFrom:
            configMapKeyRef:
              name: initdb-kv-yaml
              key: PG_USER
        - name: POSTGRES_PASSWORD
          valueFrom:
            configMapKeyRef:
              name: initdb-kv-yaml
              key: PG_PASSWORD
        - name: PGDATA
          value: "/var/lib/postgresql/data/pgdata"
        - name: POSTGRES_DB
          value: "posts"
      ports:
        - containerPort: 5432
      volumeMounts:
        # 使用 configmap 做為 file 當作初始化設定
        - mountPath: /docker-entrypoint-initdb.d
          name: initdb
  volumes:
    - name: initdb
      configMap:
        name: initdb

部署:

kubectl apply -f pg-pod.yaml

驗證注入結果#

進入容器:

kubectl exec -it db -- sh

確認 SQL 檔案是否成功掛載:

cat docker-entrypoint-initdb.d/initdb.sql

確認帳號被當作環境變數帶入並建立:

psql -U PG_USER -d posts

進入 psql 後檢查表格是否正確建立:

posts=# \d posts

可以看到 posts 表的所有欄位都依照 initdb.sql 建立完成。

小結#

本章示範了 ConfigMap 最常見的兩種用法:

  • 以「環境變數」的方式注入個別 key-value。
  • 以「檔案掛載」的方式把整份設定提供給容器使用。

在多數應用場景中,這兩種方法已經足以支撐多環境的設定切換。官方還有更進階的功能(例如 SubPath、optional、自動更新等),可以在熟悉基本用法後再深入研究。

原文出處#

  • GitHub:https://github.com/MikeHsu0618/2022-ithelp/tree/main/Day18
  • iThome:https://ithelp.ithome.com.tw/articles/10293127