Rook とは
Rookは,Cloud Native環境向けのストレージとして2018年1月にCNCFに加わりました.
Rookの現在のバージョン(v0.7)では,Software Defined Storage(SDS)のCephをコンテナ化し,Kubernetes上にPodとして実行しています.これにより,Cephがもともと備えるブロックストレージ,ファイルストレージ,オブジェクトストレージの3つのタイプのストレージを提供しています.また,データの格納場所については,KubernetesのFlexVolumeを使っています.
Rook の主な特徴
RookはCloud Nativeなストレージとして次のような特徴をもっています.
- KubernetesのSelf-healingにより,Rook自身もSelf-managing serviceを実現
- Rookはアプリケーションと合わせてスケールできるHyper-convergedのストレージクラスタを構築可能
- RookのもつCephにより,ブロックストレージ,ファイルストレージ,オブジェクトストレージを提供
- 少ないデータロスのリスクで複数のクラスタへデータを分散
- Cephによりストレージ機能のSnapshot, Cloning, Versioningが利用可能
- Kubernetes のreplicaの機能によりCephのノードを簡単にTBオーダのサイズまでスケール可能
- コモディティなハードウェアで実行可能
- Rookの監視をPrometheusで容易に実現
Minikube を使った検証
今回の検証では Minikube(Kubernetes v1.9.0)を利用しています.
Minikube特有の設定は,後述のRook ClusterのdataDirHostPath
の設定変更だけですので,他の環境で試す場合には,本設定項目を読み飛ばしてください.
Rook のダウンロード
GitHub のRootのレポジトリ からダウンロードします.
$ git clone git@github.com:rook/rook.git
Cloning into 'rook'...
<snip>
$ cd rook/cluster/examples/kubernetes/
以下,rook/cluster/examples/kubernetes/
ディレクトリで作業します.
Rook Operator のデプロイ
Rook Operator のデプロイします.
$ kubectl create -f rook-operator.yaml
namespace "rook-system" created
clusterrole "rook-operator" created
serviceaccount "rook-operator" created
clusterrolebinding "rook-operator" created
deployment "rook-operator" created
$ kubectl get pods -n rook-system
NAME READY STATUS RESTARTS AGE
rook-agent-dw98q 1/1 Running 0 9m
rook-operator-77cf655476-zpcdj 1/1 Running 0 10m
rook-systemネームスペースが新規に作成され,rook-agentと rook-operatorのPodが立ち上がります.
Rook Clusterのデプロイ
次に Rook Cluster をデプロイします.
Minikubeの場合,viなどのエディタを使い,rook-cluster.yaml
ファイルのdataDirHostPath
を/data/rookに変更してください.
Minikubeでは,/data ディレクトリが永続的なファイルを格納するディレクトリとして用意されているため,この変更により,Rookに保存されたデータが永続化されるようになります.
以下に変更箇所を示します.
$ git diff
diff --git a/cluster/examples/kubernetes/rook-cluster.yaml b/cluster/examples/kubernetes/rook-cluster.yaml
index 368ec4f..3db4d28 100644
--- a/cluster/examples/kubernetes/rook-cluster.yaml
+++ b/cluster/examples/kubernetes/rook-cluster.yaml
@@ -14,7 +14,7 @@ spec:
# The path on the host where configuration files will be persisted. If not specified, a kubernetes emptyDir will be created (not recommended).
# Important: if you reinstall the cluster, make sure you delete this directory from each host or else the mons will fail to start on the new cluster.
# In Minikube, the '/data' directory is configured to persist across reboots. Use "/data/rook" in Minikube environment.
- dataDirHostPath: /var/lib/rook
+ dataDirHostPath: /data/rook
# toggle to use hostNetwork
hostNetwork: false
# set the amount of mons to be started
dataDirHostPath
の変更が完了後,kubectl を使ってRook Clusterをデプロイします.
$ kubectl create -f rook-cluster.yaml
namespace "rook" created
cluster "rook" created
$ $ kubectl get pods -n rook
NAME READY STATUS RESTARTS AGE
rook-api-848df956bf-s6mvl 1/1 Running 0 7m
rook-ceph-mgr0-cfccfd6b8-nm4dv 1/1 Running 0 7m
rook-ceph-mon0-jf4jm 1/1 Running 0 7m
rook-ceph-mon1-4g4dc 1/1 Running 0 7m
rook-ceph-mon2-s4kwg 1/1 Running 0 7m
rook-ceph-osd-b9ckh 1/1 Running 0 7m
rookネームスペースが新規に作成され,コンテナ化されたCephと,RookのAPIサーバのコンテナが立ち上がります.
Cephのノード数を変更したい人はrook-cluster.yaml
の spec.monCount(デフォルト値はCephのモニタリングノードの最小数3) の値を変更してください.
以上で,Rook 自体のインストールは完了です.
続いて,提供するストレージの設定を行います.
ストレージの設定
Rook ではブロックストレージ,ファイルストレージ,オブジェクトストレージの3つのタイプのストレージが利用出来ます.
以下に,それぞれの特徴をまとめます.用途に応じて使い分けてください.
- ブロックストレージ
- 1台のPodからのみ利用する場合に利用
- 書き込み要求の排他制御により1台のPodからのみアクセス可能
- 整合性を考慮するRDBなどの用途向き
- ファイルストレージ
- Kubernetes Cluster内の複数Podからアクセスする場合に利用
- 複数Podでファイルをシェア可能
- オブジェクトストレージ
- Kubenetes Cluster内または外部からS3 API互換でアクセスする場合に利用
- HTTP/HTTPS プロトコルのため,Kubernetesの外部へも提供可能
今回の検証では,ファイルストレージを使います.
他のタイプを使いたい方は,公式ドキュメントを参照ください.
ファイルストレージの設定
kubectl を使って Ceph のファイルストレージ(CephFS: Ceph File System)をデプロイします.
$ kubectl create -f rook-filesystem.yaml
filesystem "myfs" created
$ $ kubectl get pod -l app=rook-ceph-mds -n rook
NAME READY STATUS RESTARTS AGE
rook-ceph-mds-myfs-7d59fdfcf4-8g278 1/1 Running 0 7m
rook-ceph-mds-myfs-7d59fdfcf4-8nm9v 1/1 Running 0 7m
新たに,rook-ceph-mds-myfs のPodが立ち上がります.
これで,Rookでファイルストレージが利用できるようになります.
以下は,今回作成したCephの設定です.
apiVersion: rook.io/v1alpha1
kind: Filesystem
metadata:
name: myfs
namespace: rook
spec:
# The metadata pool spec
metadataPool:
replicated:
# Increase the replication size if you have more than one osd
size: 1
# The list of data pool specs
dataPools:
- failureDomain: osd
replicated:
size: 1
# If you have at least three osds, erasure coding can be specified
# erasureCoded:
# dataChunks: 2
# codingChunks: 1
# The metadata service (mds) configuration
metadataServer:
# The number of active MDS instances
activeCount: 1
# Whether each active MDS instance will have an active standby with a warm metadata cache for faster failover.
# If false, standbys will be available, but will not have a warm cache.
activeStandby: true
# The affinity rules to apply to the mds deployment
placement:
# nodeAffinity:
# requiredDuringSchedulingIgnoredDuringExecution:
# nodeSelectorTerms:
# - matchExpressions:
# - key: role
# operator: In
# values:
# - mds-node
# tolerations:
# - key: mds-node
# operator: Exists
# podAffinity:
# podAntiAffinity:
resources:
# The requests and limits set here, allow the filesystem MDS Pod(s) to use half of one CPU core and 1 gigabyte of memory
# limits:
# cpu: "500m"
# memory: "1024Mi"
# requests:
# cpu: "500m"
# memory: "1024Mi"
Pod からのファイルストレージの利用
試しに,nginxのPodを作成し,Rookのファイルストレージをマウントします.
vi などのテキストエディタで以下のYAMLファイルを作成します.
ここでは,my-nginx-pod.yamlというファイル名で保存します.
apiVersion: v1
kind: Pod
metadata:
name: my-nginx-pod
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
volumeMounts:
- mountPath: "/usr/share/nginx/html"
name: cephfs
volumes:
- name: cephfs
flexVolume:
driver: rook.io/rook
fsType: ceph
options:
fsName: myfs
clusterName: rook
RookはKubernetesのFlexVolume を使っているため,volumesのタイプはflexVolume, fsTypeはcephとして設定します.
また,volumes.flexVolume.options に次の値を指定します.
- fsName: rook-filesystem.yamlで設定したFileSystemのmetadata.nameを指定 (eg. myfs)
- clusterName: Rook Clusterの Namespaceを指定 (eg. rook)
my-nginx-pod.yamlが作成出来たら,nginxをデプロイします.
$ kubectl create -f my-nginx-pod.yaml
pod "my-nginx-pod" created
RookのCephFSがマウント出来ているかnginxのPodにログインし確認します.
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
my-nginx-pod 1/1 Running 0 2m
$ kubectl exec -ti my-nginx-pod bash
root@my-nginx-pod:/# df -h
Filesystem Size Used Avail Use% Mounted on
overlay 17G 1.6G 14G 11% /
tmpfs 64M 0 64M 0% /dev
tmpfs 1001M 0 1001M 0% /sys/fs/cgroup
/dev/sda1 17G 1.6G 14G 11% /etc/hosts
shm 64M 0 64M 0% /dev/shm
10.111.0.5:6790,10.110.199.110:6790,10.100.191.29:6790:/ 18G 2.1G 16G 12% /usr/share/nginx/html
tmpfs 1001M 12K 1001M 1% /run/secrets/kubernetes.io/serviceaccount
tmpfs 1001M 0 1001M 0% /sys/firmware
root@my-nginx-pod:/# exit
exit
/usr/share/nginx/html がCephのモニタリングノードが指定され,マウントに成功しているのが確認できます.
このように,Rookで提供されたファイルストレージ(CephFS)をPodから利用することが出来ます.
Rookの便利コマンドが格納されたToolBox
Rook では ToolBoxとして,Rookの運用に役立つコマンド群が用意されています.
代表的なものとしてはRookの管理コマンドrookctl
, Cephの管理コマンドceph
,CephのバックエンドのRADOS管理コマンドrados
が格納されています
まずは,ToolBoxをセットアップします.
$ kubectl create -f rook-tools.yaml
pod "rook-tools" created
$ kubectl get pod rook-tools -n rook
NAME READY STATUS RESTARTS AGE
rook-tools 1/1 Running 0 2m
ToolBoxのコマンドを利用するには,rook-toolsのPodへログインします.
$ kubectl -n rook exec -it rook-tools bash
root@rook-tools:/# hostname
rook-tools
あとは,rookctl, ceph, rados のコマンドを実行するだけです.
rookctlコマンドの実行例
root@rook-tools:/# rookctl status
OVERALL STATUS: OK
USAGE:
TOTAL USED DATA AVAILABLE
17.11 GiB 2.00 GiB 2.19 KiB 15.10 GiB
MONITORS:
NAME ADDRESS IN QUORUM STATUS
rook-ceph-mon0 10.100.191.29:6790/0 true OK
rook-ceph-mon2 10.110.199.110:6790/0 true OK
rook-ceph-mon1 10.111.0.5:6790/0 true OK
MGRs:
NAME STATUS
rook-ceph-mgr0 Active
OSDs:
TOTAL UP IN FULL NEAR FULL
1 1 1 false false
PLACEMENT GROUPS (200 total):
STATE COUNT
active+clean 200
root@rook-tools:/# rookctl filesystem list
NAME METADATA POOL DATA POOLS
myfs myfs-metadata myfs-data0
cephコマンドの実行例
root@rook-tools:/# ceph status
cluster:
id: 451d6fe3-6982-4745-8a4b-3fa99a58f7ab
health: HEALTH_OK
services:
mon: 3 daemons, quorum rook-ceph-mon0,rook-ceph-mon2,rook-ceph-mon1
mgr: rook-ceph-mgr0(active)
mds: myfs-1/1/1 up {0=m8g278=up:active}, 1 up:standby-replay
osd: 1 osds: 1 up, 1 in
data:
pools: 2 pools, 200 pgs
objects: 21 objects, 2246 bytes
usage: 2051 MB used, 15465 MB / 17516 MB avail
pgs: 200 active+clean
io:
client: 852 B/s rd, 1 op/s rd, 0 op/s wr
root@rook-tools:/# ceph df
GLOBAL:
SIZE AVAIL RAW USED %RAW USED
17516M 15465M 2051M 11.71
POOLS:
NAME ID USED %USED MAX AVAIL OBJECTS
myfs-metadata 1 2246 0 14589M 21
myfs-data0 2 0 0 14589M 0
rados コマンドの実行例
root@rook-tools:/# rados df
POOL_NAME USED OBJECTS CLONES COPIES MISSING_ON_PRIMARY UNFOUND DEGRADED RD_OPS RD WR_OPS WR
myfs-data0 0 0 0 0 0 0 0 0 0 0 0
myfs-metadata 2246 21 0 21 0 0 0 2890 1449k 42 8192
total_objects 21
total_used 2051M
total_avail 15465M
total_space 17516M
root@rook-tools:/# rados lspools
myfs-metadata
myfs-data0
クリーンアップ
今回の検証環境を削除します.
作成した my-nginx-podを削除します.
$ kubectl delete pods my-nginx-pod
pod "my-nginx-pod" deleted
続いて,Rook Cluster, Rook Operatorを削除します.
$ kubectl delete -f rook-cluster.yaml
namespace "rook" deleted
cluster "rook" deleted
$ kubectl delete -f rook-operator.yaml
namespace "rook-system" deleted
clusterrole "rook-operator" deleted
serviceaccount "rook-operator" deleted
clusterrolebinding "rook-operator" deleted
deployment "rook-operator" deleted
最後に,Minikube 上に保存されたデータを削除します.
$ minikube ssh
_ _
_ _ ( ) ( )
___ ___ (_) ___ (_)| |/') _ _ | |_ __
/' _ ` _ `\| |/' _ `\| || , < ( ) ( )| '_`\ /'__`\
| ( ) ( ) || || ( ) || || |\`\ | (_) || |_) )( ___/
(_) (_) (_)(_)(_) (_)(_)(_) (_)`\___/'(_,__/'`\____)
$ cd /data
$ sudo rm -rf rook/
$ exit
感想
現時点のRookは,一言で言うとKubernetes上のCephです。Kubernetes上にのせることで,基本的なストレージの管理はKubernetesの操作で利用できます.そのため,管理者の学習コストは低いかと思います.一方で,ストレージベンダーから見たときには,メリットは少ないかもしれません.
管理系のコントロールプレーンは上述のようにKubernetesでカバーされてしまいます.独自の管理操作をKubernetesの世界観に追加することは,管理者からベンダーロックインの印象を持たれるリスクがあります.Cloud Nativeな世界に近づけば近づくだけロックインを避けたいと思う人々は多く,このリスクを払拭するほどのメリットがないと厳しいのではないでしょうか.
また、I/Oを司るデータプレーンに関しては、コンテナからみたブロック、ファイル、オブジェクトストレージのインターフェースは全てCeph に握られています.そのため、ストレージベンダーは,Ceph 配下のディスクを提供以外の参入は,ハードルが高い印象を受けました.現在のRookはKubernetesのFlexVolumeを利用しているため、コンテナから直接外部ストレージにI/Oが届くわけでなく、Nodeのサーバを経由して届くことになります.つまり、どれだけ外部ストレージのLatencyやThroughputが良くても,Nodeのサーバがボトルネックになり100%の性能を発揮出来ないかと思います.また,KubernetesのNodeはベアメタルのサーバよりもVMのものが多く,スケールアウトの特徴を活かすためにパススルーでのストレージ割り当てを行わない環境が多いかと思います.このような環境において,アプリケーションが載っているコンテナは,複数Nodeを自由に移動し実行されています.そのため,VM/Hypervisor/CPU(VT-x technologyなど)で,どのVMから発行されたI/Oなのか外部ストレージ側で知りうることが出来たとしても,どのコンテナ上のアプリケーションが発行したのか特定するのは難しく,少しトリッキーな方法を取らざるを得ません.やり方次第では,ベンダーロックインのリスクを抱えてしまうことになります.
上述のように,アプリケーションのコンテナ化が進んでいる今日において,Rookが成熟してくるとストレージベンダーにとって,頭の痛い存在になると思います.とはいえ,ストレージベンダーも黙っておらずコンテナ対応を加速させるでしょうから,これからの対応が楽しみです.