kubernetesのConfigMapを理解する

## 概要

k8sにはConfigMapという機能がある。

Podやコンテナで用いる設定情報をConfigMapに登録し、Podやコンテナがそれらを参照できるようにする。

パターンがいくつかある。

**ConfigMapを作成する方法**
- ファイルやディレクトリを指定して作成する方法
- マニフェストファイルから作成する方法


**Podやコンテナが読み取る方法**
- ConfigMapをボリュームマウントする方法
- ConfigMapを環境変数として渡す方法

それぞれ試してみる。


## ConfigMapを作成する方法

### ファイルやディレクトリを指定して作成する方法

まずConfigMapにしたい対象を作成する。今回はファイルで。

```params.txt
name hoge
age 1
country piyo
```

これをもとにConfigMapを作成する。`params-test`という名前のConfigMapが作成される。


```
$ kubectl create configmap params-test --from-file=params.txt
configmap/params-test created

$ kubectl get configmap
NAME          DATA   AGE
params-test   1      19s
```

中身を見てみる。

```
$ kubectl get configmap params-test -o yaml
apiVersion: v1
data:
  params.txt: |-
    name hoge
    age 1
    country piyo
kind: ConfigMap
metadata:
  creationTimestamp: "2020-03-09T04:17:50Z"
  name: params-test
  namespace: test
```

削除することもできるし、また新規で作り直すことも可能。

```
$ kubectl delete configmap params-test
configmap "params-test" deleted

$ kubectl get configmap
No resources found in test namespace.

$ kubectl create configmap test-params --from-file=params.txt
configmap/test-params created
```


### マニフェストファイルから作成する方法

以下のようなマニフェストファイルを作成。その上でapplyする。

```config.yaml
apiVersion: v1
data:
  params.txt: |-
    name hoge
    age 1
    country piyo
kind: ConfigMap
metadata:
  name: test-params
```

```
$ kubectl apply -f config.yaml
configmap/test-params created

$ kubectl get configmap test-params
NAME          DATA   AGE
test-params   1      24s
```

この方法で作るとannotationが入るようになっている。



## Podやコンテナが読み取る方法

### ConfigMapをボリュームマウントする方法

ConfigMapを読み取るPodを作成。test-paramsというConfigMapが作成されている前提で進める。

```test.yaml
apiVersion: v1
kind: Pod
metadata:
  name: test
spec:
  containers:
  - name: test
    image: nginx:alpine
    env:
    - name: master
      value: "true"
    volumeMounts:
    - mountPath: /mount
      name: config
  volumes:
  - name: config
    configMap:
      name: test-params
```

ややこしい点は2つ。
まず1つが containers.volumeMounts.name と volumes.name が一致している必要があること。
2つ目が volumes.configMap.name が先程作成したConfigMapの名前と一致していること。

これらが無視されると Podがいつまで経ってもContainerCreatingから脱しない。
例えば以下のようにpodの中身を見てみる。volumes.configMap.nameをhogeという存在しない値にすると以下のようなエラーが発生する。

```
$ kubectl apply -f test.yaml
pod/test created

$ kubectl get pods
NAME                              READY   STATUS              RESTARTS   AGE
test                              0/1     ContainerCreating   0          24s

$ kubectl describe pod test
Events:
  Type     Reason       Age                From                                                      Message
  ----     ------       ----               ----                                                      -------
  Normal   Scheduled    78s                default-scheduler                                         Successfully assigned test/test to ip-10-10-54-247.ap-northeast-1.compute.internal
  Warning  FailedMount  14s (x8 over 78s)  kubelet, ip-10-10-54-247.ap-northeast-1.compute.internal  MountVolume.SetUp failed for volume "config" : configmap "hoge" not found
```

正しく渡せたという前提で、
podの中に入ってparams.txtがどうなったか確認する。

```
$ kubectl exec -ti pod/test /bin/sh
# ls
bin    etc    lib    mnt    opt    root   sbin   sys    usr
dev    home   media  mount  proc   run    srv    tmp    var

# ls mount
params.txt

# cat mount/params.txt
name hoge
age 1
country piyo
```

問題なさそう。

### ConfigMapを環境変数として渡す方法

これも`kubernetes v1.6`を前後にやり方が異なる。方法としては2つ。

- envを使う方法（旧）
- envFromを使う方法（新）

envFromの導入によってConfigMapの複数キー指定が楽になる。

まずはenvFromを使わない、envを使った環境変数としての渡し方。

はじめにこの恩恵を感じられるようにするために、config.yamlを書き換える。

```config.yaml
apiVersion: v1
data:
  his.name: hoge
  his.age: ten
  his.country: piyo
  her.name: ham
  her.age: eleven
  her.country: spam
kind: ConfigMap
metadata:
  name: test-params-2
```

パラメータのkeyとvalueの組み合わせが増えた。
さっきまではparams.txtというkeyに対してtext情報というvalueが登録されていた。

ここに登録されているkeyとvalueをpodsに対してenvを用いて渡そうとすると、以下のようになる。

```test.yaml
apiVersion: v1
kind: Pod
metadata:
  name: test
spec:
  containers:
  - name: test
    image: nginx:alpine
    env:
    - name: his-name
      valueFrom:
        configMapKeyRef:
          name: test-params-2
          key: his.name
    - name: his-age
      valueFrom:
        configMapKeyRef:
          name: test-params-2
          key: his.age
```


```
$ kubectl apply -f test.yaml
pod/test created

$ kubectl exec -ti test /bin/sh

# printenv
・
・
his-name=hoge
his-age=ten
・
・
```

ご覧の通りenvを用いると、変数を一つずつ渡さないといけない。そのためhis.nameからher.countryまで登録するには6つのenvを記述しないといけない。かなり冗長になってしまう。

それを便利にするのがenvFromである。ConfigMap中の変数をまとめて環境変数として渡すことができる。

```test.yaml
apiVersion: v1
kind: Pod
metadata:
  name: test
spec:
  containers:
  - name: test
    image: nginx:alpine
    env:
    - name: his-name
      valueFrom:
        configMapKeyRef:
          name: test-params-2
          key: his.name
    - name: his-age
      valueFrom:
        configMapKeyRef:
          name: test-params-2
          key: his.age
```

もちろん全てを突っ込むわけではなく、prefix指定も可能。

```
$ kubectl apply -f test.yaml
pod/test created

$ kubectl exec -ti test /bin/sh

# printenv
・
・
her.country=spam
his.country=piyo
her.name=ham
her.age=eleven
his.name=hoge
his.age=ten
・
・
```

## 参考

[Kubernetes: ConfigMap / Secret の内容を一度に環境変数として読み込む (envFrom)](https://qiita.com/tkusumi/items/cf7b096972bfa2810800)
[KubernetesのConfigMapを試してみる](https://qiita.com/tkusumi/items/8e31fddda77f93ccfdd8)
[Kubernetes道場 11日目 - ConfigMap / Secretについて](https://cstoku.dev/posts/2018/k8sdojo-11/)