Rook-Ceph における RBD(RADOS Block Device)がどのようにプロビジョニングされ、どのようにノード上に配置されているのかを調べてみた。本記事は CephFS や RadosGW については説明しない。
Ceph 概要
Ceph はストレージクラスタを構成するための OSS である。Ceph は主に OSD および MON と呼ばれる重要なコンポーネントからなる。
OSD (Object Storage Device)
OSD は、Ceph クラスタ内部で直接的にストレージを管理するコンポーネントである。OSD は BlueStore と呼ばれる独自のフォーマットを用いてストレージを消費し、実際のデータは RADOS object という単位で保存される。各 OSD は、自身に割り当てられたストレージ上にデータを分散配置し、クラスター全体でのデータの冗長性と可用性を確保する。RADOS object の配置は固定的ではなく、OSD の追加や削除が発生した際に Rebalance と呼ばれる処理によってデータが再分配され、ストレージの利用効率が最適化される。
MON (Monitor)
MON は、Ceph クラスタの管理やモニタリングを担うコンポーネントである。特に、CRUSH マップと呼ばれる情報の管理し、一貫性を保つ上で重要な役割を果たしている。
RADOS オブジェクトの配置および読み出し時のファイルの復元は CRUSH アルゴリズムに基づいて行われるが、このプロセスには、RADOS オブジェクトの配置やストレージの階層構造が定義された CRUSH マップを参照する必要がある。MON は、 CRUSH マップの一貫性を維持することで、管理する。これにより、クラスター内でのデータの正確な配置が担保される。
RBD (RADOS Block Device)
RBD (RADOS Block Device) は、Ceph が提供する仮想ブロックストレージであり、実態としては RADOS object として OSD に格納されている。RBD は、クライアント側からは通常のブロックデバイスのように見えるため、ext4 や xfs のような一般的なファイルシステムとしてフォーマット可能である。これを所望のディレクトリにマウントすれば、ユーザーはファイル単位でデータにアクセスすることができる。
Rook-Ceph 概要
Rook-Cephは、Ceph クラスターを Kubernetes で簡単に構築・管理するための OSS である。簡単に表現すれば、Ceph を Kubernetes のエコシステムに組み込むために、CSI と Operator の仕組みを追加したものと言える。以下、CSI について少し触れる。
CSI (Container Storage Interface)
CSIは、Kubernetesを含むあらゆるコンテナオーケストレーションシステムとストレージシステムの間で標準化されたインターフェースである。CSI はプラグイン方式を採用しており、様々なストレージベンダーが CSI plugin を実装することで、Kubernetes 上でストレージプロビジョニングを統一されたやり方で提供することができる。
CSI が動作する過程については、RedHat の公式ブログに詳細な解説記事がある:
CSI plugin は storageClass に関連付けられており、PVC に基づいてノード上でストレージをダイナミック・プロビジョニングを行い、 Pod にそのストレージリソースを割り当てることができる。具体的には、CSI plugin の NodeStageVolume
および NodePublishVolume
と呼ばれる機能が該当する。
以下に、上記の記事内 "Mounting a volume" 節内の図 "Figure 10: Workflow when mounting a volume" を引用する。同図はKubelet が CSI plugin の機能を呼び出すフローを示している。
それぞれの CSI 機能の簡易な説明と、利用するマウントポイントを表にまとめると以下のようになる:
CSI の機能 | 説明 | Mount 先の path |
---|---|---|
NodePublishVolume |
Node 上の global mount point にマウントし、ストレージを用意・フォーマットする | /var/lib/kubelet/plugins/kubernetes.io/csi/<pv>/globalmount/ |
NodeStageVolume |
Pod の mount point にマウントする | /var/lib/kubelet/pods/<pod>/volumes/kubernetes.io~csi/<pvc>/mount |
RBD の管理
Rook-Ceph における CSI plugin の実装
Rook-Ceph における CSI plugin は次の GitHub レポジトリで管理されている。
例として、RBD plugin における NodeStageVolume()
を見てみると、以下のような具体的な処理を確認することができる:
rbd map
コマンドによる RBD pool から device への mapmkfs.<format-type>
コマンドによる RBD のフォーマット- global mount point へのマウント
全体的なコンポーネントの関係図
上記を踏まえて、OSD のブロックストレージと RBD へのデータの対応を図示してみると次のようになる:
ストレージリソースの提供側である Ceph が BlueStore を消費し OSD を構成している。ストレージリソースを消費するアプリケーション側は RBD を利用する。OSD-RBD 間は CRUSH アルゴリズムによる分散・統合の仕組みによって抽象化されている。
実機の確認
上記の概要で説明したような仕組みが、Kubenetes 上では実際どのように見えているのか、いくつかの項目に分けて確認してみる。
普通に Kubernetes + Rook/Ceph で環境を用意するのが王道だが、筆者は OKD4 (Community 版 OpenShift) と OpenShift Data Foundation (Rook-Ceph 含むストレージ統合基盤) の環境があるので、そちらを用いて検証する。筆者の環境構築の方法については下記の Qiita 記事も参考:
storageClass とその消費者の確認
storageClass を確認すると、次のものが確認できる。
$ oc get sc
NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE
localblock kubernetes.io/no-provisioner Delete WaitForFirstConsumer false 15h
ocs-storagecluster-ceph-rbd (default) openshift-storage.rbd.csi.ceph.com Delete Immediate true 15h
...
この storageClass に紐付く PVC リストアップする:
$ oc get pvc --all-namespace
NAMESPACE NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
openshift-storage ocs-deviceset-localblock-0-data-0tbv7k Bound local-pv-7471b93b 1Ti RWO localblock 15h
openshift-storage ocs-deviceset-localblock-0-data-1z247p Bound local-pv-af99c388 1Ti RWO localblock 15h
openshift-storage ocs-deviceset-localblock-0-data-24jph7 Bound local-pv-e9f02179 1Ti RWO localblock 15h
openshift-storage db-noobaa-db-pg-0 Bound pvc-282f19a9-68f0-49ae-b4c8-3cacf4f10c03 50Gi RWO ocs-storagecluster-ceph-rbd 15h
それぞれ PVC を発行している Pod を調べてみる。
利用する PVC の情報は Pod 側が持っているため、下記のようなスクリプトを作って探してみる:
#!/bin/bash
PVC_NAME=$1
oc get pods --all-namespaces -o yaml |\
yq e -N ".items[] | select(.spec.volumes[].persistentVolumeClaim.claimName == \"$PVC_NAME\") | .metadata.name + \" -n \" + .metadata.namespace"
それぞれの PVC に紐づく Pod を特定する:
$ ./pvc_to_pv.sh ocs-deviceset-localblock-0-data-0tbv7k
rook-ceph-osd-1-d7b7fbd64-655sq -n openshift-storage
$ ./pvc_to_pv.sh ocs-deviceset-localblock-0-data-1z247p
rook-ceph-osd-0-7d87ddb68f-57cp6 -n openshift-storage
$ ./pvc_to_pv.sh ocs-deviceset-localblock-0-data-24jph7
rook-ceph-osd-2-6f7c4b654-swsgb -n openshift-storage
$ ./pvc_to_pv.sh db-noobaa-db-pg-0
noobaa-db-pg-0 -n openshift-storage
次のことが確認できた。
- storageClass
localblock
に紐づく PV は、OSD の pod が消費している - storageClass
ocs-storagecluster-ceph-rbd
に紐づく PV は、通常のアプリケーション (例:noobaa-db-pg-0
) の pod が消費している
PV (RBD) の内容を Node 上から確認
まずはどこのノード上に RBD があるのか探してみる。PV がマウントしているパスには PV 名が含まれているため、各ノード上で lsblk
を実行して grep
に引っ掛かればよい。次のスクリプト find_rbd.sh
を用意する:
#!/bin/bash
usage()
{
echo "Usage:"
echo " ./find_rbd.sh <pvname>"
}
# Parse arguments
if [ "$#" -ne 1 ]; then
usage
exit 1
fi
pvname=$1
nodes=$(oc get nodes --no-headers)
while read -r line; do
node=$(echo "$line" | awk '{print $1}')
status=$(echo "$line" | awk '{print $2}')
if [ "$status" != "Ready" ]; then
continue
fi
lsblk_result=$(oc debug node/${node} -- chroot /host lsblk -d 2> /dev/null)
echo "${lsblk_result}" | grep "/${pvname}/" > /dev/null
if [[ $? -eq 0 ]]; then
echo "An RBD found on $node:"
# Output until the indentation ends
echo "${lsblk_result}" | awk -v pvname="${pvname}" '
{
if (found) {
if ($0 ~ /^ {32}/) {
print $0
} else {
found = 0
}
}
if ($0 ~ pvname) {
print $0
found = 1
}
}'
exit 0
fi
done <<< "$nodes"
echo RBD not found.
exit 1
まずは適当に、storageClass ocs-storagecluster-ceph-rbd
によってプロビジョニングされた pv を見繕う:
$ oc get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
...
pvc-282f19a9-68f0-49ae-b4c8-3cacf4f10c03 50Gi RWO Delete Bound openshift-storage/db-noobaa-db-pg-0 ocs-storagecluster-ceph-rbd 12h
上記のスクリプトで pv が置かれているノードを検索してみる:
$ ./find_rbd.sh pvc-282f19a9-68f0-49ae-b4c8-3cacf4f10c03
An RBD found on master1:
rbd0 251:0 0 50G 0 disk /var/lib/kubelet/pods/978a4ffb-5eb8-474d-aea0-3b0324a35791/volumes/kubernetes.io~csi/pvc-282f19a9-68f0-49ae-b4c8-3cacf4f10c03/mount
/var/lib/kubelet/plugins/kubernetes.io/csi/openshift-storage.rbd.csi.ceph.com/f3969b7dbf6aac8a24ec3aa7f9ac07f65cda149ebcceedb1446a27f234d507c8/globalmount/0001-0011-openshift-storage-0000000000000001-040c6736-a99c-4ca6-9437-fd34fdc7c584
実態の RBD はノード master1
上のデバイス /dev/rbd0
として存在している。また、CSI plugin によって、次の2箇所にマウントされていることが確認できる
次に、master1
にログインしてみる:
$ oc debug node/master1
Starting pod/master1-debug-ngtcq ...
To use host binaries, run `chroot /host`
Pod IP: 10.0.0.12
If you don't see a command prompt, try pressing enter.
sh-4.4# chroot /host
中身を確認してみる:
sh-5.2# ls -la /var/lib/kubelet/pods/978a4ffb-5eb8-474d-aea0-3b0324a35791/volumes/kubernetes.io~csi/pvc-282f19a9-68f0-49ae-b4c8-3cacf4f10c03/mount
total 32
drwxrwsr-x. 4 root root 4096 Jun 23 17:10 .
drwxr-x---. 3 root root 40 Jun 24 02:42 ..
drwxr-sr-x. 3 10001 root 4096 Jun 23 17:10 data
drwxrws---. 2 root root 16384 Jun 23 17:09 lost+found
-rw-r--r--. 1 10001 root 738 Jun 24 02:51 openshift-custom-postgresql.conf
-rw-r--r--. 1 10001 root 832 Jun 24 02:51 passwd
ついでに Disk format も確認してみる。ext4 でフォーマットされていることが確認できる。
sh-5.2# df -T /var/lib/kubelet/pods/978a4ffb-5eb8-474d-aea0-3b0324a35791/volumes/kubernetes.io~csi/pvc-282f19a9-68f0-49ae-b4c8-3cacf4f10c03/mount
Filesystem Type 1K-blocks Used Available Use% Mounted on
/dev/rbd0 ext4 51290592 67320 51206888 1% /var/lib/kubelet/pods/978a4ffb-5eb8-474d-aea0-3b0324a35791/volumes/kubernetes.io~csi/pvc-282f19a9-68f0-49ae-b4c8-3cacf4f10c03/mount
RBD のマウントオプションの確認
上記で調べた /dev/rbd0
は次の2つのマウントポイントを持っていた:
-
/var/lib/kubelet/pods/978a4ffb-5eb8-474d-aea0-3b0324a35791/volumes/kubernetes.io~csi/pvc-282f19a9-68f0-49ae-b4c8-3cacf4f10c03/mount
(Pod の mountpoint) -
/var/lib/kubelet/plugins/kubernetes.io/csi/openshift-storage.rbd.csi.ceph.com/f3969b7dbf6aac8a24ec3aa7f9ac07f65cda149ebcceedb1446a27f234d507c8/globalmount/0001-0011-openshift-storage-0000000000000001-040c6736-a99c-4ca6-9437-fd34fdc7c584
(Node の mountpoint)
それぞれのマウントオプションを調べてみる。
1.) Pod の mount option:
core@master1:~$ cat /proc/mounts | grep /var/lib/kubelet/pods/978a4ffb-5eb8-474d-aea0-3b03
tmpfs /var/lib/kubelet/pods/978a4ffb-5eb8-474d-aea0-3b0324a35791/volumes/kubernetes.io~projected/kube-api-access-729v8 tmpfs rw,seclabel,relatime,size=4194304k,inode64 0 0
/dev/rbd0 /var/lib/kubelet/pods/978a4ffb-5eb8-474d-aea0-3b0324a35791/volumes/kubernetes.io~csi/pvc-282f19a9-68f0-49ae-b4c8-3cacf4f10c03/mount ext4 rw,seclabel,relatime,stripe=16 0 0
2.) Node の mount option:
core@master1:~$ cat /proc/mounts | grep /var/lib/kubelet/plugins/kubernetes.io/csi/opens
/dev/rbd0 /var/lib/kubelet/plugins/kubernetes.io/csi/openshift-storage.rbd.csi.ceph.com/f3969b7dbf6aac8a24ec3aa7f9ac07f65cda149ebcceedb1446a27f234d507c8/globalmount/0001-0011-openshift-storage-0000000000000001-040c6736-a99c-4ca6-9437-fd34fdc7c584 ext4 rw,seclabel,relatime,stripe=16 0 0
bind
option がついていないことから、それぞれは bind mount ではなく直接デバイス /dev/rbd0
から mount していることがわかる。