CubeFS - 新一代云原生存儲(chǔ)系統(tǒng)
CubeFS 是一種新一代云原生存儲(chǔ)系統(tǒng),支持 S3、HDFS 和 POSIX 等訪問(wèn)協(xié)議,支持多副本與糾刪碼兩種存儲(chǔ)引擎,為用戶提供多租戶、 多 AZ 部署以及跨區(qū)域復(fù)制等多種特性。
CubeFS 作為一個(gè)云原生的分布式存儲(chǔ)平臺(tái),提供了多種訪問(wèn)協(xié)議,因此其應(yīng)用場(chǎng)景也非常廣泛,下面簡(jiǎn)單介紹幾種比較典型的應(yīng)用場(chǎng)景
- 大數(shù)據(jù)分析:兼容 HDFS 協(xié)議,為 Hadoop 生態(tài)(如 Spark、Hive)提供統(tǒng)一存儲(chǔ)底座,為計(jì)算引擎提供無(wú)限的存儲(chǔ)空間以及大帶寬的數(shù)據(jù)存儲(chǔ)能力。
- 深度訓(xùn)練/機(jī)器學(xué)習(xí):作為分布式并行文件系統(tǒng),支撐 AI 訓(xùn)練、模型存儲(chǔ)及分發(fā)、IO 加速等需求。
- 容器共享存儲(chǔ):容器集群可以將容器鏡像的配置文件或初始化加載數(shù)據(jù)存儲(chǔ)在 CubeFS 上,在容器批量加載時(shí)實(shí)時(shí)讀取。多 Pod 間通過(guò) CubeFS 共享持久化數(shù)據(jù),在 Pod 故障時(shí)可以進(jìn)行快速故障切換。
- 數(shù)據(jù)庫(kù)&中間件:為數(shù)據(jù)庫(kù)應(yīng)用如 MySQL、ElasticSearch、ClickHouse 提供高并發(fā)、低時(shí)延云盤服務(wù),實(shí)現(xiàn)徹底的存算分離。
- 在線服務(wù):為在線業(yè)務(wù)(如廣告、點(diǎn)擊流、搜索)或終端用戶的圖、文、音視頻等內(nèi)容提供高可靠、低成本的對(duì)象存儲(chǔ)服務(wù)。
- 傳統(tǒng) NAS 上云:替換線下傳統(tǒng)本地存儲(chǔ)及 NAS,助力 IT 業(yè)務(wù)上云。
特性
CubeFS 具有眾多特性,包括:
多協(xié)議
兼容 S3、POSIX、HDFS 等多種訪問(wèn)協(xié)議,協(xié)議間訪問(wèn)可互通
- POSIX 兼容:兼容 POSIX 接口,讓上層應(yīng)用的開(kāi)發(fā)變得極其簡(jiǎn)單,就跟使用本地文件系統(tǒng)一樣便捷。此外,CubeFS 在實(shí)現(xiàn)時(shí)放松了對(duì) POSIX 語(yǔ)義的一致性要求來(lái)兼顧文件和元文件操作的性能。
- 對(duì)象存儲(chǔ)兼容:兼容 AWS 的 S3 對(duì)象存儲(chǔ)協(xié)議,用戶可以使用原生的 Amazon S3 SDK 管理 CubeFS 中的資源。
- Hadoop 協(xié)議兼容:兼容 Hadoop FileSystem 接口協(xié)議,用戶可以使用 CubeFS 來(lái)替換 HDFS,做到上層業(yè)務(wù)無(wú)感。
雙引擎
支持多副本及糾刪碼兩種引擎,用戶可以根據(jù)業(yè)務(wù)場(chǎng)景靈活選擇
- 多副本存儲(chǔ)引擎:副本之間的數(shù)據(jù)為鏡像關(guān)系,通過(guò)強(qiáng)一致的復(fù)制協(xié)議來(lái)保證副本之間的數(shù)據(jù)一致性,用戶可以根據(jù)應(yīng)用場(chǎng)景靈活的配置不同副本數(shù)。
- 糾刪碼存儲(chǔ)引擎:糾刪碼引擎具備高可靠、高可用、低成本、支持超大規(guī)模(EB)的特性,根據(jù)不同 AZ 模型可以靈活選擇糾刪碼模式。
多租戶
支持多租戶管理,提供細(xì)粒度的租戶隔離策略
可擴(kuò)展
可以輕松構(gòu)建 PB 或者 EB 級(jí)規(guī)模的分布式存儲(chǔ)服務(wù),各模塊可水平擴(kuò)展
高性能
支持多級(jí)緩存,針對(duì)小文件特定優(yōu)化,支持多種高性能的復(fù)制協(xié)議
- 元數(shù)據(jù)管理:元數(shù)據(jù)集群為內(nèi)存元數(shù)據(jù)存儲(chǔ),在設(shè)計(jì)上使用兩個(gè) B-Tree(inodeBTree 與 dentryBTree)來(lái)管理索引,進(jìn)而提升元數(shù)據(jù)訪問(wèn)性能;
- 強(qiáng)一致副本協(xié)議 :CubeFS 根據(jù)文件寫入方式的不同采用不同的復(fù)制協(xié)議來(lái)保證副本間的數(shù)據(jù)一致性。(如果文件按照順序?qū)懭?,則會(huì)使用主備復(fù)制協(xié)議來(lái)優(yōu)化 IO 吞吐量;如果是隨機(jī)寫入覆蓋現(xiàn)有文件內(nèi)容時(shí),則是采用一種基于 Multi-Raft 的復(fù)制協(xié)議,來(lái)確保數(shù)據(jù)的強(qiáng)一致性);
- 多級(jí)緩存:糾刪碼卷支持多級(jí)緩存加速能力,針對(duì)熱點(diǎn)數(shù)據(jù),提供更高數(shù)據(jù)訪問(wèn)性能:
- 本地緩存:可以在 Client 機(jī)器上同機(jī)部署 BlockCache 組件,將本地磁盤作為本地緩存. 可以不經(jīng)過(guò)網(wǎng)絡(luò)直接讀取本地 Cache, 但容量受本地磁盤限制;
- 全局緩存:使用副本組件 DataNode 搭建的分布式全局 Cache, 比如可以通過(guò)部署客戶端同機(jī)房的 SSD 磁盤的 DataNode 作為全局 cache, 相對(duì)于本地 cache, 需要經(jīng)過(guò)網(wǎng)絡(luò), 但是容量更大, 可動(dòng)態(tài)擴(kuò)縮容,副本數(shù)可調(diào)。
云原生
基于 CSI 插件可以快速地在 Kubernetes 上使用 CubeFS。
整體架構(gòu)
整體上 CubeFS 由元數(shù)據(jù)子系統(tǒng)(Metadata Subsystem)、數(shù)據(jù)子系統(tǒng)(Data Subsystem)和資源管理節(jié)點(diǎn)(Master)以及對(duì)象網(wǎng)關(guān)(Object Subsystem)組成,可以通過(guò) POSIX/HDFS/S3 接口訪問(wèn)存儲(chǔ)數(shù)據(jù)。
資源管理節(jié)點(diǎn)
由多個(gè) Master 節(jié)點(diǎn)組成,負(fù)責(zé)異步處理不同類型的任務(wù),如管理數(shù)據(jù)分片與元數(shù)據(jù)分片(包括創(chuàng)建、刪除、更新以及一致性檢查等),檢查數(shù)據(jù)節(jié)點(diǎn)或者元數(shù)據(jù)節(jié)點(diǎn)的健康狀態(tài),維護(hù)管理卷信息等
Master 節(jié)點(diǎn)可以有多個(gè),節(jié)點(diǎn)之間通過(guò) Raft 算法保證元數(shù)據(jù)的一致性,并且持久化到
RocksDB
中。
元數(shù)據(jù)子系統(tǒng)
由多個(gè) Meta Node 節(jié)點(diǎn)組成,多個(gè)元數(shù)據(jù)分片(Meta Partition)和 Raft 實(shí)例(基于 Multi-Raft 復(fù)制協(xié)議)組成,每個(gè)元數(shù)據(jù)分片表示一個(gè) Inode 范圍元數(shù)據(jù),其中包含兩棵內(nèi)存 B-Tree 樹(shù):inode BTree 與 dentry BTree。
元數(shù)據(jù)實(shí)例最少需要 3 個(gè),支持水平擴(kuò)容。
數(shù)據(jù)子系統(tǒng)
分為副本子系統(tǒng)和糾刪碼子系統(tǒng),兩種子系統(tǒng)可同時(shí)存在,也都可單獨(dú)存在:
- 副本子系統(tǒng)由 DataNode 組成,每個(gè)節(jié)點(diǎn)管理一組數(shù)據(jù)分片,多個(gè)節(jié)點(diǎn)的數(shù)據(jù)分片構(gòu)成一個(gè)副本組;
- 糾刪碼子系統(tǒng)(Blobstore)主要由 BlobNode 模塊組成,每個(gè)節(jié)點(diǎn)管理一組數(shù)據(jù)塊,多個(gè)節(jié)點(diǎn)的數(shù)據(jù)塊構(gòu)成一個(gè)糾刪碼條帶。
數(shù)據(jù)節(jié)點(diǎn)支持水平擴(kuò)容。
對(duì)象子系統(tǒng)
由對(duì)象節(jié)點(diǎn)(ObjectNode)組成,提供了兼容標(biāo)準(zhǔn) S3 語(yǔ)義的訪問(wèn)協(xié)議,可以通過(guò) Amazon S3 SDK 或者是 s3cmd 等工具訪問(wèn)存儲(chǔ)資源。
卷
邏輯上的概念,由多個(gè)元數(shù)據(jù)和數(shù)據(jù)分片組成,從客戶端的角度看,卷可以被看作是可被容器訪問(wèn)的文件系統(tǒng)實(shí)例。從對(duì)象存儲(chǔ)的角度來(lái)看,一個(gè)卷對(duì)應(yīng)著一個(gè) bucket。一個(gè)卷可以在多個(gè)容器中掛載,使得文件可以被不同客戶端同時(shí)訪問(wèn)。
安裝
CubeFS 的安裝方式有很多,包括 Docker、YUM 等等,由于我們這里直接直接在 Kubernetes 上使用,因此我們可以通過(guò) Helm 來(lái)安裝 CubeFS,各組件會(huì)直接使用宿主機(jī)網(wǎng)絡(luò),使用 hostPath 將磁盤映射到容器中。
在 Kubernetes 集群中部署 CubeFS 可以按照下圖所示的架構(gòu)進(jìn)行部署:
CubeFS 目前由這四部分組成:
- Master:資源管理節(jié)點(diǎn),負(fù)責(zé)維護(hù)整個(gè)集群的元信息,部署為 StatefulSet 資源。
- DataNode:數(shù)據(jù)存儲(chǔ)節(jié)點(diǎn),需要掛載大量磁盤負(fù)責(zé)文件數(shù)據(jù)的實(shí)際存儲(chǔ),部署為 DaemonSet 資源。
- MetaNode:元數(shù)據(jù)節(jié)點(diǎn),負(fù)責(zé)存儲(chǔ)所有的文件元信息,部署為 DaemonSet 資源。
- ObjectNode:負(fù)責(zé)提供轉(zhuǎn)換 S3 協(xié)議提供對(duì)象存儲(chǔ)的能力,無(wú)狀態(tài)服務(wù),部署為 Deployment 資源。
在部署之前,我們需要擁有一個(gè)至少有 3 個(gè)節(jié)點(diǎn)(最好 4 個(gè)以上,可以容災(zāi))的 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é)點(diǎn)打上各自的標(biāo)簽,標(biāo)明這臺(tái)機(jī)器要在 CubeFS 集群中承擔(dān)的角色:
由于我們這里只有 3 個(gè)節(jié)點(diǎn),所以需要這些節(jié)點(diǎn)承擔(dān)一些共同的角色。
- Master 節(jié)點(diǎn),至少三個(gè),建議為奇數(shù)個(gè):
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é)點(diǎn),至少 3 個(gè),奇偶無(wú)所謂:
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é)點(diǎn),至少 3 個(gè),奇偶無(wú)所謂:
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 對(duì)象存儲(chǔ)節(jié)點(diǎn),可以按需進(jìn)行標(biāo)記,不需要對(duì)象存儲(chǔ)功能的話也可以不部署這個(gè)組件:
kubectl label node node1 component.cubefs.io/objectnode=enabled
kubectl label node node2 component.cubefs.io/objectnode=enabled
- CSI 組件,用于在 Kubernetes 中使用 CubeFS,需要在所有節(jié)點(diǎn)上部署:
kubectl label node node1 component.cubefs.io/csi=enabled
kubectl label node node2 component.cubefs.io/csi=enabled
CubeFS 安裝時(shí)會(huì)根據(jù)這些標(biāo)簽通過(guò) nodeSelector 進(jìn)行匹配,然后在機(jī)器創(chuàng)建起對(duì)應(yīng)的 Pod。
接下來(lái)我們就可以通過(guò) Helm 來(lái)安裝 CubeFS 了,首先我們需要將 CubeFS 的 Helm Chart 下載到本地:
git clone https://github.com/cubefs/cubefs-helm
cd cubefs-helm
然后根據(jù)自身環(huán)境定制 values 文件,比如下面是一個(gè)簡(jiǎn)單的 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 版本之前會(huì)出現(xiàn) /lib64/libstdc++.so.6: version `GLIBCXX_3.4.21' not found 錯(cuò)誤
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 要使用的磁盤,可以掛載多塊
# 格式: 掛載點(diǎn):保留的空間
# 保留的空間: 單位字節(jié),當(dāng)磁盤剩余空間小于該值時(shí)將不會(huì)再在該磁盤上寫入數(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
然后使用如下命令進(jìn)行 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
各個(gè)組件的關(guān)鍵日志會(huì)在容器標(biāo)準(zhǔn)輸出中輸出。
此外還會(huì)自動(dòng)創(chuàng)建一個(gè) StorageClass 對(duì)象,可以通過(guò) kubectl get sc 查看:
$ kubectl get sc
NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE
cfs-sc csi.cubefs.com Delete Immediate true 29m
測(cè)試
現(xiàn)在我們有了一個(gè)可用的 StorageClass 對(duì)象了,接下來(lái)可以創(chuàng)建一個(gè) PVC 對(duì)象來(lái)測(cè)試 CubeFS 的存儲(chǔ)功能。如下所示:
# cubefs-pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: cubefs-pvc
namespace: default
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 5Gi
storageClassName: cfs-sc
上面的 PVC 對(duì)象中我們通過(guò) storageClassName 指定了使用的 StorageClass 名稱,這里是 cfs-sc,這個(gè)名稱需要和我們之前創(chuàng)建的 StorageClass 名稱一致,這樣就會(huì)根據(jù) cubefs-sc 中定義的參數(shù)來(lái)創(chuàng)建存儲(chǔ)卷。當(dāng)我們?cè)诰帉?pvc yaml 主要注意一下參數(shù):
- metadata.name:pvc 的名稱,可以按需修改,同一個(gè) namespace 下 pvc 名稱是唯一的,不允許有兩個(gè)相同的名稱。
- metadata.namespace:pvc 所在的命名空間,按需修改
- spec.resources.request.storage:pvc 容量大小。
- storageClassName:這是 storage class 的名稱。如果想知道當(dāng)前集群有哪些 storageclass,可以通過(guò)命令 kubectl get sc 來(lái)查看。
這里直接應(yīng)用這個(gè) yaml 文件即可:
kubectl apply -f cubefs-pvc.yaml
執(zhí)行命令完成后,可以通過(guò)命令 kubectl get pvc -n 命名空間 來(lái)查看對(duì)應(yīng) 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,可以通過(guò)命令查看原因:
kubectl describe pvc -n 命名空間 PVC 名稱
如果報(bào)錯(cuò)消息不明顯或者看不出錯(cuò)誤,則可以使用 kubectl logs 相關(guān)命令先查看 csi controller pod 里面的 csi-provisioner 容器的報(bào)錯(cuò)信息,csi-provisioner 是 k8s 與 csi driver 的中間橋梁,很多信息都可以在這里的日志查看。
如果 csi-provisioner 的日志還看不出具體問(wèn)題,則使用 kubectl exec 相關(guān)命令查看 csi controller pod 里面的 cfs-driver 容器的日志,它的日志放在容器里面的 /cfs/logs 下。
這里不能使用 Kubectl logs 相關(guān)命令是因?yàn)?cfs-driver 的日志并不是打印到標(biāo)準(zhǔn)輸出,而其它幾個(gè)類似 csi-provisioner 的 sidecar 容器的日志是打印到標(biāo)準(zhǔn)輸出的,所以可以使用 kubectl logs 相關(guān)命令查看。
有了 PVC 則接下來(lái)就可以在應(yīng)用中掛載到指定目錄了,比如我們這里有一個(gè)如下所示的示例:
# 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
上面的資源清單中我們將一個(gè)名稱為 cubefs-pvc 的 PVC 掛載到 cfs-csi-demo 容器里面的 /usr/share/nginx/html 下。
同樣直接創(chuàng)建這個(gè)資源清單即可:
kubectl apply -f cfs-csi-demo.yaml
創(chuàng)建完成后可以通過(guò) 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>
我們可以直接通過(guò)往 /usr/share/nginx/html 目錄寫入文件來(lái)測(cè)試 CubeFS 的存儲(chǔ)功能:
$ 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:/#
然后我們可以將這個(gè) Pod 刪除重建,然后查看是否還有這個(gè)文件:
$ 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 則說(shuō)明 CubeFS 的存儲(chǔ)功能正常。