如何透過 open-local 玩轉容器本地儲存? | 龍蜥技術

OpenAnolis小助手發表於2022-08-09

編者按:雲原生背景下,本地儲存相比分散式儲存,在易用性、可維護性、IO 效能上都更勝一籌,但使用本地儲存任然有諸多問題。那麼 open-local 會帶來什麼不一樣的體驗呢?來看看作者怎麼說。本文整理自 龍蜥大講堂第 30 期,精彩分享影片回放已上傳至龍蜥官網(首頁-動態-影片),歡迎檢視!

如何透過 open-local 玩轉容器本地儲存? | 龍蜥技術

雲原生背景下,有狀態應用需要藉助一套儲存方案進行資料持久化儲存。本地儲存相比分散式儲存,在易用性、可維護性、IO 效能上都更勝一籌,但使用本地儲存作為目前低成本交付 Kubernetes 叢集,依然存在許多問題:

  • 本地儲存管理能力有限:使用本地儲存需要一定的人力成本,如透過為節點打標來限制 Pod 排程、人工管理不同機型的磁碟、人工透過 Hostpath 方式掛載指定磁碟到容器等;同時還有一些現場交付問題,如繫結了錯誤的宿主機路徑使得故障無法及時發現,這些都嚴重影響了 K8s 交付效率以及應用執行時穩定性。

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

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

針對以上問題, open-local 應用而生,接下來一起來看看 open-local 都有哪些效能?

open-local 介紹

開源地址:

open-local 是阿里巴巴開源的一款本地儲存管理系統。透過 open-local,在 Kubernetes 上使用本地儲存就像使用集中式儲存一樣簡單。

目前 open-local 支援以下儲存特性:本地儲存池管理、儲存捲動態分配、儲存排程演算法擴充套件、儲存卷擴容、儲存卷快照、儲存卷監控、儲存卷 IO 限流、原生塊裝置及臨時卷。

1、use case

  • 應用本身已支援多副本高可用,希望使用本地盤以提高儲存資源利用率,提高資料讀寫效能,如 HBase、MinIO 等。

  • 應用期望資料卷具備容量隔離能力,避免出現諸如日誌打滿系統盤的情況。

  • 應用需要大量本地儲存並依賴節點保持,如 etcd、zookeeper、Elasticsearch 等。

  • 叢集本地磁碟數量眾多,希望透過排程器實現有狀態應用的自動化部署。

  • 透過儲存快照能力為資料庫類應用備份瞬時資料等。

2、架構

┌─────────────────────────────────────────────────────────────────────────────┐
│ Master                                                                      │
│                   ┌───┬───┐           ┌────────────────┐                    │
│                   │Pod│PVC│           │   API-Server   │                    │
│                   └───┴┬──┘           └────────────────┘                    │
│                        │ bound                ▲                             │
│                        ▼                      │ watch                       │
│                      ┌────┐           ┌───────┴────────┐                    │
│                      │ PV │           │ Kube-Scheduler │                    │
│                      └────┘         ┌─┴────────────────┴─┐                  │
│                        ▲            │     open-local     │                  │
│                        │            │ scheduler-extender │                  │
│                        │      ┌────►└────────────────────┘◄───┐             │
│ ┌──────────────────┐   │      │               ▲               │             │
│ │ NodeLocalStorage │   │create│               │               │  callback   │
│ │    InitConfig    │  ┌┴──────┴─────┐  ┌──────┴───────┐  ┌────┴────────┐    │
│ └──────────────────┘  │  External   │  │   External   │  │  External   │    │
│          ▲            │ Provisioner │  │   Resizer    │  │ Snapshotter │    │
│          │ watch      ├─────────────┤  ├──────────────┤  ├─────────────┤    │
│    ┌─────┴──────┐     ├─────────────┴──┴──────────────┴──┴─────────────┤GRPC│
│    │ open-local │     │                 open-local                     │    │
│    │ controller │     │             CSI ControllerServer               │    │
│    └─────┬──────┘     └────────────────────────────────────────────────┘    │
│          │ create                                                           │
└──────────┼──────────────────────────────────────────────────────────────────┘
           │
┌──────────┼──────────────────────────────────────────────────────────────────┐
│ Worker   │                                                                  │
│          │                                                                  │
│          ▼                ┌───────────┐                                     │
│ ┌──────────────────┐      │  Kubelet  │                                     │
│ │ NodeLocalStorage │      └─────┬─────┘                                     │
│ └──────────────────┘            │ GRPC                     Shared Disks     │
│          ▲                      ▼                          ┌───┐  ┌───┐     │
│          │              ┌────────────────┐                 │sdb│  │sdc│     │
│          │              │   open-local   │ create volume   └───┘  └───┘     │
│          │              │ CSI NodeServer ├───────────────► VolumeGroup      │
│          │              └────────────────┘                                  │
│          │                                                                  │
│          │                                                 Exclusive Disks  │
│          │                ┌─────────────┐                  ┌───┐            │
│          │ update         │ open-local  │  init device     │sdd│            │
│          └────────────────┤    agent    ├────────────────► └───┘            │
│                           └─────────────┘                  Block Device     │
│                                                                             │
└─────────────────────────────────────────────────────────────────────────────┘

2.1 open-local 包含四個元件:

1)scheduler-extender:作為 Kube-Scheduler 的擴充套件元件,透過 Extender 方式實現,新增本地儲存排程演算法。

2)CSI 外掛:按照 CSI(Container Storage Interface) 標準實現本地磁碟管理能力,包含建立/刪除/擴容儲存卷、建立/刪除快照、暴露儲存卷 metrics 等能力。

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

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

2.2 open-local 包含兩個 CRD

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

如何在 ack-distro 中使用 open-local

1、初始化配置

前置條件:環境中已經安裝 lvm 工具

ack-distro 部署時會預設安裝 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 

檢查 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 擴容到 20 Gi。

# 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 
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 
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 

檢查 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 

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 檢視叢集本地儲存資訊,包含儲存裝置和儲存卷資訊。

如何透過 open-local 玩轉容器本地儲存? | 龍蜥技術

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

關於影片回放和課件獲取

【影片回放】:影片回訪已上傳至龍蜥官網: 檢視。

【PPT課件獲取】:關注微信公眾號(OpenAnolis),回覆“龍蜥課件”即可獲取。有任何疑問請隨時諮詢龍蜥助手—小龍(微信:openanolis_assis)。

—— 完 ——


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/70004278/viewspace-2909663/,如需轉載,請註明出處,否則將追究法律責任。

相關文章