您好,登錄后才能下訂單哦!
本篇文章為大家展示了云原生存儲中的容器存儲與 K8s 存儲卷怎么理解,內容簡明扼要并且容易理解,絕對能使你眼前一亮,通過這篇文章的詳細介紹希望你能有所收獲。
云原生存儲的兩個關鍵領域:Docker 存儲卷、K8s 存儲卷;
Docker 存儲卷:容器服務在單節點的存儲組織形式,關注數據存儲、容器運行時的相關技術;
K8s 存儲卷:關注容器集群的存儲編排,從應用使用存儲的角度關注存儲服務。
容器服務之所以如此流行,一大優勢即來自于運行容器時容器鏡像的組織形式。容器通過復用容器鏡像的技術,實現在相同節點上多個容器共享一個鏡像資源(更細一點說是共享某一個鏡像層),避免了每次啟動容器時都拷貝、加載鏡像文件,這種方式既節省了主機的存儲空間,又提高了容器啟動效率。
為了提高節點存儲的使用效率,容器不光在不同運行的容器之間共享鏡像資源,而且還實現了在不同鏡像之間共享數據。共享鏡像數據的實現原理:鏡像是分層組合而成的,即一個完整的鏡像會包含多個數據層,每層數據相互疊加、覆蓋組成了最終的完整鏡像。
為了實現多個容器間共享鏡像數據,容器鏡像每一層都是只讀的。而通過實踐我們得知,使用鏡像啟動一個容器的時候,其實是可以在容器里隨意讀寫的,這是如何實現的呢?
容器使用鏡像時,在多個鏡像分層的最上面還添加了一個讀寫層。每一個容器在運行時,都會基于當前鏡像在其最上層掛載一個讀寫層,用戶針對容器的所有操作都在讀寫層中完成。一旦容器銷毀,這個讀寫層也隨之銷毀。
如上圖所示例子,一個節點上共有 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);
通過上述例子可以看到,通過容器鏡像分層實現數據共享可以大幅減少容器服務對主機存儲的資源需求。
上面給出了容器讀寫層結構,而讀寫的原則:
對于讀:容器由這么多層的數據組合而成,當不同層次的數據重復時,讀取的原則是上層數據覆蓋下層數據;
對于寫:容器修改某個文件時,都是在最上層的讀寫層進行。主要實現技術有:寫時復制、用時配置。
寫時復制(CoW:copy-on-write),表示只在需要寫時才去復制,是針對已有文件的修改場景。CoW 技術可以讓所有的容器共享 image 的文件系統,所有數據都從 image 中讀取,只有當要對文件進行寫操作時,才從 image 里把要寫的文件復制到最上面的讀寫層進行修改。所以無論有多少個容器共享同一個 image,所做的寫操作都是對從 image 中復制后在復本上進行,并不會修改 image 的源文件,且多個容器操作同一個文件,會在每個容器的文件系統里生成一個復本,每個容器修改的都是自己的復本,相互隔離,相互不影響。
用時分配:在鏡像中原本沒有某個文件的場景,只有在要新寫入一個文件時才分配空間,這樣可以提高存儲資源的利用率。比如啟動一個容器,并不會為這個容器預分配一些磁盤空間,而是當有新文件寫入時,才按需分配新空間。
存儲驅動是指如何對容器的各層數據進行管理,已達到上述需要實現共享、可讀寫的效果。即:容器存儲驅動實現了容器讀寫層數據的存儲和管理。常見的存儲驅動:
AUFS
OverlayFS
Devicemapper
Btrfs
ZFS
以 AUFS 為例,我們來講述一下存儲驅動的工作原理:
AUFS 是一種聯合文件系統(UFS),是文件級的存儲驅動。
AUFS 是一個能透明疊加一個或多個現有文件系統的層狀文件系統,把多層文件系統合并成單層表示。即:支持將不同目錄掛載到同一個虛擬文件系統下的文件系統。
可以一層一層地疊加修改文件,其底層都是只讀的,只有最上層的文件系統是可寫的。
當需要修改一個文件時,AUFS 創建該文件的一個副本,使用 CoW 將文件從只讀層復制到可寫層進行修改,結果也保存在可寫層。
在 Docker 中,底下的只讀層就是 image,可寫層就是 Container 運行時。
其他各種存儲驅動這里不再細講,有興趣的同學可以到網上查詢資料。
容器中的應用讀寫數據都是發生在容器的讀寫層,鏡像層+讀寫層映射為容器內部文件系統、負責容器內部存儲的底層架構。當我們需要容器內部應用和外部存儲進行交互時,需要一個類似于計算機 U 盤一樣的外置存儲,容器數據卷即提供了這樣的功能。
另一方面:容器本身的存儲數據都是臨時存儲,在容器銷毀的時候數據會一起刪除。而通過數據卷將外部存儲掛載到容器文件系統,應用可以引用外部數據,也可以將自己產出的數據持久化到數據卷中,所以容器數據卷是容器進行數據持久化的實現方式。
容器存儲組成:只讀層(容器鏡像) + 讀寫層 + 外置存儲(數據卷)
容器數據卷從作用范圍可以分為:單機數據卷 和 集群數據卷。單機數據卷即為容器服務在一個節點上的數據卷掛載能力,docker volume 是單機數據卷的代表實現;集群數據卷則關注的是集群級別的數據卷編排能力,K8s 數據卷則是集群數據卷的主要應用方式。
Docker Volume 是一個可供多個容器使用的目錄,它繞過 UFS,包含以下特性:
數據卷可以在容器之間共享和重用;
相比通過存儲驅動實現的可寫層,數據卷讀寫是直接對外置存儲進行讀寫,效率更高;
對數據卷的更新是對外置存儲讀寫,不會影響鏡像和容器讀寫層;
數據卷可以一直存在,直到沒有容器使用。
Bind:將主機目錄/文件直接掛載到容器內部。
需要使用主機的上的絕對路徑,且可以自動創建主機目錄;
容器可以修改掛載目錄下的任何文件,是應用更具有便捷性,但也帶來了安全隱患。
Volume:使用第三方數據卷的時候使用這種方式。
Volume命令行指令:docker volume (create/rm);
是Docker提供的功能,所以在非 docker 環境下無法使用;
分為命名數據卷和匿名數據卷,其實現是一致的,區別是匿名數據卷的名字為隨機碼;
支持數據卷驅動擴展,實現更多外部存儲類型的接入。
Tmpfs:非持久化的卷類型,存儲在內存中。
數據易丟失。
-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
-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
Docker 數據卷使用方式:
匿名數據卷:docker run –d -v /data3 nginx;
會主機上默認創建目錄:/var/lib/docker/volumes/{volume-id}/_data進行映射;
命名數據卷:docker run –d -v nas1:/data3 nginx;
如果當前找不到nas1卷,會創建一個默認類型(local)的卷。
docker run -d -v /test:/data nginx
如果主機上沒有/test目錄,則默認創建此目錄。
數據卷容器是一個運行中的容器,其他容器可以繼承此容器中的掛載數據卷,則此容器的所有掛載都會在引用容器中體現。
docker run -d —volumes-from nginx1 -v /test1:/data1 nginx
繼承所有來自配置容器的數據卷,并包含自己定義的卷。
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下面可用,反之不行;
Volume 掛載可見性:
本地空目錄、鏡像空目錄:無特殊處理;
本地空目錄、鏡像非空目錄:鏡像目錄的內容拷貝到主機;(是拷貝,不是映射;即使容器刪除內容也會保存);
本地非空目錄、鏡像空目錄:本地目錄內容映射到容器;
本地非空目錄、鏡像非空目錄:本地目錄內容映射到容器,容器目錄的內容被隱藏。
Bind 掛載可見性:以主機目錄為準。
本地空目錄、鏡像空目錄:無特殊處理;
本地空目錄、鏡像非空目錄:容器目錄變成空;
本地非空目錄、鏡像空目錄:本地目錄內容映射到容器;
本地非空目錄、鏡像非空目錄:本地目錄內容映射到容器,容器目錄的內容被隱藏。
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 編排系統中如何為運行的負載(Pod)定義存儲呢?K8s 是一個容器編排系統,其關注的是容器應用在整個集群的管理和部署形式,所以在考慮 K8s 應用存儲的時候就需要從集群角度考慮。K8s 存儲卷定義了在 K8s 系統中應用與存儲的關聯關系。其包含以下概念:
數據卷定義了外置存儲的細節,并內嵌到 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"
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 定義了具體的存儲介質。
PVC 定義的模板如下:
apiVersion: v1kind: PersistentVolumeClaimmetadata: name: disk-ssd-web-0spec: 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:將數據卷掛載成塊設備的形式供應用使用。
下面為云盤數據卷 PV 對象的編排示例:
apiVersion: v1kind: PersistentVolumemetadata: labels: failure-domain.beta.kubernetes.io/region: cn-shenzhen failure-domain.beta.kubernetes.io/zone: cn-shenzhen-e name: d-wz9g2j5qbo37r2lamkg4spec: 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 的子配置項中定義了具體的存儲類型、存儲參數。
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 出現了再進行綁定。
從上面的討論我們了解到,PVC 是針對應用服務對存儲的二次抽象,具有簡潔的存儲定義接口。而 PV 是具有繁瑣存儲細節的存儲抽象,一般有專門的集群管理人員定義、維護。
根據 PV 的創建方式可以將存儲卷分為動態存儲和靜態存儲卷:
靜態存儲卷:由管理員創建的 PV
動態存儲卷:由 Provisioner 插件創建的 PV
一般先由集群管理員分析集群中存儲需求,并預先分配一些存儲介質,同時創建對應的 PV 對象,創建好的 PV 對象等待 PVC 來消費。如果負載中定義了 PVC 需求,K8s 會通過相關規則實現 PVC 和匹配的 PV 進行綁定,這樣就實現了應用對存儲服務的訪問能力。
由集群管理員配置好后端的存儲池,并創建相應的模板(storageclass),等到有 PVC 需要消費 PV 的時候,根據 PVC 定義的需求,并參考 storageclass 的存儲細節,由 Provisioner 插件動態創建一個 PV。
兩種卷的比較:
動態存儲卷和靜態存儲卷最終的效果都是:Pod -> PVC -> PV 的使用鏈路,且對象的具體模板定義都是一致的;
動態存儲卷和靜態存儲卷區別是:動態卷是插件自動創建 PV,而靜態卷是集群管理員手動創建 PV。
提供動態存儲卷的優勢:
動態卷讓 K8s 實現了 PV 的自動化生命周期管理,PV 的創建、刪除都通過 Provisioner 完成;
自動化創建 PV 對象,減少了配置復雜度和系統管理員的工作量;
動態卷可以實現 PVC 對存儲的需求容量和 Provision 出來的 PV 容量一致,實現存儲容量規劃最優。
當用戶聲明一個 PVC 時,如果在 PVC 中添加了 StorageClassName 字段,其意圖為:當 PVC 在集群中找不到匹配的 PV 時,會根據 StorageClassName 的定義觸發相應的 Provisioner 插件創建合適的 PV 供綁定,即創建動態數據卷;動態數據卷時由 Provisioner 插件創建的,并通過 StorageClassName 與 PVC 進行關聯。
StorageClass 可譯為存儲類,表示為一個創建 PV 存儲卷的模板;在 PVC 觸發自動創建PV的過程中,即使用 StorageClass 對象中的內容進行創建。其內容包括:目標 Provisioner 名字,創建 PV 的詳細參數,回收模式等配置。
StorageClasss 模板定義如下:
apiVersion: storage.k8s.io/v1kind: StorageClassmetadata: name: alicloud-disk-topologyparameters: type: cloud_ssdprovisioner: diskplugin.csi.alibabacloud.comreclaimPolicy: DeleteallowVolumeExpansion: truevolumeBindingMode: 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 啟動流程。
某種存儲(阿里云云盤)在掛載屬性上有所限制,只能將相同可用區的數據卷和 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 集群已經支持上述配置方案。
下面給出一個 pod 消費 PVC、PV 的例子:
apiVersion: v1kind: PersistentVolumeClaimmetadata: name: nas-pvcspec: accessModes: - ReadWriteOnce resources: requests: storage: 50Gi selector: matchLabels: alicloud-pvname: nas-csi-pv---apiVersion: v1kind: PersistentVolumemetadata: name: nas-csi-pv labels: alicloud-pvname: nas-csi-pvspec: 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/v1kind: Deploymentmetadata: name: deployment-nas labels: app: nginxspec: 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 掛載使用。
上述內容就是云原生存儲中的容器存儲與 K8s 存儲卷怎么理解,你們學到知識或技能了嗎?如果還想學到更多技能或者豐富自己的知識儲備,歡迎關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。