k8s Tasksを通しでやってみる：Configure a Pod to Use a Volume for Storage

参考資料
--------------------
[Configure a Pod to Use a Volume for Storage (kubernetes.io)](https://kubernetes.io/docs/tasks/configure-pod-container/configure-volume-storage/)
[Declarative Management of Kubernetes Objects Using Kustomize (kubernetes.io)](https://kubernetes.io/docs/tasks/manage-kubernetes-objects/kustomization/)
[Volumes emptyDir (kubernetes.io)](https://kubernetes.io/docs/concepts/storage/volumes/#emptydir)


何を学べるか
--------------------
- コンテナでデータを保存するには`Volume`を使う
- `Volume`種別毎に、データの生存期間が異なる

### 事前知識

- コンテナが終了(再起動)した時、コンテナが持っているファイルシステム内の変更は全て失われる
→コンテナ生成元のイメージと同等の状態に戻る
- 上記の通りなので、コンテナをステートフルで使いたい(状態、データを保存したい)ならば、永続ストレージをコンテナへマウントしておく必要がある


前提条件
--------------------
- k8s v1.14以降 (Kustomizeが必要)
- k8sクラスタ起動済み


環境情報
--------------------
- CNI実装: calico
- CRI実装: Docker
- ホストOS: Ubuntu 18.04


手順
--------------------

### 1/5 : Podを作って状態を監視する

![1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/257156/dd77a134-7c2a-ef40-6c09-67c9335c281c.png)


後続のコマンドを実行するには`C-z`で監視をバックグラウンドジョブにするか、別シェルを立ち上げましょう(`C-Shift-t`等)

```
kubectl apply -f https://k8s.io/examples/pods/storage/redis.yaml && \
kubectl get pod redis --watch

pod/redis created
NAME    READY   STATUS              RESTARTS   AGE
redis   0/1     ContainerCreating   0          0s
redis   1/1     Running             0          17s
```

### 2/5 : 作ったPodでシェルを起動する

![2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/257156/67599929-11aa-2bf2-980a-4340dca929d2.png)


```
kubectl exec -it redis -- /bin/bash

root@redis:/data#
```

### 3/5 : 起動したシェルでファイルを作る

![3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/257156/c023fda1-3103-b451-ecdf-cd0d15ef8d19.png)


```
cd /data/redis/
echo Hello > test-file && ls

test-file
```

### 4/5 : シェルでUbuntuのリポジトリ情報を更新し、`procps`パッケージをインストールし、`ps aux`で動いているプロセスの情報を見る

![4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/257156/3b9bf55d-2e65-fd5f-1d18-1411768786ba.png)


...失敗。。`coredns`がきちんと動いてませんでした。

```
apt-get update

Err:1 http://security.debian.org/debian-security buster/updates InRelease
  Temporary failure resolving 'security.debian.org'
Err:2 http://deb.debian.org/debian buster InRelease
  Temporary failure resolving 'deb.debian.org'
Err:3 http://deb.debian.org/debian buster-updates InRelease
  Temporary failure resolving 'deb.debian.org'
Reading package lists... Done
W: Failed to fetch http://deb.debian.org/debian/dists/buster/InRelease  Temporary failure resolving 'deb.debian.org'
W: Failed to fetch http://security.debian.org/debian-security/dists/buster/updates/InRelease  Temporary failure resolving 'security.debian.org'
W: Failed to fetch http://deb.debian.org/debian/dists/buster-updates/InRelease  Temporary failure resolving 'deb.debian.org'
W: Some index files failed to download. They have been ignored, or old ones used instead.
```

[こっちの記事](https://qiita.com/fastwind/items/21f8a8f3b867cd0d5ed3)にて解決したので、以下は解決後です。

```
apt-get update

Get:1 http://security-cdn.debian.org/debian-security buster/updates InRelease [39.1 kB]
Get:2 http://cdn-fastly.deb.debian.org/debian buster InRelease [118 kB]
Get:3 http://cdn-fastly.deb.debian.org/debian buster-updates InRelease [46.8 kB]
Get:4 http://security-cdn.debian.org/debian-security buster/updates/main amd64 Packages [53.4 kB]
Get:5 http://cdn-fastly.deb.debian.org/debian buster/main amd64 Packages [7897 kB]
Fetched 8154 kB in 6s (1375 kB/s)
Reading package lists... Done
```


```
apt-get install procps

...
```

```
ps aux

USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
redis        1  0.3  0.2  40704  4124 ?        Ssl  06:26   0:06 redis-server *:6379
root       338  0.6  0.1   3868  2928 pts/0    Ss   07:02   0:00 /bin/bash
root       344  0.0  0.1   7640  2656 pts/0    R+   07:02   0:00 ps aux
```

### 5/5 : `redis`プロセスを落とし、Podでシェルを起動し、作ったファイル`/data/redis/test-file`が存在する事を確かめる

![5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/257156/fe57372f-0d71-6b51-7539-fcb841b71c11.png)


```
kill 1

root@redis:/data# command terminated with exit code 137
```

`kubectl get pod redis --watch`のその後の状態

```
NAME    READY   STATUS    RESTARTS   AGE
redis   1/1     Running   0          41m
redis   0/1     Completed   0          42m
redis   0/1     CrashLoopBackOff   0          43m
redis   1/1     Running            1          43m
```

```
kubectl exec -it redis -- /bin/bash

root@redis:/data# ls /data/redis

test-file
```

`test-file`は生きてますね、今回のタスクは成功です。

終わったらクリーンアップしましょう。

```
kubectl delete pod redis
```

```
NAME    READY   STATUS    RESTARTS   AGE
redis   1/1     Running            1          43m
redis   1/1     Terminating        1          53m
redis   0/1     Terminating        1          53m
```


疑問
--------------------
### `/data/redis/`はホストOSのどこのパスにマウントされたのでしょう？

今回使ったVolume種別の[Volumes emptyDir (kubernetes.io)](https://kubernetes.io/docs/concepts/storage/volumes/#emptydir)仕様を見てみましょう。

> In this exercise, you create a Pod that runs one Container. This Pod has a Volume of type emptyDir that lasts for the life of the Pod, even if the Container terminates and restarts. Here is the configuration file for the Pod:

PodがスケジューリングされたNodeから追い出されるまでは、`emptyDir`Volumeは生きているらしいです。

  > By default, emptyDir volumes are stored on whatever medium is backing the node - that might be disk or SSD or network storage, depending on your environment. However, you can set the emptyDir.medium field to "Memory" to tell Kubernetes to mount a tmpfs (RAM-backed filesystem) for you instead

`emptyDir`は、デフォルトの動作ではNodeが持っているストレージを使い、`emptyDir.medium`フィールドへ`"Memory"`を設定した場合は、Nodeのメモリ上に`tmpfs`(RAMディスク)を構築してくれるらしいです。
今回は何も設定していないので、やはりホストOS内のストレージのルート`/`パーティション以下に`test-file`が存在すると考えるのですが・・・`locate kubernetes`でホストOS内を探してみるも、作った`test-file`相当のファイルは見当たりません。
今回はこれ以上深堀りせず、ここまでとします。


現在のPodのVolume状態チェック

```
kubectl describe pod redis

...
Containers:
  redis:
    Mounts:
      /data/redis from redis-storage (rw)
...
  redis-storage:
    Type:       EmptyDir (a temporary directory that shares a pod's lifetime)
    Medium:
    SizeLimit:  <unset>
```


まとめ
--------------------
今回は`emptyDir`というVolume種別について、簡単に触るタスクでした。
**`emptyDir`は、基本的にPodがNodeから追い出されるまでは生きているVolume**
という事で、Pod内のコンテナが死んでも、再度同じNode内で復活すれば、保存したデータも復旧できる、という事のようです。
(Node自体を信用出来ない状況になった際、別NodeへPodを移動したいケースには合わないな・・・と、k8sの運用では現実的でない感じがします)
いずれ、今回のタスクは「Pod内のコンテナにVolumeをマウントする事」がメインだったので、これは達成出来ました。

Volume, ストレージについては様々な知識を深堀りする必要はありますが、最低限

- マウント、アンマウントの方法と場所(お互いのパス、紐付け方)
- データの生存期間
- Volumeの指定方法(メモリ、永続ストレージ等)

を抑えておけば大丈夫かなと考えています。
以上です。

補足：略語(慣習的な用語を除く)
--------------------
- k8s: Kubernetes
- CNI: Container Networking Interface
- CRI: Container Runtime Interface