TECHSTEP

ITインフラ関連の記事を公開してます。

Rook-Ceph External Cluster ~Rook-Cephクラスター間でExternal Clusterを利用する~

はじめに

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)

Github - coreos/flannel

External Clusterの構築

External Clusterを利用する条件

ここからExternal Clusterを構築するのですが、その前にExternal Clusterを利用するための条件を載せます。

  1. External Cluster側に最低1つのMON endpointが利用できること
  2. External Clusterを操作するのに必要なAdmin Keyring情報
  3. Local ClusterからExternal ClusterのMON・OSD・MGR・MDSへのネットワーク接続性があること
  4. 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が存在します。

※参考リンク:

Ceph Doc - Messenger 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

Rook Doc - Ceph Cluster CRD # External cluster

Github Issue - external access to block image #4601