1 Kubernetes存儲介紹
1.1 為何引入PV装蓬、PVC以及StorageClass灿渴?
熟悉Kubernetes的都對PV下梢、PVC以及StorageClass不陌生全肮,我們經(jīng)常用到悬钳,因此這里不再詳細(xì)介紹PV盐捷、PVC以及StorageClass的用法,僅簡單聊聊為什么需要引入這三個概念他去。
我們看下最早期Pod使用Volume的寫法:
apiVersion: v1
kind: Pod
metadata:
name: test-pod
spec:
containers:
- image: ...
name: test-pod
volumeMounts:
- mountPath: /data
name: data
volumes:
- name: data
capacity:
storage: 10Gi
cephfs:
monitors:
- 172.16.0.1:6789
- 172.16.0.2:6789
- 172.16.0.3:6789
path: /opt/eshop_dir/eshop
user: admin
secretRef:
name: ceph-secret
這種方式至少存在兩個問題:
- Pod聲明與底層存儲耦合在一起毙驯,每次聲明volume都需要配置存儲類型以及該存儲插件的一堆配置,如果是第三方存儲灾测,配置會非常復(fù)雜爆价。
- 開發(fā)人員的需求可能只是需要一個20GB的卷,這種方式卻不得不強(qiáng)制要求開發(fā)人員了解底層存儲類型和配置媳搪。
比如前面的例子中每次聲明Pod都需要配置Ceph集群的mon地址以及secret铭段,特別麻煩。
于是引入了PV(Persistent Volume)秦爆,PV其實(shí)就是把Volume的配置聲明部分從Pod中分離出來:
apiVersion: v1
kind: PersistentVolume
metadata:
name: cephfs
spec:
capacity:
storage: 10Gi
accessModes:
- ReadWriteMany
cephfs:
monitors:
- 172.16.0.1:6789
- 172.16.0.2:6789
- 172.16.0.3:6789
path: /opt/eshop_dir/eshop
user: admin
secretRef:
name: ceph-secret
我們發(fā)現(xiàn)PV的spec部分幾乎和前面Pod的volume定義部分是一樣的序愚。
有了PV,在Pod中就可以不用再定義volume的配置了等限,直接引用即可爸吮,volume定義和Pod松耦合了。
但是這沒有解決volume定義的第二個問題望门,存儲系統(tǒng)通常由運(yùn)維人員管理形娇,開發(fā)人員并不知道底層存儲配置,也就很難去定義好PV筹误。
為了解決這個問題桐早,引入了PVC(Persistent Volume Claim),聲明與消費(fèi)分離,開發(fā)與運(yùn)維責(zé)任分離哄酝。
運(yùn)維人員負(fù)責(zé)存儲管理友存,可以事先根據(jù)存儲配置定義好PV,而開發(fā)人員無需了解底層存儲配置陶衅,只需要通過PVC聲明需要的存儲類型屡立、大小、訪問模式等需求即可搀军,然后就可以在Pod中引用PVC侠驯,完全不用關(guān)心底層存儲細(xì)節(jié)。
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: cephfs
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 8Gi
PVC會根據(jù)聲明的大小奕巍、存儲類型(如storageClassName)吟策、accessModes等關(guān)鍵字查找PV,如果找到了匹配的PV的止,則會與之關(guān)聯(lián)檩坚。
通過PV以及PVC,開發(fā)人員的問題是解決了诅福,但沒有解決運(yùn)維人員的問題匾委。運(yùn)維人員需要維護(hù)一堆PV列表和配置,如果PV不夠用需要手動創(chuàng)建新的PV氓润,PV空閑了還需要手動去回收赂乐,管理效率太低了。
于是又引入了StorageClass咖气,StorageClass類似聲明了一個非常大的存儲池挨措,其中一個最重要的參數(shù)是provisioner,這個provisioner聲明了誰來提供存儲源崩溪,我們熟悉的OpenStack Cinder浅役、Ceph、AWS EBS等都是provisioner伶唯。
kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
name: aws-gp2
provisioner: kubernetes.io/aws-ebs
parameters:
type: gp2
fsType: ext4
有了StorageClass后觉既,Kubernetes會根據(jù)開發(fā)人員定義的PVC中聲明的StorageClassName以及大小等需求自動創(chuàng)建PV,即Dynamic Provisioning乳幸。
而運(yùn)維人員只需要聲明好StorageClass以及Quota配額瞪讼,無需維護(hù)PV。
通過PV粹断、PVC以及StorageClass符欠,開發(fā)和運(yùn)維的工作徹底解放了。
1.2 Kubernetes存儲方案發(fā)展過程概述
我們知道Kubernetes存儲最開始是通過Volume Plugin實(shí)現(xiàn)集成外部存儲系統(tǒng)姿染,即不同的存儲系統(tǒng)對應(yīng)不同的volume plugin背亥。
Volume Plugin實(shí)現(xiàn)代碼全都放在了Kubernetes主干代碼中(in-tree),也就是說這些插件與核心Kubernetes二進(jìn)制文件一起鏈接悬赏、編譯狡汉、構(gòu)建和發(fā)布。
這種方案至少存在如下幾個問題:
- 在Kubernetes中添加新存儲系統(tǒng)支持需要在核心Kubernetes增加插件代碼闽颇,隨著存儲插件越來越多盾戴,Kubernetes代碼也會變得越來越龐大。
- Kubernetes與具體的存儲plugin耦合在一起兵多,一旦存儲接口發(fā)生任何變化都需要重新修改plugin代碼尖啡,也就是說不得不修改Kubernetes代碼,這會導(dǎo)致Kubernetes代碼維護(hù)越來越困難剩膘。
- 如果plugin有bug或者存儲系統(tǒng)故障導(dǎo)致crash衅斩,可能導(dǎo)致整個Kubernetes集群整體crash。
- 這些插件運(yùn)行時無法做權(quán)限管控怠褐,具有Kubernetes所有組件的所有權(quán)限畏梆,存在一定的安全風(fēng)險。
- 插件的實(shí)現(xiàn)必須通過Golang語言編寫并與Kubernetes一起開源奈懒,可能對一些廠商不利奠涌。
因此從1.8開始,Kubernetes停止往Kubernetes代碼中增加新的存儲支持磷杏, 并推出了一種新的插件形式支持外部存儲系統(tǒng)溜畅,即FlexVolume,不過FlexVolume其實(shí)在1.2就提出了极祸。
FlexVolume類似于CNI插件慈格,通過外部腳本集成外部存儲接口,這些腳本默認(rèn)放在/usr/libexec/kubernetes/kubelet-plugins/volume/exec/
遥金,需要安裝到所有Node節(jié)點(diǎn)上峦椰。
這樣每個存儲插件只需要通過外部腳本(out-of-tree)實(shí)現(xiàn)attach
、detach
汰规、mount
汤功、umount
等接口即可集成第三方存儲,不需要動Kubernetes源碼溜哮,可以參考官方的一個LVM FlexVolume Demo[1]滔金。
但是這種方法也有問題:
- 腳本文件放在host主機(jī)上,因此驅(qū)動不得不通過訪問宿主機(jī)的根文件系統(tǒng)去運(yùn)行腳本茂嗓。
- 這些插件如果還有第三方程序依賴或者OS兼容性要求餐茵,還需要在所有的Node節(jié)點(diǎn)安裝這些依賴并解決兼容問題。
因此這種方式雖然解決了in-tree的問題述吸,但顯然這種方式用起來不太優(yōu)雅忿族,不太原生锣笨。
因此Kubernetes從1.9開始又引入了Container Storage Interface (CSI)容器存儲接口,并于1.13版本正式GA道批。
CSI的實(shí)現(xiàn)方案和CRI類似通過gRPC與volume driver進(jìn)行通信错英,存儲廠商需要實(shí)現(xiàn)三個服務(wù)接口Identity Service、Controller Service隆豹、Node Service椭岩,
- Identity Service用于返回一些插件信息;
- Controller Service實(shí)現(xiàn)Volume的CURD操作璃赡,
- Node Service運(yùn)行在所有的Node節(jié)點(diǎn)判哥,用于實(shí)現(xiàn)把volume掛載在當(dāng)前Node節(jié)點(diǎn)的指定目錄,該服務(wù)會監(jiān)聽一個Socket碉考,controller通過這個Socket進(jìn)行通信塌计,可以參考官方提供的樣例CSI Hostpath driver Sample[2]。
更多有關(guān)CSI介紹可以參考官方的設(shè)計(jì)文檔CSI Volume Plugins in Kubernetes Design Doc[3]侯谁。
通過CSI基本解決了如上in-tree以及FlexVolume的大多數(shù)問題夺荒,未來Kubernetes會把in-tree的存儲插件都遷移到CSI。
當(dāng)然Flex Volume Plugin也會與新的CSI Volume Plugin并存以便兼容現(xiàn)有的第三方FlexVolume存儲插件良蒸。
1.3 為什么需要云原生分布式存儲
通過CSI接口或者Flex Volume Plugin解決了Kubernetes集成外部存儲的問題技扼,目前Kubernetes已經(jīng)能夠支持非常多的外部存儲系統(tǒng)了,如NFS嫩痰、GlusterFS剿吻、Ceph、OpenStack Cinder等串纺,這些存儲系統(tǒng)目前主流的部署方式還是運(yùn)行在Kubernetes集群之外單獨(dú)部署和維護(hù)丽旅,這不符合All In Kubernetes的原則。
如果已經(jīng)有分布式存儲系統(tǒng)還好纺棺,可以直接對接榄笙。但如果沒有現(xiàn)成分布式存儲,則不得不單獨(dú)部署一套分布式存儲祷蝌。
很多分布式存儲部署相對還是比較復(fù)雜的茅撞,比如Ceph。而Kubernetes天生就具有快速部署和編排應(yīng)用的能力巨朦,如果能把分布式存儲的部署也通過Kubernetes編排管理起來米丘,則顯然能夠大大降低分布式存儲的部署和維護(hù)成本,甚至可以使用一條apply命令就可以輕松部署一個Ceph集群糊啡。
這主要有兩種實(shí)現(xiàn)思路:
- 第一種思路就是重新針對云原生平臺設(shè)計(jì)一個分布式存儲拄查,這個分布式存儲系統(tǒng)組件是微服務(wù)化的,能夠復(fù)用Kubernetes的調(diào)度棚蓄、故障恢復(fù)和編排等能力堕扶,如后面要介紹的Longhorn碍脏、OpenEBS。
- 另一種思路就是設(shè)計(jì)微服務(wù)組件把已有的分布式存儲系統(tǒng)包裝管理起來稍算,使原來的分布式存儲可以適配運(yùn)行在Kubernetes平臺上典尾,實(shí)現(xiàn)通過Kubernetes管理原有的分布式存儲系統(tǒng),如后面要介紹的Rook邪蛔。
1.4 Container Attached Storage,容器存儲的未來扎狱?
我們知道組成云計(jì)算的三大基石為計(jì)算侧到、存儲和網(wǎng)絡(luò),Kubernetes計(jì)算(Runtime)淤击、存儲(PV/PVC)和網(wǎng)絡(luò)(Subnet/DNS/Service/Ingress)的設(shè)計(jì)都是開放的匠抗,可以集成不同的方案,比如網(wǎng)絡(luò)通過CNI接口支持集成Flannel污抬、Calico等網(wǎng)絡(luò)方案汞贸,運(yùn)行時(Runtime)通過CRI支持Docker、Rkt印机、Kata等運(yùn)行時方案矢腻,存儲通過volume plugin支持集成如AWS EBS、Ceph射赛、OpenStack Cinder等存儲系統(tǒng)多柑。
但是我們發(fā)現(xiàn)目前主流的方案中存儲與計(jì)算、網(wǎng)絡(luò)稍有不同楣责,計(jì)算和網(wǎng)絡(luò)都是以微服務(wù)的形式通過Kubernetes統(tǒng)一編排管理的竣灌,即Kubernetes既是計(jì)算和網(wǎng)絡(luò)的消費(fèi)者,同時也是計(jì)算和網(wǎng)絡(luò)的編排者和管理者秆麸。
而存儲則不一樣初嘹,雖然Kubernetes已經(jīng)設(shè)計(jì)了PV/PVC機(jī)制來管理外部存儲,但只是弄了一個標(biāo)準(zhǔn)接口集成沮趣,存儲本身還是通過獨(dú)立的存儲系統(tǒng)來管理屯烦,Kubernetes根本不知道底層存儲是如何編排和調(diào)度的。
社區(qū)認(rèn)為既然計(jì)算和網(wǎng)絡(luò)都由我Kubernetes統(tǒng)一編排了房铭,是不是存儲也考慮下漫贞?
于是社區(qū)提出了Container Attached Storage(CAS)理念,這個理念的目標(biāo)就是利用Kubernetes來編排存儲育叁,從而實(shí)現(xiàn)我Kubernetes編排一切迅脐,這里的一切包括計(jì)算、存儲豪嗽、網(wǎng)絡(luò)谴蔑,當(dāng)然更高一層的還包括應(yīng)用豌骏、服務(wù)、軟件等隐锭。
這個方案如何實(shí)現(xiàn)呢窃躲?CAS提出如下方案:
- 每個volume都由一個輕量級的Controller來管理,這個Controller可以是一個單獨(dú)的Pod钦睡。
- 這個Controller與使用該volume的應(yīng)用Pod在同一個Node(sidecar模式)蒂窒。
- 不同的Volume的數(shù)據(jù)使用多個獨(dú)立的Controller Pod進(jìn)行管理。
由于Pod是通過Kubernetes編排與調(diào)度的荞怒,因此毫無疑問通過這種形式其實(shí)就實(shí)現(xiàn)了Kubernetes編排和調(diào)度存儲: )
Kubernetes畢竟是目前主流趨勢洒琢,通過Kubernetes編排和管理存儲也必然是一種發(fā)展趨勢,目前OpenEBS就是CAS的一種開源實(shí)現(xiàn)褐桌,商業(yè)存儲如PortWorx衰抑、StorageOS也是基于CAS模式的。
更多關(guān)于CAS的可以參考CNCF官宣文章Container Attached Storage: A Primer[4]荧嵌。
2 簡單好用的Longhorn
2.1 Longhorn簡介
Longhorn[5]在我之前的文章輕量級Kubernetes k3s初探已經(jīng)簡單介紹過呛踊,最初由Rancher公司開發(fā)并貢獻(xiàn)給社區(qū),專門針對Kubernetes設(shè)計(jì)開發(fā)的云原生分布式塊存儲系統(tǒng)啦撮,因此和Kubernetes契合度很高谭网,主要體現(xiàn)在兩個方面,一是它本身就直接運(yùn)行在Kubernetes平臺上赃春,通過容器和微服務(wù)的形式運(yùn)行蜻底;其二是能很好的與PV/PVC結(jié)合。
與其他分布式存儲系統(tǒng)最大的不同點(diǎn)是聘鳞,Longhorn并沒有設(shè)計(jì)一個非常復(fù)雜的控制器來管理海量的volume數(shù)據(jù)卷薄辅,而是將控制器拆分成一個個非常輕量級的微控制器,這些微控制器能夠通過Kubernetes抠璃、Mesos等平臺進(jìn)行編排與調(diào)度站楚。
每個微控制器只管理一個volume,換句話說搏嗡,一個volume一個控制器窿春,每個volume都有自己的控制器,這種基于微服務(wù)的設(shè)計(jì)使每個volume相對獨(dú)立采盒,控制器升級時可以先選擇一部分卷進(jìn)行操作旧乞,如果升級出現(xiàn)問題,可以快速選擇回滾到舊版本磅氨,升級過程中只可能會影響正在升級的volume尺栖,而不會導(dǎo)致其他volume IO中斷。
Longhorn的實(shí)現(xiàn)和CAS的設(shè)計(jì)理念基本是一致的烦租,相比Ceph來說會簡單很多延赌,而又具備分布式塊存儲系統(tǒng)的一些基本功能:
- 支持多副本除盏,不存在單點(diǎn)故障;
- 支持增量快照挫以;
- 支持備份到其他外部存儲系統(tǒng)中者蠕,比如S3;
- 精簡配置(thin provisioning);
- ...
我覺得Longhorn還有一個特別好的功能是內(nèi)置了一個Web UI掐松,通過UI能夠很方便的管理Node踱侣、Volume以及Backup,不得不說Longhorn真是麻雀雖小五臟俱全大磺。
根據(jù)官方的說法抡句,Longhorn并不是為了取代其他分布式塊存儲系統(tǒng),而是為了設(shè)計(jì)一個更簡單的適合容器環(huán)境的塊存儲系統(tǒng)量没,其他分布式存儲有的一些高級功能Longhorn并沒有實(shí)現(xiàn)玉转,比如去重(deduplication)突想、壓縮殴蹄、分塊、多路徑等猾担。
Longhorn存儲管理機(jī)制比較簡單袭灯,當(dāng)在Longhorn中Node節(jié)點(diǎn)增加物理存儲時,其本質(zhì)就是把Node對應(yīng)的路徑通過HostPath掛載到Pod中绑嘹,我們可以查看該路徑的目錄結(jié)構(gòu)稽荧,在replicas
目錄中一個volume一個子目錄,文件內(nèi)容如下:
# find replicas/int32bit-volume-3-ab6717d6/
replicas/int32bit-volume-3-ab6717d6/
replicas/int32bit-volume-3-ab6717d6/volume.meta
replicas/int32bit-volume-3-ab6717d6/volume-head-000.img
replicas/int32bit-volume-3-ab6717d6/revision.counter
replicas/int32bit-volume-3-ab6717d6/volume-head-000.img.meta
其中int32bibt-volume-3
是volume名稱工腋,ab6717d6
對應(yīng)副本名稱姨丈,子目錄中包含一些volume的metadata以及img文件,而img文件其實(shí)就是一個raw格式文件:
# qemu-img info volume-head-000.img
image: volume-head-000.img
file format: raw
virtual size: 20G (21474836480 bytes)
disk size: 383M
raw格式其實(shí)就是Linux Sparse稀疏文件擅腰,由于單個文件大小受文件系統(tǒng)和分區(qū)限制蟋恬,因此Longhorn volume會受單個磁盤的大小和性能的限制,不過我覺得Kubernetes Pod其實(shí)也很少需要用到特別大的volume趁冈。
更多關(guān)于Longhorn的技術(shù)實(shí)現(xiàn)原理可以參考官宣文章Announcing Longhorn: an open source project for microservices-based distributed block storage[6]歼争。
2.2 Longhorn部署
Longhorn部署也非常簡單,只需要一個kubectl apply
命令:
kubectl apply -f https://raw.githubusercontent.com/longhorn/longhorn/master/deploy/longhorn.yaml
創(chuàng)建完后就可以通過Service或者Ingress訪問它的UI了:
在Node頁面可以管理節(jié)點(diǎn)以及物理存儲渗勘,Volume頁面可以管理所有的volumes沐绒,Backup可以查看備份等。
volume詳情頁面可以查看volume的掛載情況旺坠、副本位置乔遮、對應(yīng)的PV/PVC以及快照鏈等:
除此之外,Longhorn還支持創(chuàng)建備份計(jì)劃取刃,可以通過cron指定時間點(diǎn)或者定時對volume進(jìn)行快照或者備份到S3中申眼。
2.3 Kubernetes集成Longhorn存儲
Longhorn既支持FlexVolume也支持CSI接口瞒津,安裝時會自動根據(jù)Kubernetes版本選擇FlexVolume或者CSI。
Kubernetes集成Longhorn括尸,根據(jù)前面對StorageClass的介紹巷蚪,我們需要先安裝Longhorn StorageClass:
kubectl create -f \
https://raw.githubusercontent.com/longhorn/longhorn/master/examples/storageclass.yaml
聲明一個20Gi的PVC:
# kubectl apply -f -
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: longhorn-volv-pvc
spec:
accessModes:
- ReadWriteOnce
storageClassName: longhorn
resources:
requests:
storage: 2Gi
創(chuàng)建Pod并使用新創(chuàng)建的PVC:
# kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
name: test-volume-longhorn
spec:
containers:
- name: test-volume-longhorn
image: jocatalin/kubernetes-bootcamp:v1
volumeMounts:
- name: volv
mountPath: /data
volumes:
- name: volv
persistentVolumeClaim:
claimName: longhorn-volv-pvc
通過Longhorn Dashboard查看volume狀態(tài)已經(jīng)掛載到Pod中:
Longhorn volume不僅可以通過PV形式掛載到Kubernetes容器,還可以直接通過ISCSI接口掛載到Node節(jié)點(diǎn)上:
此時可以通過lsblk
在OS查看到塊存儲設(shè)備濒翻,通過iscsiadm
命令可以查看Node節(jié)點(diǎn)連接的設(shè)備會話:
# lsblk -S
NAME HCTL TYPE VENDOR MODEL REV TRAN
sda 0:0:0:1 disk IET VIRTUAL-DISK 0001 iscsi
# iscsiadm -m session
tcp: [3] 10.244.0.50:3260,1 iqn.2019-10.io.longhorn:int32bit-volume-1 (non-flash)
3 CAS開源實(shí)現(xiàn)OpenEBS
3.1 OpenEBS簡介
OpenEBS[7]是MayaData[8](之前叫CloudByte)公司開源的云原生容器存儲項(xiàng)目屁柏,命名上可能參考了AWS EBS(Elastic Block Storage)。
OpenEBS也是目前Container Attached Storage的一種開源實(shí)現(xiàn)方案有送,因此它直接運(yùn)行在Kubernetes平臺上淌喻,通過Kubernetes平臺進(jìn)行編排與調(diào)度。
OpenEBS支持如下三種存儲類型雀摘,分別為cStor裸删、Jiva以及LocalPV。
Jiva
后端實(shí)現(xiàn)其實(shí)就是前面介紹的Longhorn阵赠,也就是使用了raw格式sparse稀疏文件作為虛擬磁盤實(shí)現(xiàn)容器volume涯塔,這個和虛擬機(jī)的本地虛擬磁盤實(shí)現(xiàn)類似,可以通過qemu-img info
查看volume分配的虛擬大小以及實(shí)際使用的空間清蚀,稀疏文件默認(rèn)路徑為/var/openebs
生蚁,所以volume的容量總大小取決于這個路徑掛載的文件系統(tǒng)大小你弦。
實(shí)現(xiàn)上使用了Longhorn早期版本設(shè)計(jì),即一個3副本的volume會有4個Pod控制器管理,一個是主控制器狞洋,三個副本控制器沸柔,其中主控制器運(yùn)行了iSCSI Target服務(wù)眼俊,通過Service暴露ISCSI 3260端口慎颗,主控制器會把IO復(fù)制到所有副本的控制器。
[圖片上傳失敗...(image-41e5bc-1595940598655)]
cStor
這是OpenEBS最推薦的存儲類型嘶卧,測試最完備尔觉,經(jīng)過了生產(chǎn)部署考驗(yàn),支持多副本脸候、快照穷娱、克隆、精簡配置(thin provisioning)运沦、數(shù)據(jù)強(qiáng)一致性等高級特性泵额。
和Jiva不一樣的是,cStor使用了類似ZFS或者LVM的Pool的概念携添,blockdevices就相當(dāng)于LVM的PV嫁盲,而Pool則類似LVM的VG概念,volume類似LVM的LV,其中blockdevice對應(yīng)物理上的一塊磁盤或者一個分區(qū)羞秤,多個blockdevices組成Pool缸托,這些blockdevices如何存儲落盤取決于Pool策略,cStor支持的Pool策略包括striped瘾蛋、mirrored俐镐、raidz、raidz2哺哼,這些概念都不陌生佩抹。
[圖片上傳失敗...(image-bdae90-1595940598655)]
Pool是一個單Node節(jié)點(diǎn)層面的概念而不是分布式的,創(chuàng)建一個cStor Pool實(shí)際上會在每個Node節(jié)點(diǎn)創(chuàng)建相同策略的Pool實(shí)例取董,因此即使使用striped策略棍苹,數(shù)據(jù)打散后也只是存儲在本地的多塊磁盤,不會跨節(jié)點(diǎn)存儲茵汰,當(dāng)然volume副本是跨節(jié)點(diǎn)的枢里。
OpenEBS通過ISCSI接口實(shí)現(xiàn)volume的掛載,每當(dāng)創(chuàng)建一個cStor Volume蹂午,OpenEBS就會創(chuàng)建一個新的cStor target Pod栏豺,cStor target會創(chuàng)建對應(yīng)的LUN設(shè)備。
cStor target除了負(fù)責(zé)LUN設(shè)備管理画侣,還負(fù)責(zé)副本之間的數(shù)據(jù)同步冰悠,每當(dāng)用戶有數(shù)據(jù)寫入時堡妒,cStor target會把數(shù)據(jù)拷貝到其他所有副本中去配乱。
比如假設(shè)創(chuàng)建了一個三副本的cStor PV,當(dāng)用戶寫入數(shù)據(jù)時皮迟,cStor target會同時往三個副本寫入數(shù)據(jù)搬泥,只有等三個副本都寫成功后,才會響應(yīng)用戶伏尼,因此顯然OpenEBS是一個強(qiáng)一致性分布式存儲系統(tǒng)忿檩。
不過這也是cStor性能比較差的原因之一,它不像Ceph一樣一個RBD image會分塊存儲在多個節(jié)點(diǎn)多個硬盤的多個OSD上爆阶,可以避免單節(jié)點(diǎn)的IO性能瓶頸問題燥透。
LocalPV
LocalPV就是直接把本地磁盤(local disk)掛載到容器,這個其實(shí)就是Kubernetes LocalPV的增強(qiáng)版辨图,因?yàn)橹苯幼x取本地磁盤班套,相對iSCSI需要走網(wǎng)絡(luò)IO來說性能肯定是最好的,不過缺點(diǎn)是沒有多副本故河、快照吱韭、克隆等高級特性。
3.2 OpenEBS部署
直接使用kubectl安裝:
kubectl apply -f \
https://openebs.github.io/charts/openebs-operator-1.8.0.yaml
如上會把所有的/dev下的塊設(shè)備都當(dāng)作OpenEBS的block devices鱼的,建議修改下openebs-ndm-config
Configmap理盆,通過path-filter
指定分給OpenEBS的物理設(shè)備痘煤。
正如LVM有了PV還需要創(chuàng)建VG一樣,cStor需要手動創(chuàng)建一個Pool:
apiVersion: openebs.io/v1alpha1
kind: StoragePoolClaim
metadata:
name: cstor-disk-pool
annotations:
cas.openebs.io/config: |
- name: PoolResourceRequests
value: |-
memory: 2Gi
- name: PoolResourceLimits
value: |-
memory: 4Gi
spec:
name: cstor-disk-pool
type: disk
poolSpec:
poolType: striped
blockDevices:
blockDeviceList:
- blockdevice-ad96d141bd7804554d431cb13e7e61bc
- blockdevice-b14cd44f3bfcbd94d3e0bda065f6e2bd
- blockdevice-e3a5cf960033d7a96fdee46a5baee9d2
其中poolType選擇pool策略猿规,如果使用mirror需要注意每個Node的磁盤數(shù)量必須是偶數(shù)衷快,這里我們選擇striped
,即數(shù)據(jù)會打散分布存儲在Node Pool的所有磁盤姨俩。
blockDevices選擇要放入該P(yáng)ool的物理設(shè)備烦磁,這里作為測試每個Node節(jié)點(diǎn)只有一塊盤,實(shí)際生產(chǎn)時應(yīng)該至少使用3塊盤以上哼勇,使用mirror則至少兩塊盤以上.
可以通過如下命令查看可用的blockDevices:
# kubectl get blockdevices --all-namespaces -o wide
NAMESPACE NAME NODENAME PATH SIZE CLAIMSTATE STATUS AGE
openebs blockdevice-ad96d141bd7804554d431cb13e7e61bc ip-192-168-193-6.cn-northwest-1.compute.internal /dev/nvme0n1 107374182400 Claimed Active 4d17h
openebs blockdevice-b14cd44f3bfcbd94d3e0bda065f6e2bd ip-192-168-193-172.cn-northwest-1.compute.internal /dev/nvme0n1 107374182400 Claimed Active 4d17h
openebs blockdevice-e3a5cf960033d7a96fdee46a5baee9d2 ip-192-168-193-194.cn-northwest-1.compute.internal /dev/nvme0n1 107374182400 Claimed Active 4d17h
其中Claimed表示已分配都伪,PATH
對應(yīng)物理設(shè)備路徑。
使用CR cstorpools
可以查看Pool:
# kubectl get cstorpools.openebs.io --all-namespaces
NAME ALLOCATED FREE CAPACITY STATUS READONLY TYPE AGE
cstor-disk-pool-9tey 272K 99.5G 99.5G Healthy false striped 4m50s
cstor-disk-pool-lzg4 272K 99.5G 99.5G Healthy false striped 4m50s
cstor-disk-pool-yme1 272K 99.5G 99.5G Healthy false striped 4m50s
可見OpenEBS會在所有的Node節(jié)點(diǎn)創(chuàng)建Pool實(shí)例积担,與前面的解釋一致陨晶。
3.3 Kubernetes集成OpenEBS塊存儲
本小節(jié)主要以cStor為例演示下如何使用OpenEBS,前面已經(jīng)創(chuàng)建了cStor Pool帝璧,接下來只需要再創(chuàng)建對應(yīng)的StorageClass即可:
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: cstor-disk-pool
annotations:
openebs.io/cas-type: cstor
cas.openebs.io/config: |
- name: StoragePoolClaim
value: "cstor-disk-pool"
- name: ReplicaCount
value: "3"
provisioner: openebs.io/provisioner-iscsi
其中StoragePoolClaim指定使用的Pool名稱先誉,ReplicaCount指定volume的副本數(shù)。
創(chuàng)建完StorageClass后就可以創(chuàng)建PVC了:
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: test-openebs-cstor
namespace: default
spec:
storageClassName: cstor-disk-pool
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 50Gi
---
apiVersion: v1
kind: Pod
metadata:
name: test-openebs-cstor
namespace: default
spec:
containers:
- name: test-openebs-cstor
image: jocatalin/kubernetes-bootcamp:v1
volumeMounts:
- name: test-openebs-cstor
mountPath: /data
volumes:
- name: test-openebs-cstor
persistentVolumeClaim:
claimName: test-openebs-cstor
每創(chuàng)建一個PV的烁,OpenEBS就會創(chuàng)建一個Target Pod褐耳,這個Pod通過一個單副本的Deployment管理,這個Pod會創(chuàng)建一個LUN并export渴庆,通過Service暴露iSCSI端口:
# kubectl get pvc --all-namespaces
NAMESPACE NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
default test-openebs-cstor Bound pvc-6673e311-0db1-4f15-b480-aafb16a72d46 50Gi RWO cstor-disk-pool 31m
# kubectl get pod -n openebs | grep pvc-6673e311-0db1-4f15-b480-aafb16a72d46
pvc-6673e311-0db1-4f15-b480-aafb16a72d46-target-55bb467574mf7hf 3/3 Running 1 31m
# kubectl get deployments.apps -n openebs | grep pvc-6673e311-0db1-4f15-b480-aafb16a72d46
pvc-6673e311-0db1-4f15-b480-aafb16a72d46-target 1/1 1 1 32m
# kubectl get svc -n openebs | grep pvc-6673e311-0db1-4f15-b480-aafb16a72d46
pvc-6673e311-0db1-4f15-b480-aafb16a72d46 ClusterIP 10.96.24.35 <none> 3260/TCP,7777/TCP,6060/TCP,9500/TCP 32m
毫無疑問铃芦,OpenEBS的所有服務(wù)運(yùn)行、存儲調(diào)度襟雷、服務(wù)之間通信以及存儲的管理都是通過Kubernetes完成的刃滓,它就像集成到Kubernetes的一個內(nèi)嵌功能一樣,一旦配置完成耸弄,基本不需要額外的運(yùn)維和管理咧虎。
一個Volume對應(yīng)一個Target Pod,這完全遵循了CAS的設(shè)計(jì)理念计呈。
4 讓分布式存儲簡化管理的Rook
4.1 Rook簡介
Rook[9]也是目前開源中比較流行的云原生存儲編排系統(tǒng)砰诵,它和之前介紹的LongHorn和OpenEBS不一樣,它的目標(biāo)并不是重新造輪子實(shí)現(xiàn)一個全新的存儲系統(tǒng)捌显,最開始Rook項(xiàng)目僅僅專注于如何實(shí)現(xiàn)把Ceph運(yùn)行在Kubernetes平臺上茁彭。
隨著項(xiàng)目的發(fā)展,格局也慢慢變大苇瓣,僅僅把Ceph搞定是不夠的尉间,項(xiàng)目當(dāng)前的目標(biāo)是將外部已有的分布式存儲系統(tǒng)在云原生平臺托管運(yùn)行起來,借助云原生平臺具有的自動化調(diào)度、故障恢復(fù)哲嘲、彈性擴(kuò)展等能力實(shí)現(xiàn)外部存儲系統(tǒng)的自動管理贪薪、自動彈性擴(kuò)展以及自動故障修復(fù)。
按照官方的說法眠副,Rook要把原來需要對分布式存儲系統(tǒng)手動做的一些運(yùn)維工作借助云原生平臺能力(如Kubernetes)實(shí)現(xiàn)自動化画切,這些運(yùn)維工作包括部署、初始化囱怕、配置霍弹、擴(kuò)展、升級娃弓、遷移典格、災(zāi)難恢復(fù)、監(jiān)控以及資源管理等台丛,這種自動化甚至不需要人去手動觸發(fā)耍缴,而是云原生平臺自動觸發(fā)的,因此叫做self-managing挽霉,真正實(shí)現(xiàn)NoOpts防嗡。
比如集群增加一塊磁盤,Rook能自動初始化為一個OSD侠坎,并自動加入到合適的故障域中蚁趁,這個OSD在Kubernetes中是以Pod的形式運(yùn)行的。
目前除了能支持編排管理Ceph集群实胸,還支持:
- EdgeFS
- CockroachDB
- Cassandra
- NFS
- Yugabyte DB
不同的存儲通過不同的Operator實(shí)現(xiàn)他嫡,但使用起來基本一致,Rook屏蔽了底層存儲系統(tǒng)的差異童芹。
4.2 Rook部署
安裝部署Rook非常簡單涮瞻,以Ceph為例鲤拿,只需要安裝對應(yīng)的Operator即可:
git clone --single-branch --branch release-1.3 \
https://github.com/rook/rook.git
cd rook/cluster/examples/kubernetes/ceph
kubectl create -f common.yaml
kubectl create -f operator.yaml
kubectl create -f cluster.yaml
通過Rook管理Ceph假褪,理論上不需要直接通過Ceph Client命令行接口與Ceph集群直接交互。不過如果有需要近顷,可以通過如下方式進(jìn)行簡單配置:
kubectl create -f toolbox.yaml # 安裝Ceph client工具
export CEPH_TOOL_POD=$(kubectl -n rook-ceph \
get pod -l "app=rook-ceph-tools" \
-o jsonpath='{.items[0].metadata.name}')
alias ceph="kubectl -n rook-ceph exec -it $CEPH_TOOL_POD -- ceph"
alias rbd="kubectl -n rook-ceph exec -it $CEPH_TOOL_POD -- rbd"
使用ceph
命令查看集群狀態(tài):
# ceph osd df
ID CLASS WEIGHT REWEIGHT SIZE RAW USE DATA OMAP META AVAIL %USE VAR PGS STATUS
TOTAL 0 B 0 B 0 B 0 B 0 B 0 B 0
MIN/MAX VAR: -/- STDDEV: 0
我們發(fā)現(xiàn)Ceph集群是空的生音,沒有任何OSD,這是因?yàn)槲业臋C(jī)器沒有裸磁盤(即沒有安裝任何文件系統(tǒng)的分區(qū))窒升。
DeamonSet rook-discover
會定時監(jiān)視Node節(jié)點(diǎn)是否有新的磁盤缀遍,一旦有新的磁盤,就會自動啟動一個Job進(jìn)行OSD初始化饱须。如下是Node節(jié)點(diǎn)增加磁盤的結(jié)果:
如果運(yùn)行在公有云上域醇,rook還會根據(jù)節(jié)點(diǎn)的Region以及AZ自動放到不同的故障域,不需要手動調(diào)整crushmap:
如圖,由于我的測試集群部署在AWS上譬挚,并且三個節(jié)點(diǎn)都放在了一個AZ上锅铅,因此三個OSD都在zone cn-northwest-1b
中,實(shí)際生產(chǎn)環(huán)境不推薦這么做减宣。
我們發(fā)現(xiàn)整個磁盤以及OSD初始化過程盐须,無需人工干預(yù),這就是所謂的self-manage
漆腌。
想想我們平時在做Ceph集群擴(kuò)容贼邓,從準(zhǔn)備磁盤到crushmap配置,沒有半個小時是搞不定的闷尿,而通過Rook我們幾乎不用操心OSD是如何加到集群的塑径。
Rook默認(rèn)還會安裝Ceph Dashboard,可以通過Kubernetes Service rook-ceph-mgr-dashboard
進(jìn)行訪問填具,admin
的密碼保存在secret rook-ceph-dashboard-password
中晓勇,可通過如下命令獲取:
kubectl -n rook-ceph get secret rook-ceph-dashboard-password \
-o jsonpath="{['data']['password']}" \
| base64 --decode && echo
Rook會把Ceph集群的監(jiān)控導(dǎo)出,便于與Prometheus集成灌旧。
4.3 Kubernetes集成Rook Ceph存儲
Longhorn绑咱、OpenEBS都只提供了塊存儲接口,意味著一個Volume只能掛載到一個Pod枢泰,而Rook Ceph則同時提供了塊存儲描融、共享文件系統(tǒng)存儲以及對象存儲接口,其中共享文件系統(tǒng)存儲以及對象存儲都能實(shí)現(xiàn)跨節(jié)點(diǎn)的多個Pod共享衡蚂。
接下來我們通過例子演示下如何使用窿克。
4.3.1 塊存儲
Ceph通過RBD實(shí)現(xiàn)塊存儲,首先我們在Kubernetes上安裝StorageClass:
kubectl create -f \
cluster/examples/kubernetes/ceph/csi/rbd/storageclass.yaml
我們首先需要創(chuàng)建一個Ceph Pool毛甲,當(dāng)然我們可以通過ceph osd pool create
命令手動創(chuàng)建年叮,但這樣體現(xiàn)不了self-managing
,我們應(yīng)該屏蔽Ceph集群接口玻募,直接使用Kubernetes CRD進(jìn)行聲明:
# kubectl apply -f -
apiVersion: ceph.rook.io/v1
kind: CephBlockPool
metadata:
name: replicapool
namespace: rook-ceph
spec:
failureDomain: host
replicated:
size: 3
通過如上方式只损,我們基本不需要使用ceph
命令,即創(chuàng)建了一個3副本的rbd pool七咧。
可以通過kubectl get cephblockpools
查看pool列表:
# kubectl get cephblockpools
NAME AGE
replicapool 107s
創(chuàng)建一個Pod使用CephBlockPool
新建Volume:
# kubectl apply -f -
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: test-ceph-blockstorage-pvc
spec:
storageClassName: rook-ceph-block
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 20Gi
---
apiVersion: v1
kind: Pod
metadata:
name: test-ceph-blockstorage
spec:
containers:
- name: test-ceph-blockstorage
image: jocatalin/kubernetes-bootcamp:v1
volumeMounts:
- name: volv
mountPath: /data
volumes:
- name: volv
persistentVolumeClaim:
claimName: test-ceph-blockstorage-pvc
輸出結(jié)果如下:
# kubectl get pod test-ceph-blockstorage
NAME READY STATUS RESTARTS AGE
test-ceph-blockstorage 1/1 Running 0 5m4s
# kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
test-ceph-blockstorage-pvc Bound pvc-6ff56a06-86a1-437c-b04f-62bb18e76375 20Gi RWO rook-ceph-block 5m8s
# rbd -p replicapool ls
csi-vol-e65ec8ef-7cc1-11ea-b6f8-ce60d5fc8330
從輸出結(jié)果可見PV volume對應(yīng)Ceph的一個RBD image跃惫。
4.3.2 共享文件系統(tǒng)存儲
共享文件系統(tǒng)存儲即提供文件系統(tǒng)存儲接口,我們最常用的共享文件系統(tǒng)存儲如NFS艾栋、CIFS爆存、GlusterFS等,Ceph通過CephFS實(shí)現(xiàn)共享文件系統(tǒng)存儲蝗砾。
和創(chuàng)建Ceph Pool一樣先较,同樣使用Kubernetes即可聲明一個共享文件系統(tǒng)實(shí)例携冤,完全不需要調(diào)用ceph
接口:
apiVersion: ceph.rook.io/v1
kind: CephFilesystem
metadata:
name: myfs
namespace: rook-ceph
spec:
metadataPool:
replicated:
size: 3
dataPools:
- replicated:
size: 3
preservePoolsOnDelete: true
metadataServer:
activeCount: 1
activeStandby: true
可以通過如下命令查看mds服務(wù)是否就緒:
# kubectl -n rook-ceph get pod -l app=rook-ceph-mds
NAME READY STATUS RESTARTS AGE
rook-ceph-mds-myfs-a-f87d59467-xwj84 1/1 Running 0 32s
rook-ceph-mds-myfs-b-c96645f59-h7ffr 1/1 Running 0 32s
# kubectl get cephfilesystems.ceph.rook.io
NAME ACTIVEMDS AGE
myfs 1 111s
創(chuàng)建cephfs StorageClass:
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: rook-cephfs
provisioner: rook-ceph.cephfs.csi.ceph.com
parameters:
clusterID: rook-ceph
fsName: myfs
pool: myfs-data0
csi.storage.k8s.io/provisioner-secret-name: rook-csi-cephfs-provisioner
csi.storage.k8s.io/provisioner-secret-namespace: rook-ceph
csi.storage.k8s.io/controller-expand-secret-name: rook-csi-cephfs-provisioner
csi.storage.k8s.io/controller-expand-secret-namespace: rook-ceph
csi.storage.k8s.io/node-stage-secret-name: rook-csi-cephfs-node
csi.storage.k8s.io/node-stage-secret-namespace: rook-ceph
reclaimPolicy: Delete
allowVolumeExpansion: true
mountOptions:
我們知道CephFS是共享文件系統(tǒng)存儲,支持多個Pod共享闲勺,首先我們創(chuàng)建一個ReadWriteMany
的PVC:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: cephfs-pvc
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 1Gi
storageClassName: rook-cephfs
通過Deployment創(chuàng)建三個Pod共享這個PVC:
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: cephfs-demo
labels:
k8s-app: cephfs-demo
kubernetes.io/cluster-service: "true"
spec:
replicas: 3
selector:
matchLabels:
k8s-app: cephfs-demo
template:
metadata:
labels:
k8s-app: cephfs-demo
spec:
containers:
- name: cephfs-demo
image: jocatalin/kubernetes-bootcamp:v1
volumeMounts:
- name: volv
mountPath: /data
volumes:
- name: volv
persistentVolumeClaim:
claimName: cephfs-pvc
readOnly: false
等待Pod初始完成后噪叙,我們從其中一個Pod寫入數(shù)據(jù),看另一個Pod能否看到寫入的數(shù)據(jù):
如上圖霉翔,我們往Pod cephfs-demo-5799fcbf58-2sm4b
寫入數(shù)據(jù)睁蕾,從cephfs-demo-5799fcbf58-bkw9f
可以讀取數(shù)據(jù),符合我們預(yù)期债朵。
在Ceph Dashboard中我們也可以看到myfs實(shí)例一共有3個 clients:
4.3.3 對象存儲
Ceph通過RGW實(shí)現(xiàn)對象存儲接口子眶,RGW兼容AWS S3 API,因此Pod可以和使用S3一樣使用Ceph RGW序芦,比如Python可以使用boto3 SDK對桶和對象進(jìn)行操作臭杰。
首先我們需要創(chuàng)建RGW網(wǎng)關(guān):
apiVersion: ceph.rook.io/v1
kind: CephObjectStore
metadata:
name: my-store
namespace: rook-ceph
spec:
metadataPool:
failureDomain: host
replicated:
size: 3
dataPool:
failureDomain: host
erasureCoded:
dataChunks: 2
codingChunks: 1
preservePoolsOnDelete: true
gateway:
type: s3
sslCertificateRef:
port: 80
securePort:
instances: 1
網(wǎng)關(guān)就緒后,我們就可以創(chuàng)建bucket了谚中,雖然bucket不是Volume渴杆,但Rook也把bucket抽象封裝為StorageClass:
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: rook-ceph-bucket
provisioner: ceph.rook.io/bucket
reclaimPolicy: Delete
parameters:
objectStoreName: my-store
objectStoreNamespace: rook-ceph
region: us-east-1
接下來就像聲明PVC一樣創(chuàng)建bucket了,不過不叫PVC宪塔,而是叫OBC(Object Bucket Claim)磁奖,
apiVersion: objectbucket.io/v1alpha1
kind: ObjectBucketClaim
metadata:
name: ceph-bucket
spec:
generateBucketName: ceph-bkt
storageClassName: rook-ceph-bucket
其中AK(access key)以及SK(secret key)保存在Secret ceph-bucket
中,我們可以通過如下命令獲取:
export AWS_ACCESS_KEY_ID=$(kubectl -n default \
get secret ceph-bucket -o yaml \
| grep AWS_ACCESS_KEY_ID \
| awk '{print $2}' | base64 --decode)
export AWS_SECRET_ACCESS_KEY=$(kubectl -n default \
get secret ceph-bucket -o yaml \
| grep AWS_SECRET_ACCESS_KEY \
| awk '{print $2}' | base64 --decode)
# S3 Endpoint為Service rook-ceph-rgw-my-store地址
export AWS_ENDPOINT=http://$(kubectl get svc \
-n rook-ceph \
-l app=rook-ceph-rgw \
-o jsonpath='{.items[0].spec.clusterIP}')
此時就可以使用s3命令進(jìn)行操作了:
如上我們通過aws s3 cp
命令上傳了一個文本文件某筐,通過aws s3 ls
命令我們發(fā)現(xiàn)文件已經(jīng)上傳成功比搭。
4.4 總結(jié)
通過Rook,我們幾乎不需要直接對Ceph進(jìn)行任何操作南誊,Rook實(shí)現(xiàn)了Ceph對象對應(yīng)的CRD身诺,集群部署、配置抄囚、資源供給等操作都能通過Kubernetes CR進(jìn)行聲明霉赡,借助Kubernetes的能力實(shí)現(xiàn)了Ceph集群的self-managing、self-scaling以及self-healing幔托。
5 總結(jié)
本文首先介紹了PV/PVC/Storageclass穴亏、Kubernetes存儲發(fā)展過程以及CAS存儲方案,然后分別介紹了目前比較主流的開源云原生分布式存儲Longhorn柑司、OpenEBS以及Rook迫肖,其中Longhorn比較簡單,并且提供了原生的WebUI攒驰,麻雀雖小五臟俱全。OpenEBS是CAS的開源實(shí)現(xiàn)方案故爵,支持Jiva玻粪、cStor以及LocalPV存儲后端隅津,Rook Ceph則實(shí)現(xiàn)通過Kubernetes管理和運(yùn)行Ceph集群。
網(wǎng)上有一篇文章Storage on Kubernetes: OpenEBS vs Rook (Ceph) vs Rancher Longhorn[10] 針對如上開源云原生存儲方案以及部分商業(yè)產(chǎn)品的性能使用fio進(jìn)行了測試劲室,供參考伦仍。
如下表格中綠色表示性能表現(xiàn)最好,紅色表示性能最差:
轉(zhuǎn)自 https://zhuanlan.zhihu.com/p/136352369