Skip to content

Instantly share code, notes, and snippets.

@fortune
Last active February 13, 2024 07:52
Show Gist options
  • Save fortune/a10035d46f4299a14f5cb36aa25c3d68 to your computer and use it in GitHub Desktop.
Save fortune/a10035d46f4299a14f5cb36aa25c3d68 to your computer and use it in GitHub Desktop.
Kubernetes の Pod でローカルな Volume を利用する

Kubernetes の Pod でローカルな Volume を利用する

Pod 内のコンテナから、ボリュームをマウントするには次の4つの方法がある。

  1. hostPath で、Pod が作成されたノード上の特定のパスをボリュームとする。
  2. emptyDir で、Pod が作成されたノード上に、Pod と同じ寿命の空のボリュームをつくる。
  3. クラウドプロバイダ(AWS, Azure, GCE 等)が提供する作成済みのストレージをボリュームとする。
  4. PersistentVolume, PersistentVolumeClaim, StorageClass を使った永続ボリュームを利用する。

https://kubernetes.io/ja/docs/concepts/storage/volumes/

最初の2つがローカルなボリュームであり、マニフェストファイル kuard-pod-vol.yml でこれらを試してみる。

ローカルな Kubernetes クラスタでの実験

Mac OS X の Docker for Mac 上のローカルな Kubernetes クラスタで試してみる。そのために

$ kubectl config get-contexts
CURRENT   NAME                                              CLUSTER                                           AUTHINFO                                          NAMESPACE
*         docker-desktop                                    docker-desktop                                    docker-desktop                                    
          gke_todo-kube-343008_us-central1-c_todo-cluster   gke_todo-kube-343008_us-central1-c_todo-cluster   gke_todo-kube-343008_us-central1-c_todo-cluster

で、カレントコンテキストが docker-desktop になっていることを確実にしておく。

Pod をデプロイ

$ kubectl apply -f kuard-pod-vol.yml 
pod/kuard created

次に、 https://gist.github.com/fortune/0b17295b84923fbd074aaa7bf744e99e のとおりにローカルな K8S クラスタのノードになっている Linux VM に入る。

$ docker run -it --rm --privileged --pid=host justincormack/nsenter1
/ #

コンテナ単位のボリューム

Pod のマニフェストファイルでは指定できないが、コンテナのイメージ作成の Dockerfile で Volume 命令を使っていれば、そのコンテナはボリュームにマウントされる。ここでは、redis コンテナがそうだ。

Linux VM ノード内で、redis コンテナがマウントしたボリュームを見てみる。

/ # cd /var/lib/docker/volumes/
/var/lib/docker/volumes # ls
backingFsBlockDev                                                 metadata.db
c5988c5677c5496125c78c3e1d9f9bdce3f858ce91fa217cdc126a1c8a946cfc
/var/lib/docker/volumes # cd c5988c5677c5496125c78c3e1d9f9bdce3f858ce91fa217cdc126a1c8a946cfc/
/var/lib/docker/volumes/c5988c5677c5496125c78c3e1d9f9bdce3f858ce91fa217cdc126a1c8a946cfc # ls
_data
/var/lib/docker/volumes/c5988c5677c5496125c78c3e1d9f9bdce3f858ce91fa217cdc126a1c8a946cfc # cd _data
/var/lib/docker/volumes/c5988c5677c5496125c78c3e1d9f9bdce3f858ce91fa217cdc126a1c8a946cfc/_data # 
/var/lib/docker/volumes/c5988c5677c5496125c78c3e1d9f9bdce3f858ce91fa217cdc126a1c8a946cfc/_data # ls
/var/lib/docker/volumes/c5988c5677c5496125c78c3e1d9f9bdce3f858ce91fa217cdc126a1c8a946cfc/_data #

Pod 内の redis コンテナにはいってみる。

$ kubectl exec -it kuard -c redis -- /bin/bash
root@kuard:/data# pwd
/data
root@kuard:/data# 
root@kuard:/data# 
root@kuard:/data# ls
root@kuard:/data# echo hello >hello.txt

再び、Linux VM ノードの方を見てみると、redis コンテナが作成したファイルが見える。

/var/lib/docker/volumes/c5988c5677c5496125c78c3e1d9f9bdce3f858ce91fa217cdc126a1c8a946cfc/_data # cat hello.txt 
hello

このボリュームは、redis コンテナ単位で作成されているので、redis コンテナが再作成されたりすると、新しいコンテナでは使用されず、別のボリュームが作成されてしまう。

hostPath で作成したボリューム

次に、Pod マニフェストの volumes 定義で作成した kuard-data ボリュームを確認する。これは、hostPath.path により、ノードの特定のパスにマウントさせる ボリュームだ。

Pod 内の kuard コンテナに入る。

$ kubectl exec -it kuard -c kuard -- /bin/sh
~ $ cd /data
/data $ ls
/data $

test-client コンテナに入る。

$ kubectl exec -it kuard -c test-client -- /bin/bash
root@kuard:/# cd /workspace/
root@kuard:/workspace# ls
root@kuard:/workspace#
root@kuard:/workspace# echo fortunefield >fortunefield.txt
root@kuard:/workspace#

再び、kuard コンテナの方を見ると

/data $ ls
fortunefield.txt
/data $ cat fortunefield.txt 
fortunefield
/data $

になり、たしかに共通のボリュームをマウントしている。

Linux VM ノードに戻って、上で作成した fortunefield.txt を探す。

/ #
/ # find . -name fortunefield.txt
./var/lib/mount-docker-cache/entries/services.tar/7faf803132f55a3e5671b59b7fc3133d2117557fd2e00ddc8a39b88801921917/containers/services/docker/rootfs/home/fortunefield.txt
./var/lib/mount-docker-cache/entries/services.tar/7faf803132f55a3e5671b59b7fc3133d2117557fd2e00ddc8a39b88801921917/containers/services/docker/tmp/upper/home/fortunefield.txt
./var/lib/mount-docker-cache/entries/docker.tar/da2df7ce7c953bc090d2abf0e887f581c5efea69356aa7ec1134b48ea5f3835a/containers/services/docker/rootfs/home/fortunefield.txt
./var/lib/mount-docker-cache/entries/docker.tar/da2df7ce7c953bc090d2abf0e887f581c5efea69356aa7ec1134b48ea5f3835a/containers/services/docker/tmp/upper/home/fortunefield.txt
./containers/services/docker/rootfs/home/fortunefield.txt
./containers/services/docker/tmp/upper/home/fortunefield.txt
/ # 
/ # cd containers/services/docker/rootfs/home
/containers/services/docker/rootfs/home # ls
fortunefield.txt
/containers/services/docker/rootfs/home # cat fortunefield.txt 
fortunefield
/containers/services/docker/rootfs/home #

ノードの /home をボリュームとして指定していたが、直接ノードの /home がボリュームになるのではないようだ。これは、K8S クラスタによって異なるのかもしれない。

emptyDir で作成したボリューム

kuard-empty ボリュームを確認する。これは、emptyDir で作成した空のボリュームだ。

kuard コンテナで見ると

~ $ cd /empty-data/
/empty-data $ ls
/empty-data $
/empty-data $ echo tomita >tomita.txt
/empty-data $ ls
tomita.txt

test-client コンテナで見ると

root@kuard:/workspace# cd /empty-workspace/
root@kuard:/empty-workspace# ls
tomita.txt
root@kuard:/empty-workspace# cat tomita.txt 
tomita
root@kuard:/empty-workspace#

たしかに共通にマウントできてる。

ここで Pod を再作成してみる。

$ kubectl delete -f kuard-pod-vol.yml 
pod "kuard" deleted
$ kubectl apply -f kuard-pod-vol.yml 
pod/kuard created

その後で、再度、kuard コンテナの中に入って確認する。

$ kubectl exec -it kuard -c kuard -- /bin/sh
~ $ ls /data/
fortunefield.txt
~ $ 
~ $ 
~ $ ls /empty-data/
~ $ 

hotPath.path で作成した方のボリュームの内容は残っているが、emptyDir で作成したボリュームの中身は消えていることがわかる。emptyDir は Pod 内のコンテナ間の通信に使うのに向いている。

GKE 上の Kubernetes クラスタで実験

あらかじめ、GKE 上にクラスタを作成してある。それを対象にするように kubectl のコンテキストを設定して、Pod をデプロイする。

$ kubectl config use-context gke_todo-kube-343008_us-central1-c_todo-cluster 
Switched to context "gke_todo-kube-343008_us-central1-c_todo-cluster".
$ 
$ kubectl config get-contexts
CURRENT   NAME                                              CLUSTER                                           AUTHINFO                                          NAMESPACE
          docker-desktop                                    docker-desktop                                    docker-desktop                                    
*         gke_todo-kube-343008_us-central1-c_todo-cluster   gke_todo-kube-343008_us-central1-c_todo-cluster   gke_todo-kube-343008_us-central1-c_todo-cluster

$ kubectl apply -f kuard-pod-vol.yml 
pod/kuard created

$ kubectl get pod kuard
NAME    READY   STATUS    RESTARTS   AGE
kuard   3/3     Running   0          54s

kuard Pod 内の kuard コンテナに入ってみると

$ kubectl exec -it kuard -c kuard -- /bin/sh
~ $ 
~ $ 
~ $ ls
bin         dev         etc         kuard       media       proc        run         srv         tmp         var
data        empty-data  home        lib         mnt         root        sbin        sys         usr
~ $ ls /data
chronos     containerd  kubernetes
~ $ 
~ $ 
~ $ ls /empty-data/
~ $ 

test-client コンテナに入ってみると

$ kubectl exec -it kuard -c test-client -- /bin/bash
root@kuard:/# 
root@kuard:/# 
root@kuard:/# ls -l /workspace/
total 12
drwxr-xr-x  2 1000 1000 4096 Feb 18 03:56 chronos
drwxr-xr-x  2 root root 4096 Mar  3 09:19 containerd
drwxr-xr-x 11 root root 4096 Mar  3 09:19 kubernetes

root@kuard:/# 
root@kuard:/# 
root@kuard:/# ls /empty-workspace/
root@kuard:/#

kuard ボリュームはノードの /home にマウントしていた。上でわかるように、GKE のクラスタ上で、この kuard Pod が作成されたノードの /home には、

drwxr-xr-x  2 1000 1000 4096 Feb 18 03:56 chronos
drwxr-xr-x  2 root root 4096 Mar  3 09:19 containerd
drwxr-xr-x 11 root root 4096 Mar  3 09:19 kubernetes

があることがわかる。

apiVersion: v1
kind: Pod
metadata:
name: kuard
spec:
# 2 種類のボリュームを定義する
volumes:
# ノード上の特定のパス(hostPath.path)をボリュームとして使う。
# ここに作成されたファイルは、Pod が削除されても残り続ける。
- name: "kuard-data"
hostPath:
path: "/home"
# emptyDir で空のボリュームを作成する。このボリュームは、Pod が削除されると
# 中身は消える。
- name: "kuard-empty-data"
emptyDir: {}
containers:
- name: kuard
image: gcr.io/kuar-demo/kuard-amd64:1
ports:
- containerPort: 8080
# volumes で定義した 2 つのボリュームをそれぞれ別のディレクトリでマウントする。
volumeMounts:
- mountPath: "/data"
name: "kuard-data"
- mountPath: "/empty-data"
name: "kuard-empty-data"
# redis は image を作成する Dockerfile でコンテナの /data をボリュームマウントするように
# 指定しているが、それは機能する。したがって、この Pod が作成されたノードのファイルシステムの
# どこか、たとえば、/var/lib/docker/volumes/ にボリュームが作成される。
- name: redis
image: redis:3.0
# テスト用のコンテナ。
# デフォルトの bash だとすぐに終了してしまうので、とりあえず長時間スリープさせておく。
- name: test-client
image: fortunefield/linux-gcc:ubuntu-22.04
command: ['sh', '-c', 'sleep 86400']
# redis と同様にコンテナのディレクトリをボリュームマウントしようとしても
# Pod のマニフェストファイルで指定することはできない。
#volumes:
# - /workspace
# volumes で定義した 2 つのボリュームをそれぞれ別のディレクトリでマウントする。
# kuard コンテナでもマウントしているので、2つのコンテナはこれらのディレクトリを通して
# ある意味、通信ができる。
volumeMounts:
- mountPath: "/workspace"
name: "kuard-data"
- mountPath: "/empty-workspace"
name: "kuard-empty-data"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment