與容器服務 ACK 發行版的深度對話最終彈:如何通過 open-local 玩轉容器本地儲存

語言: CN / TW / HK

記者: 各位阿里巴巴雲原生的讀者朋友們大家好,又跟大家見面了。今天是我們的老朋友『阿里雲容器服務 ACK 發行版』最後一次做客探究身世之謎系列專欄,在之前的訪談中,它為我們帶來了精彩的講解,感興趣的朋友們歡迎回顧。我們瞭解到,從去年 12 月上線至今,容器服務 ACK 發行版受到了大家的關注與支援,也取得了不錯的下載量,對此您有什麼看法嗎?

阿里雲容器服務 ACK 發行版(簡稱ACK Distro): 是的,上線三個月以來有幸獲得 400+的下載量,也通過不同途徑與大家交流技術,感謝大家的關注,希望你們獲得更好的容器服務體驗。

記者: 好的,那讓我們進入正題吧~之前瞭解到 sealer 可以幫助您快速構建&部署,hybridnet 可以助力構建混合雲統一網路平面,那麼今天為我們介紹的是哪位多才多藝的小夥伴呢?

ACK Distro: 我們都知道,雲原生背景下有狀態應用需要藉助一套儲存方案進行資料持久化儲存。本地儲存相比分散式儲存,在成本、易用性、可維護性、IO 效能上都更勝一籌,所以今天給大家講解的就是——阿里巴巴開源的本地儲存管理系統 open-local,以及我是如何藉助它玩轉容器本地儲存。先給大家交代一下 open-local 誕生的契機吧,雖然剛才提到本地儲存相較於分散式儲存的優勢,但本地儲存作為目前低成本交付 Kubernetes 叢集,是依然存在許多問題的:

• Kubernetes 缺失儲存資源的感知能力: 本地儲存作為一種“非標”資源,在 Kubernetes 中的支援遠低於標準資源(cpu、記憶體等)。使用本地儲存需要一定的人力成本,如通過為節點打標來限制 Pod 排程、人工管理不同機型的磁碟、人工通過 Hostpath 方式掛載指定磁碟到容器等;同時還有一些私有化軟體現場交付問題,如綁定了錯誤的宿主機路徑使得故障無法及時發現,這些都嚴重影響了 Kubernetes 交付效率以及應用執行時的穩定性;

• 本地儲存空間隔離能力缺失: 應用掛載不適當的宿主機目錄(如掛載到宿主機根路徑)導致宿主機故障,如因應用資料寫滿磁碟導致容器執行時無響應、觸發 Pod 驅逐、Pod 之間 IO 相互影響等問題;

• Kubernetes 對有狀態應用使用本地儲存支援不足: 通過 Hostpath 無法做到節點保持,使得 Pod 漂移後應用資料丟失;使用半自動靜態 Local PV 可保證節點保持,但是無法實現全自動,仍需要人為參與(如建立資料夾路徑,為節點打標等);無法使用一些高階儲存能力(例如快照)。

而 open-local 可以最大程度上避免這些問題,讓大家獲得更好的體驗,在 Kubernetes 上使用本地儲存就像使用集中式儲存一樣簡單。

open-local 的架構組成

記者: 您可以進一步為我們講解下 open-local 的架構組成部分嗎?

ACK Distro: 當然,open-local 一共包含四個元件:

1. scheduler-extender: 作為 kube-scheduler 的擴充套件元件,通過 Extender 方式實現,擴充套件了原生排程器對本地儲存資源的感知,以實現對包括磁碟容量、多盤感知、磁碟介質(ssd or hdd)等資訊的排程決策,做到儲存資源的混合排程;

2. csi-plugin: 符合 CSI(Container Storage Interface) 標準的本地磁碟管理能力,包含建立/刪除/擴容儲存卷、建立/刪除快照、暴露儲存卷 metrics 等能力;

3. agent: 執行在叢集中的每個節點,根據配置清單初始化儲存裝置,並通過上報叢集中本地儲存裝置資訊以供 scheduler-extender 決策排程;

4. controller: 獲取叢集儲存初始化配置,並向執行在各個節點的 agent 下發詳細的資源配置清單。

同時 open-local 包含兩個 CRD:

  1. NodeLocalStorage:open-local 通過 NodeLocalStorage 資源上報每個節點上的儲存裝置資訊,該資源由 controller 建立,由每個節點的 agent 元件更新其 status。該 CRD 屬於全域性範圍的資源。
  2. NodeLocalStorageInitConfig:open-local controller 可通過 NodeLocalStorageInitConfig 資源建立每個 NodeLocalStorage 資源。NodeLocalStorageInitConfig 資源中包含全域性預設節點配置和特定節點配置,若節點的 node label 滿足表示式則使用特定節點配置,否則使用預設配置。

它的架構圖可以參照下面:

在這裡插入圖片描述

open-local 的使用場景

記者: 那麼什麼樣的需求場景下大家會用到 open-local 呢?

ACK Distro: 我總結了以下幾個使用案例,大家可以根據自己的情況對號入座。

  1. 應用期望資料卷具備容量隔離能力,避免出現諸如日誌打滿系統盤的情況;
  2. 應用需要大量本地儲存並依賴節點保持,如 Hbase、etcd、ZooKeeper、ElasticSearch 等;
  3. 叢集本地磁碟數量眾多,希望通過排程器實現有狀態應用的自動化部署;
  4. 通過儲存快照能力為資料庫類應用備份瞬時資料等。

如何在 ACK Distro 中使用 open-local

記者: 接下來又到老問題了,open-local 的優勢怎麼在您身上體現呢?或者您怎樣使用 open-local 可以達到最佳實踐呢?

ACK Distro: 我分類別為大家講解吧~

1.初始化設定

首先確保環境中已經安裝 lvm 工具,在安裝部署我時會預設安裝 open-local,編輯NodeLocalStorageInitConfig 資源,進行儲存初始化配置。

# kubectl edit nlsc open-local

使用 open-local 要求環境中有 VG(VolumeGroup),若您的環境中已存在 VG 且有剩餘空間,則可以配置在白名單中;若環境中沒有 VG,您需要提供一個塊裝置名稱供 open-local 建立 VG。

apiVersion: csi.aliyun.com/v1alpha1
kind: NodeLocalStorageInitConfig
metadata:
  name: open-local
spec:
  globalConfig: # 全域性預設節點配置,初始化建立 NodeLocalStorage 時會填充到其 Spec 中
    listConfig:
      vgs:
        include: # VolumeGroup 白名單,支援正則表示式
        - open-local-pool-[0-9]+
        - your-vg-name # 若環境中已有 VG,可以寫入白名單由 open-local 納管
    resourceToBeInited:
      vgs:
      - devices:
        - /dev/vdc  # 若環境中沒有 VG,使用者需提供一個塊裝置
        name: open-local-pool-0 # 將塊裝置 /dev/vdc 初始化為名叫 open-local-pool-0 的 VG

NodeLocalStorageInitConfig 資源編輯完畢後,controller 和 agent 會更新所有節點的 NodeLocalStorage 資源。

2.儲存捲動態供應

open-local 預設在叢集中部署了一些儲存類模板,我以 open-local-lvm、open-local-lvm-xfs 和 open-local-lvm-io-throttling 舉例:

# kubectl get sc
NAME                           PROVISIONER            RECLAIMPOLICY   VOLUMEBINDINGMODE      ALLOWVOLUMEEXPANSION   AGE
open-local-lvm                 local.csi.aliyun.com   Delete          WaitForFirstConsumer   true                   8d
open-local-lvm-xfs             local.csi.aliyun.com        Delete          WaitForFirstConsumer   true                   6h56m
open-local-lvm-io-throttling   local.csi.aliyun.com   Delete          WaitForFirstConsumer   true

建立一個 Statefulset,該 Statefulset 使用 open-local-lvm 儲存類模板。此時建立的儲存卷檔案系統為 ext4。若使用者指定 open-local-lvm-xfs 儲存模板,則儲存卷檔案系統為 xfs。

# kubectl apply -f http://raw.githubusercontent.com/alibaba/open-local/main/example/lvm/sts-nginx.yaml

檢查 Pod/PVC/PV 狀態,可看到儲存卷建立成功:

# kubectl get pod
NAME          READY   STATUS    RESTARTS   AGE
nginx-lvm-0   1/1     Running   0          3m5s
# kubectl get pvc
NAME               STATUS   VOLUME                                       CAPACITY   ACCESS MODES   STORAGECLASS     AGE
html-nginx-lvm-0   Bound    local-52f1bab4-d39b-4cde-abad-6c5963b47761   5Gi        RWO            open-local-lvm   104s
# kubectl get pv
NAME                                         CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                      STORAGECLASS    AGE
local-52f1bab4-d39b-4cde-abad-6c5963b47761   5Gi        RWO            Delete           Bound    default/html-nginx-lvm-0   open-local-lvm  2m4s
kubectl describe pvc html-nginx-lvm-0

3.儲存卷擴容

編輯對應 PVC 的 spec.resources.requests.storage 欄位,將 PVC 宣告的儲存大小從 5Gi 擴容到 20Gi。

# kubectl patch pvc html-nginx-lvm-0 -p '{"spec":{"resources":{"requests":{"storage":"20Gi"}}}}'

檢查 PVC/PV 狀態:

# kubectl get pvc
NAME                    STATUS   VOLUME                                       CAPACITY   ACCESS MODES   STORAGECLASS     AGE
html-nginx-lvm-0        Bound    local-52f1bab4-d39b-4cde-abad-6c5963b47761   20Gi       RWO            open-local-lvm   7h4m
# kubectl get pv
NAME                                         CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                           STORAGECLASS     REASON   AGE
local-52f1bab4-d39b-4cde-abad-6c5963b47761   20Gi       RWO            Delete           Bound    default/html-nginx-lvm-0        open-local-lvm            7h4m

4.儲存卷快照

open-local 有如下快照類:

# kubectl get volumesnapshotclass
NAME             DRIVER                DELETIONPOLICY   AGE
open-local-lvm   local.csi.aliyun.com   Delete           20m

建立 VolumeSnapshot 資源:

# kubectl apply -f http://raw.githubusercontent.com/alibaba/open-local/main/example/lvm/snapshot.yaml
volumesnapshot.snapshot.storage.k8s.io/new-snapshot-test created
# kubectl get volumesnapshot
NAME                READYTOUSE   SOURCEPVC          SOURCESNAPSHOTCONTENT   RESTORESIZE   SNAPSHOTCLASS    SNAPSHOTCONTENT                                    CREATIONTIME   AGE
new-snapshot-test   true         html-nginx-lvm-0                           1863          open-local-lvm   snapcontent-815def28-8979-408e-86de-1e408033de65   19s            19s
# kubectl get volumesnapshotcontent
NAME                                               READYTOUSE   RESTORESIZE   DELETIONPOLICY   DRIVER                VOLUMESNAPSHOTCLASS   VOLUMESNAPSHOT      AGE
snapcontent-815def28-8979-408e-86de-1e408033de65   true         1863          Delete           local.csi.aliyun.com   open-local-lvm        new-snapshot-test   48s

建立一個新 Pod,該 Pod 對應的儲存卷資料與之前應用快照點時刻的資料一致:

# kubectl apply -f http://raw.githubusercontent.com/alibaba/open-local/main/example/lvm/sts-nginx-snap.yaml
service/nginx-lvm-snap created
statefulset.apps/nginx-lvm-snap created
# kubectl get po -l app=nginx-lvm-snap
NAME               READY   STATUS    RESTARTS   AGE
nginx-lvm-snap-0   1/1     Running   0          46s
# kubectl get pvc -l app=nginx-lvm-snap
NAME                    STATUS   VOLUME                                       CAPACITY   ACCESS MODES   STORAGECLASS     AGE
html-nginx-lvm-snap-0   Bound    local-1c69455d-c50b-422d-a5c0-2eb5c7d0d21b   4Gi        RWO            open-local-lvm   2m11s

5.原生塊裝置

open-local 支援建立的儲存卷將以塊裝置形式掛載在容器中(本例中塊裝置在容器 /dev/sdd 路徑):

# kubectl apply -f http://raw.githubusercontent.com/alibaba/open-local/main/example/lvm/sts-block.yaml

檢查 Pod/PVC/PV 狀態:

# kubectl get pod
NAME                READY   STATUS    RESTARTS   AGE
nginx-lvm-block-0   1/1     Running   0          25s
# kubectl get pvc
NAME                     STATUS   VOLUME                                       CAPACITY   ACCESS MODES   STORAGECLASS     AGE
html-nginx-lvm-block-0   Bound    local-b048c19a-fe0b-455d-9f25-b23fdef03d8c   5Gi        RWO            open-local-lvm   36s
# kubectl describe pvc html-nginx-lvm-block-0
Name:          html-nginx-lvm-block-0
Namespace:     default
StorageClass:  open-local-lvm
...
Access Modes:  RWO
VolumeMode:    Block # 以塊裝置形式掛載入容器
Mounted By:    nginx-lvm-block-0
...

6.IO 限流

open-local 支援為 PV 設定 IO 限流,支援 IO 限流的儲存類模板如下:
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: open-local-lvm-io-throttling
provisioner: local.csi.aliyun.com
parameters:
  csi.storage.k8s.io/fstype: ext4
  volumeType: "LVM"
  bps: "1048576" # 讀寫吞吐量限制在 1024KiB/s 上下
  iops: "1024"   # IOPS 限制在 1024 上下
reclaimPolicy: Delete
volumeBindingMode: WaitForFirstConsumer
allowVolumeExpansion: true

建立一個 Statefulset,該 Statefulset 使用 open-local-lvm-io-throttling 儲存類模板。

# kubectl apply -f http://raw.githubusercontent.com/alibaba/open-local/main/example/lvm/sts-io-throttling.yaml

Pod 處於 Running 狀態後,進入 Pod 容器中:

# kubectl exec -it test-io-throttling-0 sh

此時儲存卷是以原生塊裝置掛載在 /dev/sdd 上,執行 fio 命令:

# fio -name=test -filename=/dev/sdd -ioengine=psync -direct=1 -iodepth=1 -thread -bs=16k -rw=readwrite -numjobs=32 -size=1G -runtime=60 -time_based -group_reporting

結果如下所示,可見讀寫吞吐量限制在 1024KiB/s 上下:

......
Run status group 0 (all jobs):
   READ: bw=1024KiB/s (1049kB/s), 1024KiB/s-1024KiB/s (1049kB/s-1049kB/s), io=60.4MiB (63.3MB), run=60406-60406msec
  WRITE: bw=993KiB/s (1017kB/s), 993KiB/s-993KiB/s (1017kB/s-1017kB/s), io=58.6MiB (61.4MB), run=60406-60406msec
Disk stats (read/write):
    dm-1: ios=3869/3749, merge=0/0, ticks=4848/17833, in_queue=22681, util=6.68%, aggrios=3112/3221, aggrmerge=774/631, aggrticks=3921/13598, aggrin_queue=17396, aggrutil=6.75%
  vdb: ios=3112/3221, merge=774/631, ticks=3921/13598, in_queue=17396, util=6.75%

7.臨時卷

open-local 支援為 Pod 建立臨時卷,其中臨時卷生命週期與 Pod 一致,即 Pod 刪除後臨時卷也隨之刪除。此處可理解為 open-local 版本的 emptydir。

# kubectl apply -f ./example/lvm/ephemeral.yaml

其結果如下:

# kubectl describe po file-server
Name:         file-server
Namespace:    default
......
Containers:
  file-server:
    ......
    Mounts:
      /srv from webroot (rw)
      /var/run/secrets/kubernetes.io/serviceaccount from default-token-dns4c (ro)
Volumes:
  webroot:   # 此為 CSI 臨時卷
    Type:              CSI (a Container Storage Interface (CSI) volume source)
    Driver:            local.csi.aliyun.com
    FSType:
    ReadOnly:          false
    VolumeAttributes:      size=2Gi
                           vgName=open-local-pool-0
  default-token-dns4c:
    Type:        Secret (a volume populated by a Secret)
    SecretName:  default-token-dns4c
    Optional:    false

8.監控大盤

open-local 自帶了監控大盤,使用者可通過 Grafana 檢視叢集本地儲存資訊,包含儲存裝置和儲存卷資訊。如下圖所示:

ACK Distro:總而言之,藉助 open-local ,在運維方面可減少人力成本,提高叢集執行時的穩定性;功能方面,將本地儲存的優勢最大化,使使用者不僅能體驗到本地盤的高效能,同時各種高階儲存特性豐富了應用場景,讓廣大開發者體驗雲原生帶來的紅利,實現應用上雲尤其是有狀態應用雲原生部署關鍵一步。

記者: 感謝 ACK Distro 的精彩講解,這三次的做客讓我們對它和它的小夥伴都有了更深入的瞭解,也希望訪談內容能為正在閱讀文章的您提供一些幫助。

ACK Distro: 是的,我和專案組成員在 GitHub 社群和社群歡迎大家的“騷擾”!

相關連結

[1]open-local 開源倉庫地址: http://github.com/alibaba/open-local [2]ACK Distro 官網: http://www.aliyun.com/product/aliware/ackdistro [3]ACK Distro 官方 GitHub: http://github.com/AliyunContainerService/ackdistro [4]讓創新觸手可及,阿里雲容器服務 ACK 發行版開放免費下載:http://mp.weixin.qq.com/s/Lc2afj91sykHMDLUKA_0bw [5]第一彈深度訪談: http://mp.weixin.qq.com/s/wB7AS52vEA_VeRegUyrVkA [6]第二彈深度訪談: http://mp.weixin.qq.com/s/O095yS5xPtawkh55rvitTg