この投稿は、Vagrantの仮想サーバーで構築したK8sクラスタの4回目で、NFSを利用した、永続ボリュームの利用についてのメモです。
これまでのk8s on vagrant 記事のリンクを以下にあげておきますので、必要に応じて参照いただければ幸いです。
システム構成
図の左端に表したk8sクラスタの外側にNFSサーバーを設定して、図中の中央にあるワーカーノード(k8s2,k8s3)のポッドからマウントできる様にします。 NFSサーバーは、外部ストレージ・システムを想定して、パブリック・ネットワークにデプロイします。 もちろん、自宅環境にストレージ・システムを持つのはお金が掛かりますから、Vagrantで模擬的なストレージシステムを作ります。
NFSサーバーでエクスポートしたファイルシステムを k8sのクラスタの永続ボリュームとして定義してポッドから利用します。
ワーカーノードへのNFSクライアントモジュールの追加
コンテナは、カーネルのサポートを必要としていますから、NFSのクライアント機能がインストールされていないと、ポッドはNFSサーバーのボリュームをマウントすることができません。 このため、ワーカーノードである k8s2, k8s3にNFSクライアントのパッケージをインストールします。
root@k8s2:~# apt-get install nfs-common
root@k8s3:~# apt-get install nfs-common
NFSサーバーのセットアップ
vagrantの仮想サーバーとして、NFSサーバーをセットアップします。
NFSサーバーのディレクトリを作成して、移動します。
$ mkdir nfsserver
$ cd nfsserver
次のVagrantfileを作成します。 パブリックIPアドレスは、環境に合わせて変更してくださいね。
# -*- mode: ruby -*-
# vi: set ft=ruby :
Vagrant.configure("2") do |config|
config.vm.box = "ubuntu/xenial64"
config.vm.hostname = "nfsserver"
#
public_ip = "192.168.1.98"
config.vm.network :public_network, ip: public_ip, bridge: "en0: Ethernet"
#
config.vm.provider "virtualbox" do |vb|
vb.gui = false
vb.memory = "512"
end
#
config.vm.provision "shell", inline: <<-SHELL
apt-get update
apt-get install -y nfs-kernel-server
mkdir -p /export/users
cat >> /etc/exports <<EOF
/export 192.168.1.0/24(rw,fsid=0,insecure,no_subtree_check,async)
/export/users 192.168.1.0/24(rw,nohide,insecure,no_subtree_check,async)
EOF
exportfs -a
SHELL
end
以下のコマンドで、NFSサーバーが起動すると、ポッドからNFSマウントできる状態になります。
$ vagrant up
ストレージについて
ストレージを管理する仕組みは、ポッドやデプロイメントを管理する仕組みから独立しています。 このストレージのサブシステムである PersistantVolumeサブシステムは、永続ボリュームを提供する方法を抽象化して、ユーザーが共通の方法で永続ボリュームを利用できる様にします。このために、二つのAPIが提供されており、それぞれの概要は以下になります。
**PersistentVolume(PV)**は、k8sクラスタの管理者によってプロビジョニングされたk8sクラスタ内のストレージです。 この記事の例では、管理者によってプロビジョニングされたNFSのボリュームをk8sクラスタで利用できる様に定義します。 具体的には固有の情報となり、ボリュームの提供方法であるNFSを指定, NFSサーバーのIPアドレス、そして、認証情報などを設定します。
**PersistentVolumeClaim(PVC)**は、ユーザによるストレージの要求(クレーム)です。このクレームは、サイズとアクセスモードを要求することができます。ただし、今回の例の様に、プロビジョニング時点でキャパシティが決められている場合には、PVCの指定は効力がありません。そして、アクセスモードとは以下の指定をすることができますが、今回はPVのNFSと結びつける様に設定しますから、形だけのものになります。
- ReadWriteOnce – the volume can be mounted as read-write by a single node
- ReadOnlyMany – the volume can be mounted read-only by many nodes
- ReadWriteMany – the volume can be mounted as read-write by many nodes
このPersistentVolumeClaimsは、ユーザが抽象的な記憶資源を利用することを可能しますが、NFS、GlusterFs, iSCSIなど様々な実装方法があり、このアクセスモードやサイズの指定では、ユーザーがボリュームの実装の詳細を考慮することなく、適切なボリュームを割り当てるためのStorageClassのリソースが提供されています。
参考資料: https://kubernetes.io/docs/concepts/storage/persistent-volumes/
PVの作成
ここでは前述の様に、k8sクラスタ内から利用できる様に、NFSへのアクセスに関する詳細な設定に名前(nfs-1)を付与して定義します。 今回の例では、詳細はIPアドレスとエクスポートのパスです。 NFSサーバーのアクセス制御は、IPアドレス範囲で指定したので、ユーザーIDやパスワードの設定はありません。 また、サイズの指定していますが効力は無く、NFSサーバーでexportしたファイルシステムの容量が優先されます。
PVCからPVを明示的に指定できる様に、metadata.labels.nameを設定します。 PVCではセレクターの設定にこのラベルを利用します。
apiVersion: v1
kind: PersistentVolume
metadata:
name: nfs-1
labels:
name: pv-nfs-1
spec:
capacity:
storage: 100Mi
accessModes:
- ReadWriteMany
nfs:
server: 192.168.1.98
path: "/export"
PV作成のYAML適用と確認は、以下の様にします。
$ kubectl create -f pv-nfs.yaml
persistentvolume "nfs-1" created
$ kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
nfs-1 100Mi RWX Retain Available 5s
PVCの作成
ユーザーがポッドに指定するための設定と、事前にプロビジョニングしたPVを対応づける設定します。 spec.accessModesやspec.resources.requestsの指定は、ここでは有効に利用される事はありません。 ポッドの作成時に、metadata.name:の nfs-1 を指定してPVCをアクセスします。
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: nfs-1
spec:
accessModes:
- ReadWriteMany
storageClassName: ""
resources:
requests:
storage: "100Mi"
selector:
matchLabels:
name: pv-nfs-1
PVC作成も同意な方法で、以下の様に実行します。
$ kubectl create -f pvc-nfs.yaml
persistentvolumeclaim "nfs-1" created
$ kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
nfs-1 Bound nfs-1 100Mi RWX 4s
ポッドからのマウント
次のYAMLは、ReplicationController を利用していて、Version 1.10 を利用する現在では、Deploymentを利用するべきと思いますが、そのまま利用できますので、これを利用して、PVC nfs-1 を利用するNFSクライアントのポッドを2つ作成します。 PVCのnfs-1の指定は、このYAMLの最終行にあり、claimNameの値にあります。
apiVersion: v1
kind: ReplicationController
metadata:
name: nfs-web
spec:
replicas: 2
selector:
role: web-frontend
template:
metadata:
labels:
role: web-frontend
spec:
containers:
- name: web
image: nginx
ports:
- name: web
containerPort: 80
volumeMounts:
- name: nfs
mountPath: "/usr/share/nginx/html"
volumes:
- name: nfs
persistentVolumeClaim:
claimName: nfs-1
次のコマンドで実行します。
$ kubectl create -f nfs-client.yaml
replicationcontroller "nfs-web" created
replicas: 2
を指定したので、k8s2とk8s3のワーカーノード上にそれぞれ、ポッドが作成されていることが判ります。
vagrant@k8s1:/vagrant/yaml$ kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE
nfs-web-2slrx 1/1 Running 0 7m 10.244.2.57 k8s3
nfs-web-8g9zr 1/1 Running 0 7m 10.244.1.73 k8s2
<省略>
ポッドの一つで、対話的にシェルを動かして、NFSサーバーがマウントされている事を確認します。 ボリュームの容量に注目してください。 100Miの指定は有効でない事を確認してみてください。
$ kubectl exec -it nfs-web-2slrx sh
# df -h
Filesystem Size Used Avail Use% Mounted on
none 9.7G 2.0G 7.7G 21% /
tmpfs 1001M 0 1001M 0% /dev
tmpfs 1001M 0 1001M 0% /sys/fs/cgroup
/dev/sda1 9.7G 2.0G 7.7G 21% /etc/hosts
shm 64M 0 64M 0% /dev/shm
192.168.1.98:/export 9.7G 1.1G 8.6G 11% /usr/share/nginx/html
tmpfs 1001M 12K 1001M 1% /run/secrets/kubernetes.io/serviceaccount
tmpfs 1001M 0 1001M 0% /sys/firmware
#
まとめ
NFSサーバーのファイル・システムを、PersistentVolumeとして詳細を定義して、PersistentVolumeClaimで抽象化レイアを指定して、ポッドからNFSマウントできる事を確認しました。 そして、ポッドは、コンテナをホストしているカーネルのサポートを必要としますから、 パブリック・クラウド や k8sソフトウェア製品では、自動的にセットアップされるので、心配ないと思いますが、今回の例の様に、CNCFからダウンロードするケースでは、ワーカーノード k8s2, k8s3にNFSクライアントのパッケージを追加しなければならない事に注意を払う必要があることが判りました。
やっぱり、PVCだけで実行するダイナミック・プロビジョニングも試してみたいですね。。。