雲原生儲存詳解:容器儲存與 K8s 儲存卷

yc98zp發表於2020-06-23

頭圖.png

作者 | 闞俊寶 阿里雲技術專家

導讀:雲原生儲存詳解系列文章將從雲原生儲存服務的概念、特點、需求、原理、使用及案例等方面,和大家一起探討雲原生儲存技術新的機遇與挑戰。本文為該系列文章的第二篇,會對容器儲存的相關概念進行講述,歡迎大家在留言區參與討論。

相關文章推薦:

雲原生儲存詳解:雲原生應用的基石
雲原生儲存詳解:容器儲存與 K8s 儲存卷

雲原生儲存的兩個關鍵領域:Docker 儲存卷、K8s 儲存卷;

  • Docker 儲存卷:容器服務在單節點的儲存組織形式,關注資料儲存、容器執行時的相關技術;
  • K8s 儲存卷:關注容器叢集的儲存編排,從應用使用儲存的角度關注儲存服務。

Docker 儲存

容器服務之所以如此流行,一大優勢即來自於執行容器時容器映象的組織形式。容器通過複用容器映象的技術,實現在相同節點上多個容器共享一個映象資源(更細一點說是共享某一個映象層),避免了每次啟動容器時都拷貝、載入映象檔案,這種方式既節省了主機的儲存空間,又提高了容器啟動效率。

1. 容器讀寫層

為了提高節點儲存的使用效率,容器不光在不同執行的容器之間共享映象資源,而且還實現了在不同映象之間共享資料。共享映象資料的實現原理:映象是分層組合而成的,即一個完整的映象會包含多個資料層,每層資料相互疊加、覆蓋組成了最終的完整映象。

為了實現多個容器間共享映象資料,容器映象每一層都是隻讀的。而通過實踐我們得知,使用映象啟動一個容器的時候,其實是可以在容器裡隨意讀寫的,這是如何實現的呢?

容器使用映象時,在多個映象分層的最上面還新增了一個讀寫層。每一個容器在執行時,都會基於當前映象在其最上層掛載一個讀寫層,使用者針對容器的所有操作都在讀寫層中完成。一旦容器銷燬,這個讀寫層也隨之銷燬。

1.png

如上圖所示例子,一個節點上共有 3 個容器,分別基於 2 個映象執行。

映象儲存層說明如下:

該節點上共包含 6 個映象層:Layer 1~6。

  • 映象 1 由:Layer 1、3、4、5 組成;
  • 映象 2 由:Layer 2、3、5、6 組成。

所以兩個映象共享了 Layer 3、5 兩個映象層;

容器儲存說明:

  • 容器 1:使用映象 1 啟動
  • 容器 2:使用映象 1 啟動
  • 容器 3:使用映象 2 啟動

容器 1 和容器 2 共享映象 1,且每個容器有自己的可寫層;
容器 1(2)和容器 3 共享映象 2 個層(Layer3、5);

通過上述例子可以看到,通過容器映象分層實現資料共享可以大幅減少容器服務對主機儲存的資源需求。

上面給出了容器讀寫層結構,而讀寫的原則:

對於讀:容器由這麼多層的資料組合而成,當不同層次的資料重複時,讀取的原則是上層資料覆蓋下層資料;
對於寫:容器修改某個檔案時,都是在最上層的讀寫層進行。主要實現技術有:寫時複製、用時配置。

1)寫時複製

寫時複製(CoW:copy-on-write),表示只在需要寫時才去複製,是針對已有檔案的修改場景。CoW 技術可以讓所有的容器共享 image 的檔案系統,所有資料都從 image 中讀取,只有當要對檔案進行寫操作時,才從 image 裡把要寫的檔案複製到最上面的讀寫層進行修改。所以無論有多少個容器共享同一個 image,所做的寫操作都是對從 image 中複製後在複本上進行,並不會修改 image 的原始檔,且多個容器操作同一個檔案,會在每個容器的檔案系統裡生成一個複本,每個容器修改的都是自己的複本,相互隔離,相互不影響。

2)用時配置

用時分配:在映象中原本沒有某個檔案的場景,只有在要新寫入一個檔案時才分配空間,這樣可以提高儲存資源的利用率。比如啟動一個容器,並不會為這個容器預分配一些磁碟空間,而是當有新檔案寫入時,才按需分配新空間。

2. 儲存驅動

儲存驅動是指如何對容器的各層資料進行管理,已達到上述需要實現共享、可讀寫的效果。即:容器儲存驅動實現了容器讀寫層資料的儲存和管理。常見的儲存驅動:

  • AUFS
  • OverlayFS
  • Devicemapper
  • Btrfs
  • ZFS

以 AUFS 為例,我們來講述一下儲存驅動的工作原理:

2.png

AUFS 是一種聯合檔案系統(UFS),是檔案級的儲存驅動。

AUFS 是一個能透明疊加一個或多個現有檔案系統的層狀檔案系統,把多層檔案系統合併成單層表示。即:支援將不同目錄掛載到同一個虛擬檔案系統下的檔案系統。
可以一層一層地疊加修改檔案,其底層都是隻讀的,只有最上層的檔案系統是可寫的。
當需要修改一個檔案時,AUFS 建立該檔案的一個副本,使用 CoW 將檔案從只讀層複製到可寫層進行修改,結果也儲存在可寫層。
在 Docker 中,底下的只讀層就是 image,可寫層就是 Container 執行時。

其他各種儲存驅動這裡不再細講,有興趣的同學可以到網上查詢資料。

3. Docker 資料卷介紹

容器中的應用讀寫資料都是發生在容器的讀寫層,映象層+讀寫層對映為容器內部檔案系統、負責容器內部儲存的底層架構。當我們需要容器內部應用和外部儲存進行互動時,需要一個類似於計算機 U 盤一樣的外接儲存,容器資料卷即提供了這樣的功能。

另一方面:容器本身的儲存資料都是臨時儲存,在容器銷燬的時候資料會一起刪除。而通過資料卷將外部儲存掛載到容器檔案系統,應用可以引用外部資料,也可以將自己產出的資料持久化到資料卷中,所以容器資料卷是容器進行資料持久化的實現方式。

容器儲存組成:只讀層(容器映象) + 讀寫層 + 外接儲存(資料卷)

容器資料卷從作用範圍可以分為:單機資料卷 和 叢集資料卷。單機資料卷即為容器服務在一個節點上的資料卷掛載能力,docker volume 是單機資料卷的代表實現;叢集資料卷則關注的是叢集級別的資料卷編排能力,K8s 資料卷則是叢集資料卷的主要應用方式。

Docker Volume 是一個可供多個容器使用的目錄,它繞過 UFS,包含以下特性:

  • 資料卷可以在容器之間共享和重用;
  • 相比通過儲存驅動實現的可寫層,資料卷讀寫是直接對外接儲存進行讀寫,效率更高;
  • 對資料卷的更新是對外接儲存讀寫,不會影響映象和容器讀寫層;
  • 資料卷可以一直存在,直到沒有容器使用。

1)Docker 資料卷型別

Bind:將主機目錄/檔案直接掛載到容器內部。

  • 需要使用主機的上的絕對路徑,且可以自動建立主機目錄;
  • 容器可以修改掛載目錄下的任何檔案,是應用更具有便捷性,但也帶來了安全隱患。

Volume:使用第三方資料卷的時候使用這種方式。

  • Volume命令列指令:docker volume (create/rm);
  • 是Docker提供的功能,所以在非 docker 環境下無法使用;
  • 分為命名資料卷和匿名資料卷,其實現是一致的,區別是匿名資料卷的名字為隨機碼;
  • 支援資料卷驅動擴充套件,實現更多外部儲存型別的接入。

Tmpfs:非持久化的卷型別,儲存在記憶體中。

資料易丟失。

2)Bind 掛載方式語法

-v: src:dst:opts 只支援單機版。

  • Src:表示卷對映源,主機目錄或檔案,需要是絕對地址;
  • Dst:容器內目標掛載地址;
  • Opts:可選,掛載屬性:ro, consistent, delegated, cached, z, Z;
  • Consistent, delegated, cached:為mac系統配置共享傳播屬性;
  • Z、z:配置主機目錄的selinux label。

示例:

$ docker run -d --name devtest -v /home:/data:ro,rslave nginx
$ docker run -d --name devtest --mount type=bind,source=/home,target=/data,readonly,bind-propagation=rslave nginx
$ docker run -d --name devtest -v /home:/data:z nginx

3)Volume 掛載方式語法

-v: src:dst:opts 只支援單機版。

  • Src:表示卷對映源,資料卷名、空;
  • Dst:容器內目標目錄;
  • Opts:可選,掛載屬性:ro(只讀)。

示例:

$ docker run -d --name devtest -v myvol:/app:ro nginx
$ docker run -d --name devtest --mount source=myvol2,target=/app,readonly nginx

4. Docker 資料卷使用

Docker 資料卷使用方式:

1)Volume 型別

  • 匿名資料卷:docker run –d -v /data3 nginx;
  • 會主機上預設建立目錄:/var/lib/docker/volumes/{volume-id}/_data進行對映;
  • 命名資料卷:docker run –d -v nas1:/data3 nginx;
  • 如果當前找不到nas1卷,會建立一個預設型別(local)的卷。

2)Bind 方式

docker run -d -v /test:/data nginx
如果主機上沒有/test目錄,則預設建立此目錄。

3)資料卷容器

資料卷容器是一個執行中的容器,其他容器可以繼承此容器中的掛載資料卷,則此容器的所有掛載都會在引用容器中體現。

docker run -d —volumes-from nginx1 -v /test1:/data1 nginx
繼承所有來自配置容器的資料卷,幷包含自己定義的卷。

4)資料卷的掛載傳播

Docker volume 支援掛載傳播的配置:Propagation。

  • Private:掛載不傳播,源目錄和目標目錄中的掛載都不會在另一方體現;
  • Shared:掛載會在源和目的之間傳播;
  • Slave:源物件的掛載可以傳播到目的物件,反之不行;
  • Rprivate:遞迴 Private,預設方式;
  • Rshared:遞迴 Shared;
  • Rslave:遞迴 Slave。

示例:

$ docker run –d -v /home:/data:shared nginx
表示:主機/home下面掛載的目錄,在容器/data下面可用,反之可行;
$ docker run –d -v /home:/data:slave nginx
表示:主機/home下面掛載的目錄,在容器/data下面可用,反之不行;

5)資料卷掛載的可見性

Volume 掛載可見性:

  • 本地空目錄、映象空目錄:無特殊處理;
  • 本地空目錄、映象非空目錄:映象目錄的內容拷貝到主機;(是拷貝,不是對映;即使容器刪除內容也會儲存);
  • 本地非空目錄、映象空目錄:本地目錄內容對映到容器;
  • 本地非空目錄、映象非空目錄:本地目錄內容對映到容器,容器目錄的內容被隱藏。

Bind 掛載可見性:以主機目錄為準。

  • 本地空目錄、映象空目錄:無特殊處理;
  • 本地空目錄、映象非空目錄:容器目錄變成空;
  • 本地非空目錄、映象空目錄:本地目錄內容對映到容器;
  • 本地非空目錄、映象非空目錄:本地目錄內容對映到容器,容器目錄的內容被隱藏。

5. Docker資料卷外掛

Docker 資料卷實現了將容器外部儲存掛載到容器檔案系統的方式。為了擴充套件容器對外部儲存型別的需求,docker 提出了通過儲存外掛的方式掛載不同型別的儲存服務。擴充套件外掛統稱為 Volume Driver,可以為每種儲存型別開發一種儲存外掛。

  • 單個節點上可以部署多個儲存外掛;
  • 一個儲存外掛負責一種儲存型別的掛載服務。

3.png

Docker Daemon 與 Volume driver 通訊方式有:

  • Sock檔案:linux 下放在/run/docker/plugins 目錄下
  • Spec檔案:/etc/docker/plugins/convoy.spec 定義
  • Json檔案:/usr/lib/docker/plugins/infinit.json 定義

實現介面:

Create, Remove, Mount, Path, Umount, Get, List, Capabilities;

使用示例:

$ docker volume create --driver nas -o diskid="" -o host="10.46.225.247" -o path=”/nas1" -o mode="" --name nas1

Docker Volume Driver 適用在單機容器環境或者 swarm 平臺進行資料卷管理,隨著 K8s 的流行其使用場景已經越來越少,關於 VolumeDriver 的詳細介紹這裡不在細講,有興趣可以參考: https://docs.docker.com/engine/extend/plugins_volume/

K8s 儲存卷

1. 基礎概念

根據之前的描述,為了實現容器資料的持久化我們需要使用資料卷的功能,在 K8s 編排系統中如何為執行的負載(Pod)定義儲存呢?K8s 是一個容器編排系統,其關注的是容器應用在整個叢集的管理和部署形式,所以在考慮 K8s 應用儲存的時候就需要從叢集角度考慮。K8s 儲存卷定義了在 K8s 系統中應用與儲存的關聯關係。其包含以下概念:

1)Volume 資料卷

資料卷定義了外接儲存的細節,並內嵌到 Pod 中作為 Pod 的一部分。其實質是外接儲存在 K8s 系統的一個記錄物件,當負載需要使用外接儲存的時候,從資料卷中查到相關資訊並進行儲存掛載操作。

  • 生命週期和 Pod 一致,即 pod 被刪除的時候資料卷也一起消失(注意不是資料刪除);
  • 儲存細節定義在編排模板中,應用編排感知儲存細節;
  • 一個負載(Pod)中可以同時定義多個 volume,可以是相同型別或不同型別的儲存;
  • Pod 的每個 container 可以引用一個或多個 volume,不同 container 可以同時使用相同 volume。

K8S Volume 常用型別:

  • 本地儲存:如 HostPath、emptyDir,這些儲存卷的特點是,資料儲存在叢集的特定節點上,並且不能隨著應用飄逸,節點當機時資料即不再可用;
  • 網路儲存:Ceph、Glusterfs、NFS、Iscsi 等型別,這些儲存卷的特點是資料不在叢集的某個節點上,而是在遠端的儲存服務上,使用儲存卷時需要將儲存服務掛載到本地使用;
  • Secret/ConfigMap:這些儲存卷型別,其資料是叢集的一些物件資訊,並不屬於某個節點,使用時將物件資料以卷的形式掛載到節點上供應用使用;
  • CSI/Flexvolume:這是兩種資料卷擴容方式,可以理解為抽象的資料卷型別。每種擴充套件方式都可再細化成不同的儲存型別;
  • PVC:一種資料卷定義方式,將資料卷抽象成一個獨立於 pod 的物件,這個物件定義(關聯)的儲存資訊即儲存卷對應的真正儲存資訊,供 K8s 負載掛載使用。

一些 volume 模板示例如下:

volumes:
  - name: hostpath
    hostPath:
      path: /data
      type: Directory
---
  volumes:
  - name: disk-ssd
    persistentVolumeClaim:
      claimName: disk-ssd-web-0
  - name: default-token-krggw
    secret:
      defaultMode: 420
      secretName: default-token-krggw
---
  volumes:
    - name: "oss1"
      flexVolume:
        driver: "alicloud/oss"
        options:
          bucket: "docker"
          url: "oss-cn-hangzhou.aliyuncs.com"

2)PVC和PV

  • K8s 儲存卷是一個叢集級別的概念,其物件作用範圍是整個 K8s 叢集,而不是而一個節點;
  • K8s 儲存捲包含一些物件(PVC、PV、SC),這些物件和應用負載(Pod)是獨立,通過編排模板進行關聯;
  • K8s 儲存卷可以有自己的獨立生命週期,不依附於 Pod。

PVC 是 PersistentVolumeClaim 的縮寫,譯為儲存宣告;PVC 是在 K8s 中一種抽象的儲存卷型別,代表了某個具體型別儲存的資料卷表達。其設計意圖是:儲存與應用編排分離,將儲存細節抽象出來並實現儲存的編排(儲存卷)。這樣 K8s 中儲存卷物件獨立於應用編排而單獨存在,在編排層面使應用和儲存解耦。

PV 是 PersistentVolume 的縮寫,譯為持久化儲存卷;PV 在 K8s 中代表一個具體儲存型別的卷,其物件中定義了具體儲存型別和卷引數。即目標儲存服務所有相關的資訊都儲存在 PV 中,K8s 引用 PV 中的儲存資訊執行掛載操作。

應用負載、PVC、PV 的關聯關係為:

4.png

從實現上看,只要有了 PV 既可以實現儲存和應用的編排分離,也能實現資料卷的掛載,為何要用 PVC + PV 兩個物件呢?K8s 這樣設計是從應用角度對儲存捲進行二次抽象;由於 PV 描述的是對具體儲存型別,需要定義詳細的儲存資訊,而應用層使用者在消費儲存服務的時候往往不希望對底層細節知道的太多,讓應用編排層面來定義具體的儲存服務不夠友好。這時對儲存服務再次進行抽象,只把使用者關係的引數提煉出來,用 PVC 來抽象更底層的 PV。所以 PVC、PV 關注的物件不一樣,PVC 關注使用者對儲存需求,給使用者提供統一的儲存定義方式;而 PV 關注的是儲存細節,可以定義具體儲存型別、儲存掛載使用的詳細引數等。

使用時應用層會宣告一個對儲存的需求(PVC),而 K8s 會通過最佳匹配的方式選擇一個滿足 PVC 需求的 PV,並與之繫結。所以從職責上 PVC 是應用所需要的儲存物件,屬於應用作用域(和應用處於一個名詞空間);PV 是儲存平面的儲存物件,屬於整個儲存域(不屬於某個名詞空間);

下面給出 PVC、PV 的一些屬性:

  • PVC 和 PV 總是成對出現的,PVC 必須與 PV 繫結後才能被應用(Pod)消費;
  • PVC 和 PV 是一一繫結關係,不存在一個 PV 被多個 PVC 繫結,或者一個 PVC 繫結多個 PV 的情況;
  • PVC 是應用層面的儲存概念,是屬於具體的名詞空間的;
  • PV 是儲存層面的儲存概念,是叢集級別的,不屬於某個名詞空間;PV 常由專門的儲存運維人員負責管理;
  • 消費關係上:Pod 消費 PVC,PVC 消費 PV,而 PV 定義了具體的儲存介質。

3)PVC 詳細定義

PVC 定義的模板如下:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: disk-ssd-web-0
spec:
  accessModes:
  - ReadWriteOnce
  resources:
    requests:
      storage: 20Gi
  storageClassName: alicloud-disk-available
  volumeMode: Filesystem

PVC 定義的儲存介面包括:儲存的讀寫模式、資源容量、卷模式等;主要引數說明如下:

accessModes:儲存卷的訪問模式,支援:ReadWriteOnce、ReadWriteMany、ReadOnlyMany 三種模式。

  • ReadWriteOnce 表示 pvc 只能同時被一個 pod 以讀寫方式消費;
  • ReadWriteMany 可以同時被多個 pod 以讀寫方式消費;
  • ReadOnlyMany 表示可以同時被多個 pod 以只讀方式消費;

注意:這裡定義的訪問模式只是編排層面的宣告,具體應用在讀寫儲存檔案的時候是否可讀可寫,需要具體的儲存外掛實現確定。

storage:定義此 PVC 物件期望提供的儲存容量,同樣此處的資料大小也只是編排宣告的值,具體儲存容量要看底層儲存服務型別。

volumeMode:表示儲存卷掛載模式,支援 FileSystem、Block 兩種模式;

FileSystem:將資料卷掛載成檔案系統的方式供應用使用;
Block:將資料卷掛載成塊裝置的形式供應用使用。

4)PV 詳細定義

下面為雲盤資料卷 PV 物件的編排示例:

apiVersion: v1
kind: PersistentVolume
metadata:
  labels:
    failure-domain.beta.kubernetes.io/region: cn-shenzhen
    failure-domain.beta.kubernetes.io/zone: cn-shenzhen-e
  name: d-wz9g2j5qbo37r2lamkg4
spec:
  accessModes:
  - ReadWriteOnce
  capacity:
    storage: 30Gi
  flexVolume:
    driver: alicloud/disk
    fsType: ext4
    options:
      VolumeId: d-wz9g2j5qbo37r2lamkg4
  persistentVolumeReclaimPolicy: Delete
  storageClassName: alicloud-disk-available
  volumeMode: Filesystem
  • accessModes:儲存卷的訪問模式,支援:ReadWriteOnce、ReadWriteMany、ReadOnlyMany 三種模式;具體含義同 PVC 欄位;
  • capacity:定義儲存卷容量;
  • persistentVolumeReclaimPolicy:定義回收策略,即刪除 pvc 的時候如何處理 PV;支援 Delete、Retain 兩種型別,動態資料卷部分會詳細說明此引數;
  • storageClassName:表示儲存卷的使用的儲存類名字,動態資料卷部分會詳細說明此引數;
  • volumeMode:同 PVC 中的 volumeMode 定義;
  • Flexvolume:此欄位表示具體的儲存型別,這裡 Flexvolume 為一種抽象的儲存型別,並在 flexvolume 的子配置項中定義了具體的儲存型別、儲存引數。

5)PVC/PV 繫結

PVC 只有繫結了 PV 之後才能被 Pod 使用,而 PVC 繫結 PV 的過程即是消費 PV 的過程,這個過程是有一定規則的,下面規則都滿足的 PV 才能被 PVC 繫結:

  • VolumeMode:被消費 PV 的 VolumeMode 需要和 PVC 一致;
  • AccessMode:被消費 PV 的 AccessMode 需要和 PVC 一致;
  • StorageClassName:如果 PVC 定義了此引數,PV 必須有相關的引數定義才能進行繫結;
  • LabelSelector:通過 label 匹配的方式從 PV 列表中選擇合適的 PV 繫結;
  • storage:被消費 PV 的 capacity 必須大於或者等於 PVC 的儲存容量需求才能被繫結。

滿足上述所有需要的 PV 才可以被 PVC 繫結。

如果同時有多個 PV 滿足需求,則需要從 PV 中選擇一個更合適的進行繫結;通常選擇容量最小的,如果容量最小的也有多個,則隨機選擇。
如果沒有滿足上述需求的 PV 儲存,則 PVC 會處於 Pending 狀態,等待有合適的 PV 出現了再進行繫結。

2. 靜態、動態儲存卷

從上面的討論我們瞭解到,PVC 是針對應用服務對儲存的二次抽象,具有簡潔的儲存定義介面。而 PV 是具有繁瑣儲存細節的儲存抽象,一般有專門的叢集管理人員定義、維護。

根據 PV 的建立方式可以將儲存卷分為動態儲存和靜態儲存卷:

  • 靜態儲存卷:由管理員建立的 PV
  • 動態儲存卷:由 Provisioner 外掛建立的 PV

1)靜態儲存卷

一般先由叢集管理員分析叢集中儲存需求,並預先分配一些儲存介質,同時建立對應的 PV 物件,建立好的 PV 物件等待 PVC 來消費。如果負載中定義了 PVC 需求,K8s 會通過相關規則實現 PVC 和匹配的 PV 進行繫結,這樣就實現了應用對儲存服務的訪問能力。

2)動態儲存卷

由叢集管理員配置好後端的儲存池,並建立相應的模板(storageclass),等到有 PVC 需要消費 PV 的時候,根據 PVC 定義的需求,並參考 storageclass 的儲存細節,由 Provisioner 外掛動態建立一個 PV。

兩種卷的比較:

  • 動態儲存卷和靜態儲存卷最終的效果都是:Pod -> PVC -> PV 的使用鏈路,且物件的具體模板定義都是一致的;
  • 動態儲存卷和靜態儲存卷區別是:動態卷是外掛自動建立 PV,而靜態卷是叢集管理員手動建立 PV。

提供動態儲存卷的優勢:

  • 動態卷讓 K8s 實現了 PV 的自動化生命週期管理,PV 的建立、刪除都通過 Provisioner 完成;
  • 自動化建立 PV 物件,減少了配置複雜度和系統管理員的工作量;
  • 動態卷可以實現 PVC 對儲存的需求容量和 Provision 出來的 PV 容量一致,實現儲存容量規劃最優。

3)動態卷的實現流程

當使用者宣告一個 PVC 時,如果在 PVC 中新增了 StorageClassName 欄位,其意圖為:當 PVC 在叢集中找不到匹配的 PV 時,會根據 StorageClassName 的定義觸發相應的 Provisioner 外掛建立合適的 PV 供繫結,即建立動態資料卷;動態資料卷時由 Provisioner 外掛建立的,並通過 StorageClassName 與 PVC 進行關聯。

StorageClass 可譯為儲存類,表示為一個建立 PV 儲存卷的模板;在 PVC 觸發自動建立PV的過程中,即使用 StorageClass 物件中的內容進行建立。其內容包括:目標 Provisioner 名字,建立 PV 的詳細引數,回收模式等配置。

StorageClasss 模板定義如下:

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: alicloud-disk-topology
parameters:
  type: cloud_ssd
provisioner: diskplugin.csi.alibabacloud.com
reclaimPolicy: Delete
allowVolumeExpansion: true
volumeBindingMode: WaitForFirstConsumer
  • provisioner:為一個註冊外掛的名字,此外掛實現了建立 PV 的功能;一個 StorageClass 只能定義一個Provisioner;
  • parameters:表示建立資料卷的具體引數;例如這裡表示建立一個 SSD 型別的雲盤;
  • reclaimPolicy:用來指定建立 PV 的 persistentVolumeReclaimPolicy 欄位值,支援 Delete/Retain;Delete 表示動態建立的 PV,在銷燬的時候也會自動銷燬;Retain 表示動態建立的 PV,不會自動銷燬,而是由管理員來處理;
  • allowVolumeExpansion:定義由此儲存類建立的 PV 是否執行動態擴容,預設為 false;是否能動態擴容是有底層儲存外掛來實現的,這裡只是一個開關;
  • volumeBindingMode:表示動態建立 PV 的時間,支援 Immediate/WaitForFirstConsumer;分別表示立即建立和延遲建立。

5.png

使用者建立一個 PVC 宣告時,會在叢集尋找合適的 PV 進行繫結,如果沒有合適的 PV 與之繫結,則觸發下面流程:

  • Volume Provisioner 會 watch 到這個 PVC 的存在,若這個 PVC 定義了 StorageClassName,且 StorageClass 物件中定義的 Provisioner 外掛是自己,Provisioner 會觸發建立 PV 的流程;
  • Provisioner 根據 PVC 定義的引數(Size、VolumeMode、AccessModes)以及 StorageClass 定義的引數(ReclaimPolicy、Parameters)執行 PV 建立;
  • Provisioner 會在儲存介質端建立資料卷(通過 API 呼叫,或者其他方式),完成後會建立 PV 物件;
  • PV 建立完成後,實現與 PVC 的繫結;以滿足後續的 Pod 啟動流程。

4)延遲繫結動態資料卷

某種儲存(阿里云云盤)在掛載屬性上有所限制,只能將相同可用區的資料卷和 Node 節點進行掛載,不在同一個可用區不可以掛載。這種型別的儲存卷通常遇到如下問題:

  • 建立了 A 可用區的資料卷,但是 A 可用區的節點資源已經耗光,導致 Pod 啟動無法完成掛載;
  • 叢集管理員在規劃 PVC、PV 的時候不能確定在哪些可用區建立多個 PV 來備用。

StorageClass 中的 volumeBindingMode 欄位正是用來解決此問題,如果將 volumeBindingMode 配置為 WaitForFirstConsumer 值,則表示 Provisioner 在收到 PVC Pending 的時候不會立即進行資料卷建立,而是等待這個 PVC 被 Pod 消費的時候才執行建立流程。

其實現原理是:

  • Provisioner 在收到 PVC Pending 狀態的時候不會立即進行資料卷建立,而是等待這個 PVC 被 Pod 消費;
  • 如果有 Pod 消費此 PVC,排程器發現 PVC 是延遲繫結,則 pv 繼續完成排程功能(後續會詳細講解儲存排程);且排程器會將排程結果 patch 到 PVC 的 metadata 中;
  • 當 Provisioner 發現 PVC 中寫入了排程資訊時,會根據排程資訊獲取建立目標資料卷的位置資訊(zone、Node),並觸發 PV 的建立流程。

通過上述流程可見:延遲繫結會先讓應用負載進行排程(確定有充足的資源供 pod 使用),然後再觸發動態卷的建立流程,這樣就避免了資料卷所在可用區沒有資源的問題,也避免了儲存預規劃的不準確性問題。

在多可用區叢集環境中,更推薦使用延遲繫結的動態卷方案,目前阿里雲 ACK 叢集已經支援上述配置方案。

3. 使用示例

下面給出一個 pod 消費 PVC、PV 的例子:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: nas-pvc
spec:
  accessModes:
  - ReadWriteOnce
  resources:
    requests:
      storage: 50Gi
  selector:
    matchLabels:
      alicloud-pvname: nas-csi-pv
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: nas-csi-pv
  labels:
    alicloud-pvname: nas-csi-pv
spec:
  capacity:
    storage: 50Gi
  accessModes:
    - ReadWriteOnce
  persistentVolumeReclaimPolicy: Retain
  flexVolume:
    driver: "alicloud/nas"
    options:
      server: "***-42ad.cn-shenzhen.extreme.nas.aliyuncs.com"
      path: "/share/nas"
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: deployment-nas
  labels:
    app: nginx
spec:
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx1
        image: nginx:1.8
      - name: nginx2
        image: nginx:1.7.9
        volumeMounts:
          - name: nas-pvc
            mountPath: "/data"
      volumes:
        - name: nas-pvc
          persistentVolumeClaim:
            claimName: nas-pvc

模板解析:

  • 此應用為 Deployment 方式編排的一個 Nginx 服務,每個 pod 包含 2 個容器:nginx1、nginx2;
  • 模板中定義了 Volumes 欄位,說明期望掛載資料卷給應用使用,此例中使用了 PVC 這種資料卷定義方式;
  • 應用內部:將資料卷 nas-pvc 掛載到 nginx2容器的 /data 目錄上;nginx1 容器並沒有掛載;
  • PVC(nas-pvc)定義為一個不小於 50G 容量、讀寫方式為 ReadWriteOnce 的儲存卷需求,且對 PV 有 Label 設定的需求;
  • PV(nas-csi-pv)定義為一個容量為 50G、讀寫方式為 ReadWriteOnce、回收模式為 Retain、型別為 Flexvolume 抽象型別的儲存卷,且具有 Label 配置;

根據 PVC、PV 繫結的邏輯,此 PV 符合 PVC 消費要求,則 PVC 會和此 PV 進行繫結,並供 pod 掛載使用。

總結

此篇文章較為詳細的講述了容器儲存的整體面貌,包括單機範圍的 Docker 資料卷、和叢集式的 K8s 資料卷;K8s 資料卷更多關注的時候叢集級別的儲存編排能力,同時也在節點上實現了具體的資料卷掛載流程。K8s 為了實現上述複雜的儲存卷編排能力,其實現架構也較為複雜,下節內容我們將為您介紹 K8s 的儲存架構和實現流程。

課程推薦

為了更多開發者能夠享受到 Serverless 帶來的紅利,這一次,我們集結了 10+ 位阿里巴巴 Serverless 領域技術專家,打造出最適合開發者入門的 Serverless 公開課,讓你即學即用,輕鬆擁抱雲端計算的新正規化——Serverless。

點選即可免費觀看課程: https://developer.aliyun.com/learning/roadmap/serverless

阿里巴巴雲原生關注微服務、Serverless、容器、Service Mesh 等技術領域、聚焦雲原生流行技術趨勢、雲原生大規模的落地實踐,做最懂雲原生開發者的公眾號。”

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

相關文章