# ConfigMap & Secret 介紹 前面部署 App 時有用到 Secret 來儲存資料庫的敏感資料,例如登入帳號、密碼等等。ConfigMap 也是類似的用途,可以拿來存放設定檔。今天就來介紹這兩種 object。 ## [ConfigMap](https://kubernetes.io/docs/concepts/configuration/configmap/) ConfigMap 以 key-value 形式儲存非機密資料。Pod 可以將 ConfigMap 作為**環境變數**、**命令列參數**或 **volume 中的設定檔**使用。 ConfigMap 可讓您將特定於環境的配置與 container images 解耦,以便您的應用程式易於攜帶。 :::warning :warning: **警告:** ConfigMap 不提供保密或加密。如果您要儲存的資料是機密的,請使用 Secret 而不是 ConfigMap,或使用其他(第三方)工具來保持資料的隱私。 ::: :::success :notebook: **Note:** ConfigMap 並不是為了儲存大量資料而設計的。ConfigMap 中儲存的資料不能超過 1 MiB。如果您需要儲存大於此限制的設置,您可能需要考慮掛載 volume 或使用另外的資料庫或檔案服務。 ::: ConfigMap 具有 **`data`** 和 **`binaryData`** 字段。這些字段接受 key-value 形式作為其值。**`data`** 和 **`binaryData`** 字段都是可選的。**`data`** 字段設計為包含 UTF-8 字串,而 **`binaryData`** 字段設計為包含作為 base64 編碼字串的二進位資料。 從 v1.19 開始,您可以為 ConfigMap 定義新增 **`immutable`** 字段來建立 [**immutable ConfigMap**](https://kubernetes.io/docs/concepts/configuration/configmap/#configmap-immutable)。 ### 與 Pod 的使用方式 您可以編寫一個 Pod **`spec`**,引用一個 ConfigMap 並根據 ConfigMap 中的資料配置該 Pod 中的 Container。Pod 和 ConfigMap 必須在同一個 Namespace 中。 下面是一個 ConfigMap 範例,其中一些 key 具有單一值,而其他 key 的值看起來像是配置格式的片段。 ```yaml apiVersion: v1 kind: ConfigMap metadata: name: game-demo data: # property-like keys; each key maps to a simple value APP_COLOR: blue APP_MODE: prod # file-like keys game.properties: | enemy.types=aliens,monsters player.maximum-lives=5 user-interface.properties: | color.good=purple color.bad=yellow allow.textmode=true ``` 您可以透過四種不同的方式使用 ConfigMap 在 Pod 內設定 Container: 1. Container 內的 **`command`** 和 **`args`**。 2. Container 的環境變數。 3. 在唯讀 volume 中添加一個檔案,供應用程式讀取。 4. 編寫程式碼在 Pod 內運行,使用 Kubernetes API 讀取 ConfigMap。 對於前三種方法,kubelet 在為 Pod 啟動 Container 時使用 ConfigMap 中的資料。 第四種方法意味著您需要編寫代碼來讀取 ConfigMap 及其數據。然而,由於您直接使用 Kubernetes API,您的應用程序可以在 ConfigMap 變更時獲得更新,並在發生變化時做出反應。通過直接訪問 Kubernetes API,這種技術還允許您訪問不同 Namespace 中的 ConfigMap。 接下來,我們示範一下三種 Pod **`spec`** 引用 ConfigMap 的語法: 1. 利用 **`envFrom`** ```yaml apiVersion: v1 kind: Pod metadata: name: configmap-envfrom-pod spec: containers: - name: configmap-envfrom-pod image: alpine command: ["sleep", "3600"] envFrom: - configMapRef: name: game-demo # 前面先建好的 ConfigMap name ``` 用 **`envFrom`** 的方式可以直接 reference 整個 ConfigMap。用 **`printenv`** 來看看是不是真的設定好了我們定義的 key-value。 ```bash kubectl exec -it configmap-envfrom-pod -- /bin/sh / $ printenv # ... # APP_COLOR=blue # ... / $ echo $APP_COLOR # blue ``` :::warning :warning: **警告:** 但我們發現,如果是使用 file-like key 得到的變數會不如預期,這時需要使用 **`volumes`** 的方式去掛 ConfigMap 才能讀到正確的內容。 以下是不如預期的狀況: ```bash / $ printenv # ... # game.properties=enemy.types=aliens,monsters # ... / $ echo $game.properties # .properties ``` ::: 2. 利用 **`env`** ```yaml apiVersion: v1 kind: Pod metadata: name: configmap-env-pod spec: containers: - name: configmap-env-pod image: alpine command: ["sleep", "3600"] env: - name: APP_COLOR_NEW # 這邊是自行定義,不需要跟 ConfigMap 裡的 key name 一致 valueFrom: configMapKeyRef: name: game-demo # ConfigMap name key: APP_COLOR # 這邊的 key 是 ConfigMap 裡面使用的 key name ``` **`env`** 則是可以分別定義單一個環境變數,注意這邊 **`env.name`** 與 ConfigMap 定義的名字不同。 ```bash kubectl exec -it configmap-env-pod -- /bin/sh / $ printenv # ... # APP_COLOR_NEW=blue # ... / $ echo $APP_COLOR_NEW # blue ``` 3. 利用 **`volumes`** ```yaml apiVersion: v1 kind: Pod metadata: name: configmap-volume-pod spec: containers: - name: configmap-volume-pod image: alpine command: ["sleep", "3600"] volumeMounts: - name: game-demo-volume # volume 的名稱,定義如下 mountPath: "/config" readOnly: true volumes: - name: game-demo-volume configMap: name: game-demo # 前面先建好的 Configmap name ``` 這個方式會將 ConfigMap 定義的 key 轉成一個一個檔案。 ```bash kubectl exec -it configmap-volume-pod -- /bin/sh / $ cd /config /config $ ls # APP_COLOR APP_MODE game.properties user-interface.properties /config $ cat APP_COLOR # blue /config $ cat game.properties # enemy.types=aliens,monsters # player.maximum-lives=5 /config $ cat user-interface.properties # color.good=purple # color.bad=yellow # allow.textmode=true ``` 最後來看 property-like key 和 file-like key 同時使用的設定: ```yaml apiVersion: v1 kind: Pod metadata: name: configmap-demo-pod spec: containers: - name: configmap-demo-pod image: alpine command: ["sleep", "3600"] env: # Define the environment variable - name: APP_COLOR # Notice that the case is different here from the key name in the ConfigMap. valueFrom: configMapKeyRef: name: game-demo # The ConfigMap this value comes from. key: APP_COLOR # The key to fetch. - name: APP_MODE valueFrom: configMapKeyRef: name: game-demo key: APP_MODE volumeMounts: - name: config mountPath: "/config" readOnly: true volumes: # You set volumes at the Pod level, then mount them into containers inside that Pod - name: config configMap: # Provide the name of the ConfigMap you want to mount. name: game-demo # An array of keys from the ConfigMap to create as files items: - key: "game.properties" path: "game.properties" - key: "user-interface.properties" path: "user-interface.properties" ``` 定義一個 volume 並將其掛載到 **`demo`** Container 內的 **`/config`**,即使 ConfigMap 中有四個 key,也會創建兩個檔案,**`/config/game.properties`** 和 **`/config/user-interface.properties`**。這是因為在 **`volumes`** 部分指定了一個 **`items`** 陣列。如果完全省略 **`items`** 陣列,則 ConfigMap 中的每個 key 都會成為與該 key 同名的檔案,並且您將獲得 4 個檔案。 ### 已掛載的 ConfigMap 會自動更新 當前在 volume 中使用的 ConfigMap 更新時,projected keys 也會最終更新。 作為**環境變數**使用的 ConfigMap 不會自動更新,需要重新啟動 Pod。 :::success :notebook: **Note:** 使用 ConfigMap 作為 [subPath](https://kubernetes.io/docs/concepts/storage/volumes#using-subpath) volume 掛載的 Container 將不會接收 ConfigMap 更新。 ::: ### 不可變的 ConfigMaps 您可以將 **`immutable`** 字段設為 **`true`** 來建立不可變的 ConfigMap。 ```yaml apiVersion: v1 kind: ConfigMap metadata: ... data: ... immutable: true ``` 一旦 ConfigMap 被標記為不可變,就**無法**恢復此更改,也無法更改 **`data`** 或 **`binaryData`** 字段的內容。您只能刪除並重新建立 ConfigMap。由於現有 Pod 維護已刪除的 ConfigMap 的掛載點,因此建議重新建立這些 Pod。 ## [Secret](https://kubernetes.io/docs/concepts/configuration/secret/) 建立 Secret 的 yaml 範例如下: ```yaml apiVersion: v1 kind: Secret metadata: name: secret-basic-auth type: kubernetes.io/basic-auth stringData: username: admin # required field for kubernetes.io/basic-auth password: t0p-Secret # required field for kubernetes.io/basic-auth ``` [Secret type 官方文件](https://kubernetes.io/docs/concepts/configuration/secret/#secret-types) 一般使用比較熟悉的是 **`Opaque`**,如果不指定 type 預設也會是這個種類。之前部署時底下內容是用 **`data`** 字段,必須是 base64-encoded 字串。這邊用 **`stringData`** 就會是明文啦。 Pod 使用 Secret 的方式與 ConfigMap 相同,可以用三種方式引用: 1. 利用 **`envFrom`** ```yaml apiVersion: v1 kind: Pod metadata: name: secret-envfrom-pod spec: containers: - name: secret-envfrom-pod image: alpine command: ["sleep", "3600"] envFrom: - secretRef: name: secret-basic-auth ``` 2. 利用 **`env`** ```yaml apiVersion: v1 kind: Pod metadata: name: secret-env-pod spec: containers: - name: secret-env-pod image: alpine command: ["sleep", "3600"] env: - name: DB_PWD valueFrom: secretKeyRef: name: secret-basic-auth key: password ``` 3. 利用 **`volumes`** ```yaml apiVersion: v1 kind: Secret metadata: name: dotfile-secret data: .secret-file: SGVsbG8gV29ybGQ= # Hello World --- apiVersion: v1 kind: Pod metadata: name: secret-dotfiles-pod spec: containers: - name: dotfile-test-container image: alpine command: ["sleep", "3600"] volumeMounts: - name: secret-volume readOnly: true mountPath: "/etc/secret-volume" volumes: - name: secret-volume secret: secretName: dotfile-secret ``` 我們進去到 Container 裡面查看 **`/etc/secret-volume/.secret-file`** 檔案的內容: ```bash kubectl exec -it secret-dotfiles-pod -- /bin/sh / $ cat /etc/secret-volume/.secret-file # Hello World ``` 再執行 **`echo -n "SGVsbG8gV29ybGQ=" | base64 --decode`** 指令,看看結果是不是一樣。 ```bash echo -n "SGVsbG8gV29ybGQ=" | base64 --decode # Hello World ```