この記事は Kubernetes道場 Advent Calendar 2018 5日目の記事です。

今回はPodで使用できるVolumeについて。

Volumeについて

コンテナのデータは一時的なもので、Pod/コンテナが削除されればデータが消えてしまう。 また、コンテナがクラッシュしてKubernetesが再起動させてくれば場合でもコンテナのデータは消えてしまう。

Volumeはデータの永続化をしてくれる。 永続化したいデータは指定したVolumeに保存することで削除やクラッシュした際でもデータが残る。

また、Volumeは別の目的でも使用される。それはPod内でのコンテナ間のデータ共有だ。

3日目のPodの解説 の際にも出てきた画像だが、以下のようにPod内のコンテナはVolumeを通してデータを共有することが出来る。

Volumeにはいくつかの種類がある。

などなど。

awsElasticBlockStoregcePersistentDisk などはクラウドサービスなどを利用してVolumeを作成し、マウントする方法だ。 しかし、これはクラウドを使っている場合においてだ。

minikubeのローカル環境を使用しているため emptyDirhostPath などが使用できる。今回は emptyDir を使ってVolumeを利用してみよう。

emptyDirを使う

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
apiVersion: v1
kind: Pod
metadata:
  name: date-tail
spec:
  containers:
  - name: date
    image: alpine
    command: ["sh", "-c"]
    args:
    - |
      exec >> /var/log/date-tail/output.log
      echo -n 'Start at: '
      while true; do
        date
        sleep 1
      done
    volumeMounts:
    - name: log-volume
      mountPath: /var/log/date-tail
  - name: tail
    image: alpine
    command: ["sh", "-c"]
    args:
    - |
      tail -f /var/log/date-tail/output.log
    volumeMounts:
    - name: log-volume
      mountPath: /var/log/date-tail
  volumes:
  - name: log-volume
    emptyDir:
  terminationGracePeriodSeconds: 0

このようなyamlを作ってみた。

emptyDir を使ったVolumeに log-volume という名前を付けた。この名前はPod内でユニークである必要がある。

このVolumeを2つのコンテナにマウントした。mountPath(マウント先)は /var/log/date-tail としている。 1つ目のコンテナではマウントしたVolumeに date の出力を output.log に追記している。 もう一つのコンテナではマウントされているVolumeの中にある output.logtail している。

さて、作成してみよう。

1
2
$ kubectl apply date-tail.yaml
pod/date-tail created

ファイルを確認してみる。

1
2
3
4
$ kubectl exec -it date-tail -c date ls /var/log/date-tail
output.log
$ kubectl exec -it date-tail -c tail ls /var/log/date-tail
output.log

どちらのコンテナからもファイルが確認できる。 tailコンテナの方では tail -f を実行しているので kubectl logs でその出力が確認できるはずだ。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
$ kubectl logs date-tail -c tail -f --tail=10
Start at: Tue Dec  4 16:27:55 UTC 2018
Tue Dec  4 16:27:56 UTC 2018
Tue Dec  4 16:27:57 UTC 2018
Tue Dec  4 16:27:58 UTC 2018
Tue Dec  4 16:27:59 UTC 2018
Tue Dec  4 16:28:00 UTC 2018
Tue Dec  4 16:28:01 UTC 2018
Tue Dec  4 16:28:02 UTC 2018
Tue Dec  4 16:28:03 UTC 2018
Tue Dec  4 16:28:04 UTC 2018
Tue Dec  4 16:28:05 UTC 2018
Tue Dec  4 16:28:06 UTC 2018
^C⏎ 

この様にファイルに追記されている。Volumeでデータが共有できていることが確認できる。

このコンテナを削除して再作成し、logsでファイルが永続化されているか確認してみる。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
$ kubectl replace --force -f date-tail.yaml
pod "date-tail" deleted
pod/date-tail replaced
$ kubectl logs date-tail -c tail -f
Start at: Tue Dec  4 16:29:39 UTC 2018
Tue Dec  4 16:29:40 UTC 2018
Tue Dec  4 16:29:41 UTC 2018
Tue Dec  4 16:29:42 UTC 2018
Tue Dec  4 16:29:43 UTC 2018
Tue Dec  4 16:29:44 UTC 2018
Tue Dec  4 16:29:45 UTC 2018
Tue Dec  4 16:29:46 UTC 2018
Tue Dec  4 16:29:47 UTC 2018
Tue Dec  4 16:29:48 UTC 2018
Tue Dec  4 16:29:49 UTC 2018
Tue Dec  4 16:29:50 UTC 2018
^C⏎ 

Start atの日時が違うようだ。このことから永続化されていないことが分かる。

実は emptyDir はPodが削除された際に対象のVolumeのデータも削除される。 よってコンテナ間のデータ共有のみで使用できる。

このため永続化も行いたい場合、minikubeでは hostPath を使用する。

その前にお片付け。

1
2
$ kubectl delete -f date-tail.yaml
pod "date-tail" deleted

hostPathを使う

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
apiVersion: v1
kind: Pod
metadata:
  name: date-tail
spec:
  containers:
  - name: date
    image: alpine
    command: ["sh", "-c"]
    args:
    - |
      exec >> /var/log/date-tail/output.log
      echo -n 'Start at: '
      while true; do
        date
        sleep 1
      done
    volumeMounts:
    - name: log-volume
      mountPath: /var/log/date-tail
  - name: tail
    image: alpine
    command: ["sh", "-c"]
    args:
    - |
      tail -f /var/log/date-tail/output.log
    volumeMounts:
    - name: log-volume
      mountPath: /var/log/date-tail
  volumes:
  - name: log-volume
    hostPath:
      path: /data/date-tail
  terminationGracePeriodSeconds: 0

volumeのタイプを hostPath に変更して、 path/data/date-tail を指定した。

minikubeでは以下のパスが使用できるようになっている。

  • /data
  • /var/lib/minikube
  • /var/lib/docker
  • /tmp/hostpath_pv
  • /tmp/hostpath-provisioner

詳細は以下から。

Persistent Volumes

ここ話を掘り下げると長くなるのでこの話については後日掘り下げるかも。

一旦はminikubeで上記のパス以下で path を指定すれば良い。

さて、Podを作成してみる。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
$ kubectl logs date-tail -c tail
Start at: Tue Dec  4 16:48:07 UTC 2018
Tue Dec  4 16:48:08 UTC 2018
Tue Dec  4 16:48:09 UTC 2018
Tue Dec  4 16:48:10 UTC 2018
Tue Dec  4 16:48:11 UTC 2018
Tue Dec  4 16:48:12 UTC 2018
Tue Dec  4 16:48:13 UTC 2018
Tue Dec  4 16:48:14 UTC 2018
Tue Dec  4 16:48:15 UTC 2018

ここまでは今までどおり。さて、ここで再作成してみよう。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
$ kubectl replace --force -f date-tail.yaml
pod "date-tail" deleted
pod/date-tail replaced
$ kubectl logs date-tail -c tail
Tue Dec  4 16:48:42 UTC 2018
Tue Dec  4 16:48:43 UTC 2018
Tue Dec  4 16:48:44 UTC 2018
Tue Dec  4 16:48:45 UTC 2018
Tue Dec  4 16:48:46 UTC 2018
Tue Dec  4 16:48:47 UTC 2018
Start at: Tue Dec  4 16:48:49 UTC 2018
Tue Dec  4 16:48:50 UTC 2018
Tue Dec  4 16:48:51 UTC 2018
Tue Dec  4 16:48:52 UTC 2018
Tue Dec  4 16:48:53 UTC 2018
Tue Dec  4 16:48:54 UTC 2018

Start atの前にログが出力されており、データが永続化されていることが分かる。

この hostPath はKubernetesが実行されているサーバーにデータが置いてある。確認してみよう。

minikubeでは minikube ssh というコマンドでminikubeで構築したローカル環境のVMにSSHで接続できる。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
$ minikube ssh
                         _             _
            _         _ ( )           ( )
  ___ ___  (_)  ___  (_)| |/')  _   _ | |_      __
/' _ ` _ `\| |/' _ `\| || , <  ( ) ( )| '_`\  /'__`\
| ( ) ( ) || || ( ) || || |\`\ | (_) || |_) )(  ___/
(_) (_) (_)(_)(_) (_)(_)(_) (_)`\___/'(_,__/'`\____)
$ ls /data/
date-tail  minikube
$ ls /data/date-tail/
output.log
$ tail -n5 /data/date-tail/output.log
Tue Dec  4 17:02:46 UTC 2018
Tue Dec  4 17:02:47 UTC 2018
Tue Dec  4 17:02:48 UTC 2018
Tue Dec  4 17:02:49 UTC 2018
Tue Dec  4 17:02:50 UTC 2018

hostPath はサーバー上のファイルシステムにデータを保存するため、実際のKubernetesクラスタでは今回のような期待した動きにならない。 別のサーバーでPodが起動した場合に、そのサーバーの hostPath にはデータが存在しないためだ。

このため、実際のKubernetesクラスタでは

のようなマネージドのストレージやファイルサーバーなどを使ってVolumeとして使用する。こちらの使用例については今後機会があれば解説しようと思う。


というわけで今回はここまで。

次回は Init Container と Lifecycle について見ていこう。

それでは。