はじめに
【2022/03/18追記】NFSを利用したPVの構築は、RWX(ReadWriteMany)モードを簡単に実現できるので便利です。以前は、Rook/Ceph がRWXモードをサポートしなかったり、CephFilesystem が一つしか使えないなどの制約があって、NFSを利用していました。現在では Rook/Ceph がRWXをサポートするようになっていますので、既存のNFSサーバーを再利用したい、各ワーカー・ノードに空のドライブが接続されていない、といった理由がなければ Rook/Ceph の利用をお勧めします。Rook/Cephを利用すれば、BlockStorageだけでなく、CephFilesystemでも、任意のサイズでコンテナから利用できる永続化デバイスを作成でき、後からサイズを拡張(resize)することも簡単にできます。
いろいろアプリケーションをデプロイするため、cephfsを利用しようと思ったのですが、まずはお手軽なNFSから手をつけてみました。
お題として、k8sのドキュメントにあるWordpressのデプロイをkubesprayで準備したクラスターに適用してみます。
試してみてわかった事
1つのPV(PersistentVolumes)に複数のPVC(PV-Claim)を割り当てるといった事ができません。
そのためNFSサーバーで公開(export)した領域にサブディレクトリを作成し、k8s側ではこのサブディレクトリをPVとして定義しています。
パフォーマンス上の懸念はありますので、本番環境では物理ディスクを分けて複数のNFS領域を公開したり、専用マシンを準備したりする必要があると思われます。
テストで利用する分には、この方法はそれなりに有用だと思います。特に小さい領域を複数PVCとして確保したい場合には、便利です。それでも、Rook/Cephに慣れてしまうと、もう戻れないと思います。
以前のRook/CephではRWXな領域の確保ができなかったのですが、最新版では複数のファイルシステムが定義できるのでRWX接続が必要であってもNFS接続を積極的に利用する理由はなくなりました。
それでもiSCSI環境が整備されていたり、既設のNFSサーバーインフラがあれば、この方法もまだ役に立つかもしれません。
2020//03/23追記: ".metadata.annotations.volume.beta.kubernetes.io/storage-class"に"slow" を指定する方法は古く、現在では".spec.storageClassName"で"slow"を指定する方法が正しい表記です。詳しくは本家のドキュメントを参照してください。 In the past, the annotation volume.beta.kubernetes.io/storage-class was used instead of the storageClassName attribute. This annotation is still working; however, it will become fully deprecated in a future Kubernetes release.
NFS Serverの準備
外部に専用のNFSサーバーを準備した例と、k8sノードの1台をNFSサーバーにした例の2つで試しています。
それぞれUbuntu 16.04 LTSが前提です。
2020/03/23追記: 18.04 LTSをNFSサーバーとする場合でも同様に動作します。
$ sudo apt-get install nfs-kernel-server
適当なディレクトリのexport
今回は /export/nfs/
を利用することにします。IPアドレス(192.168.0.0/16
)の部分は環境に合わせて、192.168.1.0/24や10.0.0.0/8などに適宜変更してください。
$ sudo mkdir /export/nfs
$ sudo chmod 1777 /export/nfs/.
$ echo "/export/nfs 192.168.0.0/16(rw,async,no_root_squash) 127.0.0.1/32(rw,async,no_root_squash)" | tee -a /etc/exports
$ sudo systemctl restart nfs-server rpcbind
$ sudo systemctl enable nfs-server rpcbind
PVとして利用するサブディレクトリの作成
例として100個分のディレクトリを作成します。
$ i=1;i_max=100; while test $i -le ${i_max}; do j=$(printf "%04d" $i); sudo mkdir /export/nfs/pv$j ; i=$(($i+1)) ; done
次のように大量の空ディレクトリができました。
$ ls /export/nfs/
pv0001/ pv0010/ pv0019/ pv0028/ pv0037/ pv0046/ pv0055/ pv0064/ pv0073/ pv0082/ pv0091/ pv0100/
pv0002/ pv0011/ pv0020/ pv0029/ pv0038/ pv0047/ pv0056/ pv0065/ pv0074/ pv0083/ pv0092/
pv0003/ pv0012/ pv0021/ pv0030/ pv0039/ pv0048/ pv0057/ pv0066/ pv0075/ pv0084/ pv0093/
...
k8sノードへのnfs-commonパッケージの導入
後の工程でkubectl describe pods <podsname>
を実行したところ、k8sのノードからNFSサーバーにアクセスできていないことが分かりました。
Ubuntu 16.04 LTSを利用して、Kubesprayでインスタンスを構築した場合には、nfs-commonパッケージの導入が必要になります。
nfs-commonパッケージをk8sの全ノードにインストールしておきます。
実際にはansibleコマンドを利用して全台に導入しています。
$ sudo apt-get install nfs-common
or
$ ansible all -i inventory/mycluster/hosts.ini -b -m apt -e "name='nfs-common'"
PV(PersistentVolume)の割り当て
NFSサーバーのIPアドレスは適宜変更してください。
サーバー上ではあらかじめ/export/nfs/pv0001を作成しています。
volume.beta.kubernetes.io/storage-class: "slow"
を指定しているのは、SSDで準備した領域やRook/Cephなどと区別するためで、"slow" である必要はありませんが、何らかのannotationを加えることをお勧めします。
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv0001
annotations:
volume.beta.kubernetes.io/storage-class: "slow"
spec:
capacity:
storage: 5Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Recycle
nfs:
server: 192.168.1.1
path: /export/nfs/pv0001
ここで作成したファイルをkubectlで適用してみます。
$ kubectl create -f 01.create-persistent-volume.yaml
終わったら必ず $ kubectl get pv
で確認します。
pv0001ディレクトリが存在しないなど、何か問題があるとStatusがAvailableになりません。
ただStatusがAvailableになるまで、少し時間がかかる場合もあるようです。
$ kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
pv0001 5Gi RWO Recycle Available slow 18h
続いて、PVCを作成していきます。
この定義は公式ドキュメントのExampleから抜き出していますが、annotationsを追加しているのと、割り当てサイズを20Giから5Giに変更しています。
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: mysql-pv-claim
labels:
app: wordpress
annotations:
"volume.beta.kubernetes.io/storage-class": "slow"
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 5Gi
PVと同様に適用しています。
annotationsやaccessModesがPV作成時の定義とマッチしないと割り当てに失敗します。今回は"slow"を指定しているので、必ずこの例と同じようにannotationsを指定してください。
$ kubectl create -f 02.create-persitent-claim.yaml
ここまで進めて、kubectlで割り当て状況を確認します。
$ kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
pv0001 20Gi RWO Recycle Bound wordpress/mysql-pv-claim slow 25m
$ kubctl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
mysql-pv-claim Bound pv0001 20Gi RWO slow 25m6
作成したmysql-pv-claimを利用したWordpress用のMysqlのデプロイメント
公式ドキュメントのExampleにあるYAMLファイルを実行していきますが、PVCの割り当ても同時行なっているので、その箇所は手元で削ったファイルを実行しています。
apiVersion: apps/v1
kind: Deployment
metadata:
name: wordpress-mysql
labels:
app: wordpress
spec:
selector:
matchLabels:
app: wordpress
tier: mysql
strategy:
type: Recreate
template:
metadata:
labels:
app: wordpress
tier: mysql
spec:
containers:
- image: mysql:5.6
name: mysql
env:
- name: MYSQL_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: mysql-pass
key: password
ports:
- containerPort: 3306
name: mysql
volumeMounts:
- name: mysql-persistent-storage
mountPath: /var/lib/mysql
volumes:
- name: mysql-persistent-storage
persistentVolumeClaim:
claimName: mysql-pv-claim
---
apiVersion: v1
kind: Service
metadata:
name: wordpress-mysql
labels:
app: wordpress
spec:
ports:
- port: 3306
selector:
app: wordpress
tier: mysql
clusterIP: None
同様にkubectl create -f
の引数に渡して実行します。
nfs-commonパッケージが各ノードに導入されていれば問題はないと思いますが、Pendingのまま有効にならないといった事があれば、$ kubectl describe
を利用してデバッグすることが必要になります。
公式ドキュメントのExampleとの相違点
この後、ExampleはWordpressの導入に進みますが、その中でもPVCの定義があるので、同様にannotationsセクションの追加とサイズの変更が必要になりますが、それ以外はほぼExampleのままです。
軽微な違いとして、最後のEXTERNAL-IPの割り当てとアクセス用URLのところで、別の記事でMetalLBを準備しているので、自動的に割り当てられ、ポート番号は80番となっています。
ちなみに、公式ドキュメントには、この他にもStatless Applicationの例としてPHPのMessageBoardをデプロイする記事がありますが、こちらは、そのままYAMLファイルを順番に実行していけば準備が完了します。
【追記】ReadWriteOnce, ReadWriteManyなど数種類のPVを準備してみた
実際に公式のExampleなどを実施しようとすると、20Giを要求していたり、Webサーバーのクラスターを構成したりすると、ReadWriteOnceに加えてReadWriteManyも欲くなったり、いくつかの種類のPVをあらかじめ準備する必要がでてきました。
今回のようにpv0000-pv0099まで100ディレクトリを準備しておいて、その中にReadWriteOnly(RWO)、ReadWriteMany(RWX)を混在させようとして次のようなテンプレートとスクリプトファイルを準備しました。
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv__N__
annotations:
volume.beta.kubernetes.io/storage-class: "slow"
spec:
capacity:
storage: 20Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Recycle
nfs:
server: 192.168.1.1
path: /export/nfs/pv__N__
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv__N__
annotations:
volume.beta.kubernetes.io/storage-class: "slow"
spec:
capacity:
storage: 20Gi
accessModes:
- ReadWriteMany
persistentVolumeReclaimPolicy: Recycle
nfs:
server: 192.168.1.1
path: /export/nfs/pv__N__
作成したファイルを次のようなスクリプトの冒頭の変数に並べて、ファイルの数に応じたPVの定義を作成するYAMLファイルを生成させます。
#!/bin/bash
## variable definition
declare -a TEMPLLIST=("template01.yaml" "template02.yaml")
i=0
i_max=100
## main-loop
while test "${i}" -lt "${i_max}"
do
r="$((${i} % ${#TEMPLLIST[@]}))" ## r <= 0 or 1
n="$(printf %04d ${i})" ## n <= 0001 ... 0099
echo "---"
sed -e s/__N__/${n}/ "${TEMPLLIST[${r}]}"
i=$(($i+1))
done
作成したファイルは$ kubectl apply -f
で適用します。
Rook/Cephを良く使っていますが、このBlock StorageではRWXなアクセスモードが機能しないので、StatefulSetやDeploymentで作成した複数のレプリカでディスクを共有したい場合にはNFS Storageもまだまだ使えると思います。
以上