k8s集羣對接cephfs

cephfs簡介

CephFS是個與POSIX標準兼容的文件系統,坐於基於對象的 Ceph 存儲集羣之上,其內的文件被映射到 Ceph 存儲集羣內的對象。客戶端可以把此文件系統掛載在內核對象或用戶空間文件系統( FUSE )上。文件目錄和其他元數據存儲在ceph的RADOS中,而MDS緩存元信息和文件目錄信息。
主要特點如下:

  • 可以支持內核空間mount -t ceph方式掛載,也支持用戶空間的ceph-fuse方式掛載
  • 多個clients可以同時讀寫,屬於共享文件系統
  • 由於底層是ceph,並且MDS支持主備,能夠實現高可用
  • 配額管理:
    1)用戶空間的ceph-fuse方式掛載支持空間的配額管理
    2)內核空間的掛載支持配額管理需要ceph mimic+以上版本的集羣和掛載cephfs的客戶端kernel版本爲>=4.17

cephfs架構圖如下:
k8s集羣對接cephfs

在k8s環境下使用ceph rbd和cephfs的對比

1,多client掛載
ceph rbd支持同一個node下多pod的掛載;
cephfs支持跨node的多pod的掛載,實現共享;
2,性能
ceph rbd讀取寫入延遲低,I/O帶寬表現良好;
cephfs讀取延遲低,寫入延遲差一點,I/O帶寬表現良好,尤其是block size較大一些的文件;存儲空間使用過大可能出現性能瓶頸;
3,配額管理
ceph rbd支持;
cephfs有條件支持;







環境準備

需要提前部署好ceph集羣,見:https://blog.51cto.com/leejia/2499684

創建cephfs

cephfs需要至少一個mds(Ceph Metadata Server)服務用來存放cepfs服務依賴元數據信息,有條件的可以創建2個會自動成爲主備。在ceph1上創建mds服務:

# ceph-deploy mds create ceph1 ceph2

一個cephfs需要至少兩個RADOS存儲池,一個用於數據、一個用於元數據。配置這些存儲池時需考慮:

  • 爲元數據存儲池設置較高的副本水平,因爲此存儲池丟失任何數據都會導致整個文件系統失效;
  • 爲元數據存儲池分配低延時存儲器(例如SSD),因爲它會直接影響到客戶端的操作延時;
    我們在ceph1上來創建存儲池和和cephfs:
    ceph osd pool create cephfs-data 128 128
    ceph osd pool create cephfs-metadata 128 128
    ceph fs new cephfs cephfs-metadata cephfs-data

    創建完成之後,查看mds和fs的狀態:

    # ceph mds stat
    e6: 1/1/1 up {0=ceph2=up:active}, 1 up:standby
    # ceph fs ls
    name: cephfs, metadata pool: cephfs-metadata, data pools: [cephfs-data ]

    cephfs手動掛載測試

    在ceph1上創建一個能訪問cephfs的用戶

    # ceph auth get-or-create client.cephfs mon "allow r" mds "allow rw" osd "allow rw pool=cephfs-data, allow rw pool=cephfs-metadata"
    [client.cephfs]
    key = AQD1LfVffTlMHBAAove2EgMyJ8flMNYZG9VbTA==
    # ceph auth get client.cephfs
    exported keyring for client.cephfs
    [client.cephfs]
    key = AQD1LfVffTlMHBAAove2EgMyJ8flMNYZG9VbTA==
    caps mds = "allow rw"
    caps mon = "allow r"
    caps osd = "allow rw pool=cephfs-data, allow rw pool=cephfs-metadata"

把cephfs用戶對應的key訪問如文件,並使用mount命令來掛載。卸載的話,通過umount命令直接卸載即可:

# echo "AQD1LfVffTlMHBAAove2EgMyJ8flMNYZG9VbTA==" >> /tmp/lee.secret
# mount -t ceph 172.18.2.172:6789,172.18.2.178:6789,172.18.2.189:6789:/ /mnt -o name=admin,secretfile=/tmp/lee.secret

# df -h|grep mnt
172.18.2.172:6789,172.18.2.178:6789,172.18.2.189:6789:/  586G   98G  488G  17% /mnt

通過storageclass動態創建基於cephfs的pv

現在cephfs已經部署完成,現在需要考慮k8s對接cephfs使用的問題了。k8s使用cephfs進行數據持久化時,主要有三種方式:

  • 使用k8s支持的cephfs類型volume直接掛載。
  • 使用k8s支持的PV&PVC方式進行數據卷的掛載。
  • 使用社區提供的一個cephfs provisioner來支持以storageClass的方式動態的分配PV,來進行掛載。目前使用的k8s1.18內置provisioner暫時還沒有提供這個功能。

我們接下來介紹通過storageclass資源來動態分配pv的方法。

storageclass資源介紹

storageclass一般由管理員創建,它作爲存儲資源的抽象定義,對用戶設置的PVC申請屏蔽後端存儲的細節操作,一方面減少了用戶對於存儲資源細節的關注,另一方面減輕了管理員手工管理PV的工作,由系統根據spec自動完成PV的創建和綁定,實現了動態的資源供應。並且,storageclass是不受namespace限制的。
storageclass的關鍵組成:

  • provisioner
    每個storageclass都有一個provisioner,用來決定使用哪個卷插件創建PV,該字段必須指定。不同的存儲有對應的provisioner,但是k8s內置了一些存儲的provisioner,另外有一些存儲沒有內置到k8s中。沒有內置的,可以使用外部第三方的provisioner,第三方的provisioner要符合k8s定義的規範https://github.com/kubernetes/community/blob/master/contributors/design- proposals/volume-provisioning.md
  • parameters
    後端存儲資源提供者的參數設置,不同的provisioner包括不同的參數設置。某些參數可以不顯示設定,provisioner將使用其默認值。例如ceph存儲的參數可以由ceph集羣的monitor地址,存儲池,默認文件系統等參數構成。
  • reclaimPolicy
    由storageclass動態創建的pv會在類的reclaimPolicy字段中指定回收策略,可以是Delete或者 Retain。如果storageclass對象被創建時沒有指定reclaimPolicy,它將默認爲Delete。通過storageclass手動創建並管理的pv會使用它們被創建時指定的回收政策。

動態創建流程圖(來源於kubernetes in action):
k8s集羣對接cephfs

安裝cephfs-provisioner

由於k8s沒有內置cephfs的provisioner,故需要安裝第三方的。我們先來簡單看下此provisioner的架構:
k8s集羣對接cephfs
主要有兩部分:

  • cephfs-provisioner.go
    是cephfs-provisioner(cephfs的storageclass)的核心,主要是 watch kubernetes中 PVC 資源的CURD事件,然後以命令行方式調用 cephfs_provisor.py腳本創建PV。
  • cephfs_provisioner.py
    python 腳本實現的與cephfs交互的命令行工具。cephfs-provisioner 對cephfs端volume的創建都是通過該腳本實現。裏面封裝了volume的增刪改查等功能。

安裝

# git clone https://github.com/kubernetes-retired/external-storage.git
# cd external-storage/ceph/cephfs/deploy/
# NAMESPACE=kube-system
# sed -r -i "s/namespace: [^ ]+/namespace: $NAMESPACE/g" ./rbac/*.yaml
# sed -i "/PROVISIONER_SECRET_NAMESPACE/{n;s/value:.*/value: $NAMESPACE/;}" rbac/deployment.yaml
# kubectl -n $NAMESPACE apply -f ./rbac

過幾分鐘檢查是否安裝成功

# kubectl get pods -n kube-system|grep 'cephfs-provisioner'
cephfs-provisioner-6c4dc5f646-swncq        1/1     Running   0          1h

爲cephfs創建stoageclass資源

我們複用ceph rdb存儲的secret作爲cephfs的secret:

# vim sc.yaml
kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
  name: cephfs
provisioner: ceph.com/cephfs
parameters:
    monitors: 172.18.2.172:6789,172.18.2.178:6789,172.18.2.189:6789
    adminId: admin
    adminSecretNamespace: "kube-system"
    adminSecretName: ceph-secret

# kubectl apply -f sc.yaml
# kubectl get storageclass
NAME                 PROVISIONER                  RECLAIMPOLICY   VOLUMEBINDINGMODE   ALLOWVOLUMEEXPANSION   AGE
ceph-rbd (default)   ceph.com/rbd                 Delete          Immediate           false                  216d
cephfs               ceph.com/cephfs              Delete          Immediate           false                  2h

跨不同node的pod之間存儲共享

創建pvc並配置對應的storageclass,並確保pvc的status爲Bound,代表storageclass創建和綁定pv成功:

# vim pvc.yaml
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: claim-local
spec:
  accessModes:
    - ReadWriteMany
  resources:
    requests:
      storage: 1Gi
  storageClassName: "cephfs"

# kubectl apply -f pvc.yaml
# kubectl get pvc|grep claim-local
NAME                                                  STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS      AGE
claim-local                                           Bound    pvc-d30fda86-acfd-48e2-b7bc-568f6148332f   1Gi        RWX            cephfs            25s

創建一個綁定此pvc的pod,名字爲cephfs-pv-pod1:

# vim pod.yaml
kind: Pod
apiVersion: v1
metadata:
  name: cephfs-pv-pod1
spec:
  containers:
  - name: cephfs-pv-busybox1
    image: busybox
    command: ["sleep", "60000"]
    volumeMounts:
    - mountPath: "/mnt/cephfs"
      name: cephfs-vol1
      readOnly: false
  volumes:
  - name: cephfs-vol1
    persistentVolumeClaim:
      claimName: claim-local

# kubectl apply -f pod.yaml
# kubectl get pods -o wide
NAME                                  READY   STATUS             RESTARTS   AGE    IP               NODE    NOMINATED NODE   READINESS GATES
cephfs-pv-pod1                        1/1     Running            0          33s    10.101.26.40     work4   <none>           <none>

我們發現cephfs-pv-pod1被調度到了work4,故我們給work1添加一個label,然後創建一個cephfs-pv-pod2並設置label把此pod調度到work1:

# kubectl label nodes work1 type=test
# kubectl get nodes --show-labels|grep work1
work1     Ready    <none>   237d   v1.18.2   app=dashboard,beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=work1,kubernetes.io/os=linux,type=test

# vim pod.yaml
kind: Pod
apiVersion: v1
metadata:
  name: cephfs-pv-pod2
spec:
  containers:
  - name: cephfs-pv-busybox1
    image: busybox
    command: ["sleep", "60000"]
    volumeMounts:
    - mountPath: "/mnt/cephfs"
      name: cephfs-vol1
      readOnly: false
  volumes:
  - name: cephfs-vol1
    persistentVolumeClaim:
      claimName: claim-local
  nodeSelector:
    type: test

# kubectl apply -f pod.yaml
# kubectl get pods -o wide|grep cephfs
cephfs-pv-pod1                        1/1     Running     0          8m39s   10.101.26.40     work4   <none>           <none>
cephfs-pv-pod2                        1/1     Running     0          34s     10.99.1.167      work1   <none>           <none>

我們看到兩個被調度到不同node的pod已經運行正常了,現在我們在cephfs-pv-pod1的存儲寫入數據,然後查看cephfs-pv-pod2的存儲是否正常同步:

# kubectl exec -it cephfs-pv-pod1 sh
/ # echo "test" >> /mnt/cephfs/1.txt

# kubectl exec -it cephfs-pv-pod2 sh
/ # cat /mnt/cephfs/1.txt
test

我們發現cephfs存儲已經正常掛載和使用了,至此k8s對接cephfs完成。

參考資料

https://docs.ceph.com/en/latest/cephfs/quota/
https://www.twblogs.net/a/5baf8cda2b7177781a0f2989
https://github.com/kubernetes-retired/external-storage/tree/master/ceph/cephfs

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章