Kubernetesは現在、バックエンドエンジンとしてDockerを利用しています。
Podの作成はYAML形式でエディターで書いて、kubectl createコマンドで簡単に作成できます。
作成したPodはkubectl execコマンドを使って、bashシェルなどにログインすれば仮想マシンにアクセスする様に環境にログインして操作できます。
ただ、何も特別な設定をしていない状態でPodを作成すると、非永続のストレージしか利用できません。従ってPod内のコンテナーで再起動が走ってしまうとデータが消去されてしまいます。
たとえば次のようなYAMLファイルでPodを作成したとします。
kind: Pod
apiVersion: v1
metadata:
name: mywebpod
spec:
containers:
- name: myfrontend
image: nginx:latest
ports:
- containerPort: 80
hostPort: 50080
NGINXのDockerイメージでは、Web rootは/usr/share/nginx/htmlがデフォルト設定になっています。確認のため、ここに任意のindex.htmlを作成してみましょう。
$ kubectl exec -it mywebpod bash root@mywebpod:/# ls /usr/share/nginx/html 50x.html index.html (Web rootを確認) root@mywebpod:/# echo "<html><h1>hello world</h1></html>" > /usr/share/nginx/html/index.html (適当な内容で上書き) root@mywebpod:/# cat /usr/share/nginx/html/index.html <html><h1>hello world</h1></html> (ファイルを確認)
ではこのコンテナーのプロセスを再起動してみましょう。一旦シェルから抜けて、docker stopとdocker startコマンドでコンテナーを再起動してみます。一度プロセスが終了されたため、index.htmlはデフォルトのページに戻ってしまいます。
~$ docker ps |grep mywebpod
b1fb6d922358 nginx@sha256:0fb320e2a1b1620b4905facb3447e3d84ad36da0b2c8aa8fe3a5a81d1187b884 "nginx -g 'daemon ..." 9 minutes ago Up 9 minutes k8s_myfrontend_mywebpod_default_f827a307-6235-11e8-babe-984be167d804_0
150426c756a4 k8s.gcr.io/pause-amd64:3.1 "/pause" 9 minutes ago Up 9 minutes k8s_POD_mywebpod_default_f827a307-6235-11e8-babe-984be167d804_0
~$ docker stop b1fb6d922358
b1fb6d922358
~$ docker start b1fb6d922358
b1fb6d922358
(コンテナーを再起動)
$ kubectl exec -it mywebpod bash
(Podのシェルにログイン)
root@mywebpod:/# cat /usr/share/nginx/html/index.html
(初期ページに戻ってしまった)
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
body {
width: 35em;
margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif;
}
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
と、いうわけでデータを永続して持っておきたい場合、永続ストレージが必要であるという事がわかります。
Kubernetesと永続ストレージ
本当はこの辺りを書きたかったのですが、Kubernetes公式のドキュメントやRed Hat社のドキュメントが纏まっていたので、リンクだけ貼り付けておきます。
- Storage - Kubernetes
- https://access.redhat.com/documentation/ja-jp/red_hat_enterprise_linux_atomic_host/7/html/getting_started_with_containers/get_started_provisioning_storage_in_kubernetes
Kubernetesと永続ストレージについては公式のドキュメントを参照して欲しいのですが、色々ある用語のうち、特に重要なキーワードは次の二つです。
Persistent Volumes
ざっくりいうと、永続ストレージを定義するためのものです。
PersistentVolumeClaims
ざっくりいうと、Podに紐づけるストレージを要求するためのものです。
Kubernetesで利用できるストレージはStorage Classesに纏まっています。今回はその中からNFS、Redis、Cephの使い方についてまとめます。
NFS共有ボリュームをPodにマウントする
NFS共有ボリュームをPodにマウントするにはNFSサービスと共用ボリュームがあらかじめ必要です。 まず、次のようなYAMLを記述して、PVとPVCについて定義します。
この中で重要なのは「persistentVolumeReclaimPolicy」です。この設定はデフォルトはRecycleが設定されており、簡易的なクリーンアップが施された上で新たに割り当てられます。データを保持したい場合は「Retain」に設定する必要があります。
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv1
labels:
volume: my-volume1
spec:
capacity:
storage: 10Gi
volumeMode: Filesystem
accessModes:
- ReadWriteOnce #ストレージのアクセスモード
persistentVolumeReclaimPolicy: Retain #保持オプション
storageClassName: slow
mountOptions: #NFSマウントオプション
- hard
- nfsvers=3
nfs:
path: "/dist-volume" #NFS共有ボリュームパス
server: 172.17.14.100 #NFSサーバーのIPアドレス
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc1
spec:
accessModes:
- ReadWriteOnce
volumeMode: Filesystem
resources:
requests:
storage: 2Gi
storageClassName: slow
selector:
matchLabels:
volume: my-volume1
pv1にmy-volume1と言う名前のラベルを定義して、PVCでmy-volume1のボリュームを要求しています。 このPVCにはpvc1という名前を設定しています。
あとは、Podを作成するときにpersistentVolumeClaimを指定することで、定義ずみのPVから要求したボリュームをPodに割り当てる事ができます。以下、PodのYAMLの例です。
本例はmynfspodと言う名前のPodにpvc1を割り当てる例です。
apiVersion: v1
kind: Pod
metadata:
name: mynfspod
spec:
containers:
- name: myfrontend
image: nginx:latest
ports:
- containerPort: 80
hostPort: 51080
volumeMounts:
- mountPath: "/usr/share/nginx/html"
name: mynfspod
volumes:
- name: mynfspod
persistentVolumeClaim:
claimName: pvc1
次のように実行すると、PodにNFS共有ボリュームがマウントされている事が確認できます。
% kubectl exec -it mynfspod -- df -hT Filesystem Type Size Used Avail Use% Mounted on none aufs 275G 12G 250G 5% / tmpfs tmpfs 24G 0 24G 0% /dev tmpfs tmpfs 24G 0 24G 0% /sys/fs/cgroup /dev/sda1 ext4 275G 12G 250G 5% /etc/hosts shm tmpfs 64M 0 64M 0% /dev/shm 172.17.14.100:/dist-volume nfs 27G 1.2G 26G 5% /usr/share/nginx/html tmpfs tmpfs 24G 12K 24G 1% /run/secrets/kubernetes.io/serviceaccount tmpfs tmpfs 24G 0 24G 0% /sys/firmware
Redisを永続ボリューム用途でPodにマウントする
次にRedisをPodで使う方法を説明します。RedisはいわゆるNoSQLデータベースのことで、オンメモリーのストレージとしても使うことができます。DockerやKubernetesでRedisを使うには、公式のDockerイメージを使ってコンテナーを起動するだけで利用できます。
このDockerイメージの実態は、redis-serverデーモンによって特定のディレクトリーのデータを永続化しているだけです。各Podの中でredis-serverを動作させます。特定の外部サーバー(NFS,iSCSIといった)が必要ないために手軽に利用できる反面、オンメモリストレージなので大きいデータのやり取りは不向きかもしれません。というより要注意です。
以下は公式のRedisイメージを使ってPodを起動するYAMLの例です。
apiVersion: v1
kind: Pod
metadata:
name: redis
spec:
containers:
- name: redis
image: redis
volumeMounts:
- name: redis-storage
mountPath: /data/redis
volumes:
- name: redis-storage
emptyDir: {}
上記の内容のYAMLでPodを起動すると、ディレクトリー/data/redisがマウントされるのが確認できます。
% kubectl exec -it redis bash root@redis:/data# ls -F redis/ (起動したPodに/data/redisがある事が確認できる) root@redis:/data# echo "TEST File" > /data/redis/test.data (適当に書きこむ) % docker stop 5c61f8fdb56f 5c61f8fdb56f % docker start 5c61f8fdb56f (workerノードでコンテナーのプロセスを終了し、再開する) % kubectl exec -it redis bash (シェルに再ログインして) root@redis:/data# cat /data/redis/test.data TEST File (書き込んだデータはまだ残っていた)
Redisの公式イメージはDebian版とAlpine Linux版が存在します。
CephをPodにマウントする
CephをKubernetesで使うにはRookを使うと簡単です。 まず、ソースをダウンロードします。2018年6月14日時点は0.7.1が最新です。
% wget https://github.com/rook/rook/archive/v0.7.1.zip % unzip v0.7.1.zip % cd ~/rook-0.7.1/cluster/examples/kubernetes
次に三つのコマンドを実行すると利用することができます。
% kubectl create -f rook-operator.yaml % kubectl create -f rook-cluster.yaml % kubectl create -f rook-storageclass.yaml
デフォルトではrook-cluster.yamlで定義している「dataDirHostPath」のパスが共有領域として使われます。
ポイントとして、ホスト上の物理デバイスをKubernetesからアクセスするため、Kubernetesの各ノード(masterとworker)が特権モードで動作している必要があるようです。Jujuの場合はこちらの設定をtrueにする必要があります。

rookはrookとrook-systemと言う名前のネームスペースを作り、その中で動作します。デプロイ中のログやPodの状況を確認するには、Kubernetesのダッシュボードを表示して確認する他、コマンドで確認することもできます。
次のように全てのネームスペース上のPodを確認して、kubectl logsコマンドでそれぞれ個別のログを確認できます。
% kubectl get pods --all-namespaces % kubectl logs rook-operator-5c89ff9496-kjq8m --namespace=rook-system
準備ができたら、次のようなYAMLファイルを用意して、Podを作成してみましょう。
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: myclaim
labels:
app: myclaim
spec:
storageClassName: rook-block
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 2Gi
---
kind: Pod
apiVersion: v1
metadata:
name: mypod
spec:
containers:
- name: myfrontend
image: nginx:latest
volumeMounts:
- mountPath: "/usr/share/nginx/html/"
name: mypod-nginxroot
volumes:
- name: mypod-nginxroot
persistentVolumeClaim:
claimName: myclaim
うまくいくと、次の例と同じようにPVとPVCを表示できるはずです。
% kubectl create -f test-pvcpod.yml persistentvolumeclaim "myclaim" created pod "mypod" created (Podを作成) % kubectl get pv,pvc NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE persistentvolume/pvc-f64ee657-6267-11e8-85de-984be167d804 2Gi RWO Delete Bound default/myclaim rook-block 1m NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE persistentvolumeclaim/myclaim Bound pvc-f64ee657-6267-11e8-85de-984be167d804 2Gi RWO rook-block 1m (PVとPVCを表示) $ kubectl get -f test-pvcpod.yml NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE persistentvolumeclaim/myclaim Bound pvc-f64ee657-6267-11e8-85de-984be167d804 2Gi RWO rook-block 24s (Podの状態を確認) NAME READY STATUS RESTARTS AGE pod/mypod 1/1 Running 0 24s % kubectl exec -it mypod -- df -h Filesystem Size Used Avail Use% Mounted on none 275G 14G 248G 6% / tmpfs 24G 0 24G 0% /dev tmpfs 24G 0 24G 0% /sys/fs/cgroup /dev/sda1 275G 14G 248G 6% /etc/hosts shm 64M 0 64M 0% /dev/shm /dev/rbd0 2.0G 3.0M 1.8G 1% /usr/share/nginx/html tmpfs 24G 12K 24G 1% /run/secrets/kubernetes.io/serviceaccount tmpfs 24G 0 24G 0% /sys/firmware (マウントされていることを確認)
と言うわけで、今回はKubernetesの永続ストレージの使い方まとめでした。