# Test Ceph RBD with Talos Kubernetes <style> .indent-title-1{ margin-left: 1em; } .indent-title-2{ margin-left: 2em; } .indent-title-3{ margin-left: 3em; } </style> ## Preface <div class="indent-title-1"> 本篇文章會主要會先在 Talos Kubernetes 上建立 Ceph-csi 和 Ceph StorageClass,再實作本次測試目標。 可以透過點擊以下目錄,選擇想看的內容,跳轉至特定章節 :::warning :::spoiler {state="open"} 目錄 [TOC] ::: </div> ## 目標 <div class="indent-title-1"> 測試 Deployment Object 可不可以共享同一個 Ceph RBD </div> ## 測試環境 <div class="indent-title-1"> ```bash $ kubectl get nodes -o wide NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME m1 Ready control-plane 2d6h v1.28.3 192.168.61.31 <none> Talos (v1.5.5) 6.1.61-talos containerd://1.6.23 w1 Ready <none> 2d6h v1.28.3 192.168.61.34 <none> Talos (v1.5.5) 6.1.61-talos containerd://1.6.23 w2 Ready <none> 2d6h v1.28.3 192.168.61.35 <none> Talos (v1.5.5) 6.1.61-talos containerd://1.6.23 $ kubectl get nodes -o custom-columns=NAME:.metadata.name,TAINTS:.spec.taints NAME TAINTS m1 [map[effect:NoSchedule key:node-role.kubernetes.io/control-plane]] w1 <none> w2 <none> ``` > Talos OS version: v1.5.5 > K8S version: v1.28.3 > Ceph Version: 18.2.0 Reef (stable) > 使用 `nodeName` 可以無視節點上的 Taints,或是使用 toleration (容忍度),可以讓 Pod 容忍節點的 Taints </div> </div> </div> </div> </div> </div> </div> </div> ## Ceph 設定 ### 1. 建立 Ceph Pool <div class="indent-title-1"> 在 Ceph Node 執行以下命令 : ```bash $ ceph osd pool create kubernetes # Use the rbd tool to initialize the pool $ rbd pool init kubernetes # 設定 pool 最大容量為 10GB $ ceph osd pool set-quota kubernetes max_bytes $((10 * 1024 * 1024 * 1024)) ``` > Ceph pool 是否有其他 init 方式? </div> ### 2. 建立 Ceph Client 身分驗證 <div class="indent-title-1"> ```! $ ceph auth get-or-create client.kubernetes mon 'profile rbd' osd 'profile rbd pool=kubernetes' mgr 'profile rbd pool=kubernetes' ``` 螢幕輸出 : ``` [client.kubernetes] key = AQBqkoFlJoe0MxAAb3UMOPeQSsDWI3FTRj8UTw== ``` ![image](https://hackmd.io/_uploads/SkUIfrEwp.png) </div> ## 設定與部屬 Ceph-CSI RBD plugins ### 1. Download ceph-csi <div class="indent-title-1"> 在 Talos k8s 外部管理主機執行以下命令 : ```bash $ cd ~ && git clone https://github.com/ceph/ceph-csi.git ``` </div> ### 2. 建立並切換至 csi-ceph namespace <div class="indent-title-1"> ```bash $ kubectl create ns csi-ceph namespace/csi-ceph created $ kubectl config set-context --current --namespace=csi-ceph Context "admin@topgun" modified. ``` </div> ### 3. 配置 ceph-csi 設定檔 <div class="indent-title-1"> #### 3.1. 獲取 Ceph monitor 和 fsid 資訊 <div class="indent-title-2"> 在 Proxmox Ceph Node ( monitor ) 執行以下命令 : ```bash $ ceph mon dump ``` 螢幕輸出 : ``` epoch 4 fsid cd4925d8-31e9-4245-a392-469d661fedce last_changed 2023-12-16T22:35:48.168822+0800 created 2023-11-22T09:44:04.138233+0800 min_mon_release 18 (reef) election_strategy: 1 0: [v2:192.168.61.11:3300/0,v1:192.168.61.11:6789/0] mon.pve 1: [v2:192.168.61.12:3300/0,v1:192.168.61.12:6789/0] mon.pve2 2: [v2:192.168.61.13:3300/0,v1:192.168.61.13:6789/0] mon.pve3 dumped monmap epoch 4 ``` </div> #### 3.2. 設定 Ceph-csi configmap <div class="indent-title-2"> 在 Talos 外部管理主機執行以下命令 : ```! $ cd ~/ceph-csi/deploy/rbd/kubernetes $ cat <<EOF > csi-config-map.yaml --- apiVersion: v1 kind: ConfigMap data: config.json: |- [ { "clusterID": "cd4925d8-31e9-4245-a392-469d661fedce", "monitors": [ "192.168.61.11:6789", "192.168.61.12:6789", "192.168.61.13:6789" ] } ] metadata: name: ceph-csi-config EOF ``` > 須設定 `clusterID` 和 `monitors 的 IP Address` </div> #### 3.3. 設定 csidriver 的 pod 不要 Mount host 主機的 `/etc/selinux` 到 pods 裡面 <div class="indent-title-2"> ```! $ 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 ``` </div> #### 3.4. 將所有 Yaml 中定義的物件都更換為 csi-ceph Namespace <div class="indent-title-2"> ```! $ sed -i 's|namespace: default|namespace: csi-ceph|g' *.yaml ``` </div> #### 3.5. 設定 csi-rbdplugin-provisioner 和 csi-rbdplugin 的 Pod 能夠在 Contorlplane Node 上執行 <div class="indent-title-2"> ```! $ sed -i '36i\ tolerations:\n - operator: Exists' csi-rbdplugin-provisioner.yaml $ sed -i '24i\ tolerations:\n - operator: Exists' csi-rbdplugin.yaml ``` </div> #### 3.6. 產生 CEPH-CSI cephx secret <div class="indent-title-2"> ``` $ cat <<EOF > ~/ceph-csi/examples/rbd/secret.yaml --- apiVersion: v1 kind: Secret metadata: name: csi-rbd-secret namespace: csi-ceph stringData: userID: kubernetes userKey: AQBqkoFlJoe0MxAAb3UMOPeQSsDWI3FTRj8UTw== # Encryption passphrase encryptionPassphrase: test_passphrase EOF ``` > 須設定 namespace、userID 和 userKey 的值 </div> #### 3.7. 建立 CEPH-CSI cephx secret <div class="indent-title-2"> ```! $ kubectl apply -f ~/ceph-csi/examples/rbd/secret.yaml ``` </div> </div> ### 4. 部屬 ceph-csi <div class="indent-title-1"> #### 4.1. 設定 csi-ceph Namespace 中的 Pod 能夠擁有 privileged 權限 <div class="indent-title-2"> ```! $ kubectl label ns csi-ceph pod-security.kubernetes.io/enforce=privileged ``` > `csi-rbdplugin` 和 `csi-rbdplugin-provisioner` 的 Pod 會需要 privileged 的權限。 > Talos K8S PSA 預設所有的 Pod 不能有 privileged 的權限,此時可以透過幫 Namespace 貼上面這個 label ,就能夠覆蓋掉 K8S PSA 的設定。 </div> #### 4.2. 最新版本的 ceph-csi 還需要另一個 ConfigMap 物件來定義 Ceph 的設定資訊,以便新增至 CSI Container 內的 `ceph.conf` 檔案中 <div class="indent-title-2"> ```! $ kubectl apply -f ~/ceph-csi/deploy/ceph-conf.yaml ``` > This is a sample configmap that helps define a Ceph configuration as required by the CSI plugins. </div> #### 4.3. 開始部屬 ceph-csi <div class="indent-title-2"> ```! $ cd ~/ceph-csi/examples/rbd $ ./plugin-deploy.sh ~/ceph-csi/deploy/rbd/kubernetes ## 移除 vault $ kubectl delete -f ../kms/vault/vault.yaml ``` </div> #### 4.4. 檢視 ceph-csi 部屬狀態 <div class="indent-title-2"> ``` $ kubectl get all ``` 螢幕輸出 : ``` NAME READY STATUS RESTARTS AGE pod/csi-rbdplugin-2dls2 3/3 Running 0 111m pod/csi-rbdplugin-j7fx6 3/3 Running 0 111m pod/csi-rbdplugin-l84w5 3/3 Running 0 111m pod/csi-rbdplugin-provisioner-ddb58c7f4-brz59 7/7 Running 0 111m pod/csi-rbdplugin-provisioner-ddb58c7f4-jdwcr 7/7 Running 0 111m pod/csi-rbdplugin-provisioner-ddb58c7f4-td422 7/7 Running 0 111m NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE service/csi-metrics-rbdplugin ClusterIP 10.98.76.185 <none> 8080/TCP 111m service/csi-rbdplugin-provisioner ClusterIP 10.104.214.86 <none> 8080/TCP 111m NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE daemonset.apps/csi-rbdplugin 3 3 3 3 3 <none> 111m NAME READY UP-TO-DATE AVAILABLE AGE deployment.apps/csi-rbdplugin-provisioner 3/3 3 3 111m NAME DESIRED CURRENT READY AGE replicaset.apps/csi-rbdplugin-provisioner-ddb58c7f4 3 3 3 111m ``` </div> #### 4.5. 設定 StorageClass Yaml 檔 <div class="indent-title-2"> ``` $ 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: cd4925d8-31e9-4245-a392-469d661fedce pool: kubernetes 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` 的值 </div> #### 4.6. 建立 StorageClass <div class="indent-title-2"> ``` $ kubectl apply -f csi-rbd-sc.yaml ``` </div> #### 4.7. 檢視 StorageClass <div class="indent-title-2"> ``` $ kubectl get sc csi-rbd-sc ``` 螢幕輸出 : ``` NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE csi-rbd-sc rbd.csi.ceph.com Delete Immediate true 9s ``` > 以上環境部屬完畢,以下開始測試本次實作目標。 </div> </div> </div> </div> </div> --- ## 開始實作測試目標 ### 1. 設定 PVC <div class="indent-title-1"> ```! $ cat <<EOF > raw-block-pvc-rwx.yaml --- apiVersion: v1 kind: PersistentVolumeClaim metadata: name: raw-block-pvc spec: accessModes: - ReadWriteMany volumeMode: Block resources: requests: storage: 1Gi storageClassName: csi-rbd-sc EOF ``` > Note: Using ceph-csi, specifying Filesystem for volumeMode can support both ReadWriteOnce and ReadOnlyMany accessMode claims, and specifying Block for volumeMode can support ReadWriteOnce, ReadWriteMany, and ReadOnlyMany accessMode claims. </div> ### 2. 建立 PVC Yaml 檔 <div class="indent-title-1"> ```! $ kubectl apply -f raw-block-pvc-rwx.yaml ``` </div> ### 3. 檢查 PVC 建立狀態 <div class="indent-title-1"> ```! $ kubectl get pvc ``` 螢幕輸出 : ```! NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE raw-block-pvc Bound pvc-629444ef-f0c0-4afd-8263-443d6f4beb25 1Gi RWX csi-rbd-sc 3s ``` </div> ### 4. 設定 Deployment Object Yaml 檔 <div class="indent-title-1"> ``` $ cat <<EOF > raw-block-deployment.yaml --- apiVersion: apps/v1 kind: Deployment metadata: name: pod-with-raw-block-volume labels: os: alpine spec: replicas: 3 selector: matchLabels: os: alpine template: metadata: labels: os: alpine spec: containers: - name: alpine image: quay.io/cloudwalker/alp.base:latest command: ["/bin/sleep", "infinity"] volumeDevices: - name: data devicePath: /dev/xvda securityContext: capabilities: add: ["SYS_ADMIN"] volumes: - name: data persistentVolumeClaim: claimName: raw-block-pvc affinity: podAntiAffinity: requiredDuringSchedulingIgnoredDuringExecution: - labelSelector: matchExpressions: - key: os operator: In values: - alpine topologyKey: kubernetes.io/hostname tolerations: - operator: Exists EOF ``` </div> ### 5. 建立 Deployment Object Yaml 檔 <div class="indent-title-1"> ```! $ kubectl apply -f raw-block-deployment.yaml ``` </div> ### 6. 檢查 Pods 運作狀態 <div class="indent-title-1"> ```! $ kubectl get pods -l os=alpine -o wide ``` 螢幕輸出 : ``` NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES pod-with-raw-block-volume-8677b97b-m64wt 1/1 Running 0 26s 10.244.1.19 m1 <none> <none> pod-with-raw-block-volume-8677b97b-nnpt2 1/1 Running 0 26s 10.244.0.53 w1 <none> <none> pod-with-raw-block-volume-8677b97b-x2qh9 1/1 Running 0 26s 10.244.2.46 w2 <none> <none> ``` </div> </div> </div> </div> </div> </div> </div> ### 7. 進入不同的 Pods 檢視已掛載的 Ceph Block Device <div class="indent-title-1"> ``` $ kubectl exec pod-with-raw-block-volume-8677b97b-2dfj9 -- ls -l /dev/xvda $ kubectl exec pod-with-raw-block-volume-8677b97b-ft4kw -- ls -l /dev/xvda $ kubectl exec pod-with-raw-block-volume-8677b97b-sbp6m -- ls -l /dev/xvda ``` 3 次執行的結果應該跟以下螢幕輸出類似 : ``` brw------- 1 root root 252, 0 Dec 19 09:25 /dev/xvda ``` </div> ### 8. 測試使用 XFS 檔案系統 Fotmat Ceph Block Device,並將 /ceph 目錄掛載上去,最後建立 test 空檔案 <div class="indent-title-1"> ``` $ kubectl exec -it pod-with-raw-block-volume-8677b97b-m64wt -- bash pod-with-raw-block-volume-8677b97b-m64wt:/# apk add xfsprogs pod-with-raw-block-volume-8677b97b-m64wt:/# mkfs.xfs /dev/xvda pod-with-raw-block-volume-8677b97b-m64wt:/# mkdir /ceph pod-with-raw-block-volume-8677b97b-m64wt:/# mount /dev/xvda /ceph pod-with-raw-block-volume-8677b97b-m64wt:/# lsblk /dev/xvda NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS rbd0 252:0 0 1G 0 disk /ceph pod-with-raw-block-volume-8677b97b-m64wt:/# touch /ceph/test pod-with-raw-block-volume-8677b97b-m64wt:/# exit exit ``` </div> ### 9. 進到另外兩個 Pods 檢查是否有看到 test 這個空檔案 <div class="indent-title-1"> ``` $ kubectl exec -it pod-with-raw-block-volume-8677b97b-nnpt2 -- bash pod-with-raw-block-volume-8677b97b-nnpt2:/# mkdir /ceph pod-with-raw-block-volume-8677b97b-nnpt2:/# mount /dev/xvda /ceph pod-with-raw-block-volume-8677b97b-nnpt2:/# ls -l /ceph/ total 0 -rw-r--r-- 1 root root 0 Dec 19 13:54 test pod-with-raw-block-volume-8677b97b-nnpt2:/# exit exit $ kubectl exec -it pod-with-raw-block-volume-8677b97b-x2qh9 -- bash pod-with-raw-block-volume-8677b97b-x2qh9:/# mkdir /ceph pod-with-raw-block-volume-8677b97b-x2qh9:/# mount /dev/xvda /ceph pod-with-raw-block-volume-8677b97b-x2qh9:/# ls -l /ceph/ total 0 -rw-r--r-- 1 root root 0 Dec 19 13:54 test pod-with-raw-block-volume-8677b97b-x2qh9:/# exit exit ``` </div> ### 10. 列出在 kubernetes pool 底下的 RBD images <div class="indent-title-1"> 在 Ceph Node 執行以下命令 ``` $ rbd ls kubernetes ``` 螢幕輸出 : ``` csi-vol-89e67965-9fb9-47cb-b25b-210268f6d03e ``` </div> ### 11. 查看 RBD Image 的詳細資訊 <div class="indent-title-1"> ``` $ rbd info kubernetes/csi-vol-89e67965-9fb9-47cb-b25b-210268f6d03e ``` 螢幕輸出 : ``` rbd image 'csi-vol-89e67965-9fb9-47cb-b25b-210268f6d03e': size 1 GiB in 256 objects order 22 (4 MiB objects) snapshot_count: 0 id: 13c94844fea3c6 block_name_prefix: rbd_data.13c94844fea3c6 format: 2 features: layering op_features: flags: create_timestamp: Tue Dec 19 21:50:23 2023 access_timestamp: Tue Dec 19 21:50:23 2023 modify_timestamp: Tue Dec 19 21:50:23 2023 ``` </div> </div> </div> </div> </div> </div> </div> ## 清除測試環境 <div class="indent-title-1"> ```! ## 在 Talos 外部管理主機執行以下命令 $ kubectl delete -f raw-block-deployment.yaml,raw-block-pvc-rwx.yaml $ kubectl delete -f csi-rbd-sc.yaml,secret.yaml $ kubectl delete -f ~/ceph-csi/deploy/ceph-conf.yaml $ ./plugin-teardown.sh ~/ceph-csi/deploy/rbd/kubernetes/ $ kubectl label ns csi-ceph pod-security.kubernetes.io/enforce- $ kubectl get all,configmap,secret NAME DATA AGE configmap/kube-root-ca.crt 1 20h $ kubectl config set-context --current --namespace=default $ kubectl delete ns csi-ceph $ cd ~ && rm -r ceph-csi/ ## 在 Ceph Node 執行以下命令 $ rbd -p kubernetes ls $ ceph auth rm client.kubernetes $ ceph osd pool rm kubernetes kubernetes --yes-i-really-really-mean-it ``` </div> ## 參考文件 - [BLOCK DEVICES AND KUBERNETES - Ceph Docs](https://docs.ceph.com/en/quincy/rbd/rbd-kubernetes/) - [How to test RBD and CephFS plugins with Kubernetes 1.14+ - Ceph/ceph-csi Github](https://github.com/ceph/ceph-csi/tree/devel/examples) - [設定 StorageClass (以 Ceph RBD 為例) - 小信豬的原始部落](https://godleon.github.io/blog/Kubernetes/k8s-Config-StorageClass-with-Ceph-RBD/) - [Pod Security - Talos Docs](https://www.talos.dev/v1.6/kubernetes-guides/configuration/pod-security/)