CubeFS - 新一代云原生存儲系統(tǒng)
CubeFS 是一種新一代云原生存儲系統(tǒng),支持 S3、HDFS 和 POSIX 等訪問協(xié)議,支持多副本與糾刪碼兩種存儲引擎,為用戶提供多租戶、 多 AZ 部署以及跨區(qū)域復制等多種特性。
CubeFS 作為一個云原生的分布式存儲平臺,提供了多種訪問協(xié)議,因此其應用場景也非常廣泛,下面簡單介紹幾種比較典型的應用場景
- 大數(shù)據(jù)分析:兼容 HDFS 協(xié)議,為 Hadoop 生態(tài)(如 Spark、Hive)提供統(tǒng)一存儲底座,為計算引擎提供無限的存儲空間以及大帶寬的數(shù)據(jù)存儲能力。
- 深度訓練/機器學習:作為分布式并行文件系統(tǒng),支撐 AI 訓練、模型存儲及分發(fā)、IO 加速等需求。
- 容器共享存儲:容器集群可以將容器鏡像的配置文件或初始化加載數(shù)據(jù)存儲在 CubeFS 上,在容器批量加載時實時讀取。多 Pod 間通過 CubeFS 共享持久化數(shù)據(jù),在 Pod 故障時可以進行快速故障切換。
- 數(shù)據(jù)庫&中間件:為數(shù)據(jù)庫應用如 MySQL、ElasticSearch、ClickHouse 提供高并發(fā)、低時延云盤服務,實現(xiàn)徹底的存算分離。
- 在線服務:為在線業(yè)務(如廣告、點擊流、搜索)或終端用戶的圖、文、音視頻等內(nèi)容提供高可靠、低成本的對象存儲服務。
- 傳統(tǒng) NAS 上云:替換線下傳統(tǒng)本地存儲及 NAS,助力 IT 業(yè)務上云。
特性
CubeFS 具有眾多特性,包括:
多協(xié)議
兼容 S3、POSIX、HDFS 等多種訪問協(xié)議,協(xié)議間訪問可互通
- POSIX 兼容:兼容 POSIX 接口,讓上層應用的開發(fā)變得極其簡單,就跟使用本地文件系統(tǒng)一樣便捷。此外,CubeFS 在實現(xiàn)時放松了對 POSIX 語義的一致性要求來兼顧文件和元文件操作的性能。
- 對象存儲兼容:兼容 AWS 的 S3 對象存儲協(xié)議,用戶可以使用原生的 Amazon S3 SDK 管理 CubeFS 中的資源。
- Hadoop 協(xié)議兼容:兼容 Hadoop FileSystem 接口協(xié)議,用戶可以使用 CubeFS 來替換 HDFS,做到上層業(yè)務無感。
雙引擎
支持多副本及糾刪碼兩種引擎,用戶可以根據(jù)業(yè)務場景靈活選擇
- 多副本存儲引擎:副本之間的數(shù)據(jù)為鏡像關系,通過強一致的復制協(xié)議來保證副本之間的數(shù)據(jù)一致性,用戶可以根據(jù)應用場景靈活的配置不同副本數(shù)。
- 糾刪碼存儲引擎:糾刪碼引擎具備高可靠、高可用、低成本、支持超大規(guī)模(EB)的特性,根據(jù)不同 AZ 模型可以靈活選擇糾刪碼模式。
多租戶
支持多租戶管理,提供細粒度的租戶隔離策略
可擴展
可以輕松構建 PB 或者 EB 級規(guī)模的分布式存儲服務,各模塊可水平擴展
高性能
支持多級緩存,針對小文件特定優(yōu)化,支持多種高性能的復制協(xié)議
- 元數(shù)據(jù)管理:元數(shù)據(jù)集群為內(nèi)存元數(shù)據(jù)存儲,在設計上使用兩個 B-Tree(inodeBTree 與 dentryBTree)來管理索引,進而提升元數(shù)據(jù)訪問性能;
- 強一致副本協(xié)議 :CubeFS 根據(jù)文件寫入方式的不同采用不同的復制協(xié)議來保證副本間的數(shù)據(jù)一致性。(如果文件按照順序?qū)懭耄瑒t會使用主備復制協(xié)議來優(yōu)化 IO 吞吐量;如果是隨機寫入覆蓋現(xiàn)有文件內(nèi)容時,則是采用一種基于 Multi-Raft 的復制協(xié)議,來確保數(shù)據(jù)的強一致性);
- 多級緩存:糾刪碼卷支持多級緩存加速能力,針對熱點數(shù)據(jù),提供更高數(shù)據(jù)訪問性能:
- 本地緩存:可以在 Client 機器上同機部署 BlockCache 組件,將本地磁盤作為本地緩存. 可以不經(jīng)過網(wǎng)絡直接讀取本地 Cache, 但容量受本地磁盤限制;
- 全局緩存:使用副本組件 DataNode 搭建的分布式全局 Cache, 比如可以通過部署客戶端同機房的 SSD 磁盤的 DataNode 作為全局 cache, 相對于本地 cache, 需要經(jīng)過網(wǎng)絡, 但是容量更大, 可動態(tài)擴縮容,副本數(shù)可調(diào)。
云原生
基于 CSI 插件可以快速地在 Kubernetes 上使用 CubeFS。
整體架構
整體上 CubeFS 由元數(shù)據(jù)子系統(tǒng)(Metadata Subsystem)、數(shù)據(jù)子系統(tǒng)(Data Subsystem)和資源管理節(jié)點(Master)以及對象網(wǎng)關(Object Subsystem)組成,可以通過 POSIX/HDFS/S3 接口訪問存儲數(shù)據(jù)。
資源管理節(jié)點
由多個 Master 節(jié)點組成,負責異步處理不同類型的任務,如管理數(shù)據(jù)分片與元數(shù)據(jù)分片(包括創(chuàng)建、刪除、更新以及一致性檢查等),檢查數(shù)據(jù)節(jié)點或者元數(shù)據(jù)節(jié)點的健康狀態(tài),維護管理卷信息等
Master 節(jié)點可以有多個,節(jié)點之間通過 Raft 算法保證元數(shù)據(jù)的一致性,并且持久化到
RocksDB
中。
元數(shù)據(jù)子系統(tǒng)
由多個 Meta Node 節(jié)點組成,多個元數(shù)據(jù)分片(Meta Partition)和 Raft 實例(基于 Multi-Raft 復制協(xié)議)組成,每個元數(shù)據(jù)分片表示一個 Inode 范圍元數(shù)據(jù),其中包含兩棵內(nèi)存 B-Tree 樹:inode BTree 與 dentry BTree。
元數(shù)據(jù)實例最少需要 3 個,支持水平擴容。
數(shù)據(jù)子系統(tǒng)
分為副本子系統(tǒng)和糾刪碼子系統(tǒng),兩種子系統(tǒng)可同時存在,也都可單獨存在:
- 副本子系統(tǒng)由 DataNode 組成,每個節(jié)點管理一組數(shù)據(jù)分片,多個節(jié)點的數(shù)據(jù)分片構成一個副本組;
- 糾刪碼子系統(tǒng)(Blobstore)主要由 BlobNode 模塊組成,每個節(jié)點管理一組數(shù)據(jù)塊,多個節(jié)點的數(shù)據(jù)塊構成一個糾刪碼條帶。
數(shù)據(jù)節(jié)點支持水平擴容。
對象子系統(tǒng)
由對象節(jié)點(ObjectNode)組成,提供了兼容標準 S3 語義的訪問協(xié)議,可以通過 Amazon S3 SDK 或者是 s3cmd 等工具訪問存儲資源。
卷
邏輯上的概念,由多個元數(shù)據(jù)和數(shù)據(jù)分片組成,從客戶端的角度看,卷可以被看作是可被容器訪問的文件系統(tǒng)實例。從對象存儲的角度來看,一個卷對應著一個 bucket。一個卷可以在多個容器中掛載,使得文件可以被不同客戶端同時訪問。
安裝
CubeFS 的安裝方式有很多,包括 Docker、YUM 等等,由于我們這里直接直接在 Kubernetes 上使用,因此我們可以通過 Helm 來安裝 CubeFS,各組件會直接使用宿主機網(wǎng)絡,使用 hostPath 將磁盤映射到容器中。
在 Kubernetes 集群中部署 CubeFS 可以按照下圖所示的架構進行部署:
CubeFS 目前由這四部分組成:
- Master:資源管理節(jié)點,負責維護整個集群的元信息,部署為 StatefulSet 資源。
- DataNode:數(shù)據(jù)存儲節(jié)點,需要掛載大量磁盤負責文件數(shù)據(jù)的實際存儲,部署為 DaemonSet 資源。
- MetaNode:元數(shù)據(jù)節(jié)點,負責存儲所有的文件元信息,部署為 DaemonSet 資源。
- ObjectNode:負責提供轉(zhuǎn)換 S3 協(xié)議提供對象存儲的能力,無狀態(tài)服務,部署為 Deployment 資源。
在部署之前,我們需要擁有一個至少有 3 個節(jié)點(最好 4 個以上,可以容災)的 Kubernetes 集群,且集群版本需要大于等于 1.15。
$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
master Ready control-plane 46d v1.28.7
node1 Ready <none> 46d v1.28.7
node2 Ready <none> 46d v1.28.7
首先我們需要給節(jié)點打上各自的標簽,標明這臺機器要在 CubeFS 集群中承擔的角色:
由于我們這里只有 3 個節(jié)點,所以需要這些節(jié)點承擔一些共同的角色。
- Master 節(jié)點,至少三個,建議為奇數(shù)個:
kubectl label node master component.cubefs.io/master=enabled
kubectl label node node1 component.cubefs.io/master=enabled
kubectl label node node2 component.cubefs.io/master=enabled
- MetaNode 元數(shù)據(jù)節(jié)點,至少 3 個,奇偶無所謂:
kubectl label node master component.cubefs.io/metanode=enabled
kubectl label node node1 component.cubefs.io/metanode=enabled
kubectl label node node2 component.cubefs.io/metanode=enabled
- Datanode 數(shù)據(jù)節(jié)點,至少 3 個,奇偶無所謂:
kubectl label node master component.cubefs.io/datanode=enabled
kubectl label node node1 component.cubefs.io/datanode=enabled
kubectl label node node2 component.cubefs.io/datanode=enabled
- ObjectNode 對象存儲節(jié)點,可以按需進行標記,不需要對象存儲功能的話也可以不部署這個組件:
kubectl label node node1 component.cubefs.io/objectnode=enabled
kubectl label node node2 component.cubefs.io/objectnode=enabled
- CSI 組件,用于在 Kubernetes 中使用 CubeFS,需要在所有節(jié)點上部署:
kubectl label node node1 component.cubefs.io/csi=enabled
kubectl label node node2 component.cubefs.io/csi=enabled
CubeFS 安裝時會根據(jù)這些標簽通過 nodeSelector 進行匹配,然后在機器創(chuàng)建起對應的 Pod。
接下來我們就可以通過 Helm 來安裝 CubeFS 了,首先我們需要將 CubeFS 的 Helm Chart 下載到本地:
git clone https://github.com/cubefs/cubefs-helm
cd cubefs-helm
然后根據(jù)自身環(huán)境定制 values 文件,比如下面是一個簡單的 values 文件:
# cubefs-values.yaml
component:
master: true
datanode: true
metanode: true
objectnode: false
client: false
csi: true
monitor: false
ingress: true
image:
# 3.3.0 版本之前會出現(xiàn) /lib64/libstdc++.so.6: version `GLIBCXX_3.4.21' not found 錯誤
server: cubefs/cfs-server:v3.3.0
client: cubefs/cfs-client:v3.3.0
csi_driver: cnych/cubefs-cfs-csi-driver:3.2.0.150.0
csi_provisioner: cnych/csi-provisioner:v2.2.2
csi_attacher: cnych/csi-attacher:v3.4.0
csi_resizer: cnych/csi-resizer:v1.3.0
driver_registrar: cnych/csi-node-driver-registrar:v2.5.0
master:
# The replicas of master component, at least 3, recommend to be an odd number
replicas: 3
tolerations:
- key: "node-role.kubernetes.io/control-plane"
operator: "Exists"
effect: "NoSchedule"
resources:
enabled: true
requests:
memory: "512Mi"
cpu: "500m"
limits:
memory: "512Mi"
cpu: "500m"
metanode:
total_mem: "4000000000"
tolerations:
- key: "node-role.kubernetes.io/control-plane"
operator: "Exists"
effect: "NoSchedule"
resources:
enabled: true
requests:
memory: "512Mi"
cpu: "500m"
limits:
memory: "512Mi"
cpu: "500m"
datanode:
# DataNode 要使用的磁盤,可以掛載多塊
# 格式: 掛載點:保留的空間
# 保留的空間: 單位字節(jié),當磁盤剩余空間小于該值時將不會再在該磁盤上寫入數(shù)據(jù)
disks:
- /data0:10000000000
tolerations:
- key: "node-role.kubernetes.io/control-plane"
operator: "Exists"
effect: "NoSchedule"
resources:
enabled: true
requests:
memory: "512Mi"
cpu: "500m"
limits:
memory: "512Mi"
cpu: "500m"
csi:
driverName: csi.cubefs.com
logLevel: error
kubeletPath: /var/lib/kubelet
controller:
tolerations: []
nodeSelector:
component.cubefs.io/csi: "enabled"
node:
tolerations: []
nodeSelector:
component.cubefs.io/csi: "enabled"
resources:
enabled: true
requests:
memory: "512Mi"
cpu: "500m"
limits:
memory: "512Mi"
cpu: "500m"
storageClass:
setToDefault: false
reclaimPolicy: "Delete"
# CSI 客戶端配置
provisioner:
# Kubelet 的主目錄
kubelet_path: /var/lib/kubelet
然后使用如下命令進行 CubeFS 部署:
helm upgrade --install cubefs -n cubefs-system ./cubefs-helm/cubefs -f cubefs-values.yaml --create-namespace
部署完成后可以使用命令 kubectl get pods -n cubefs-system 等待所有組件狀態(tài)變?yōu)?Running 即可:
$ kubectl get pods -n cubefs-system
NAME READY STATUS RESTARTS AGE
cfs-csi-controller-66cdbb664f-pqkp6 4/4 Running 0 28m
cfs-csi-node-966t9 2/2 Running 0 25m
cfs-csi-node-9f4ts 2/2 Running 0 25m
datanode-4zfhc 1/1 Running 0 28m
datanode-blc8w 1/1 Running 0 28m
datanode-ldj72 1/1 Running 0 28m
master-0 1/1 Running 0 28m
master-1 1/1 Running 0 23m
master-2 1/1 Running 0 23m
metanode-5csgt 1/1 Running 0 7m31s
metanode-jvqnl 1/1 Running 0 7m31s
metanode-vpjtj 1/1 Running 0 7m31s
各個組件的關鍵日志會在容器標準輸出中輸出。
此外還會自動創(chuàng)建一個 StorageClass 對象,可以通過 kubectl get sc 查看:
$ kubectl get sc
NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE
cfs-sc csi.cubefs.com Delete Immediate true 29m
測試
現(xiàn)在我們有了一個可用的 StorageClass 對象了,接下來可以創(chuàng)建一個 PVC 對象來測試 CubeFS 的存儲功能。如下所示:
# cubefs-pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: cubefs-pvc
namespace: default
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 5Gi
storageClassName: cfs-sc
上面的 PVC 對象中我們通過 storageClassName 指定了使用的 StorageClass 名稱,這里是 cfs-sc,這個名稱需要和我們之前創(chuàng)建的 StorageClass 名稱一致,這樣就會根據(jù) cubefs-sc 中定義的參數(shù)來創(chuàng)建存儲卷。當我們在編寫 pvc yaml 主要注意一下參數(shù):
- metadata.name:pvc 的名稱,可以按需修改,同一個 namespace 下 pvc 名稱是唯一的,不允許有兩個相同的名稱。
- metadata.namespace:pvc 所在的命名空間,按需修改
- spec.resources.request.storage:pvc 容量大小。
- storageClassName:這是 storage class 的名稱。如果想知道當前集群有哪些 storageclass,可以通過命令 kubectl get sc 來查看。
這里直接應用這個 yaml 文件即可:
kubectl apply -f cubefs-pvc.yaml
執(zhí)行命令完成后,可以通過命令 kubectl get pvc -n 命名空間 來查看對應 pvc 的狀態(tài),Pending 代表正在等待,Bound 代表創(chuàng)建成功。
$ kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
cubefs-pvc Bound pvc-53cc95b7-8a05-43f8-8903-f1c6f7b11c05 5Gi RWO cfs-sc 3s
如果 PVC 的狀態(tài)一直處于 Pending,可以通過命令查看原因:
kubectl describe pvc -n 命名空間 PVC 名稱
如果報錯消息不明顯或者看不出錯誤,則可以使用 kubectl logs 相關命令先查看 csi controller pod 里面的 csi-provisioner 容器的報錯信息,csi-provisioner 是 k8s 與 csi driver 的中間橋梁,很多信息都可以在這里的日志查看。
如果 csi-provisioner 的日志還看不出具體問題,則使用 kubectl exec 相關命令查看 csi controller pod 里面的 cfs-driver 容器的日志,它的日志放在容器里面的 /cfs/logs 下。
這里不能使用 Kubectl logs 相關命令是因為 cfs-driver 的日志并不是打印到標準輸出,而其它幾個類似 csi-provisioner 的 sidecar 容器的日志是打印到標準輸出的,所以可以使用 kubectl logs 相關命令查看。
有了 PVC 則接下來就可以在應用中掛載到指定目錄了,比如我們這里有一個如下所示的示例:
# cfs-csi-demo.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: cfs-csi-demo
namespace: default
spec:
selector:
matchLabels:
app: cfs-csi-demo-pod
template:
metadata:
labels:
app: cfs-csi-demo-pod
spec:
nodeSelector:
component.cubefs.io/csi: enabled
containers:
- name: cfs-csi-demo
image: nginx:1.17.9
imagePullPolicy: "IfNotPresent"
ports:
- containerPort: 80
name: "http-server"
volumeMounts:
- mountPath: "/usr/share/nginx/html"
mountPropagation: HostToContainer
name: mypvc
volumes:
- name: mypvc
persistentVolumeClaim:
claimName: cubefs-pvc
上面的資源清單中我們將一個名稱為 cubefs-pvc 的 PVC 掛載到 cfs-csi-demo 容器里面的 /usr/share/nginx/html 下。
同樣直接創(chuàng)建這個資源清單即可:
kubectl apply -f cfs-csi-demo.yaml
創(chuàng)建完成后可以通過 kubectl get pods 查看 Pod 的狀態(tài):
$ kubectl get pods -owide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
cfs-csi-demo-5d456c8d97-sjsvw 1/1 Running 0 78s 10.0.1.85 node1 <none> <none>
我們可以直接通過往 /usr/share/nginx/html 目錄寫入文件來測試 CubeFS 的存儲功能:
$ kubectl exec -it cfs-csi-demo-5d456c8d97-sjsvw -- /bin/bash
root@cfs-csi-demo-5d456c8d97-sjsvw:/# echo "Hello, CubeFS" > /usr/share/nginx/html/index.html
root@cfs-csi-demo-5d456c8d97-sjsvw:/#
然后我們可以將這個 Pod 刪除重建,然后查看是否還有這個文件:
$ kubectl delete pod cfs-csi-demo-5d456c8d97-sjsvw
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
cfs-csi-demo-5d456c8d97-c245z 1/1 Running 0 3m22s
$ kubectl exec -it cfs-csi-demo-5d456c8d97-c245z -- ls /usr/share/nginx/html
index.html
$ kubectl exec -it cfs-csi-demo-5d456c8d97-c245z -- cat /usr/share/nginx/html/index.html
Hello, CubeFS
如果能夠看到 Hello, CubeFS 則說明 CubeFS 的存儲功能正常。