k8s中的Volume體系與存儲架構


一、容器中使用Volume

1、作為文件系統掛載

容器中通過volumeMounts字段使用Volume作為文件系統掛載:
(1)name字段指定使用哪個Volume;
(2)mountPath字段指定Volume在容器中的掛載路徑;
(3)readOnly字段指定掛載的Volume是否只讀;
(4)多個容器共享Volume時,可以隔離不同容器在Volume上數據存儲的路徑
subPath直接指定子目錄的名字,
subPathExpr則指定${XXX},通過環境變量獲取子目錄的名字
這兩個字段都默認為空,不能同時配置
(5)通過mountPropagation配置掛載傳播
None(默認):此Volume掛載不會接收到任何后續掛載到該Volume(或子目錄)下的掛載
HostToContainer:此Volume掛載將會接收到任何后續掛載到該Volume(或子目錄)下的掛載
Bidirectional:類似HostToContainer,但容器創建的Volume掛載都將傳播回主機和所有使用相同Volume的容器
三種掛載傳播機制分別對應了Linux內核mount namespace的private、rslave、rshared傳播機制

 2、作為塊設備掛載

容器中通過volumeDevices字段使用Volume作為塊設備掛載
(1)name字段指定使用哪個Volume
(2)devicePath指定volume在容器中的掛載路徑

二、Pod中聲明Volume

Pod在spec.volumes中聲明掛載了哪些Volume,VolumeSource大體分為以下幾類:

1、本地存儲

(1)emptyDir:在Pod創建過程中創建的一個臨時目錄,這個目錄里的數據隨着Pod刪除也會被清除
存儲介質包括內存(基於RAM的temfs)、磁盤等、巨頁三種
目前也支持設置SizeLimit
存儲介質為內存的Volume會同時受SizeLimit和Pod內所有容器總內存limit的限制,都沒設的話就會限制為主機內存的一半
(2)hostPath:宿主機上的一個路徑,在Pod刪除之后還存在。路徑如果是軟鏈接的話會自動轉向實際路徑。
種類包括Directory、File、Socket、CharDevice、BlockDevice五種
考慮到Directory、File在主機上可能不存在,可以設置為DirectoryOrCreate、FileOrCreate
 

2、網絡存儲

如NFS、CephFS、RBD、 ISCSI等
 

3、雲廠商提供的存儲

例如在vSphere平台上部署Kubernetes時,可以使用vSphereVolume作為后端存儲
在GCE可使用GCEPersistentDisk
在AWS上可以使用AWSElasticBlockStore
在Azure可以使用AzureFile
......
 

4、配置文件 

(1)DownwardAPI可以將Pod API對象的部分字段以臨時文件的形式注入到容器中
(2)可以將ConfigMap/Secret以持久化Volume的形式掛載到容器中,讓容器中的程序可以通過POSIX接口來訪問配置數據;也支持僅將它們的一部分以臨時文件的形式注入到容器中
(3)Projected可以將配置信息的一部分,以臨時文件的形式注入到容器中
包括Configmap、Secret、DownwardAPI、ServiceAccount Token四種
例如:將Pod的metadata.labels中的內容,以labels這個文件名掛載到mountPath下
  volumes:
    - name: podinfo
      projected:
        sources:
        - downwardAPI:
            items:
              - path: "labels"
                fieldRef:
                  fieldPath: metadata.labels

5、CSI和FlexVolume

CSIVolumeSource的數據結構:
  • Driver
根據Driver name指定由哪個CSI Plugin來處理該Volume
  • ReadyOnly
  • FsType
希望掛載的文件系統種類,如ext4、xfs等
  • VolumeAttribute
用於附加參數,如PV定義的是OSS,就可以在這里定義bucket、訪問的地址等信息
  • NodePublishSecretRef
指定一個Secret的Name
調用CSI Plugin的NodePublishVolume/NodeUnpublishVolume接口時,使用該Secret傳遞敏感信息
 
FlexVolumeSource的數據結構:
  • Driver
  • ReadyOnly
  • FsType
  • SecretRef
  • Options
 
由於是在Pod中聲明的,所以其實是一種臨時Volume。Volume需要和Pod一起生成,並且作為Pod的一部分,和Pod一起終結。
 

6、PVC

Pod中聲明的Volume,生命周期與Pod相同。 k8s又引入了PV(Persistent Volumes)概念,它可以將存儲和計算分離,通過不同的組件來管理存儲資源和計算資源,解耦Pod和Volume之間生命周期的關聯。
當Pod刪除后,它使用的PV仍然存在,還可以被新建的Pod復用。
用戶只需在 PVC(PersistentVolumeClaim)中聲明自己需要的Resources、AccessModes等需求,而不需要關心存儲實現細節。
PV才是存儲的實際信息的承載體。PV和對應的后端存儲信息交由集群管理員統一運維和管控,安全策略更容易控制。
 

PersistentVolumeSpec的數據結構

  • Capacity
使用map[ResourceName]resource.Quantity來描述資源量
一般只設置一對ResourceName為storage、resource.Quantity為xxx Gi的kv,表明創建的存儲大小
  • PersistentVolumeSource
該PV真正的Source
和Pod中直接聲明Volume所支持的Source種類幾乎一樣(只是少了emptyDir、配置文件的四種、Ephemeral和自己) 
  • AccessModes
PV訪問策略控制列表,表明創建出來的存儲的訪問方式
    ReadWriteOnce只允許單node上的Pod訪問
    ReadOnlyMany允許多個node的Pod只讀訪問
    ReadWriteMany允許多個node上的Pod讀寫訪問
  • VolumeMode
Volume將被如何掛載,包括Block和Filesystem兩種
  • ClaimRef
記錄着PVC的綁定信息
  • PersistentVolumeReclaimPolicy
表明該Volume被release后(即與之綁定的PVC被刪除后)的回收策略
    Delete:Volume被released后直接刪除,需要Volume plugin支持
    Retain:默認策略,由系統管理員來手動管理該Volume。
  • StorageClassName
  • MountOptions
傳遞掛載選項的String切片
PS:傳遞過程不會做驗證,如果Volume plugin不支持某個Options,則分配操作可能會失敗。
  • NodeAffinity
限制了可以訪問該Volumes的node,對使用該Volume的Pod調度有影響
 

PersistentVolumeClaimSpec的數據結構

  • AccessModes
  • Selector
通過標簽選擇器選擇一組PV
  • VolumeName
直接指定要綁定的PV名
  • Resources
包括request和limit兩種,使用map[ResourceName]resource.Quantity來描述資源需求量
  • StorageClassName
  • VolumeMode
  • DataSource

 

快照體系

k8s引入了VolumeSnapshotContent/VolumeSnapshot/VolumeSnapshotClass體系,以支持存儲快照功能。不過目前只有CSI支持。
(1)首先創建VolumeSnapshotClass
apiVersion: snapshot.storage.k8s.io/v1a1pha1
kind: VolumeSnapshotClass
metadata:
    name: disk-snapshotclass
snapshotter: xxxxxx
snapshotter指定了真正創建存儲快照所使用的Volume Plugin
(2)通過Volumesnapshot聲明創建存儲快照:
apiVersion: snapshot.storage.k8s.io/v1a1pha1
kind: VolumeSnapshot
metadata:
    name:disk-snapshot
    namespace: xxx
spec:
    snapshotClassName: disk-snapshotclass
    source:
        name: disk-pvc
        kind: PersistentVolumeClaim
.spec.snapshotClassName指定了使用的VolumeSnapshotClass
.spec.source.name指定了作為數據源的PVC
 
提交VolumeSnapshot對象后,集群中的相關組件會找到數據源PVC對應的PV存儲,對這個PV存儲做一次快照。並自動創建Volumesnapshotcontent對象。
Volumesnapshotcontent對象中記錄了雲存儲廠商返回的snapshot的ID、作為數據源的PVC
刪除VolumeSnapshot后Volumesnapshotcontent也會自動刪除
(3)數據恢復時,將PVC對象的DataSource指定為VolumeSnapshot對象。
這樣當PVC提交之后,會由集群中的相關組件找到dataSource所指向的VolumeSnapshotContent,然后新創建對應的存儲以及PV,將存儲快照數據恢復到新的PV中。
 

綁定

通過kube-controller-manager中的PV Controller將PV和合適的PVC綁定到一起,綁定有三種方式:

(1)靜態產生方式 - Static Volume Provisioning

集群管理員事先規划用戶怎樣使用存儲,並預分配一些存儲(即預創建一些 PV)
用戶在使用存儲的時候,需要先提交自己的存儲需求(即PVC)
K8s內部相關組件會根據PVC的size和 accessMode ,判斷它跟靜態創建的哪個PV匹配,然后綁定到一起。
注:一個PV可以設置多個訪問策略。PVC與PV綁定時,PV Controller會優先找到AccessModes列表最短並且匹配PVC AccessModes列表的PV集合,然后從該集合中找到Capacity最小且符合PVC size需求的PV對象
之后,用戶通過Pod使用存儲時,可以在volumes里聲明要用哪個PVC。 Pod通過該PVC找到綁定的PV。

(2)動態產生方式 - Dynamic Volume Provisioning

集群管理員不預分配PV,而是預先准備StoregeClass(創建PV的模板),它包含了創建某種具體類型(塊存儲、文件存儲等)的PV所需要的參數信息。
StorageClass為管理員提供了描述存儲 "類" 的方法,其 數據結構:
  • metav1.TypeMeta
  • metav1.ObjectMeta
  • Provisioner
指定了使用哪個 Volume Plugin的 Provisioner( 存儲分配器)來進行 PV的provision/delete操作。
其中,Internal Provisioner的 名稱前綴為 kubernetes.io 並打包在Kubernetes中(由PV Controller進行此工作)
Volume Plugin
Internal Provisioner
Config Example
AWSElasticBlockStore
AzureFile
AzureDisk
CephFS
-
-
Cinder
FC
-
-
FlexVolume
-
-
Flocker
-
GCEPersistentDisk
Glusterfs
iSCSI
-
-
Quobyte
NFS
-
-
RBD
VsphereVolume
PortworxVolume
ScaleIO
StorageOS
Local
-
  • ReclaimPolicy
由SC 動態創建出來的PV 將使用該 ReclaimPolicy
  • AllowVolumeExpansion
若設置為true,且底層 Volume Plugin 支持卷擴展,可以編輯 PVC對象來擴大 (不能縮小) Volume 大小
  • MountOptions
由SC動態創建的PV將使用該 MountOptions
  • VolumeBindingMode
控制了 卷綁定和動態分配的發生時間:
    Immediate模式表示一旦創建了PVC,也就完成了卷的動態分配和綁定。對於由於拓撲限制而非集群所有節點可達的存儲后端,PV會在不知道Pod調度要求的情況下分配、綁定。
    WaitForFirstConsumer模式將延遲PV的分配和綁定,直到使用該PVC的Pod被創建。PV再根據Pod的調度結果分配和綁定。
  • AllowedTopologies
指定了允許的拓撲結構
VolumeBindingMode配置為WaitForFirstConsumer模式的情況下一般無需再配置此字段,除非除了Pod調度, 動態創建的PV也有拓撲位置的限制。
此時需要同時配置 WaitForFirstConsumer和 AllowedTopologies,這樣既限制了 動態創建的PV 要能被這個可用區訪問、也限制了Pod在選擇node時 要落在這個可用區內。
  • Parameters
以key:values的形式描述屬於Volume的參數,最多可定義512個,總長度不超過256KB
PV分配器可以接受哪些參數, 需要 后端存儲的提供者提供。
該產生方式下, PVC配置方式不變,只需在PVC中指定需要使用的StoregeClass模板
此時,若 PVC 找不到相應的 PV ,就會用指定的 StorageClass模板 去動態創建 PV, 並將PVC和PV進行綁定 ;存在一個滿足條件的 PV 時,就會直接使用現有的 PV

(3)靜態產生方式下滿足PV的拓撲限制

Local PV一般通過靜態創建的方式,先要聲明PV對象,在PV對象中通過nodeAffinity來給這個PV加上拓撲限制,限制其只能在單node上訪問
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv-local
spec:
  capacity:
    storage: 5Gi
  volumeMode: Filesystem
  accessModes:
  - ReadWriteOnce
  persistentVolumeReclaimPolicy: Delete
  storageClassName: local-storage
  local:
    path: /data
  nodeAffinity:
    required:
      nodeSelectorTerms:
      - matchExpressions:
        - key: kubernetes.io/hostname
          operator: In
          values:
          - cn-beijing.192.168.1.147
雖然是靜態產生方式,仍然需要創建SC
kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
    name:lacal-storage
provisioner: kubernetes.io/noprovisioner
volumeBindingMode:WaitFirstConsumer
provisioner指定為no-provisioner,目的是告訴PV Controller遇到該PVC時無需進行任何操作
VolumeBindingMode需要設置為WaitForFirstConsumer。 用戶提交PVC后,PV Controller找到相應的SC,發現BindingMode是延遲綁定,就不會做任何事情。
 
當真正使用這個PVC的Pod,在調度的時恰好調度在符合PV拓撲要求的節點上之后,Pod里使用的PVC才會真正與PV做綁定。這樣就能保證最終創建出來的Pod能訪問這塊Local PV。
 

PV和PVC的狀態流轉:

創建PV對象后,它會暫時處於pending狀態。等真正的PV創建好之后,它處在available狀態(可以使用)。
創建PVC對象后,它會暫時處於pending狀態,直到PV和PVC就結合到一起,此時兩者都處在bound狀態。
當用戶在使用完PVC將其刪除后,PV處於released狀態,依賴於ReclaimPolicy配置被刪除或保留。在默認的 Retain策略下,保留的 PV無法從released狀態回到available狀態(即無法被一個新的PVC綁定),需要:
  1. 新建一個PV對象,把之前的released的PV的相關字段的信息填到新的PV對象里面,這個PV就可以結合新的PVC了;
  2. 刪除Pod之后不刪除PVC對象,這樣給PV綁定的PVC還存在。下次Pod使用的時候,就可以直接通過PVC去復用。(K8s中的StatefulSet管理的Pod帶存儲的遷移就是通過這種方式)。
  3. 從PV的spec.claimRef字段刪除PVC綁定信息,即可重新釋放PV從而達到available。
處於Bound狀態的PVC,與其關聯的PV被刪除后,變成Lost狀態;重新與PV綁定后變成Bound狀態。
 

7、ephemeral

區別於Persistent Volumes,k8s又提出了 Ephemeral Volumes
其類似 emptyDir,可以為每個Pod提供臨時的數據存放目錄
由於是通過 volumeClaimTemplate聲明的,其實是一種通用臨時卷,可以由所有支持PV的存儲所提供
spec:
  volumes:
    - name: scratch-volume
      ephemeral:
         volumeClaimTemplate:
          metadata:
            labels:
              type: my-frontend-volume
          spec:
            accessModes: [ "ReadWriteOnce" ]
            storageClassName: "scratch-storage-class"
            resources:
              requests:
                storage: 1Gi

 

三、Volume掛載流程

(1)PV Controller

負責PV/PVC的綁定、生命周期管理,並根據需求進行數據卷的Provision/Delete操作。
主要有兩個實現邏輯:
①ClaimWorker實現PVC的狀態遷移:
②VolumeWorker實現PV的狀態遷移:

(2)Attach/Detach Controller

負責存儲設備的Attach/Detach操作,將設備掛載到目標節點
有兩個核心對象:
下圖是AD Controller實現的邏輯框圖:
對於使用了CSI Plugin的PV/PVC,PV Controller和AD Controller不會進行Provision/Delete和Attach/Detach,只會進行一些輔助工作,詳見container storage interface

(3)Volume manager

Volume Manager實際是Kubelet中 眾多Manager中的一個 。它主要是用來做本節點Volume的Attach/Detach/Mount/Unmount操作 、卷設備的格式化
邏輯上和AD Controller類似, 一樣包含有desireStateofWorld以及actualStateofWorld, 通過desiredStateOfWorldPopulator進行數據的同步、通過Reconciler進行接口的調用。
Kubelet啟動參數 "--enable-controller-attach-detach" 為True, Attach/Detach操作 由AD Controller來控制;若為False,則由Volume Manager來做(一般不需要,后續可能刪除此功能)。
下圖是 Volume Manager 實現邏輯圖:

(4)Volume Plugins

PV Controller、AD Controller、Volume Manager主要是進行操作的調用,而具體操作則是由Volume Plugins實現的。
根據源碼的位置可將Volume Plugins分為In-Tree和Out-of-Tree兩類:
  • Out-of-Tree類的Volume Plugins的代碼放在Kubernetes內部,和Kubernetes一起發布、管理與迭代,缺點是迭代速度慢、靈活性差;
  • Out-of-Tree類的Volume Plugins的代碼獨立於Kubernetes,它是由存儲商提供實現的,目前主要有Flexvolume和CSI兩種實現機制。通過抽象接口將不同存儲的driver實現從k8s代碼倉庫中剝離,是社區主推的一種實現網絡存儲插件的方式。

Volume Plugins的發現

Kubernetes會在PV Controller、AD Controller以及Volume Manager中做插件管理。通過VolumePlguinMg對象進行管理。主要包含Plugins和Prober兩個數據結構。
Plugins主要是用來保存Plugins列表的一個對象,而Prober是一個探針,用於發現新的Plugin
下圖是插件管理的整個過程:

參考資料:

[1] https://kubernetes.io/docs/home/

[2] https://edu.aliyun.com/roadmap/cloudnative

[3] 鄭東旭《Kubernetes源碼剖析》


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM