Kubernetes-儲存(二)
前言
本篇是Kubernetes第十三篇,大家一定要把環境搭建起來,看是解決不了問題的,必須實戰。
Kubernetes系列文章:
Kubernetes介紹 Kubernetes環境搭建 Kubernetes-kubectl介紹 Kubernetes-Pod介紹(-) Kubernetes-Pod介紹(二)-生命週期 Kubernetes-Pod介紹(三)-Pod排程 Kubernetes-Pod介紹(四)-Deployment Kubernetes-Service介紹(一)-基本概念 Kubernetes-Service介紹(二)-服務發現 Kubernetes-Service介紹(三)-Ingress(含最新版安裝踩坑實踐) Kubernetes-網路 Kubernetes-儲存(一)
發展歷程
為了能夠遮蔽底層儲存實現的細節,便於使用和管理,Kubernetes從1.0版本就引入PersistentVolume(PV)和PersistentVolumeClaim(PVC)兩個資源物件來實現對儲存的管理子系統。
PV是對底層網路共享儲存的抽象,將共享儲存定義為一種“資源”,比如Node也是一種容器應用可以“消費”的資源。PV由管理員建立和配置,它與共享儲存的具體實現直接相關,例如GlusterFS、iSCSI、RBD或GCE或AWS公有云提供的共享儲存,通過外掛式的機制完成與共享儲存的對接,以供應用訪問和使用。
PVC則是使用者對儲存資源的一個“申請”。就像Pod“消費”Node的資源一樣,PVC能夠“消費”PV資源。PVC可以申請特定的儲存空間和訪問模式。
若使用PVC“申請”到一定的儲存空間仍然不能滿足應用對儲存裝置的需求。比如通常應用程式都會對儲存裝置的特性和效能有不同的要求,包括讀寫速度、併發效能、資料冗餘等更高的要求,因此Kubernetes從1.4版本開始引入了一個新的資源物件StorageClass,用於標記儲存資源的特性和效能。
Kubernetes 1.6版本時,StorageClass和動態資源供應的機制得到了完善,實現了儲存卷的按需建立。通過StorageClass的定義,管理員可以將儲存資源定義為某種類別(Class),正如儲存裝置對於自身的配置描述(Profile),例如“快速儲存”“慢速儲存”“有資料冗餘”“無資料冗餘”等。使用者根據StorageClass的描述就能夠直觀地得知各種儲存資源的特性,就可以根據應用對儲存資源的需求去申請儲存資源。
Kubernetes從1.9版本開始引入容器儲存介面Container Storage Interface(CSI)機制,目標是在Kubernetes和外部儲存系統之間建立一套標準的儲存管理介面,通過該介面為容器提供儲存服務,類似於CRI(容器執行時介面)和CNI(容器網路介面)。
PV和PVC使用介紹
對於PV和PVC使用有兩種使用方式,一種靜態方式建立,一種是動態方式建立;
靜態方式建立
我們建立一個 hostPath 型別的 PersistentVolume。Kubernetes 支援 hostPath 型別的 PersistentVolume 使用節點上的檔案或目錄來模擬附帶網路的儲存,但是需要注意的是在生產叢集中,我們不會使用 hostPath,叢集管理員會提供網路儲存資源,比如 NFS 共享卷或 Ceph 儲存卷,叢集管理員還可以使用 StorageClasses 來設定動態提供儲存。因為 Pod 並不是始終固定在某個節點上面的,所以要使用 hostPath 的話我們就需要將 Pod 固定在某個節點上,這樣顯然降低了應用的容錯性。
我們在demo-slave-2節點上建立如下目錄/data/k8s/hostpath/index.html;
<!DOCTYPEhtml>
<html>
<head>
<title>helloworld</title>
</head>
<body>
<p>helloworld</p>
</body>
</html>
建立一個 hostPath 型別的 PV 資源物件,配置檔案中指定了該卷位於叢集節點上的 /data/k8s/hostpath 目錄,還指定了 10G 大小的空間和 ReadWriteOnce 的訪問模式,定義了名稱為 manual 的 StorageClass,該名稱用來將 PersistentVolumeClaim 請求繫結到該 PersistentVolum;
apiVersion:v1
kind:PersistentVolume
metadata:
name:pv-hostpath
labels:
type:local
spec:
capacity:
storage:10Gi
accessModes:
-ReadWriteOnce
storageClassName:slow
hostPath:
path:"/data/k8s/hostpath"
建立PV資源;
kubectlapply-fpv-hostpath.yaml
檢視PersistentVolume的狀態,PersistentVolume的狀態為Available,表示可用狀態,還未被任何 PVC 繫結,Reclaim Policy的狀態為Retain,表示保留資料,需要管理員手工清理資料;
kubectlgetpvpv-hostpath
建立一個對應的PVC來和PV進行繫結;
apiVersion:v1
kind:PersistentVolumeClaim
metadata:
name:pvc-hostpath
spec:
resources:
requests:
storage:1Gi
storageClassName:slow
accessModes:
-ReadWriteOnce
建立 PVC 之後,Kubernetes 就會去查詢滿足我們宣告要求的PV;
kubectlcreate-fpvc-hostpath.yaml
接下來我們再次檢視 PV 的資訊,我們會發現PV已經處於被繫結狀態;
接下來我們也看下PVC的資訊,我們會發現PVC已經處於被繫結狀態;
接下來我們建立Pod,宣告的PVC作為儲存卷,我們通過nodeSelector繫結demo-slave-2節點;
apiVersion:v1
kind:Pod
metadata:
name:pv-hostpath-pod
spec:
volumes:
-name:pv-hostpath
persistentVolumeClaim:
claimName:pvc-hostpath
nodeSelector:
kubernetes.io/hostname:demo-slave-2
containers:
-name:pv-hostpath-pod
image:nginx
ports:
-containerPort:80
volumeMounts:
-mountPath:"/usr/share/nginx/html"
name:pv-hostpath
檢視Pod建立的狀態;
kubectlgetpodpv-hostpath-pod
進入容器驗證是否Pod是否從hostPath卷提供index.html檔案,我們可以看到輸出結果是我們前面寫到 hostPath卷種的index.html 檔案中的內容;
#進入容器
kubectlexec-itpv-hostpath-pod--/bin/bash
#安裝curl
apt-getupdate
apt-getinstallcurl-y
#訪問
curllocalhost
動態方式建立
一個大規模的 Kubernetes 叢集裡很可能有成千上萬個 PVC,這就意味著運維人員必須得事先創建出成千上萬個 PV。更麻煩的是,隨著新的 PVC 不斷被提交,運維人員就不得不繼續新增新的、能滿足條件的 PV,否則新的 Pod 就會因為 PVC 繫結不到 PV 而失敗。在實際操作中,這幾乎沒辦法靠人工做,Kubernetes 為我們提供了一套可以自動建立 PV 的機制,這裡我們採用Local Persistent Volum先做簡單實驗,但是Local Persistent Volum不支援StorageClass動態繫結,這裡我們首先建立PV,先講明白用法,當使用Kubernetes的支援的持久卷的是不需要建立PV的,動態建立的流程如下:
實驗前Local Persistent Volum建立準備:
apiVersion:v1
kind:PersistentVolume
metadata:
name:pv-local
spec:
capacity:
storage:10Gi
volumeMode:Filesystem
accessModes:
-ReadWriteOnce
persistentVolumeReclaimPolicy:Delete
storageClassName:local-storage
local:
path:"/data/k8s/local"
nodeAffinity:
required:
nodeSelectorTerms:
-matchExpressions:
-key:kubernetes.io/hostname
operator:In
values:
-demo-slave-1
建立StorageClass物件,StorageClass配置了volumeBindingMode=WaitForFirstConsumer,告訴 Kubernetes在發現這個StorageClass關聯的PVC與PV進行的延遲繫結,通過這個延遲繫結機制;
apiVersion:storage.k8s.io/v1
kind:StorageClass
metadata:
name:local-storage
provisioner:kubernetes.io/no-provisioner
volumeBindingMode:WaitForFirstConsumer
檢視StorageClass建立情況;
kubectlapply-fpv-local.yaml
建立PVC資源;
kind:PersistentVolumeClaim
apiVersion:v1
metadata:
name:pvc-local
spec:
accessModes:
-ReadWriteOnce
resources:
requests:
storage:2Gi
storageClassName:local-storage
檢查繫結狀況,PVC處於Pending狀態,也就是等待繫結的狀態,原本實時發生的 PVC 和 PV 的繫結過程,就被延遲到了 Pod 第一次排程的時候在排程器中進行,從而保證了這個繫結結果不會影響 Pod 的正常排程;
kubectlgetpvc
建立一個Pod來使用pvc-local這個PVC;
apiVersion:v1
kind:Pod
metadata:
name:pv-local-pod
spec:
volumes:
-name:pv-local-demo
persistentVolumeClaim:
claimName:pvc-local
containers:
-name:pv-local-pod
image:nginx
ports:
-containerPort:80
volumeMounts:
-mountPath:/usr/share/nginx/html
name:pv-local-demo
檢視PVC狀況,這個時候我們會發現被綁定了;
核心概念介紹
PV
PV是對儲存資源的抽象,將儲存定義為容器可以使用的資源,管理員進行建立和配置。主要包括儲存能力、訪問模式、儲存型別、回收策略、後端儲存型別等關鍵資訊的設定。
生命週期
PV在生命週期中可能處於以下4個階段:
Available:可用狀態,還未與某個PVC繫結;
Bound:已與某個PVC繫結;
Released:繫結的PVC已經刪除,資源已釋放,但沒有被叢集回收;
Failed:資源回收失敗;
建立好一個 PV 以後,我們就處於一個 Available 的狀態,當一個 PVC 和一個 PV 繫結的時候,這個 PV 就進入了 Bound 的狀態,此時如果我們把 PVC 刪掉,Bound 狀態的 PV 就會進入 Released 的狀態。
一個 Released 狀態的 PV 會根據自己定義的 ReclaimPolicy 欄位來決定自己是進入一個 Available 的狀態還是進入一個 Deleted 的狀態。如果 ReclaimPolicy 定義的是 recycle型別,它會進入一個Available狀態,如果轉變失敗,就會進入 Failed 的狀態。
PVC
PVC是使用者對儲存資源的一個申請,PVC消耗PV的資源。PVC可以申請儲存大小和訪問模式等。
生命週期介紹
PVC在生命週期中可能處於以下3個階段:
Pending:等待與PV進行繫結;
Bound: PVC找到對應PV進行繫結;
Lost: 繫結狀態的PVC,PV被刪除掉;
一個建立好的 PVC 會處於 Pending 狀態,當一個 PVC 與 PV 繫結之後,PVC 就會進入 Bound 的狀態,當一個 Bound 狀態的 PVC 的 PV 被刪掉之後,該 PVC 就會進入一個 Lost 的狀態。對於一個 Lost 狀態的 PVC,它的 PV 如果又被重新建立,並且重新與該 PVC 繫結之後,該 PVC 就會重新回到 Bound 狀態。
StorageClass
StorageClass作為對儲存資源的抽象定義,充當 PV 的模板,對使用者設定的PVC申請遮蔽後端儲存的細節,既減少了使用者對於儲存資源細節的關注,又減輕了管理員手工管理PV的工作,由系統自動完成PV的建立和繫結,實現了動態的資源供應。StorageClass的定義主要包括名稱、後端儲存的提供者、後端儲存的相關引數配置以及回收策略。
CSI
CSI是什麼
CSI是Container Storage Interface(容器儲存介面)的簡寫。CSI的目的是定義行業標準容器儲存介面,使儲存供應商能夠開發一個符合CSI標準的外掛並使其可以在多個容器編排系統中工作。Kubernetes將通過CSI介面來跟第三方儲存廠商進行通訊,來操作儲存,從而提供容器儲存服務。
為什麼要有CSI
其實在沒有CSI之前Kubernetes就已經提供了強大的儲存卷外掛系統,但是這些外掛系統實現是Kubernetes程式碼的一部分,需要隨Kubernetes元件二進位制檔案一起釋出,這樣就會存在一些問題。
如果第三方儲存廠商發現有問題需要修復或者優化,即使修復後也不能單獨釋出,需要與Kubernetes一起釋出,對於k8s本身而言,不僅要考慮自身的正常迭代發版,還需要考慮到第三方儲存廠商的迭代發版,這裡就存在雙方互相依賴、制約的問題,不利於雙方快速迭代; 另外第三方廠商的程式碼跟Kubernetes程式碼耦合在一起,還會引起安全性、可靠性問題,還增加了Kubernetes程式碼的複雜度以及後期的維護成本等等。
基於以上問題,Kubernetes將儲存體系抽象出了外部儲存元件介面即CSI,Kubernetes通過grpc介面與第三方儲存廠商的儲存卷外掛系統進行通訊。
這樣一來,對於第三方儲存廠商來說,既可以單獨釋出和部署自己的儲存外掛,進行正常迭代,而又無需接觸Kubernetes核心程式碼,降低了開發的複雜度。同時,對於Kubernetes來說,這樣不僅降低了自身的維護成本,還能為使用者提供更多的儲存選項。
CSI系統架構
核心概念介紹
volume plugin
擴充套件各種儲存型別的卷的管理能力,實現第三方儲存的各種操作能力與Kubernetes儲存系統的結合。呼叫第三方儲存的介面或命令,從而提供資料卷的建立/刪除、attach/detach、mount/umount的具體操作實現,可以認為是第三方儲存的代理人。根據原始碼所在位置,volume plugin分為in-tree與out-of-tree。
in-tree
在Kubernetes原始碼內部實現,和Kubernetes一起釋出、管理,更新迭代慢、靈活性差;
out-of-tree
程式碼獨立於Kubernetes,由儲存廠商實現,有CSI、FlexVolume兩種實現;
CSI-plugin
Kubernetes獨立拆分出來,實現 CSI 標準規範介面的邏輯控制與呼叫,是整個 CSI 控制邏輯的核心樞紐;
Node-driver-registrar
一個由官方 Kubernetes維護的輔助容器(sidecar),它使用 kubelet 外掛註冊機制向 kubelet 註冊外掛,需要請求 CSI 外掛的 Identity 服務來獲取外掛資訊;
external-provisioner
一個由官方 Kubernetes維護的輔助容器(sidecar),主要功能是實現持久卷的建立(Create)、刪除(Delete);
external-attacher
一個由官方 Kubernetes維護的輔助容器(sidecar),主要功能是實現持久卷的附著(Attach)、分離(Detach);
external-snapshotter
一個由官方 Kubernetes小組維護的輔助容器(sidecar),主要功能是實現持久卷的快照(VolumeSnapshot)、備份恢復等能力;
external-resizer
一個由官方Kubernetes小組維護的輔助容器(sidecar),主要功能是實現持久卷的彈性擴縮容,需要雲廠商外掛提供相應的能力;
kube-controller-manager
Kubernetes資源控制器,主要通過 PV Controller, AttachDetach 實現持久卷的繫結(Bound)/解綁(Unbound)、附著(Attach)/分離(Detach);
PV Controller
負責pv、pvc的繫結與生命週期管理(如建立/刪除底層儲存,建立/刪除pv物件,pv與pvc物件的狀態變更);
in-tree:建立/刪除底層儲存、建立/刪除pv物件的操作,由PV controller呼叫volume plugin(in-tree)來完成;
out-tree CSI:建立/刪除底層儲存、建立/刪除pv物件的操作由external-provisioner與csi plugin共同來完成;
AD Controller
AD Cotroller全稱Attachment/Detachment 控制器,主要負責建立、刪除VolumeAttachment物件,並呼叫volume plugin來做儲存裝置的Attach/Detach操作(將資料卷掛載到特定node節點上/從特定node節點上解除掛載),以及更新node.Status.VolumesAttached等;
不同的volume plugin的Attach/Detach操作邏輯有所不同,對於csi plugin(out-tree volume plugin)來說,AD controller的Attach/Detach操作只是修改VolumeAttachment物件的狀態,而不會真正的將資料卷掛載到節點/從節點上解除掛載,真正的節點儲存掛載/解除掛載操作由kubelet中volume manager呼叫csi plugin來完成;
volume manager
主要是管理卷的Attach/Detach(與AD controller作用相同,通過kubelet啟動引數控制哪個元件來做該操作)、mount/umount等操作;
對於CSI來說,volume manager的Attach/Detach操作只建立/刪除VolumeAttachment物件,而不會真正的將資料卷掛載到節點/從節點上解除掛載;csi-attacer元件也不會做掛載/解除掛載操作,只是更新VolumeAttachment物件,真正的節點儲存掛載/解除掛載操作由kubelet中volume manager呼叫呼叫csi plugin來完成。
Kubernetes建立與掛載Volume
in-tree
使用者建立PVC;
PV Controller watch到PVC的建立,尋找合適的PV與之繫結;
當找不到合適的PV時,將呼叫volume plugin來建立volume,並建立PV物件,之後該PV物件與PVC物件繫結;
使用者建立掛載PVC的Pod;
kube-scheduler watch到Pod的建立,為其尋找合適的Node排程;
Pod排程完成後,AD controller/volume manager watch到Pod宣告的volume沒有進行attach操作,將呼叫volume plugin來做attach操作;
volume plugin進行attach操作,將volume掛載到pod所在node節點,成為如/dev/vdb的裝置;
attach操作完成後,volume manager watch到Pod宣告的volume沒有進行mount操作,將呼叫volume plugin來做mount操作;
volume plugin進行mount操作,將Node節點上的得到的/dev/vdb裝置掛載到指定目錄;
out-of-tree
使用者建立PVC;
PV Controller watch到PVC的建立,尋找合適的PV與之繫結。當尋找不到合適的PV時,讓external-provisioner元件開始開始建立儲存與PV物件的操作;
external-provisioner watch到PVC的建立/更新事件,通過判斷PVC的annotation[volume.beta.kubernetes.io/storage-provisioner]是否與自己的provisioner名稱相等,用來判斷PVC是否需要動態建立儲存卷,是則呼叫csi-plugin ControllerServer來建立儲存,並建立PV物件;
PV Controller將上一步建立的PV與PVC繫結;
使用者建立掛載PVC的Pod;
kube-scheduler watch到Pod的建立,為其尋找合適的Node排程;
Pod排程完成後,AD Controller/volume manager watch到Pod宣告的volume沒有進行attach操作,將呼叫csi-attacher來做attach操作,實際上只是建立VolumeAttachement物件;
external-attacher元件watch到VolumeAttachment物件的建立,呼叫csi-plugin進行attach操作;
csi-plugin ControllerServer進行attach操作,將volume掛載到Pod所在Node節點,成為如/dev/vdb的裝置;
attach操作完成後,volume manager watch到Pod宣告的volume沒有進行mount操作,將呼叫csi-mounter來做mount操作;
csi-mounter呼叫csi-plugin NodeServer進行mount操作,將Node節點上的/dev/vdb裝置掛載到指定目錄;
參考
從零開始入門 K8s:Kubernetes 儲存架構及外掛使用
結束
歡迎大家點點關注,點點贊!