はじめに
Dynamic Provisioningの動作を確認するために、StorageClassの設定が必要になります。
これまでのPersistentVolume(PV)/PersistentVolumeClaim(PVC)の操作ではPVを事前に作成してましたが、Dynamic Provisioningを使ってClaimする場合、PVが動的に作成されるようになります。
今回はこのStorageClassを設定して、Dynamic Provisioningの動作を確認したいと思います。
Storageの準備
StorageClassを設定する際に、Provisionerを指定する必要があります。
Provisionerは利用する各クラウドサービスなどのVolumePluginに応じて指定します。残念ながらクラウドサービスを利用する予算がありませんので、ここはローカル環境にZFSを構築したいと思います。
ZFSはSolarisで慣れ親しんだファイルシステム兼ボリューム管理ツールで、Linuxにも移植されています。今回はCentOS 7上に構築します。
ZFSストレージの構築
少々手こずってしまったので、ログが整理できていません。。。
これらを参考に構築しました。検索すると他にも出てきます。
https://github.com/openzfs/zfs/wiki/RHEL-and-CentOS
https://ameblo.jp/mikan2k/entry-12319758936.html
http://bobgosso.blog13.fc2.com/blog-entry-547.html
構築した環境はこちらです。
[zfs_server]$ cat /etc/redhat-release
CentOS Linux release 7.8.2003 (Core)
[zfs_server]$ zpool list
NAME SIZE ALLOC FREE CKPOINT EXPANDSZ FRAG CAP DEDUP HEALTH ALTROOT
pool1 1.88G 273K 1.87G - - 0% 0% 1.00x ONLINE -
[zfs_server]$ zpool status
pool: pool1
state: ONLINE
scan: none requested
config:
NAME STATE READ WRITE CKSUM
pool1 ONLINE 0 0 0
sdb ONLINE 0 0 0
sdc ONLINE 0 0 0
errors: No known data errors
[zfs_server]$ zpool version
zfs-0.8.3-1
zfs-kmod-0.8.3-1
[zfs_server]$ zpool upgrade
This system supports ZFS pool feature flags.
All pools are formatted using feature flags.
Every feature flags pool has all supported features enabled.
CentOSでZFSが使えるとは感激です。
1GBの内蔵ディスク2本でストライピングという可用性も何もない構成ですが、検証用ですしいいでしょう。そもそも同じPCのHDDから切り出した仮想ディスクですしね。
versionの表示とかがSolarisとは異なりますね。今回はZFSの検証がメインではないので、ZFSについてはこの辺にしておきます。
Provisionerの設定
構築したZFSに対して、以下に沿ってProvisionerの設定をしていきます。
環境構築
GO言語を使うんですね。初めて使います。
パッケージをインストールします。
[zfs_server]# yum install golang-go go-dep
読み込んだプラグイン:fastestmirror, langpacks
Loading mirror speeds from cached hostfile
epel/x86_64/metalink
・・・
インストール:
golang-bin.x86_64 0:1.13.6-1.el7
依存性関連をインストールしました:
golang.x86_64 0:1.13.6-1.el7 golang-src.noarch 0:1.13.6-1.el7 mercurial.x86_64 0:2.6.2-11.el7
完了しました!
[zfs_server]# yum install git mercurial
読み込んだプラグイン:fastestmirror, langpacks
Loading mirror speeds from cached hostfile
* base: ftp-srv2.kddilabs.jp
* epel: ftp.jaist.ac.jp
* extras: ftp-srv2.kddilabs.jp
* updates: ftp-srv2.kddilabs.jp
パッケージ git-1.8.3.1-22.el7_8.x86_64 はインストール済みか最新バージョンです
パッケージ mercurial-2.6.2-11.el7.x86_64 はインストール済みか最新バージョンです
何もしません
使用するバイナリなどをCloneします。
[zfs_server]# git clone https://github.com/gentics/kubernetes-zfs-provisioner
Cloning into 'kubernetes-zfs-provisioner'...
remote: Enumerating objects: 232, done.
remote: Total 232 (delta 0), reused 0 (delta 0), pack-reused 232
Receiving objects: 100% (232/232), 54.06 KiB | 0 bytes/s, done.
Resolving deltas: 100% (110/110), done.
GOPATHなどを設定します。
[zfs_server]# mkdir -p ~/go
[zfs_server]# export GOPATH=$HOME/go
[zfs_server]# mkdir $GOPATH/bin
[zfs_server]# export PATH=$PATH:$GOPATH/bin
[zfs_server]# mkdir -p $GOPATH/src
[zfs_server]# ln -s ~/kubernetes-zfs-provisioner $GOPATH/src/kubernetes-zfs-provisioner
[zfs_server]# cd $GOPATH/src/kubernetes-zfs-provisioner
depをインストールします。
[zfs_server]# curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 5230 100 5230 0 0 5032 0 0:00:01 0:00:01 --:--:-- 5038
ARCH = amd64
OS = linux
Will install into /root/go/bin
Fetching https://github.com/golang/dep/releases/latest..
Release Tag = v0.5.4
Fetching https://github.com/golang/dep/releases/tag/v0.5.4..
Fetching https://github.com/golang/dep/releases/download/v0.5.4/dep-linux-amd64..
Setting executable permissions.
Moving executable to /root/go/bin/dep
インストール、ビルドします。
[zfs_server]# dep ensure
dep: WARNING: branch, version, revision, or source should be provided for "github.com/spf13/viper"
dep: WARNING: branch, version, revision, or source should be provided for "k8s.io/apimachinery"
dep: WARNING: branch, version, revision, or source should be provided for "k8s.io/client-go"
[zfs_server]# make build
mkdir -p bin
env GOOS=linux go build -o bin/zfs-provisioner cmd/zfs-provisioner/main.go
WARNINGは出ていますが、インストールはできたようです。
デプロイ/インストール
systemd service fileの作成
以下のファイルを作成します。
[Unit]
Description=kubernetes zfs provisioner
After=nfs-kernel-server.service
After=kubelet.service
[Service]
TimeoutStartSec=0
Restart=on-failure
Environment=ZFS_PARENT_DATASET=pool1/pv
Environment=ZFS_SHARE_SUBNET=10.xx.xx.xx/24
Environment=ZFS_SERVER_HOSTNAME=10.xx.xx.yy
Environment=ZFS_KUBE_CONF=/etc/kubernetes/admin.conf
Environment=ZFS_KUBE_RECLAIM_POLICY=Retain
Environment=ZFS_METRICS_PORT=8081
ExecStart=/usr/local/bin/zfs-provisioner
[Install]
WantedBy=multi-user.target
変数 | 概要 |
---|---|
ZFS_PARENT_DATASET | PVが作成されるデータセットを指定します。この指定するデータセットはあらかじめ作成しておきます。 |
ZFS_SHARE_SUBNET | ZFS ServerとKubernetesクラスタがあるサブネットを指定します。 |
ZFS_SERVER_HOSTNAME | ZFS Serverのホスト名、またはIPアドレスを指定します。 |
ZFS_KUBE_CONF | Kubernetesクラスタへ接続する際に使用するコンフィグファイルを指定します。 |
ZFS_KUBE_RECLAIM_POLICY | Reclaim Policyを指定します。「Delete」もしくは「Retain」が指定できます。 |
ZFS_METRICS_PORT | PrometheusのメトリクスをExportするポートを指定します。 |
zfs-provisionerのコピー
ビルドしたzfs-provisionerを/usr/local/bin配下にコピーします。
[zfs_server]# cp kubernetes-zfs-provisioner/bin/zfs-provisioner /usr/local/bin/
kubernetes configuration fileのコピー
Masterノードのkubernetes configuration fileをZFS ServerのZFS_KUBE_CONFで指定したディレクトリにコピーします。
[k8s-master ~]$ sudo scp /etc/kubernetes/admin.conf user-name@zfs_server:~
user-nanme@zfs_server's password:
admin.conf 100% 5451 175.3KB/s 00:00
[zfs_server]$ sudo mkdir /etc/kubernetes
[zfs_server]$ sudo mv admin.conf /etc/kubernetes/
zfs-provisioner serviceの起動
[zfs_server]# systemctl start zfs-provisioner.service
[zfs_server]# systemctl status zfs-provisioner.service
● zfs-provisioner.service - kubernetes zfs provisioner
Loaded: loaded (/etc/systemd/system/zfs-provisioner.service; disabled; vendor preset: disabled)
Active: active (running) since 土 2020-05-09 22:48:08 JST; 13s ago
Main PID: 4241 (zfs-provisioner)
Tasks: 5
CGroup: /system.slice/zfs-provisioner.service
mq4241 /usr/local/bin/zfs-provisioner
[zfs_server]# systemctl enable zfs-provisioner.service
Created symlink from /etc/systemd/system/multi-user.target.wants/zfs-provisioner.service to /etc/systemd/system/zfs-provisioner.service.
StorageClassとPodの作成
Provisionerの設定が終わりましたので、StorageClassとPodを作成していきます。
namespaceの作成
apiVersion: v1
kind: Namespace
metadata:
name: zfs-system
$ kubectl apply -f zfs-namespace.yml
namespace/zfs-system created
StorageClassの作成
kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
name: zfs-sc
namespace: zfs-system
provisioner: gentics.com/zfs
reclaimPolicy: Retain
$ kubectl apply -f storageclass.yml
storageclass.storage.k8s.io/zfs-sc created
$ kubectl get storageclasses.storage.k8s.io
NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE
zfs-sc gentics.com/zfs Retain Immediate false 13s
PVCの作成
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: example-pvc
namespace: zfs-system
annotations:
volume.beta.kubernetes.io/storage-class: "zfs-sc"
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 100Mi
$ kubectl apply -f pvc.yml
persistentvolumeclaim/example-pvc created
$ kubectl -n zfs-system get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
example-pvc Bound pvc-dd4120d9-f4d9-4018-b917-2d9efe9085f6 100Mi RWX zfs-sc 7s
PVが作成されました。
ZFS Server側でも確認してみます。
[zfs_server]$ zfs list
NAME USED AVAIL REFER MOUNTPOINT
pool1 100M 1.65G 25.5K /pool1
pool1/pv 100M 1.65G 25.5K /pool1/pv
pool1/pv/pvc-dd4120d9-f4d9-4018-b917-2d9efe9085f6 100M 100M 24K /pool1/pv/pvc-dd4120d9-f4d9-4018-b917-2d9efe9085f6
指定したデータセット配下に新しいデータセットが作られていますね。また、PVCで指定した容量になっています。
[zfs_server]$ zfs get type pool1/pv/pvc-dd4120d9-f4d9-4018-b917-2d9efe9085f6
NAME PROPERTY VALUE SOURCE
pool1/pv/pvc-dd4120d9-f4d9-4018-b917-2d9efe9085f6 type filesystem -
ZVOL(Volume)が作成されるのかと思いましたが、ファイルシステムが作成されていますね。
Podの作成と動作確認
PVCまでが作成できましたので、Podを作成して動作を確認します。
kind: Pod
apiVersion: v1
metadata:
name: nginx-example
namespace: zfs-system
spec:
containers:
- name: nginx-example
image: nginx:latest
volumeMounts:
- name: zfs-volume
mountPath: /cache
volumes:
- name: zfs-volume
persistentVolumeClaim:
claimName: example-pvc
$ kubectl apply -f nginx-pod.yaml
pod/nginx-example created
$ kubectl -n zfs-system exec -it nginx-example /bin/bash
root@nginx-example:/# df -h
Filesystem Size Used Avail Use% Mounted on
overlay 17G 7.7G 9.4G 46% /
tmpfs 64M 0 64M 0% /dev
tmpfs 1.4G 0 1.4G 0% /sys/fs/cgroup
k8s-client:/pool1/pv/pvc-dd4120d9-f4d9-4018-b917-2d9efe9085f6 100M 0 100M 0% /cache
/dev/mapper/centos-root 17G 7.7G 9.4G 46% /etc/hosts
shm 64M 0 64M 0% /dev/shm
tmpfs 1.4G 12K 1.4G 1% /run/secrets/kubernetes.io/serviceaccount
tmpfs 1.4G 0 1.4G 0% /proc/acpi
tmpfs 1.4G 0 1.4G 0% /proc/scsi
tmpfs 1.4G 0 1.4G 0% /sys/firmware
root@nginx-example:/# touch /cache/testfile
touch: cannot touch '/cache/testfile': Permission denied
マウントはできていますが、Permission denied
んー。
試しに、ZFS Server側でファイルを作成して読み込めるか試してみます。
root@nginx-example:/# ls /cache/
testfile
root@nginx-example:/# cat /cache/testfile
hello from zfs server
読み込みはできるようです。
調査
kubernetes-zfs-provisionerのREADMEには以下の一文があります。作成したZFSのデータセットはNFSでShareしているようですので、NFS周りの設定を確認してみます。
It creates ZFS datasets and shares them via NFS to make them mountable to pods.
ZFS Serverの確認と設定変更
PVCによって作成されたデータセットのNFSの設定値を確認します。
[zfs_server]# zfs get sharenfs pool1/pv/pvc-dd4120d9-f4d9-4018-b917-2d9efe9085f6
NAME PROPERTY VALUE SOURCE
pool1/pv/pvc-dd4120d9-f4d9-4018-b917-2d9efe9085f6 sharenfs rw=@10.0.0.0/8 local
「rw=@10.0.0.0/8」になっていますが、これはデフォルト値ですね。
systemd service fileで指定できるのですが、指定していないためデフォルト値になっています。
これはこれで大丈夫そうなのですが、root権限でマウントしてないのかなと思って調べてみました。ZFSのsharenfsはデフォルトでroot権限でマウントできるようになっているという記事があったのですが、明示的にroot権限を与えてみたいと思います。
systemd service fileを編集します。
[Unit]
Description=kubernetes zfs provisioner
After=nfs-kernel-server.service
After=kubelet.service
[Service]
TimeoutStartSec=0
Restart=on-failure
Environment=ZFS_PARENT_DATASET=pool1/pv
Environment=ZFS_SHARE_SUBNET=10.20.30.0/24
Environment=ZFS_SERVER_HOSTNAME=k8s-client
Environment=ZFS_KUBE_CONF=/etc/kubernetes/admin.conf
Environment=ZFS_KUBE_RECLAIM_POLICY=Retain
Environment=ZFS_METRICS_PORT=8081
Environment=ZFS_SHARE_OPTIONS=rw=@k8s-master,rw=@k8s-worker01,rw=@k8s-worker02,no_root_squash #追記
ExecStart=/usr/local/bin/zfs-provisioner
[Install]
WantedBy=multi-user.target
ZFS_SHARE_OPTIONSを追記しました。「no_root_squash」を指定して、root権限でマウントできるようにします。
また、マウントできるノードをKubernetesクラスタを構成するノードのみにしておきます。
サービスをリスタートします。
[zfs_server]# systemctl daemon-reload
[zfs_server]# systemctl restart zfs-provisioner.service
PVC/Podの再作成
PVC/Podは削除して同じマニフェストで再作成しました。
$ kubectl -n zfs-system get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
example-pvc Bound pvc-a3c27f70-3afa-470b-8f4e-2a1a5f5ab91b 100Mi RWX zfs-sc 4s
$ kubectl -n zfs-system get pod
NAME READY STATUS RESTARTS AGE
nginx-example 1/1 Running 0 13s
ZFS Server側でsharenfsの値を確認しておきます。
[zfs_server]# zfs get sharenfs pool1/pv/pvc-a3c27f70-3afa-470b-8f4e-2a1a5f5ab91b
NAME PROPERTY VALUE SOURCE
pool1/pv/pvc-a3c27f70-3afa-470b-8f4e-2a1a5f5ab91b sharenfs rw=@k8s-master,rw=@k8s-worker01,rw=@k8s-worker02,no_root_squash local
想定通りに設定できていますね。
コンテナにログインして、書き込みできるか確認します。
$ kubectl -n zfs-system exec -it nginx-example /bin/bash
root@nginx-example:/# df -h
Filesystem Size Used Avail Use% Mounted on
overlay 17G 7.7G 9.4G 46% /
tmpfs 64M 0 64M 0% /dev
tmpfs 1.4G 0 1.4G 0% /sys/fs/cgroup
k8s-client:/pool1/pv/pvc-a3c27f70-3afa-470b-8f4e-2a1a5f5ab91b 100M 0 100M 0% /cache
/dev/mapper/centos-root 17G 7.7G 9.4G 46% /etc/hosts
shm 64M 0 64M 0% /dev/shm
tmpfs 1.4G 12K 1.4G 1% /run/secrets/kubernetes.io/serviceaccount
tmpfs 1.4G 0 1.4G 0% /proc/acpi
tmpfs 1.4G 0 1.4G 0% /proc/scsi
tmpfs 1.4G 0 1.4G 0% /sys/firmware
root@nginx-example:/# echo "hello from nginx-example" > /cache/testfile
root@nginx-example:/# cat /cache/testfile
hello from nginx-example
書き込みできました!
まとめ
Dynamic Provisioningを利用するとPVを予め作成する手間が省けます。また、PVCがPVを要求する際は「こういうPV」という指定ですので、要求よりも大きいサイズのPVがBoundされることもあり、これも回避できますね。
今回はCentOS上でZFSを構築して、それをProvisionerにしてPVを作成するという、Production環境ではありえないだろう構成で検証しました。ただ、個人的には久しぶりにZFSを使えて懐かしさを感じながら楽しくできたので、とても満足しています。
追記(allowVolumeExpansion)
StrageClassのパラメータの「allowVolumeExpansion」を「true」にすることによってPVを拡張することができます。
allowVolumeExpansionをサポートしているVolume Typeは以下に記載されています。もちろん、ZFSは記載されていません。試してみましたが、できませんでした。
予想通りではあります。
マニフェストに「allowVolumeExpansion: true」を追記するだけです。
kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
name: zfs-sc-resize
namespace: zfs-system
provisioner: gentics.com/zfs
reclaimPolicy: Retain
allowVolumeExpansion: true
StrageClassは作成できますが、PVCを作成後、PVCの容量の変更はできませんでした。
$ kubectl -n zfs-system describe pvc example-pvc-resize
Name: example-pvc-resize
・・・
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal ExternalProvisioning 21m (x2 over 21m) persistentvolume-controller waiting for a volume to be created, either by external provisioner "gentics.com/zfs" or manually created by system administrator
Normal Provisioning 21m gentics.com/zfs k8s-client 47e7e117-9385-11ea-9d63-08002777e78c External provisioner is provisioning volume for claim "zfs-system/example-pvc-resize"
Normal ProvisioningSucceeded 21m gentics.com/zfs k8s-client 47e7e117-9385-11ea-9d63-08002777e78c Successfully provisioned volume pvc-7c2c5169-5cd6-407e-ab41-21ec0ad6f08e
Warning ExternalExpanding 90s volume_expand Ignoring the PVC: didn't find a plugin capable of expanding the volume; waiting for an external controller to process this PVC.
pluginがないとWarningが出てますね。
ZFS自体はファイルシステムの拡張はできますので、作り込めばできるんでしょうが、そこまではね。