做者 | 闞俊寶 阿里雲技術專家linux
導讀:雲原生存儲詳解系列文章將從雲原生存儲服務的概念、特色、需求、原理、使用及案例等方面,和你們一塊兒探討雲原生存儲技術新的機遇與挑戰。本文爲該系列文章的第二篇,會對容器存儲的相關概念進行講述,歡迎你們在留言區參與討論。nginx
相關文章推薦:web
雲原生存儲詳解:雲原生應用的基石 雲原生存儲詳解:容器存儲與 K8s 存儲卷docker
雲原生存儲的兩個關鍵領域:Docker 存儲卷、K8s 存儲卷;json
- Docker 存儲卷:容器服務在單節點的存儲組織形式,關注數據存儲、容器運行時的相關技術;
- K8s 存儲卷:關注容器集羣的存儲編排,從應用使用存儲的角度關注存儲服務。
Docker 存儲
容器服務之因此如此流行,一大優點即來自於運行容器時容器鏡像的組織形式。容器經過複用容器鏡像的技術,實如今相同節點上多個容器共享一個鏡像資源(更細一點說是共享某一個鏡像層),避免了每次啓動容器時都拷貝、加載鏡像文件,這種方式既節省了主機的存儲空間,又提升了容器啓動效率。後端
1. 容器讀寫層
爲了提升節點存儲的使用效率,容器不光在不一樣運行的容器之間共享鏡像資源,並且還實現了在不一樣鏡像之間共享數據。共享鏡像數據的實現原理:鏡像是分層組合而成的,即一個完整的鏡像會包含多個數據層,每層數據相互疊加、覆蓋組成了最終的完整鏡像。api
爲了實現多個容器間共享鏡像數據,容器鏡像每一層都是隻讀的。而經過實踐咱們得知,使用鏡像啓動一個容器的時候,實際上是能夠在容器裏隨意讀寫的,這是如何實現的呢?安全
容器使用鏡像時,在多個鏡像分層的最上面還添加了一個讀寫層。每個容器在運行時,都會基於當前鏡像在其最上層掛載一個讀寫層,用戶針對容器的全部操做都在讀寫層中完成。一旦容器銷燬,這個讀寫層也隨之銷燬。網絡
如上圖所示例子,一個節點上共有 3 個容器,分別基於 2 個鏡像運行。架構
鏡像存儲層說明以下:
該節點上共包含 6 個鏡像層:Layer 1~6。
- 鏡像 1 由:Layer 一、三、四、5 組成;
- 鏡像 2 由:Layer 二、三、五、6 組成。
因此兩個鏡像共享了 Layer 三、5 兩個鏡像層;
容器存儲說明:
- 容器 1:使用鏡像 1 啓動
- 容器 2:使用鏡像 1 啓動
- 容器 3:使用鏡像 2 啓動
容器 1 和容器 2 共享鏡像 1,且每一個容器有本身的可寫層; 容器 1(2)和容器 3 共享鏡像 2 個層(Layer三、5);
經過上述例子能夠看到,經過容器鏡像分層實現數據共享能夠大幅減小容器服務對主機存儲的資源需求。
上面給出了容器讀寫層結構,而讀寫的原則:
對於讀:容器由這麼多層的數據組合而成,當不一樣層次的數據重複時,讀取的原則是上層數據覆蓋下層數據; 對於寫:容器修改某個文件時,都是在最上層的讀寫層進行。主要實現技術有:寫時複製、用時配置。
1)寫時複製
寫時複製(CoW:copy-on-write),表示只在須要寫時纔去複製,是針對已有文件的修改場景。CoW 技術可讓全部的容器共享 image 的文件系統,全部數據都從 image 中讀取,只有當要對文件進行寫操做時,才從 image 裏把要寫的文件複製到最上面的讀寫層進行修改。因此不管有多少個容器共享同一個 image,所作的寫操做都是對從 image 中複製後在複本上進行,並不會修改 image 的源文件,且多個容器操做同一個文件,會在每一個容器的文件系統裏生成一個複本,每一個容器修改的都是本身的複本,相互隔離,相互不影響。
2)用時配置
用時分配:在鏡像中本來沒有某個文件的場景,只有在要新寫入一個文件時才分配空間,這樣能夠提升存儲資源的利用率。好比啓動一個容器,並不會爲這個容器預分配一些磁盤空間,而是當有新文件寫入時,才按需分配新空間。
2. 存儲驅動
存儲驅動是指如何對容器的各層數據進行管理,已達到上述須要實現共享、可讀寫的效果。即:容器存儲驅動實現了容器讀寫層數據的存儲和管理。常見的存儲驅動:
- AUFS
- OverlayFS
- Devicemapper
- Btrfs
- ZFS
以 AUFS 爲例,咱們來說述一下存儲驅動的工做原理:
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,能夠爲每種存儲類型開發一種存儲插件。
- 單個節點上能夠部署多個存儲插件;
- 一個存儲插件負責一種存儲類型的掛載服務。
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 的關聯關係爲:
從實現上看,只要有了 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;分別表示當即建立和延遲建立。
用戶建立一個 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 個容器:nginx一、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 等技術領域、聚焦雲原生流行技術趨勢、雲原生大規模的落地實踐,作最懂雲原生開發者的公衆號。」