# Harvester 使用第三方 storage(Rook Ceph RBD) ## 部署 Rook Ceph RBD Server 可以[參考](https://hackmd.io/@wu-andy/ryUHrzVfbl) ## Ceph Server 創建 Pool * 在 Ceph Server 建立 Ceph Pool ``` $ kubectl -n rook-ceph exec -it deploy/rook-ceph-tools -- ceph osd pool create harvester # Use the rbd tool to initialize the pool $ kubectl -n rook-ceph exec -it deploy/rook-ceph-tools -- rbd pool init harvester # 設定 pool 最大容量為 100GB $ kubectl -n rook-ceph exec -it deploy/rook-ceph-tools -- ceph osd pool set-quota harvester max_bytes $((100 * 1024 * 1024 * 1024)) ``` ``` $ kubectl -n rook-ceph exec -it deploy/rook-ceph-tools -- ceph osd pool get-quota harvester quotas for pool 'harvester': max objects: N/A max bytes : 100 GiB (current num bytes: 19 bytes) ``` * 在 Ceph 系統中建立一個專門的使用者帳號(hx),並賦予它存取特定硬碟池 (Pool) 的權限 ``` $ kubectl -n rook-ceph exec -it deploy/rook-ceph-tools -- ceph auth get-or-create client.hx mon 'profile rbd' osd 'profile rbd pool=harvester' mgr 'profile rbd pool=harvester' [client.hx] key = AQBF2jdpkyB5CxAAA5lX96UfP3Y1MGZzQHKaPw== # 這個就是待會要填的 userkey ``` ``` $ kubectl -n rook-ceph exec -it deploy/rook-ceph-tools -- ceph auth get-key client.hx AQBF2jdpkyB5CxAAA5lX96UfP3Y1MGZzQHKaPw== ``` ![image](https://hackmd.io/_uploads/By-dc8SzZl.png) * 獲取 Ceph monitor 和 fsid 資訊 ``` $ kubectl -n rook-ceph exec -it deploy/rook-ceph-tools -- ceph mon dump epoch 3 fsid b295be2e-5166-43dc-8b2f-2adcc9cd457b # 此行就是 clusterID last_changed 2025-12-09T06:00:34.717245+0000 created 2025-12-09T06:00:13.104271+0000 min_mon_release 19 (squid) election_strategy: 1 0: [v2:172.20.1.51:3300/0,v1:172.20.1.51:6789/0] mon.a 1: [v2:172.20.1.52:3300/0,v1:172.20.1.52:6789/0] mon.b 2: [v2:172.20.1.50:3300/0,v1:172.20.1.50:6789/0] mon.c ``` ## harvester 安裝 Ceph-CSI RBD plugins ``` $ kubectl get no -owide NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME hx-1 Ready control-plane,etcd,master 7d5h v1.33.5+rke2r1 172.20.2.11 <none> Harvester v1.6.1 5.14.21-150500.55.121-default containerd://2.1.4-k3s2 hx-2 Ready <none> 6d6h v1.33.5+rke2r1 172.20.2.12 <none> Harvester v1.6.1 5.14.21-150500.55.121-default containerd://2.1.4-k3s2 hx-3 Ready <none> 21m v1.33.5+rke2r1 172.20.2.13 <none> Harvester v1.6.1 5.14.21-150500.55.121-default containerd://2.1.4-k3s2 ``` * 在其他環境打包,並上傳至 harvester ``` $ git clone https://github.com/ceph/ceph-csi.git $ tar -czvf ceph-csi.tar.gz ceph-csi/ $ scp -O ceph-csi.tar.gz rancher@172.20.2.11:~ ``` * 在 harvester 上解壓縮,建立並切換至 `csi-ceph` namespace ``` $ tar -zxvf ceph-csi.tar.gz $ kubectl create ns csi-ceph $ kubectl config set-context --current --namespace=csi-ceph ``` * 設定 Ceph-csi configmap ``` $ cd ~/ceph-csi/deploy/rbd/kubernetes $ cat <<EOF > csi-config-map.yaml --- apiVersion: v1 kind: ConfigMap data: config.json: |- [ { "clusterID": "b295be2e-5166-43dc-8b2f-2adcc9cd457b", "monitors": [ "172.20.1.51:6789", "172.20.1.52:6789", "172.20.1.53:6789" ] } ] metadata: name: ceph-csi-config EOF ``` > 須設定 clusterID 和 monitors 的 IP Address * 設定 csidriver 的 pod 不要 Mount host 主機的 `/etc/selinux` 到 pods 裡面 ``` $ sed -i 's|seLinuxMount: true|seLinuxMount: false|g' csidriver.yaml $ sed -i '/- mountPath: \/etc\/selinux/,+2d' csi-rbdplugin.yaml $ sed -i '/- name: etc-selinux/,+2d' csi-rbdplugin.yaml ``` * 將所有 Yaml 中定義的物件都更換為 csi-ceph Namespace ``` $ sed -i 's|namespace: default|namespace: csi-ceph|g' *.yaml ``` * 設定 csi-rbdplugin-provisioner 和 csi-rbdplugin 的 Pod 能夠在 Contorlplane Node 上執行 ``` $ sed -i '36i\ tolerations:\n - operator: Exists' csi-rbdplugin-provisioner.yaml $ sed -i '24i\ tolerations:\n - operator: Exists' csi-rbdplugin.yaml ``` * 產生 CEPH-CSI cephx secret ``` $ cat <<EOF > ~/ceph-csi/examples/rbd/secret.yaml --- apiVersion: v1 kind: Secret metadata: name: csi-rbd-secret namespace: csi-ceph # change stringData: userID: hx # change userKey: AQBF2jdpkyB5CxAAA5lX96UfP3Y1MGZzQHKaPw== # change # Encryption passphrase encryptionPassphrase: test_passphrase EOF ``` > 須設定 namespace、userID 和 userKey 的值 * 建立 CEPH-CSI cephx secret ``` $ kubectl apply -f ~/ceph-csi/examples/rbd/secret.yaml ``` * 給予 csi-ceph 這個 Namespace 最高權限 ``` $ kubectl label ns csi-ceph pod-security.kubernetes.io/enforce=privileged ``` > csi-rbdplugin 和 csi-rbdplugin-provisioner 的 Pod 會需要 privileged 的權限。 * 最新版本的 ceph-csi 還需要另一個 ConfigMap 物件來定義 Ceph 的設定資訊,以便新增至 CSI Container 內的 ceph.conf 檔案中 ``` $ kubectl apply -f ~/ceph-csi/deploy/ceph-conf.yaml ``` * 開始部屬 ceph-csi ``` $ cd ~/ceph-csi/examples/rbd $ ./plugin-deploy.sh ~/ceph-csi/deploy/rbd/kubernetes ## 移除 vault $ kubectl delete -f ../kms/vault/vault.yaml ``` * 檢視 ceph-csi 部屬狀態 ``` $ kubectl get all NAME READY STATUS RESTARTS AGE pod/csi-rbdplugin-lt295 3/3 Running 0 28m pod/csi-rbdplugin-provisioner-57ff7fc887-j8gcj 7/7 Running 0 29m pod/csi-rbdplugin-provisioner-57ff7fc887-s7rd4 7/7 Running 0 29m pod/csi-rbdplugin-provisioner-57ff7fc887-vvdc4 7/7 Running 0 29m pod/csi-rbdplugin-vrgpr 3/3 Running 0 16m pod/csi-rbdplugin-x7kjh 3/3 Running 1 (15m ago) 16m NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE service/csi-metrics-rbdplugin ClusterIP 10.96.233.216 <none> 8080/TCP 38m service/csi-rbdplugin-provisioner ClusterIP 10.96.72.154 <none> 8080/TCP 38m NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE daemonset.apps/csi-rbdplugin 3 3 3 3 3 <none> 38m NAME READY UP-TO-DATE AVAILABLE AGE deployment.apps/csi-rbdplugin-provisioner 3/3 3 3 38m NAME DESIRED CURRENT READY AGE replicaset.apps/csi-rbdplugin-provisioner-57ff7fc887 3 3 3 38m ``` * 設定 StorageClass Yaml 檔 ``` $ cat <<EOF > csi-rbd-sc.yaml --- apiVersion: storage.k8s.io/v1 kind: StorageClass metadata: name: csi-rbd-sc provisioner: rbd.csi.ceph.com parameters: clusterID: b295be2e-5166-43dc-8b2f-2adcc9cd457b # change pool: harvester # change imageFeatures: layering csi.storage.k8s.io/provisioner-secret-name: csi-rbd-secret csi.storage.k8s.io/provisioner-secret-namespace: csi-ceph csi.storage.k8s.io/controller-expand-secret-name: csi-rbd-secret csi.storage.k8s.io/controller-expand-secret-namespace: csi-ceph csi.storage.k8s.io/node-stage-secret-name: csi-rbd-secret csi.storage.k8s.io/node-stage-secret-namespace: csi-ceph reclaimPolicy: Delete allowVolumeExpansion: true mountOptions: - discard EOF ``` > 須設定 clusterID 和 pool 的值 * 建立 StorageClass ``` $ kubectl apply -f csi-rbd-sc.yaml ``` * 檢視 StorageClass ``` $ kubectl get sc csi-rbd-sc NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE csi-rbd-sc rbd.csi.ceph.com Delete Immediate true 11s ``` * 設定 VolumeSnapshotClass Yaml 檔 ``` $ cat <<EOF > snapshotclass.yaml --- apiVersion: snapshot.storage.k8s.io/v1 kind: VolumeSnapshotClass metadata: name: csi-rbdplugin-snapclass driver: rbd.csi.ceph.com parameters: clusterID: b295be2e-5166-43dc-8b2f-2adcc9cd457b csi.storage.k8s.io/snapshotter-secret-name: csi-rbd-secret csi.storage.k8s.io/snapshotter-secret-namespace: csi-ceph deletionPolicy: Delete EOF ``` > 須設定 clusterID 和 pool 的值 * 建立 SnapshotClass ``` $ kubectl apply -f snapshotclass.yaml ``` * 檢視 SnapshotClass ``` $ kubectl get VolumeSnapshotClass NAME DRIVER DELETIONPOLICY AGE csi-rbdplugin-snapclass rbd.csi.ceph.com Delete 30s longhorn driver.longhorn.io Delete 7d5h longhorn-snapshot driver.longhorn.io Delete 7d5h ``` * 產生 `volumegroupsnapshot` CRD,如果沒有這個 CRD 會導致 volume snapshot 失敗 ``` $ kubectl apply -f https://raw.githubusercontent.com/kubernetes-csi/external-snapshotter/refs/heads/master/client/config/crd/groupsnapshot.storage.k8s.io_volumegroupsnapshotclasses.yaml $ kubectl apply -f https://raw.githubusercontent.com/kubernetes-csi/external-snapshotter/refs/heads/master/client/config/crd/groupsnapshot.storage.k8s.io_volumegroupsnapshotcontents.yaml $ kubectl apply -f https://raw.githubusercontent.com/kubernetes-csi/external-snapshotter/refs/heads/master/client/config/crd/groupsnapshot.storage.k8s.io_volumegroupsnapshots.yaml ``` * 切回 `default` namespace ``` $ kubectl config set-context --current --namespace=default ``` ## harvester 設定 csi-driver-config ![image](https://hackmd.io/_uploads/SkSe-DBfbe.png) ## 驗證 ### harvester 建立 vm 使用 Rook Ceph RBD * 第一顆硬碟掛載 ISO ![image](https://hackmd.io/_uploads/B11hrwHz-l.png) * 第二顆硬碟用來安裝 OS,使用 Rook Ceph RBD 儲存 ![image](https://hackmd.io/_uploads/B1x18wrGbe.png) * 安裝好後可以正常開機 ![image](https://hackmd.io/_uploads/SJHziwSzbx.png) ### 測試 volume snapshot 功能 ![image](https://hackmd.io/_uploads/B1lYW_BfWx.png) ![image](https://hackmd.io/_uploads/B1Jq-dBfbg.png) * 確認 snapshot 狀態正常 ![image](https://hackmd.io/_uploads/SJQISF8z-g.png) * 在 Volume Snapshots 可以看到 snpshot ![image](https://hackmd.io/_uploads/H1Pj-OHfbl.png) * 測試 restore ![image](https://hackmd.io/_uploads/SJiDLYUG-x.png) * restore 時要命名 volume ,他會再產生一個 volume ![image](https://hackmd.io/_uploads/BkBFLtIfbl.png) * 新產生的 volume ![image](https://hackmd.io/_uploads/Skjp8YIzbx.png) * 將 vm 先關機 ![image](https://hackmd.io/_uploads/Bk7JYK8M-x.png) * 將 vm 原有的 volume 移除,然後再點選 `Add Existing Volume` 選擇剛剛 restore 的 volume ![image](https://hackmd.io/_uploads/H1V6FtLzbx.png) * 重新開機後,並成功將資料回復成 snapshot 當時的內容 ![image](https://hackmd.io/_uploads/BkNx2YIfbg.png) ## 參考 https://docs.harvesterhci.io/v1.6/advanced/csidriver/ https://github.com/kubernetes-csi/external-snapshotter/tree/master/client/config/crd