eBay Kubernetes集羣的存儲實踐

如今,eBay已在內部廣泛使用Kubernetes作爲容器管理的平臺,並自研了AZ和聯邦級別的控制平面,用以負責50多個集羣的創建、部署、監控、修復等工作,並且規模在不斷擴大。

我們的生產集羣上,針對各種應用場景,大量使用了本地存儲網絡存儲,並通過原生的PV/PVC來使用。其中本地存儲分爲靜態分區類型基於lvm的動態類型,支持ssd, hdd, nvme等介質。網絡塊存儲使用ceph RBD和ISCSI,共享文件存儲使用cephfs和nfs。

一、本地存儲

01 靜態分區

我們最早於2016年開始做localvolume(本地卷),當時社區還沒有本地的永久存儲方案,爲了支持內部的NoSQL應用使用PV(Persistent Volume),開發了第一版的localvolume方案:

首先,在節點創建的時候,provision系統根據節點池flavor定義對數據盤做分區和格式化,並將盤的信息寫入系統配置文件。

同時,我們在集羣內部署了daemonset localvolume-provisioner,當節點加入集羣后,provisioner會從配置文件中讀取配置信息並生成相應的PV,其中包含相應的path,type等信息。這樣,每個PV對象也就對應着節點上的一個分區。

除此之外,我們改進了scheduler,將本地 PV/PVC的綁定(binding)延遲到scheduler裏進行。這也對應現在社區的volumeScheduling feature。

現在cgroup v1不能很好地支持buffer io的限流和隔離,對於一些io敏感的應用來說,需要儘可能防止這些“noisy neighbors”干擾。同時對於disk io load很高的應用,應儘可能平均每塊盤的負擔,延長磁盤壽命。

因此,我們增加了PVC的反親和性(anti affinity)調度特性,在滿足節點調度的同時,會儘可能調度到符合反親和性規則的盤上。

具體做法是,在PV中打上標籤表明屬於哪個節點的哪塊盤,在PVC中指定反親和性規則,如下圖一所示。scheduler裏增加相應的預選功能,保證聲明瞭同類型的反親和性的PVC,不會綁定到處在同一塊盤的PV上,並在最終調度成功後,完成選定PV/PVC的綁定。

圖1(點擊可查看大圖)

02 LVM動態存儲

對於上述靜態存儲的方案,PV大小是固定的,我們同時希望volume空間能夠更靈活地按需申請,即動態分配存儲。

類似地,我們在節點flavor裏定義一個vg作爲存儲池,節點創建的時候,provision系統會根據flavor做好分區和vg的創建。同時集羣內部署了daemonset local-volume-dynamic-provisioner,實現CSI的相應功能。

在CSI 0.4版本中,該daemonset由CSI的四個基本組件組成,即:csi-attacher, csi-provisioner, csi-registrar以及csi-driver。其中csi-attacher, csi-provisioner和csi-registrar爲社區的sidecar controller。csi-driver是根據存儲後端自己實現CSI接口, 目前支持xfs和ext4兩種主流文件系統,也支持直接掛載裸盤供用戶使用。

爲了支持scheduler能夠感知到集羣的存儲拓撲,我們在csi-registrar中把從csi-driver拿到的拓撲信息同步到Kubernetes節點中存儲,供scheduler預選時提取,避免了在kubelet中改動代碼。

如圖2所示,pod創建後,scheduler將根據vg剩餘空間選擇節點、local-volume-dynamic-provisioner來申請相應大小的lvm logical volume,並創建對應的PV,掛載給pod使用。

圖2(點擊可查看大圖)

二、網絡存儲

01 塊存儲

對於網絡塊存儲,我們使用ceph RBD和ISCSI作爲存儲後端,其中ISCSI爲遠端SSD,RBD爲遠端HDD,通過openstack的cinder組件統一管理。

網絡存儲卷的管理主要包括provision/deletion/attach/detach等,在provision/deletion的時候,相比於localvolume(本地卷)需要以daemonset的方式部署,網絡存儲只需要一箇中心化的provisioner

我們利用了社區的cinder provisioner方案(詳情可見:https://github.com/kubernetes/cloud-provider-openstack),並加以相應的定製,比如支持利用已有快照卷(snapshot volume)來創建PV,secret統一管理等。

Provisioner的基本思路是

watch PVC創建請求

→ 調用cinder api創建相應類型和大小的卷,獲得卷id

→ 調用cinder的initialize_connection api,獲取後端存儲卷的具體連接信息和認證信息,映射爲對應類型的PV對象

→ 往apiserver發請求創建PV

→ PV controller負責完成PVC和PV的綁定。

Delete爲逆過程。

Attach由volume plugin或csi來實現,直接建立每個節點到後端的連接,如RBD map, ISCSI會話連接,並在本地映射爲塊設備。這個過程是分立到每個節點上的操作,無法在controller manager裏實現中心化的attach/detach。因此放到kubelet或csi daemonset來做,而controller manager主要實現邏輯上的accessmode的檢查和volume接口的僞操作,通過節點的狀態與kubelet實現協同管理。

Detach爲逆過程。

在使用RBD的過程中,我們也遇到過一些問題

1)RBD map hang:
RBD map進程hang,然而設備已經map到本地並顯示爲/dev/rbdX。經分析,發現是RBD client端的代碼在執行完attach操作後,會進入順序等待udevd event的loop,分別爲"subsystem=rbd" add event和"subsystem=block" add event。而udevd並不保證遵循kernel uevent的順序,因此如果"subsystem=block" event先於 “subsystem=rbd” event, RBD client將一直等待下去。通過人爲觸發add event(udevadm trigger --type=devices --action=add),就可能順利退出。
這個問題後來在社區得到解決,我們反向移植(backport)到所有的生產集羣上。
(詳情可見:https://tracker.ceph.com/issues/39089
2)kernel RBD支持的RBD feature非常有限,很多後端存儲的特性無法使用。
3)當節點map了RBD device並被container使用,節點重啓會一直hang住,原因是network shutdown先於RBD umount,導致kernel在cleanup_mnt()的時候kRBD連接ceph集羣失敗,進程處於D狀態。我們改變systemd的配置ShutdownWatchdogSec爲1分鐘,來避免這個問題。

除了kernel RBD模塊,Ceph也支持塊存儲的用戶態librbd實現:rbd-nbd。Kubernetes也支持使用rbd-nbd。

如圖3所示,我們對kRBD和rbd-nbd做了對比:

圖3(點擊可查看大圖)

如上,rbd-nbd在使用上有16個device的限制,同時會耗費更多的cpu資源,綜合考慮我們的使用需求,決定繼續使用kRBD。

圖4爲三類塊存儲的性能比較:

圖4(點擊可查看大圖)

02 文件存儲

我們主要使用cephfs作爲存儲後端,cephfs可以使用kernel mount,也可以使用cephfs-fuse mount,類似於前述kRBD和librbd的區別。前者工作在內核態,後者工作在用戶態

經過實際對比,發現性能上fuse mount遠不如kernel mount,而另一方面,fuse能更好地支持後端的feature,便於管理。目前社區cephfs plugin裏默認使用ceph fuse,爲了滿足部分應用的讀寫性能要求,我們提供了pod annotation(註解)選項,應用可自行選擇使用哪類mount方式,默認爲fuse mount。

下面介紹一下在使用ceph fuse的過程中遇到的一些問題(ceph mimic version 13.2.5, kernel 4.15.0-43)

1)ceph fuse internal type overflow導致mount目錄不可訪問

ceph fuse設置掛載目錄dentry的attr_timeout爲0,應用每次訪問時kernel都會重新驗證該dentry cache是否可用,而每次lookup會對其對應inode的reference count + 1。
經過分析,發現在kernel fuse driver裏count是uint_64類型,而ceph-fuse裏是int32類型。當反覆訪問同一路徑時,ref count一直增加,如果節點內存足夠大,kernel沒能及時觸發釋放 dentry緩存,會導致ceph-fuse裏ref count值溢出。
針對該問題,臨時的解決辦法是週期性釋放緩存(drop cache),這樣每次會生成新的dentry,重新開始計數。同時我們存儲的同事也往ceph社區提交補丁,將ceph-fuse中該值改爲uint_64類型,同kernel 匹配起來。(詳情可見:https://tracker.ceph.com/issues/40775

2)kubelet du hang

kubelet會週期性通過du來統計emptydir volume使用情況,我們發現在部分節點上存在大量du進程hang,並且隨着時間推移數量越來越多,一方面使系統load增高,另一方面耗盡pid資源,最終導致節點不響應。
經分析,du會讀取到cephfs路徑,而cephfs不可達是導致du hang的根本原因,主要由以下兩類問題導致
a. 網絡問題導致mds連接斷開。如圖5所示,通過ceph admin socket,可以看到存在失效鏈接(stale connection),原因是client端沒有主動去重連,導致所有訪問mount路徑的操作hang在等待fuse answer上,在節點啓用了client_reconnect_stale選項後,得到解決。
b. mds連接卡在opening狀態,同樣導致du hang。原因是服務端打開了mds_session_blacklist_on_evict,導致連接出現問題時客戶端無法重連。

圖5(點擊可查看大圖)

3)性能

kernel mount性能遠高於fuse性能,經過調試,發現啓用了fuse_big_write後,在大塊讀寫的場景下,fuse性能幾乎和kernel差不多。

三、應用場景

01 本地數據備份還原

本地存儲相比網絡存儲,具有成本低,性能高的優點,但是如果節點失效,將會導致數據丟失,可靠性比網絡存儲低。

爲了保證數據可靠性,應用實現了自己的備份還原機制。使用本地PV存儲數據,同時掛載RBD類型的PV,增量傳輸數據至遠端備份集羣。同時遠端會根據事先定義規則,週期性地在這些RBD盤上打snapshot(快照),在還原的時候,選定特定snapshot,provision出對應PV,並掛載到節點上,恢復到本地PV。

02 盤加密

對於安全要求級別高的應用,如支付業務,我們使用了kata安全容器方案,同時對kata container的存儲進行加密。如圖6所示,我們使用了kernel dm-crypt對盤進行加密,並將生成的key對稱加密存入eBay的密鑰管理服務中,最後給container使用的是解密後的盤,在pod生命週期結束後,會關閉加密盤,防止數據泄漏。

圖6(點擊可查看大圖)

四、磁盤監控

對於本地存儲來說,節點壞盤,丟盤等錯誤,都會影響到線上應用,需要實時有效的監控手段。我們基於社區的node-problem-detector項目,往其中增加了硬盤監控(disk monitor)的功能。

(詳情可見:https://github.com/Kubernetes/node-problem-detector)

主要監控手段有三類:

1)smart工具檢測每塊盤的健康狀況。
2)系統日誌中是否有壞盤信息。根據已有的模式(pattern)對日誌進行匹配,如圖7所示。
3)丟盤檢測,對比實際檢測到的盤符和節點flavor定義的盤符。

圖7(點擊可查看大圖)

以上檢測結果以metrics(指標) 的形式被prometheus收集,同時也更新到自定義crd computenode的狀態中,由專門的remediation controller(修復控制器)接管,如滿足預定義的節點失效策略,將會進入後續修復流程。

對於有問題的盤,monitor會對相應PV標記taint,scheduler裏會防止綁定到該類PV,同時對於已綁定的PV,會給綁定到的PVC發event,通知應用。

五、管理部署

以上提到了幾類組件,local-volume-provisioner,local-volume-dynamic-provisioner,cinder-provisioner,node-problem-detector等,我們開發了gitops + salt的方案對其進行統一管理

首先把每個組件作爲一個salt state,定義對應的salt state文件和pillar,寫入git repo,對於key等敏感信息則存放在secret中。這些manifest文件通過AZ控制面同步到各個集羣並執行。我們將所有的組件視爲addon,salt會生成最終的yaml定義文件,交由kube addon manager進行apply。在需要更新的時候,只需更新相應的salt文件和pillar值即可。

六、後續工作

1)對於網絡存儲,將後端控制面由cinder切換到SDS,屆時將會對接新的SDS api,實現新的dynamic provision controller和csi插件;
2)實現Kubernetes平臺上的volume snapshot(卷快照)功能;
3)將in-tree 的volume插件全部遷移到CSI,並將CSI升級到最新版本,方便部署和升級;
4)引入cgroup v2, 以實現blkio qos控制;
5)實現本地存儲的自動擴容能力。

本文轉載自公衆號eBay技術薈(ID:eBayTechRecruiting)

原文鏈接

https://mp.weixin.qq.com/s/VeyR4dSkH_bOH7YmbwSE2w

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