はじめに
Rook-Cephでは、自身のクラスター外にあるCephクラスターを利用する機能を提供しています。前回はVM上に構築したCephクラスターを利用する方法について紹介しましたが、今回はVM上でなく、Kubernetes上に構築したRook-Cephクラスターを、別のRook-Cephクラスターから利用する方法について紹介します。
External Clusterとは
External Clusterについては前回の記事などを見ていただければと思います。Local/External Clusterは、以下のように分けています。
Local Cluster
: 外部のClusterを利用する側External CLuster
: 外部から別のClusterによって利用される側
検証環境
今回もAzureVMを利用し、Kubernetesクラスターを2つ用意します。ネットワーク構成をシンプルにするため、全てのVMを同じサブネットに配置しました。
Local Cluster
- master node: 1台 (IP Address:
10.3.0.7
) - worker node: 1台 (IP Address:
10.3.0.8
)
External Cluster
- master node: 1台 (IP Address:
10.3.0.9
) - worker node: 3台 (IP Address:
10.3.0.4
10.3.0.5
10.3.0.6
)
Kubernetes Clusterの構築
2つのKubernetesクラスターはkubeadmを利用して構築し、CNIはFlannelを使用しました。
※参考リンク:
Qiita - Kubernetes v1.13の構築をkubeadmで行う(Centos7, AWS)
External Clusterの構築
External Clusterを利用する条件
ここからExternal Clusterを構築するのですが、その前にExternal Clusterを利用するための条件を載せます。
- External Cluster側に最低1つのMON endpointが利用できること
- External Clusterを操作するのに必要なAdmin Keyring情報
- Local ClusterからExternal ClusterのMON・OSD・MGR・MDSへのネットワーク接続性があること
- External ClusterのCephバージョンが、Local Clusterの利用できるバージョンの条件を満たすこと
上記4つのうち、2番目の条件はExternal Cluster側の情報をLocal Clusterに与えれば実現できます。4番目の条件については、CephCluster
リソース構築時に指定するCephバージョンを合わせれば満たします(記事作成時のデフォルトはceph/ceph:v14.2.6
)。
今回のポイントは1・3番目の条件で、External Cluster側はspec.network.hostNetwork
を有効にすることで実現します。
spec.network.hostNetwork
を有効にした場合、クラスターのホストが利用するネットワークを用いてCephクラスターを構築します。通常Rook-Cephを利用する場合、このパラメータは無効化されており、CNI等の管理するネットワークアドレスから各PodにIPアドレスが割り振られますが、hostNetwork
を有効にした場合、Podはホストの持つIPアドレスを利用します。
Rook公式ドキュメントには特に記載はありませんが、Github Issueのこちらのコメントを参考に有効化したところ、Local ClusterからExternal Clusterへの接続に成功したため、条件として追加しました。
※参考リンク:
Rook Doc - Ceph Cluster CRD #Cluster settings
Rook-Ceph External Clusterの構築
上記ポイントを踏まえたうえで、External Clusterを構築します。
まずは通常通り、Rook Operatorまでをデプロイします。
[root@k8s-master ~]# git clone --single-branch --branch release-1.2 [root@k8s-master ~]# cd rook/cluster/examples/kubernetes/ceph/ [root@k8s-master ceph]# kubectl apply -f common.yaml [root@k8s-master ceph]# kubectl apply -f operator.yaml
次にCephクラスターをデプロイします。今回は以下のyamlファイルを利用しました。コメント等の不要な個所は削除しています。
apiVersion: ceph.rook.io/v1 kind: CephCluster metadata: name: rook-ceph namespace: rook-ceph spec: cephVersion: image: ceph/ceph:v14.2.6 allowUnsupported: false dataDirHostPath: /var/lib/rook skipUpgradeChecks: false continueUpgradeAfterChecksEvenIfNotHealthy: false mon: count: 3 allowMultiplePerNode: false dashboard: enabled: true ssl: true monitoring: enabled: false rulesNamespace: rook-ceph network: hostNetwork: true # 有効にする rbdMirroring: workers: 0 crashCollector: disable: false annotations: resources: removeOSDsIfOutAndSafeToRemove: false storage: useAllNodes: true useAllDevices: false devices: - name: "sdc" # デバイスを指定 config: disruptionManagement: managePodBudgets: false osdMaintenanceTimeout: 30 manageMachineDisruptionBudgets: false machineDisruptionBudgetNamespace: openshift-machine-api
上記yamlファイルをデプロイします。リソース作成後のPodのアドレスを見ると、各Podの配置されたノードの持つIPアドレスを割り振られていることが確認できます。また、通常は作成されるMON Pod用のServiceも作成されません。
# 各PodはホストのIPアドレスを持つ [root@k8s-master ceph]# kubectl get pods -n rook-ceph -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES csi-cephfsplugin-c8ws4 3/3 Running 9 47h 10.3.0.5 k8s-worker02 <none> <none> csi-cephfsplugin-nk72s 3/3 Running 9 47h 10.3.0.6 k8s-worker03 <none> <none> csi-cephfsplugin-p6knr 3/3 Running 9 47h 10.3.0.4 k8s-worker01 <none> <none> csi-cephfsplugin-provisioner-5c85564f4c-hmstq 4/4 Running 12 47h 10.244.3.38 k8s-worker03 <none> <none> csi-cephfsplugin-provisioner-5c85564f4c-rxlg4 4/4 Running 16 47h 10.244.2.48 k8s-worker02 <none> <none> csi-rbdplugin-6bgzk 3/3 Running 9 47h 10.3.0.5 k8s-worker02 <none> <none> csi-rbdplugin-bxmnq 3/3 Running 9 47h 10.3.0.6 k8s-worker03 <none> <none> csi-rbdplugin-cv569 3/3 Running 9 47h 10.3.0.4 k8s-worker01 <none> <none> csi-rbdplugin-provisioner-7bb78d6c66-485s8 5/5 Running 23 47h 10.244.3.36 k8s-worker03 <none> <none> csi-rbdplugin-provisioner-7bb78d6c66-lzc5b 5/5 Running 15 47h 10.244.1.47 k8s-worker01 <none> <none> rook-ceph-crashcollector-k8s-worker01-ff7b7d96c-d72lz 1/1 Running 3 47h 10.3.0.4 k8s-worker01 <none> <none> rook-ceph-crashcollector-k8s-worker02-6946c7746-5x57z 1/1 Running 3 47h 10.3.0.5 k8s-worker02 <none> <none> rook-ceph-crashcollector-k8s-worker03-7457b8bf97-5hxdn 1/1 Running 3 47h 10.3.0.6 k8s-worker03 <none> <none> rook-ceph-mgr-a-5cd8d49d96-qkfvv 1/1 Running 9 47h 10.3.0.5 k8s-worker02 <none> <none> rook-ceph-mon-a-fd8cb5bc6-6fsg2 1/1 Running 3 47h 10.3.0.5 k8s-worker02 <none> <none> rook-ceph-mon-b-549d8bbb96-jff42 1/1 Running 3 47h 10.3.0.4 k8s-worker01 <none> <none> rook-ceph-mon-c-66c858f497-tc6nq 1/1 Running 3 47h 10.3.0.6 k8s-worker03 <none> <none> rook-ceph-operator-69d86f8597-qwvnv 1/1 Running 4 47h 10.244.1.49 k8s-worker01 <none> <none> rook-ceph-osd-0-7b4757c9cc-lvxwp 1/1 Running 3 47h 10.3.0.6 k8s-worker03 <none> <none> rook-ceph-osd-1-5cbf6fd496-jw4vd 1/1 Running 3 47h 10.3.0.4 k8s-worker01 <none> <none> rook-ceph-osd-2-6967864446-6cn44 1/1 Running 3 47h 10.3.0.5 k8s-worker02 <none> <none> rook-ceph-osd-prepare-k8s-worker01-5hx7x 1/1 Running 0 19h 10.3.0.4 k8s-worker01 <none> <none> rook-ceph-osd-prepare-k8s-worker02-hxzm6 1/1 Running 0 18h 10.3.0.5 k8s-worker02 <none> <none> rook-ceph-osd-prepare-k8s-worker03-7kc97 1/1 Running 0 19h 10.3.0.6 k8s-worker03 <none> <none> rook-discover-5d4d6 1/1 Running 3 47h 10.244.2.47 k8s-worker02 <none> <none> rook-discover-9f2cm 1/1 Running 3 47h 10.244.1.48 k8s-worker01 <none> <none> rook-discover-nw9kn 1/1 Running 3 47h 10.244.3.37 k8s-worker03 <none> <none> [root@k8s-master ceph]# [root@k8s-master ~]# kubectl get svc -n rook-ceph NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE csi-cephfsplugin-metrics ClusterIP 10.111.248.24 <none> 8080/TCP,8081/TCP 2d4h csi-rbdplugin-metrics ClusterIP 10.104.119.236 <none> 8080/TCP,8081/TCP 2d4h rook-ceph-mgr ClusterIP 10.99.129.139 <none> 9283/TCP 2d4h rook-ceph-mgr-dashboard ClusterIP 10.101.212.253 <none> 8443/TCP 2d4h [root@k8s-master ~]#
この時、MON PodにアクセスするためのPortを確認するため、External Cluster Nodeでnetstat
等のコマンドを実行し、Portを確認します。ここでは、3300
6789
PortがMON endpointとして利用できることが確認できます。
[root@k8s-worker01 ~]# netstat -anp | grep ceph-mon tcp 0 0 10.3.0.4:3300 0.0.0.0:* LISTEN 3811/ceph-mon tcp 0 0 10.3.0.4:6789 0.0.0.0:* LISTEN 3811/ceph-mon tcp 0 0 10.3.0.4:59864 10.3.0.5:6808 ESTABLISHED 3811/ceph-mon tcp 0 0 10.3.0.4:53526 10.3.0.5:3300 ESTABLISHED 3811/ceph-mon tcp 0 0 10.3.0.4:6789 10.3.0.8:38282 ESTABLISHED 3811/ceph-mon tcp 0 0 10.3.0.4:3300 10.3.0.4:53826 ESTABLISHED 3811/ceph-mon unix 2 [ ACC ] STREAM LISTENING 60070 3811/ceph-mon /var/run/ceph/ceph-mon.b.asok
なお、Cephはコンポーネント間の通信を行うプロトコルが2種類あり、6789
ポートを用いるv1、3300
ポートを用いるv2が存在します。
※参考リンク:
Local Clusterの構築
続いてLocal Clusterを構築します。
Rook Operatorの作成
こちらもまずはRook Operatorまで作成します。この時、External Clusterを利用するために用意されているcommon-external.yaml
をデプロイし、rook-ceph-external
Namespace等のリソースを作成します。
[root@rook-master ~]# git clone --single-branch --branch release-1.2 [root@rook-master ~]# cd rook/cluster/examples/kubernetes/ceph/ [root@rook-master ceph]# kubectl apply -f common.yaml [root@rook-master ceph]# kubectl apply -f operator.yaml [root@rook-master ceph]# kubectl apply -f common-external.yaml
ConfigMap・Secretリソースの生成
次に、External Clusterと接続するのに必要な、ConfigMap・Secretリソースを作成します。前回の記事と同様、import-external-cluster.sh
を利用して各リソースを作成します。
まずはスクリプトを動作させるのに必要な環境変数を設定します。
# NAMESPACE:Local Clusterを利用するNamespace名 [root@rook-master ~]# export NAMESPACE=rook-ceph-external # ROOK_EXTERNAL_FSID:External ClusterのFSID [root@rook-master ~]# export ROOK_EXTERNAL_FSID=3c80e7a2-6cb2-411e-8c50-6284b5b8e341 # ROOK_EXTERNAL_ADMIN_SECRET:External Clusterのcluster.adminのSecret [root@rook-master ~]# export ROOK_EXTERNAL_ADMIN_SECRET=AQAgfVRe/+m6KBAApHO9B0mVq73YZKAvO+QpYA== # ROOK_EXTERNAL_CEPH_MON_DATA:External ClusterのMON endpoint [root@rook-master ~]# export ROOK_EXTERNAL_CEPH_MON_DATA=a=10.3.0.5:6789,b=10.3.0.4:6789,c=10.3.0.6:6789
上記環境変数の値は、External Cluster側でtoolbox
Podを利用して取得します。
[root@k8s-master ceph]# kubectl exec -it rook-ceph-tools-9b7b66bbb-9ljts -n rook-ceph -- ceph fsid 3c80e7a2-6cb2-411e-8c50-6284b5b8e341 [root@k8s-master ceph]# kubectl exec -it rook-ceph-tools-9b7b66bbb-9ljts -n rook-ceph -- ceph auth get-key client.admin AQAgfVRe/+m6KBAApHO9B0mVq73YZKAvO+QpYA==[root@k8s-master ceph]# [root@k8s-master ceph]# kubectl exec -it rook-ceph-tools-9b7b66bbb-9ljts -n rook-ceph -- ceph mon stat e3: 3 mons at {a=[v2:10.3.0.5:3300/0,v1:10.3.0.5:6789/0],b=[v2:10.3.0.4:3300/0,v1:10.3.0.4:6789/0],c=[v2:10.3.0.6:3300/0,v1:10.3.0.6:6789/0]}, election epoch 44, leader 0 a, quorum 0,1,2 a,b,c [root@k8s-master ceph]#
Local Clusterの作成
最後にLocal Clusterを作成します。今回は以下のyamlファイルを利用します。なお、External Clusterではspec.network.hostNetwork: true
に設定しましたが、Local Cluster側ではどちらに設定しても問題なく動作することを確認しています。
apiVersion: ceph.rook.io/v1 kind: CephCluster metadata: name: rook-ceph-external namespace: rook-ceph-external spec: external: enable: true dataDirHostPath: /var/lib/rook # providing an image is optional, do this if you want to create other CRs (rgw, mds, nfs) cephVersion: image: ceph/ceph:v14.2.6 # MUST match external cluster version
上記ファイルをデプロイし、しばらくすると、Connected
状態となり、接続されたことが確認できます。HEALTH_WARN
状態となっていますが、これはExternal Cluster側の問題のため、ここでは特に触れません。
[root@rook-master ceph]# kubectl get cephcluster.ceph.rook.io -A NAMESPACE NAME DATADIRHOSTPATH MONCOUNT AGE STATE HEALTH rook-ceph-external rook-ceph-external /var/lib/rook 9s Connected HEALTH_WARN # External Clusterの状態 [root@k8s-master ~]# kubectl exec -it rook-ceph-tools-9b7b66bbb-9ljts -n rook-ceph -- ceph status cluster: id: 3c80e7a2-6cb2-411e-8c50-6284b5b8e341 health: HEALTH_WARN 7 daemons have recently crashed too few PGs per OSD (2 < min 30) services: mon: 3 daemons, quorum a,b,c (age 5h) mgr: a(active, since 86m) osd: 3 osds: 3 up (since 24h), 3 in (since 2d) data: pools: 1 pools, 8 pgs objects: 5 objects, 19 B usage: 3.0 GiB used, 186 GiB / 189 GiB avail pgs: 8 active+clean [root@k8s-master ~]#
Local ClusterからExternal Clusterを利用する
ここから、接続したExternal Clusterを利用し、テスト用Podを作成します。今回はブロックストレージを利用するため、CephBlockPool
StorageClass
PersistentVolumeClaims
リソースを利用します。
CephBlockPool
StorageClass
の作成
まずはCephBlockPool・Storage Classを作成します。作成に配下のyamlファイルを利用します。
apiVersion: ceph.rook.io/v1 kind: CephBlockPool metadata: name: replicapool-external04 namespace: rook-ceph-external spec: failureDomain: host replicated: size: 1
apiVersion: storage.k8s.io/v1 kind: StorageClass metadata: name: rook-ceph-block provisioner: rook-ceph.rbd.csi.ceph.com parameters: clusterID: rook-ceph-external # local clusterのnamespaceに合わせる pool: replicapool-external04 # cephblockpoolで指定したPool名に合わせる imageFormat: "2" imageFeatures: layering csi.storage.k8s.io/provisioner-secret-name: rook-csi-rbd-provisioner csi.storage.k8s.io/provisioner-secret-namespace: rook-ceph-external csi.storage.k8s.io/controller-expand-secret-name: rook-csi-rbd-provisioner csi.storage.k8s.io/controller-expand-secret-namespace: rook-ceph-external csi.storage.k8s.io/node-stage-secret-name: rook-csi-rbd-node csi.storage.k8s.io/node-stage-secret-namespace: rook-ceph-external csi.storage.k8s.io/fstype: ext4 allowVolumeExpansion: true reclaimPolicy: Delete
上記2つのファイルをデプロイし、リソースを作成します。またPool作成後にExternal Clusterを確認すると、Poolが作成されたことも確認できます。
[root@rook-master rbd]# kubectl get cephblockpool.ceph.rook.io -n rook-ceph-external NAME AGE replicapool-external04 25m [root@rook-master rbd]# kubectl get sc -n rook-ceph-external NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE rook-ceph-block rook-ceph.rbd.csi.ceph.com Delete Immediate true 25m # External Cluster側で確認 [root@k8s-master ceph]# kubectl exec -it rook-ceph-tools-9b7b66bbb-9ljts -n rook-ceph -- ceph osd pool ls replicapool-external03 replicapool-external04 ★ [root@k8s-master ceph]#
PersistentVolumeClaims
の作成
次にPVCを作成します。前回の作業時と同様に、Local Cluster側のrook-ceph-csi-config
ConfigMapを修正することで作成できるようになります。
# ConfigMapの修正 [root@rook-master rbd]# kubectl get sc -n rook-ceph-external NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE rook-ceph-block rook-ceph.rbd.csi.ceph.com Delete Immediate true 25m [root@rook-master rbd]# kubectl describe cm rook-ceph-csi-config -n rook-ceph Name: rook-ceph-csi-config Namespace: rook-ceph Labels: <none> Annotations: <none> Data ==== csi-cluster-config-json: ---- [] # ここを修正 Events: <none> [root@rook-master rbd]# [root@rook-master rbd]# kubectl edit cm rook-ceph-csi-config -n rook-ceph configmap/rook-ceph-csi-config edited # 修正後 [root@rook-master rbd]# kubectl get sc -n rook-ceph-external NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE rook-ceph-block rook-ceph.rbd.csi.ceph.com Delete Immediate true 25m [root@rook-master rbd]# kubectl describe cm rook-ceph-csi-config -n rook-ceph Name: rook-ceph-csi-config Namespace: rook-ceph Labels: <none> Annotations: <none> Data ==== csi-cluster-config-json: ---- [{"clusterID":"rook-ceph-external","monitors":["10.3.0.5:6789","10.3.0.4:6789","10.3.0.6:6789"]}] Events: <none> [root@rook-master rbd]#
PVCのyamlファイルは以下の通りです。
apiVersion: v1 kind: PersistentVolumeClaim metadata: name: rbd-pvc spec: accessModes: - ReadWriteOnce resources: requests: storage: 1Gi storageClassName: rook-ceph-block
上記ファイルをデプロイします。
[root@rook-master rbd]# kubectl get pvc NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE rbd-pvc Bound pvc-4c51820d-e28a-4819-9026-02f814fe7999 1Gi RWO rook-ceph-block 21m
Podの作成
ここまでくればあとはPodをデプロイすれば動作します。Pod用のyamlファイルは以下の通りです。
apiVersion: v1 kind: Pod metadata: name: csirbd-demo-pod spec: containers: - name: web-server image: nginx volumeMounts: - name: mypvc mountPath: /var/lib/www/html volumes: - name: mypvc persistentVolumeClaim: claimName: rbd-pvc readOnly: false
上記ファイルをデプロイします。
[root@rook-master rbd]# kubectl apply -f pod.yaml pod/csirbd-demo-pod created [root@rook-master rbd]# kubectl get pods NAME READY STATUS RESTARTS AGE csirbd-demo-pod 1/1 Running 0 24m
なお、rook-ceph-csi-config
ConfigMapで指定するMON endpointのポートを3300
に指定した場合、上記PodをデプロイしてもContainerCreating
状態で止まってしまいます。
公式ドキュメント等には該当する記載が見当たらなかったので、Issueを発行したりRook Slackチャンネルで質問したりと色々と調べてみたところ、どうやら現状ではceph-csiはv1 protocolにしか対応しておらず、v2にあたる3300
Portでは通信ができないことが原因だとわかりました。
参考ドキュメント
Github - Rook/Rook: Rook and External Ceph Clusters