https://cloud.tencent.com/developer/article/1470034
如果你正在運行 Kubernetes,你可能正在使用,或者准備使用動態供給的塊存儲卷,而首當其沖的問題就是為集群選擇合適的存儲技術。這個事情並不能用一個簡單的測試來做出簡單的回答,告訴你目前市面上最好的技術是什么。存儲技術的選擇過程中,集群上運行的負載類型是一個重要的輸入。對於裸金屬集群來說,需要根據實際用例進行選擇,並集成到自己的硬件之中。公有雲中的托管 K8s,例如 AKS、EKS 或者 GKE,都具有開箱可用的塊存儲能力,然而這也不見得就是最好的選擇。有很多因素需要考慮,比如說公有雲的 StorageClass 的故障轉移時間太長。例如在 一個針對 AWS EBS 的故障測試中,加載了卷的 Pod 用了超過五分鍾才成功的在另一個節點上啟動。Portworx 或者 OpenEBS 這樣的雲原生存儲產品,正在嘗試解決這類問題。
本文的目標是使用最常見的 Kubernetes 存儲方案,進行基本的性能對比。我覺得在 Azure AKS 上使用下列后端:
- AKS 原生 Storageclass:Azure native premium
- 使用 cStor 后端的 OpenEBS
- Portworx
- Heketi 管理的 Gluster
- Rook 管理的 Ceph
現在我們來介紹每種存儲后端,並交代一下安裝過程,然后進入 AKS 測試環境進行測試,最后得出結果。
存儲
這一節中介紹測試中用到的存儲方案,包含安裝過程以及該方案的優缺點。
Azure 原生 StorageClass
我選擇這一方案的動機是以此作為所有測試的基線。這個方案應該提供最佳性能。Azure 動態的創建托管磁盤,並把它們映射到 K8s 的虛擬機中,最終成為 Pod 的存儲卷。
這個方案很方便,什么多余的步驟都不需要。創建一個新的 AKS 集群之后,就自動提供了兩個預定義的 StorageClass,分別是 default
和 managed-premium
,premium 使用的是基於 SSD 的高性能低延遲磁盤。
$ kubectl get storageclasses
NAME PROVISIONER AGE
default (default) kubernetes.io/azure-disk 8m managed-premium kubernetes.io/azure-disk 8m $ kubectl get pvc NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE dbench-pv-claim Bound pvc-e7bd34a4-1dbd-11e9-8726-ae508476e8ad 1000Gi RWO managed-premium 10s $ kubectl get po NAME READY STATUS RESTARTS AGE dbench-w7nqf 0/1 ContainerCreating 0 29s
優點
AKS 開箱即用。
缺點
故障轉移非常緩慢,有時需要十分鍾以后,存儲卷才能重新掛載到不同節點上的 Pod 里。
OpenEBS
對我來說 OpenEBS 是個全新事物,因此我很有興趣做他的測試。他提出了一個新的 Container Attached Storage(容器掛載存儲)概念,這是一個基於微服務的存儲控制器,以及多個基於微服務的存儲副本。他和 Portworx 同樣,屬於雲原生存儲分類的成員。
它是一個完全開源的方案,目前提供兩種后端——Jiva 和 cStor。我最開始選擇的是 Jiva,后來切換到 cStor。cStor 有很多長處,例如他的控制器和副本被部署到單一的 OpenEBS 所在的命名空間之中,能夠管理原始磁盤等。每個 K8s 卷都有自己的存儲控制器,能在節點存儲容量的許可范圍內對存儲進行擴展。
在 AKS 上運行
在 AKS 上的安裝非常容易。
- 連接到所有 K8s 節點上,安裝 iSCSI,這是因為他需要使用 iSCSI 協議在 K8s 節點之間進行 Pod 和控制器的連接。 apt-get update apt install -y open-iscsi
- 使用一個 YAML 定義在 K8s 集群上完成部署: kubectl apply -f https://openebs.github.io/charts/openebs-operator-0.8.0.yaml
- 下一步,OpenEBS 控制器發現了節點中的所有磁盤。但是我必須手工標識出我附加的 AWS 托管磁盤。 $ kubectl get disk NAME AGE disk-184d99015253054c48c4aa3f17d137b1 5m disk-2f6bced7ba9b2be230ca5138fd0b07f1 5m disk-806d3e77dd2e38f188fdaf9c46020bdc 5m
- 然后把這些磁盤加入 StoragePoolClaim,這個對象會在 StorageClass 中進行引用: --- apiVersion: storage.k8s.io/v1 kind: StorageClass metadata: name: openebs-custom annotations: openebs.io/cas-type: cstor cas.openebs.io/config: | - name: StoragePoolClaim value: "cstor-disk" provisioner: openebs.io/provisioner-iscsi --- apiVersion: openebs.io/v1alpha1 kind: StoragePoolClaim metadata: name: cstor-disk spec: name: cstor-disk type: disk maxPools: 3 poolSpec: poolType: striped disks: diskList: - disk-2f6bced7ba9b2be230ca5138fd0b07f1 - disk-806d3e77dd2e38f188fdaf9c46020bdc - disk-184d99015253054c48c4aa3f17d137b1
完成這些步驟之后,就可以用 K8s 的 PVC 來動態的創建存儲卷了。
優點
- 開源
- Maya 在資源使用的可視化方面做得非常好。可以在 K8s 中部署多個服務,方便的為集群的各方面數據設置監控和日志。對於排錯工作來說,這十分重要。
- CAS 概念:我非常欣賞這一概念,我相信這是未來的趨勢。
- OpenEBS 社區:在社區中我的任何問題都能在幾分鍾內得到解決。Slack 上的團隊非常有幫助。
缺點
- 不成熟:OpenEBS 還很年輕,目前還沒有發布穩定版。核心團隊還在進行后端的優化,未來幾個月里會對性能做出很大提升。
- Kubelet 和存儲控制器之間的 iSCSI 連接是通過 K8s Service 進行的,這在 Tungsten Fabric 之類的 CNI 插件環境中可能會出問題。
- 需要在 K8s 節點上安裝額外的軟件(iSCSI),這對於托管集群來說非常不便。
注:OpenEBS 團隊對我的案例場景進行了調整:
https://github.com/kmova/openebs/tree/fio-perf-tests/k8s/demo/dbench
Portworx
Portworx 是另一個面向 Kubernetes 的容器原生存儲方案,它專注於高度分布式的環境。這是一個主機可尋址的存儲,每個卷都直接映射到掛在的主機上。他提供了基於應用 I/O 類型的自動微調能力。官方網站提供了更多信息。不幸的是,它也是本文中唯一的非開源產品。然而它提供了 3 節點的免費試用。
在 AKS 上運行
在 AKS 上的安裝同樣簡單,我用了他們網站提供的生成器。
- 選擇基於 Portworx 的 ETCD,指定 K8s 版本為 1.11.4。
- 因為我用了 Azure CNI,因此必須把數據網卡設置為
azure0
。否則 Portworx 會使用 docker bridge 的 IP 地址,而非 VM 網卡。 - 最后網站會生成渲染完成的 YAML 文件。
- 提交后,會看到節點上運行的 Portworx Pod:
$ kubectl get pods -o wide -n kube-system -l name=portworx NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE portworx-g9csq 1/1 Running 0 14m 10.0.1.66 aks-agentpool-20273348-2 <none> portworx-nt2lq 1/1 Running 0 14m 10.0.1.4 aks-agentpool-20273348-0 <none> portworx-wcjnx 1/1 Running 0 14m 10.0.1.35 aks-agentpool-20273348-1 <none>
為 PVC 創建一個 StorageClass,定義高優先級,以及三個副本:
root@aks-agentpool-20273348-0:~# kubectl get storageclass -o yaml portworx-sc apiVersion: storage.k8s.io/v1 kind: StorageClass metadata: creationTimestamp: 2019-01-28T21:10:28Z name: portworx-sc resourceVersion: "55332" selfLink: /apis/storage.k8s.io/v1/storageclasses/portworx-sc uid: 23455e40-2341-11e9-bfcb-a23b1ec87092 parameters: priority_io: high repl: "3" provisioner: kubernetes.io/portworx-volume reclaimPolicy: Delete volumeBindingMode: Immediate
優點
- 部署方便:生成器包含配置細節。
- 不像 Ceph 和 Glusterfs 那樣需要進行額外配置。
- 雲原生存儲:公有雲和裸金屬都可以運行。
- 存儲級別感知和應用感知的 I/O 微調。
缺點
- 閉源:商業解決方案
GlusterFS Heketi
GlusterFS 是知名的開源存儲方案,是由 Redhat 提供的開源存儲方案。Heketi 是 GlusterFS 的 RESTful 卷管理界面。它提供了易用的方式為 GlusterFS 卷提供了動態供給的功能。如果沒有 Heketi 的輔助,就只能手工創建 GlusterFS 卷並映射到 K8s PV 了。關於 GlusterFS 的更多信息,請閱讀官方文檔。
在 AKS 上運行
根據 Heketi 的快速入門文檔進行部署。
- 參照樣例,創建一個包含磁盤和主機名的拓撲文件。
- Heketi 主要的開發和測試都在基於 RHEL 的操作系統上,我在 AKS 上使用 Ubuntu 主機時,出現了內核模塊路徑錯誤的問題,我提交了一個 PR 來修正這個問題。
+++ b/deploy/kube-templates/glusterfs-daemonset.yaml
@@ -67,7 +67,7 @@ spec:
mountPath: "/etc/ssl"
readOnly: true
- name: kernel-modules
- mountPath: "/usr/lib/modules"
+ mountPath: "/lib/modules"
readOnly: true
securityContext:
capabilities: {}
@@ -131,4 +131,4 @@ spec:
path: "/etc/ssl"
- name: kernel-modules
hostPath:
- path: "/usr/lib/modules"
+ path: "/lib/modules"
- 我在 AKS 環境中遇到的另一個問題是一個非空磁盤,所以我用
wipefs
為 glusterfs 進行清理。這個磁盤並未用過。 $ wipefs -a /dev/sdc /dev/sdc: 8 bytes were erased at offset 0x00000218 (LVM2_member): 4c 56 4d 32 20 30 30 31 - 最后運行
gk-deploy -g -t topology.json
,會在每個節點上運行 Heketi 控制器管理之下的 GlusterFS Pod。 $ kubectl get po -o wide NAME READY STATUS RESTARTS IP NODE NOMINATED NODE glusterfs-fgc8f 1/1 Running 0 10.0.1.35 aks-agentpool-20273348-1 glusterfs-g8ht6 1/1 Running 0 10.0.1.4 aks-agentpool-20273348-0 glusterfs-wpzzp 1/1 Running 0 10.0.1.66 aks-agentpool-20273348-2 heketi-86f98754c-n8qfb 1/1 Running 0 10.0.1.69 aks-agentpool-20273348-2
然后我遇到了新問題。K8s 控制面無法使用 Heketi 的 restURL
。我測試了一下 kube dns 的記錄,pod IP 和 svc IP 都沒有生效。最后只能手工使用 Heketi CLI 來創建存儲卷。
$ export HEKETI_CLI_SERVER=http://10.0.1.69:8080 $ heketi-cli volume create --size=10 --persistent-volume --persistent-volume-endpoint=heketi-storage-endpoints | kubectl create -f - persistentvolume/glusterfs-efb3b155 created $ kubectl get pv NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE glusterfs-efb3b155 10Gi RWX Retain Available
然后把現存 PV 映射為 PVC,加載給測試工具進行測試。
kind: PersistentVolumeClaim
apiVersion: v1 metadata: name: glusterfs-efb3b155 spec: accessModes: - ReadWriteMany storageClassName: "" resources: requests: storage: 10Gi volumeName: glusterfs-efb3b155
$ kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
glusterfs-efb3b155 Bound glusterfs-efb3b155 10Gi RWX 36m
Heketi 的更多輸出:
$ gluster volume info vol_efb3b15529aa9aba889d7900f0ce9849
Volume Name: vol_efb3b15529aa9aba889d7900f0ce9849
Type: Replicate Volume ID: 96fde36b-e389-4dbe-887b-baae32789436 Status: Started Snapshot Count: 0 Number of Bricks: 1 x 3 = 3 Transport-type: tcp Bricks: Brick1: 10.0.1.66:/var/lib/heketi/mounts/vg_5413895eade683e1ca035760c1e0ffd0/brick_cd7c419bc4f4ff38bbc100c6d7b93605/brick Brick2: 10.0.1.35:/var/lib/heketi/mounts/vg_3277c6764dbce56b5a01426088901f6d/brick_6cbd74e9bed4758110c67cfe4d4edb53/brick Brick3: 10.0.1.4:/var/lib/heketi/mounts/vg_29d6152eeafc57a707bef56f091afe44/brick_4856d63b721d794e7a4cbb4a6f048d96/brick Options Reconfigured: transport.address-family: inet nfs.disable: on performance.client-io-threads: off $ kubectl get svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE heketi ClusterIP 192.168.101.75 <none> 8080/TCP 5h heketi-storage-endpoints ClusterIP 192.168.103.66 <none> 1/TCP 5h $ kubectl get endpoints NAME ENDPOINTS AGE heketi 10.0.1.69:8080 5h heketi-storage-endpoints 10.0.1.35:1,10.0.1.4:1,10.0.1.66:1 5h kubernetes 172.31.22.152:443 1d root@aks-agentpool-20273348-0:~# kubectl get endpoints heketi-storage-endpoints -o yaml apiVersion: v1 kind: Endpoints metadata: creationTimestamp: 2019-01-29T15:14:28Z name: heketi-storage-endpoints namespace: default resourceVersion: "142212" selfLink: /api/v1/namespaces/default/endpoints/heketi-storage-endpoints uid: 91f802eb-23d8-11e9-bfcb-a23b1ec87092 subsets: - addresses: - ip: 10.0.1.35 - ip: 10.0.1.4 - ip: 10.0.1.66 ports: - port: 1 protocol: TCP
優點
- 久經考驗的存儲方案。
- 比 Ceph 輕量。
缺點
- Heketi 在公有雲上表現不佳。在私有雲上表現良好,安裝會方便一些。
- 並非為結構化數據設計,例如 SQL 數據庫。然而可以使用 GlusterFS 為數據庫提供備份和恢復支持。
Ceph Rook
我在 OpenStack 私有雲上嘗試過安裝和運行 Ceph。它需要為特定硬件定制參數,根據數據類型設計 pg 組、SSD 分區和 CRUSH 圖等。所以第一次聽說在 3 節點的 K8s 集群上運行 Ceph 的時候,我不太相信它能工作。結果 Rook 的編排工具讓我印象深刻,它把所有的步驟和 K8s 的編排能力結合在一起,讓安裝變得非常簡便。
在 AKS 上運行
Rook 的缺省安裝無需任何特定步驟,如果沒什么高級配置,會非常簡單。
- 我使用的是 Ceph 快速入門指南
- 為 AKS 配置
FLEXVOLUME_DIR_PATH
,這是因為它需要/etc/kubernetes/volumeplugins/
,而不是 Ubuntu 中缺省的/usr/libexec
,沒有這個步驟,Kubelet 就無法加載 PVC 了。diff --git a/cluster/examples/kubernetes/ceph/operator.yaml b/cluster/examples/kubernetes/ceph/operator.yaml
index 73cde2e..33f45c8 100755
--- a/cluster/examples/kubernetes/ceph/operator.yaml
+++ b/cluster/examples/kubernetes/ceph/operator.yaml
@@ -431,8 +431,8 @@ spec:
# - name: AGENT_MOUNT_SECURITY_MODE
# value: "Any"
# Set the path where the Rook agent can find the flex volumes
- # - name: FLEXVOLUME_DIR_PATH
- # value: "<PathToFlexVolumes>"
+ - name: FLEXVOLUME_DIR_PATH
+ value: "/etc/kubernetes/volumeplugins"
# Set the path where kernel modules can be found
# - name: LIB_MODULES_DIR_PATH
# value: "<PathToLibModules>"
- 還要在
deviceFilter
中指定要使用的設備,這里是/dev/sdc
。diff --git a/cluster/examples/kubernetes/ceph/cluster.yaml b/cluster/examples/kubernetes/ceph/cluster.yaml
index 48cfeeb..0c91c48 100755
--- a/cluster/examples/kubernetes/ceph/cluster.yaml
+++ b/cluster/examples/kubernetes/ceph/cluster.yaml
@@ -227,7 +227,7 @@ spec:
storage: # cluster level storage configuration and selection
useAllNodes: true
useAllDevices: false
- deviceFilter:
+ deviceFilter: "^sdc"
location:
config:
- 安裝之后,創建一個 Ceph block pool,以及 StorageClass,使用如下配置。 apiVersion: ceph.rook.io/v1 kind: CephBlockPool metadata: name: replicapool namespace: rook-ceph spec: failureDomain: host replicated: size: 3 --- apiVersion: storage.k8s.io/v1 kind: StorageClass metadata: name: rook-ceph-block provisioner: ceph.rook.io/block parameters: blockPool: replicapool clusterNamespace: rook-ceph fstype: xfs reclaimPolicy: Retain
- 最后使用部署工具進行檢查。
ceph status
cluster:
id: bee70a10-dce1-4725-9285-b9ec5d0c3a5e health: HEALTH_OK services: mon: 3 daemons, quorum c,b,a mgr: a(active) osd: 3 osds: 3 up, 3 in data: pools: 0 pools, 0 pgs objects: 0 objects, 0 B usage: 3.0 GiB used, 3.0 TiB / 3.0 TiB avail pgs: [root@aks-agentpool-27654233-0 /]# [root@aks-agentpool-27654233-0 /]# [root@aks-agentpool-27654233-0 /]# ceph osd status +----+--------------------------+-------+-------+--------+---------+--------+---------+-----------+ | id | host | used | avail | wr ops | wr data | rd ops | rd data | state | +----+--------------------------+-------+-------+--------+---------+--------+---------+-----------+ | 0 | aks-agentpool-27654233-0 | 1025M | 1021G | 0 | 0 | 0 | 0 | exists,up | | 1 | aks-agentpool-27654233-1 | 1025M | 1021G | 0 | 0 | 0 | 0 | exists,up | | 2 | aks-agentpool-27654233-2 | 1025M | 1021G | 0 | 0 | 0 | 0 | exists,up | +--