【Kubernetes】ConfigMapの使い方

## はじめに
Kubernetesで使用する`ConfigMap`の内容について理解が浅いため、具体的にどのような使い方をするのか？を記載していきたいと思います。

## ConfigMapとは
Kubernetesの公式ドキュメントには以下のように説明されています。
https://kubernetes.io/ja/docs/concepts/configuration/configmap/

> ConfigMapは、 機密性のないデータをキーと値のペアで保存するために使用されるAPIオブジェクトです。Podは、環境変数、コマンドライン引数、またはボリューム内の設定ファイルとしてConfigMapを使用できます。
> 
> ConfigMapを使用すると、環境固有の設定をコンテナイメージから分離できるため、アプリケーションを簡単に移植できるようになります。

`docker-compose.yaml`を書いた人なら少し似たようなものである、というイメージが付くかもしれません。
`Key:Value`の形式のyamlファイルを定義して、ConfigMapに記述された、環境変数などのyamlファイルをPodにマウントします。
具体例を見てみましょう。

## ConfigMapに定義する具体例

ConfigMap
以下のConfigMap
```yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: db-config
data:
  DB_HOST: "mysql.xxxxxx.rds.amazonaws.com" # DB識別子.ホストID.rds.amazonaws.com
  DB_PORT: "3306"
```

Deployment
```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app
spec:
  replicas: 2
  selector:
    matchLabels:
      app: my-app
  template:
    metadata:
      labels:
        app: my-app
    spec:
      containers:
        - name: my-app               # コンテナ名
          image: my-app-image:latest # 使用するDockerイメージ
          env:                       # 環境変数を定義するセクション
            - name: DB_HOST          # 環境変数の名前を`DB_HOST`とする
              valueFrom:             # ConfigMapで定義した値を環境変数として取得する
                configMapKeyRef:
                  name: db-config    # ConfigMapの名前を参照
                  key: DB_HOST       # ConfigMap内で定義しているDB_HOSTの値を環境変数にセット
            - name: DB_PORT          # 環境変数の名前を`DB_PORT`とする
              valueFrom:
                configMapKeyRef:     
                  name: db-config    # ConfigMapの名前を参照
                  key: DB_PORT       # ConfigMap内で定義しているDB_PORTの値を環境変数にセット
```

ConfigMapとDeploymentのマニフェストファイルを2つ並べてみると以下のように参照しています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/379725/3e256cdf-1ff7-2e88-8e69-caa5959413cf.png)


このConfigMapの定義により、Pod内の環境変数は以下のように設定されます。
```bash
DB_HOST=mysql.xxxxxx.rds.amazonaws.com
DB_PORT=3306
```

## ConfigMapを使用する意図は
ConfigMapを使用する意図はいくつかあります。

1. ConfigMapを使用せずにマニフェストファイルに直接記述することも可能だが、セキュリティの観点からDBなどホスト情報をマニフェストファイル内に記述することはよろしくありません。
基本的にDockerfileやマニフェストファイルはGitHub上に管理することになるかと思いますが、誤って公開リポジトリにコミットしてしまうリスクがある点です。

2. DBホスト情報などを別ファイルとして定義することで管理しやすくなります。
仮に、DBの情報に変更があった際にはConfigMapの情報を更新すれば良いだけになるため、関連しているマニフェストファイル側をすべて変更、といった作業がなくなります。

ConfigMapに変更が生じた際には以下コマンドを実行することでConfigMapを更新すれば良いです。
```bash
kubectl apply -f configmap.yml
```

3. 環境によってDBのホスト情報は異なるかと思いますが、各環境分のConfigMapを用意しておくことで、マニフェストファイルを環境分用意する必要がない。
環境差異は`dev`, `stg`, `prod`などの識別子が異なるかと思います。
ConfigMap側でこの識別子の異なる環境分のファイルを用意しておくことで管理がしやすくなります。

表にまとめると以下です。

| 方法                      | セキュリティ                         | 管理のしやすさ                           |
|---------------------------|----------------------------------|----------------------------------|
| **Deployment に直接記述**  | ❌ Git で漏洩リスク高い            | ❌ 設定変更時に Deployment を適用し直す必要あり |
| **ConfigMap を利用**      | ✅ DB ホスト情報の分離で安全性向上 | ✅ ConfigMap だけ更新すれば変更が適用される |

## ConfigMapをマウントする
ConfigMapがどんなものか？ということがここまでの内容で少しずつ分かってきたかと思います。
ではこのConfigMapをDepoloymentが読み込むためにはどのようにすればよいでしょうか？
方法はいくつかありますが、本記事ではPod内に読み取り専用のディレクトリにConfigMapの内容をマウントする方法で解説します。

以下Deploymentで定義した内容はPod内の`/config`ディレクトリにConfigMapをマウントする方法です。

```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app
spec:
  replicas: 2
  selector:
    matchLabels:
      app: my-app
  template:
    metadata:
      labels:
        app: my-app
    spec:
      containers:
        - name: my-app
          image: my-app-image:latest
          env:
            - name: DB_HOST
              valueFrom:
                configMapKeyRef:
                  name: db-config
                  key: DB_HOST
            - name: DB_PORT
              valueFrom:
                configMapKeyRef:
                  name: db-config
                  key: DB_PORT
          volumeMounts:               # ここでマウントするディレクトリを定義
            - name: config-volume     
              mountPath: "/config"    # マウントするディレクトリを指定
              readOnly: true          # 読み取り専用とする
      volumes:                        # config-volumeで定義したボリュームを実際に読み込む
        - name: config-volume         # 何を見に行くか？ -> config-volumeを見に行く
          configMap:                  # どのConfigMapを参照するか？
            name: db-config           # ConfigMapの名前を指定

```
volumeMountsセクションでPod内のマウントするディレクトリを指定します。
volumesセクションでどのConfigMapを参照するのかを指定します。

## ConfigMapの実態はどこに存在しているのか
Pod内のディレクトリにConfigMapをボリュームマウントする、というのはわかったのですが、ConfigMapの実態はどこにあるのか？という疑問をいだきました。

Podが対象のディレクトリを参照しに行く際に、ConfigMapに書かれた内容を参照するわけですが、ConfigMap用のPodが建っているわけでもないのにどこから参照できるのか？がいまいち理解できていなかったのですが、ConfigMapはKubernetesのetcd内に保存されているようです。
`etcd`が何かについては以下で解説しています。

https://qiita.com/triple321jhango/items/5374ae3d1735750e6cf9

ConfigMapの情報を取得する流れは以下です。

1. `etcd`に保存されたConfigMapの情報を`kubelet`(ノードで動作するエージェント)がKube-apiserverに問い合わせてConfigMapの内容を取得
2. ConfigMapのデータを一時的なファイルシステム(`tmpfs`)としてノードに作成
3. Pod内の`/config`に`tmpfs`をマウント

上記によりPodはConfigMapの情報をファイルが存在しているように見えてる、ということです。

`kubelet`についても以下で解説しています。

https://qiita.com/triple321jhango/items/5374ae3d1735750e6cf9

## 最後ｎ
今回はConfigMapとは何？についてとConfigMapを実際にどう使うのか？またその仕組みについて解説しました。
概念を理解して使うことで、実際の業務で調査するときにどこを見れば良いか？がわかるようになるかと思いますので、引き続きKubernetesに関する情報はアウトプットしていければと思います。