溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

從零開始入門 K8s | 應(yīng)用存儲(chǔ)和持久化數(shù)據(jù)卷:核心知識(shí)

發(fā)布時(shí)間:2020-07-11 12:57:21 來源:網(wǎng)絡(luò) 閱讀:351 作者:阿里系統(tǒng)軟件技術(shù) 欄目:云計(jì)算

作者 | 至天?阿里巴巴高級(jí)研發(fā)工程師

一、Volumes 介紹

Pod Volumes

首先來看一下 Pod Volumes 的使用場景:

  • 場景一:如果 pod 中的某一個(gè)容器在運(yùn)行時(shí)異常退出,被 kubelet 重新拉起之后,如何保證之前容器產(chǎn)生的重要數(shù)據(jù)沒有丟失?
  • 場景二:如果同一個(gè) pod 中的多個(gè)容器想要共享數(shù)據(jù),應(yīng)該如何去做?

以上兩個(gè)場景,其實(shí)都可以借助 Volumes 來很好地解決,接下來首先看一下 Pod Volumes 的常見類型:

  1. 本地存儲(chǔ),常用的有 emptydir/hostpath;
  2. 網(wǎng)絡(luò)存儲(chǔ):網(wǎng)絡(luò)存儲(chǔ)當(dāng)前的實(shí)現(xiàn)方式有兩種,一種是 in-tree,它的實(shí)現(xiàn)代碼是放在 K8s 代碼倉庫中的,隨著 K8s 對存儲(chǔ)類型支持的增多,這種方式會(huì)給 K8s 本身的維護(hù)和發(fā)展帶來很大的負(fù)擔(dān);而第二種實(shí)現(xiàn)方式是 out-of-tree,它的實(shí)現(xiàn)其實(shí)是給 K8s 本身解耦的,通過抽象接口將不同存儲(chǔ)的 driver 實(shí)現(xiàn)從 K8s 代碼倉庫中剝離,因此 out-of-tree 是后面社區(qū)主推的一種實(shí)現(xiàn)網(wǎng)絡(luò)存儲(chǔ)插件的方式;
  3. Projected Volumes:它其實(shí)是將一些配置信息,如 secret/configmap 用卷的形式掛載在容器中,讓容器中的程序可以通過 POSIX 接口來訪問配置數(shù)據(jù);
  4. PV 與 PVC 就是今天要重點(diǎn)介紹的內(nèi)容。

?

Persistent Volumes

從零開始入門 K8s | 應(yīng)用存儲(chǔ)和持久化數(shù)據(jù)卷:核心知識(shí)cdn.com/626e004b4affc7da801ab22d2a79de1db3808837.png">

接下來看一下 PV(Persistent Volumes)。既然已經(jīng)有了 Pod Volumes,為什么又要引入 PV 呢?我們知道 pod 中聲明的 volume 生命周期與 pod 是相同的,以下有幾種常見的場景:

  • 場景一:pod 重建銷毀,如用 Deployment 管理的 pod,在做鏡像升級(jí)的過程中,會(huì)產(chǎn)生新的 pod并且刪除舊的 pod ,那新舊 pod 之間如何復(fù)用數(shù)據(jù)?
  • 場景二:宿主機(jī)宕機(jī)的時(shí)候,要把上面的 pod 遷移,這個(gè)時(shí)候 StatefulSet 管理的 pod,其實(shí)已經(jīng)實(shí)現(xiàn)了帶卷遷移的語義。這時(shí)通過 Pod Volumes 顯然是做不到的;
  • 場景三:多個(gè) pod 之間,如果想要共享數(shù)據(jù),應(yīng)該如何去聲明呢?我們知道,同一個(gè) pod 中多個(gè)容器想共享數(shù)據(jù),可以借助 Pod Volumes 來解決;當(dāng)多個(gè) pod 想共享數(shù)據(jù)時(shí),Pod Volumes 就很難去表達(dá)這種語義;
  • 場景四:如果要想對數(shù)據(jù)卷做一些功能擴(kuò)展性,如:snapshot、resize 這些功能,又應(yīng)該如何去做呢?

以上場景中,通過 Pod Volumes 很難準(zhǔn)確地表達(dá)它的復(fù)用/共享語義,對它的擴(kuò)展也比較困難。因此 K8s 中又引入了?Persistent Volumes 概念,它可以將存儲(chǔ)和計(jì)算分離,通過不同的組件來管理存儲(chǔ)資源和計(jì)算資源,然后解耦 pod 和 Volume 之間生命周期的關(guān)聯(lián)。這樣,當(dāng)把 pod 刪除之后,它使用的 PV 仍然存在,還可以被新建的 pod 復(fù)用。

PVC 設(shè)計(jì)意圖

從零開始入門 K8s | 應(yīng)用存儲(chǔ)和持久化數(shù)據(jù)卷:核心知識(shí)

了解 PV 后,應(yīng)該如何使用它呢?

用戶在使用 PV 時(shí)其實(shí)是通過 PVC,為什么有了 PV 又設(shè)計(jì)了 PVC 呢?主要原因是為了簡化 K8s 用戶對存儲(chǔ)的使用方式,做到職責(zé)分離。通常用戶在使用存儲(chǔ)的時(shí)候,只用聲明所需的存儲(chǔ)大小以及訪問模式。

訪問模式是什么?其實(shí)就是:我要使用的存儲(chǔ)是可以被多個(gè) node 共享還是只能單 node 獨(dú)占訪問(注意是 node level 而不是 pod level)?只讀還是讀寫訪問?用戶只用關(guān)心這些東西,與存儲(chǔ)相關(guān)的實(shí)現(xiàn)細(xì)節(jié)是不需要關(guān)心的。

通過 PVC 和 PV 的概念,將用戶需求和實(shí)現(xiàn)細(xì)節(jié)解耦開,用戶只用通過 PVC 聲明自己的存儲(chǔ)需求。PV是有集群管理員和存儲(chǔ)相關(guān)團(tuán)隊(duì)來統(tǒng)一運(yùn)維和管控,這樣的話,就簡化了用戶使用存儲(chǔ)的方式。可以看到,PV 和 PVC 的設(shè)計(jì)其實(shí)有點(diǎn)像面向?qū)ο蟮慕涌谂c實(shí)現(xiàn)的關(guān)系。用戶在使用功能時(shí),只需關(guān)心用戶接口,不需關(guān)心它內(nèi)部復(fù)雜的實(shí)現(xiàn)細(xì)節(jié)。

既然 PV 是由集群管理員統(tǒng)一管控的,接下來就看一下 PV 這個(gè)對象是怎么產(chǎn)生的。

Static Volume Provisioning

第一種產(chǎn)生方式:靜態(tài)產(chǎn)生方式 - 靜態(tài) Provisioning。

從零開始入門 K8s | 應(yīng)用存儲(chǔ)和持久化數(shù)據(jù)卷:核心知識(shí)

靜態(tài) Provisioning:由集群管理員事先去規(guī)劃這個(gè)集群中的用戶會(huì)怎樣使用存儲(chǔ),它會(huì)先預(yù)分配一些存儲(chǔ),也就是預(yù)先創(chuàng)建一些 PV;然后用戶在提交自己的存儲(chǔ)需求(也就是 PVC)的時(shí)候,K8s 內(nèi)部相關(guān)組件會(huì)幫助它把 PVC 和 PV 做綁定;之后用戶再通過 pod 去使用存儲(chǔ)的時(shí)候,就可以通過 PVC 找到相應(yīng)的 PV,它就可以使用了。

靜態(tài)產(chǎn)生方式有什么不足呢?可以看到,首先需要集群管理員預(yù)分配,預(yù)分配其實(shí)是很難預(yù)測用戶真實(shí)需求的。舉一個(gè)最簡單的例子:如果用戶需要的是 20G,然而集群管理員在分配的時(shí)候可能有 80G 、100G 的,但沒有 20G 的,這樣就很難滿足用戶的真實(shí)需求,也會(huì)造成資源浪費(fèi)。有沒有更好的方式呢?

Dynamic Volume Provisioning

第二種訪問方式:動(dòng)態(tài) Dynamic Provisioning。

從零開始入門 K8s | 應(yīng)用存儲(chǔ)和持久化數(shù)據(jù)卷:核心知識(shí)

動(dòng)態(tài)供給是什么意思呢?就是說現(xiàn)在集群管理員不預(yù)分配 PV,他寫了一個(gè)模板文件,這個(gè)模板文件是用來表示創(chuàng)建某一類型存儲(chǔ)(塊存儲(chǔ),文件存儲(chǔ)等)所需的一些參數(shù),這些參數(shù)是用戶不關(guān)心的,給存儲(chǔ)本身實(shí)現(xiàn)有關(guān)的參數(shù)。用戶只需要提交自身的存儲(chǔ)需求,也就是 PVC 文件,并在 PVC 中指定使用的存儲(chǔ)模板(StorageClass)。

K8s 集群中的管控組件,會(huì)結(jié)合 PVC 和 StorageClass 的信息動(dòng)態(tài),生成用戶所需要的存儲(chǔ)(PV),將 PVC 和 PV 進(jìn)行綁定后,pod 就可以使用 PV 了。通過 StorageClass 配置生成存儲(chǔ)所需要的存儲(chǔ)模板,再結(jié)合用戶的需求動(dòng)態(tài)創(chuàng)建 PV 對象,做到按需分配,在沒有增加用戶使用難度的同時(shí)也解放了集群管理員的運(yùn)維工作。

二、用例解讀

接下來看一下 Pod Volumes、PV、PVC 及 StorageClass 具體是如何使用的。

Pod Volumes 的使用

從零開始入門 K8s | 應(yīng)用存儲(chǔ)和持久化數(shù)據(jù)卷:核心知識(shí)

首先來看一下 Pod Volumes 的使用。如上圖左側(cè)所示,我們可以在 pod yaml 文件中的 Volumes 字段中,聲明我們卷的名字以及卷的類型。聲明的兩個(gè)卷,一個(gè)是用的是 emptyDir,另外一個(gè)用的是 hostPath,這兩種都是本地卷。在容器中應(yīng)該怎么去使用這個(gè)卷呢?它其實(shí)可以通過 volumeMounts 這個(gè)字段,volumeMounts 字段里面指定的 name 其實(shí)就是它使用的哪個(gè)卷,mountPath 就是容器中的掛載路徑。

這里還有個(gè) subPath,subPath 是什么?

先看一下,這兩個(gè)容器都指定使用了同一個(gè)卷,就是這個(gè) cache-volume。那么,在多個(gè)容器共享同一個(gè)卷的時(shí)候,為了隔離數(shù)據(jù),我們可以通過 subPath 來完成這個(gè)操作。它會(huì)在卷里面建立兩個(gè)子目錄,然后容器 1 往 cache 下面寫的數(shù)據(jù)其實(shí)都寫在子目錄 cache1 了,容器 2 往 cache 寫的目錄,其數(shù)據(jù)最終會(huì)落在這個(gè)卷里子目錄下面的 cache2 下。

還有一個(gè) readOnly 字段,readOnly 的意思其實(shí)就是只讀掛載,這個(gè)掛載你往掛載點(diǎn)下面實(shí)際上是沒有辦法去寫數(shù)據(jù)的。

另外 emptyDir、hostPath 都是本地存儲(chǔ),它們之間有什么細(xì)微的差別呢?emptyDir 其實(shí)是在 pod 創(chuàng)建的過程中會(huì)臨時(shí)創(chuàng)建的一個(gè)目錄,這個(gè)目錄隨著 pod 刪除也會(huì)被刪除,里面的數(shù)據(jù)會(huì)被清空掉;hostPath 顧名思義,其實(shí)就是宿主機(jī)上的一個(gè)路徑,在 pod 刪除之后,這個(gè)目錄還是存在的,它的數(shù)據(jù)也不會(huì)被丟失。這就是它們兩者之間一個(gè)細(xì)微的差別。

靜態(tài) PV 使用

從零開始入門 K8s | 應(yīng)用存儲(chǔ)和持久化數(shù)據(jù)卷:核心知識(shí)

接下來再看一下,PV 和 PVC 是怎么使用的。

先看一個(gè)靜態(tài) PV 創(chuàng)建方式。靜態(tài) PV 的話,首先是由管理員來創(chuàng)建的,管理員我們這里以 NAS,就是阿里云文件存儲(chǔ)為例。我需要先在阿里云的文件存儲(chǔ)控制臺(tái)上去創(chuàng)建 NAS 存儲(chǔ),然后把 NAS 存儲(chǔ)的相關(guān)信息要填到 PV 對象中,這個(gè) PV 對象預(yù)創(chuàng)建出來后,用戶可以通過 PVC 來聲明自己的存儲(chǔ)需求,然后再去創(chuàng)建 pod。創(chuàng)建 pod 還是通過我們剛才講解的字段把存儲(chǔ)掛載到某一個(gè)容器中的某一個(gè)掛載點(diǎn)下面。

那么接下來看一下 yaml 怎么寫。集群管理員首先是在云存儲(chǔ)廠商那邊先去把存儲(chǔ)創(chuàng)建出來,然后把相應(yīng)的信息填寫到 PV 對象中。

從零開始入門 K8s | 應(yīng)用存儲(chǔ)和持久化數(shù)據(jù)卷:核心知識(shí)

剛剛創(chuàng)建的阿里云 NAS 文件存儲(chǔ)對應(yīng)的 PV,有個(gè)比較重要的字段:capacity,即創(chuàng)建的這個(gè)存儲(chǔ)的大小,accessModes,創(chuàng)建出來的這個(gè)存儲(chǔ)它的訪問方式,我們后面會(huì)講解總共有幾種訪問方式。

然后有個(gè) ReclaimPolicy,ReclaimPolicy 的意思就是:這塊存儲(chǔ)在被使用后,等它的使用方 pod 以及 PVC 被刪除之后,這個(gè) PV 是應(yīng)該被刪掉還是被保留呢?其實(shí)就是 PV 的回收策略。

接下來看看用戶怎么去使用該 PV 對象。用戶在使用存儲(chǔ)的時(shí)候,需要先創(chuàng)建一個(gè) PVC 對象。PVC 對象里面,只需要指定存儲(chǔ)需求,不用關(guān)心存儲(chǔ)本身的具體實(shí)現(xiàn)細(xì)節(jié)。存儲(chǔ)需求包括哪些呢?首先是需要的大小,也就是 resources.requests.storage;然后是它的訪問方式,即需要這個(gè)存儲(chǔ)的訪問方式,這里聲明為 ReadWriteMany,也即支持多 node 讀寫訪問,這也是文件存儲(chǔ)的典型特性。

從零開始入門 K8s | 應(yīng)用存儲(chǔ)和持久化數(shù)據(jù)卷:核心知識(shí)

上圖中左側(cè),可以看到這個(gè)聲明:它的 size 和它的access mode,跟我們剛才靜態(tài)創(chuàng)建這塊 PV 其實(shí)是匹配的。這樣的話,當(dāng)用戶在提交 PVC 的時(shí)候,K8s 集群相關(guān)的組件就會(huì)把 PV 的 PVC bound 到一起。之后,用戶在提交 pod yaml 的時(shí)候,可以在卷里面寫上 PVC 聲明,在 PVC 聲明里面可以通過 claimName 來聲明要用哪個(gè) PVC。這時(shí),掛載方式其實(shí)跟前面講的一樣,當(dāng)提交完 yaml 的時(shí)候,它可以通過 PVC 找到 bound 著的那個(gè) PV,然后就可以用那塊存儲(chǔ)了。這是靜態(tài) Provisioning 到被 pod 使用的一個(gè)過程。

動(dòng)態(tài) PV 使用

然后再看一下動(dòng)態(tài) Provisioning。動(dòng)態(tài) Provisioning 上面提到過,系統(tǒng)管理員不再預(yù)分配 PV,而只是創(chuàng)建一個(gè)模板文件。

從零開始入門 K8s | 應(yīng)用存儲(chǔ)和持久化數(shù)據(jù)卷:核心知識(shí)

這個(gè)模板文件叫 StorageClass,在 StorageClass 里面,我們需要填的重要信息:第一個(gè)是 provisioner,provisioner 是什么?它其實(shí)就是說我當(dāng)時(shí)創(chuàng)建 PV 和對應(yīng)的存儲(chǔ)的時(shí)候,應(yīng)該用哪個(gè)存儲(chǔ)插件來去創(chuàng)建。

這些參數(shù)是通過 K8s 創(chuàng)建存儲(chǔ)的時(shí)候,需要指定的一些細(xì)節(jié)參數(shù)。對于這些參數(shù),用戶是不需要關(guān)心的,像這里 regionld、zoneld、fsType 和它的類型。ReclaimPolicy 跟我們剛才講解的 PV 里的意思是一樣的,就是說動(dòng)態(tài)創(chuàng)建出來的這塊 PV,當(dāng)使用方使用結(jié)束、Pod 及 PVC 被刪除后,這塊 PV 應(yīng)該怎么處理,我們這個(gè)地方寫的是 delete,意思就是說當(dāng)使用方 pod 和 PVC 被刪除之后,這個(gè) PV 也會(huì)被刪除掉。

接下來看一下,集群管理員提交完 StorageClass,也就是提交創(chuàng)建 PV 的模板之后,用戶怎么用,首先還是需要寫一個(gè) PVC 的文件。

從零開始入門 K8s | 應(yīng)用存儲(chǔ)和持久化數(shù)據(jù)卷:核心知識(shí)

PVC 的文件里存儲(chǔ)的大小、訪問模式是不變的。現(xiàn)在需要新加一個(gè)字段,叫 StorageClassName,它的意思是指定動(dòng)態(tài)創(chuàng)建 PV 的模板文件的名字,這里 StorageClassName 填的就是上面聲明的 csi-disk。

在提交完 PVC之后,K8s 集群中的相關(guān)組件就會(huì)根據(jù) PVC 以及對應(yīng)的 StorageClass 動(dòng)態(tài)生成這塊 PV 給這個(gè) PVC 做一個(gè)綁定,之后用戶在提交自己的 yaml 時(shí),用法和接下來的流程和前面的靜態(tài)使用方式是一樣的,通過 PVC 找到我們動(dòng)態(tài)創(chuàng)建的 PV,然后把它掛載到相應(yīng)的容器中就可以使用了。

PV Spec 重要字段解析

接下來,我們講解一下 PV 的一些重要字段:

從零開始入門 K8s | 應(yīng)用存儲(chǔ)和持久化數(shù)據(jù)卷:核心知識(shí)

  • Capacity:這個(gè)很好理解,就是存儲(chǔ)對象的大??;
  • AccessModes:也是用戶需要關(guān)心的,就是說我使用這個(gè) PV 的方式。它有三種使用方式。
    • 一種是單 node 讀寫訪問;
    • 第二種是多個(gè) node 只讀訪問,是常見的一種數(shù)據(jù)的共享方式;
    • 第三種是多個(gè) node 上讀寫訪問。

用戶在提交 PVC 的時(shí)候,最重要的兩個(gè)字段 —— Capacity 和 AccessModes。在提交 PVC后,K8s 集群中的相關(guān)組件是如何去找到合適的 PV 呢?首先它是通過為 PV 建立的 AccessModes 索引找到所有能夠滿足用戶的 PVC 里面的 AccessModes 要求的 PV list,然后根據(jù) PVC 的 Capacity,StorageClassName, Label Selector 進(jìn)一步篩選 PV,如果滿足條件的 PV 有多個(gè),選擇 PV 的 size 最小的,accessmodes 列表最短的 PV,也即最小適合原則。

  • ReclaimPolicy:這個(gè)就是剛才提到的,我的用戶方 PV 的 PVC 在刪除之后,我的 PV 應(yīng)該做如何處理?常見的有三種方式。
    • 第一種方式我們就不說了,現(xiàn)在 K8s 中已經(jīng)不推薦使用了;
    • 第二種方式 delete,也就是說 PVC 被刪除之后,PV 也會(huì)被刪除;
    • 第三種方式 Retain,就是保留,保留之后,后面這個(gè) PV 需要管理員來手動(dòng)處理;
  • StorageClassName:StorageClassName 這個(gè)我們剛才說了,我們動(dòng)態(tài) Provisioning 時(shí)必須指定的一個(gè)字段,就是說我們要指定到底用哪一個(gè)模板文件來生成 PV ;
  • NodeAffinity:就是說我創(chuàng)建出來的 PV,它能被哪些 node 去掛載使用,其實(shí)是有限制的。然后通過 NodeAffinity 來聲明對node的限制,這樣其實(shí)對 使用該 PV 的 pod 調(diào)度也有限制,就是說 pod 必須要調(diào)度到這些能訪問 PV 的 node 上,才能使用這塊 PV,這個(gè)字段在我們下一講講解存儲(chǔ)拓?fù)湔{(diào)度時(shí)在細(xì)說。

PV 狀態(tài)流轉(zhuǎn)

從零開始入門 K8s | 應(yīng)用存儲(chǔ)和持久化數(shù)據(jù)卷:核心知識(shí)

接下來我們看一下 PV 的狀態(tài)流轉(zhuǎn)。首先在創(chuàng)建 PV 對象后,它會(huì)處在短暫的pending 狀態(tài);等真正的 PV 創(chuàng)建好之后,它就處在 available 狀態(tài)。

available 狀態(tài)意思就是可以使用的狀態(tài),用戶在提交 PVC 之后,被 K8s 相關(guān)組件做完 bound(即:找到相應(yīng)的 PV),這個(gè)時(shí)候 PV 和 PVC 就結(jié)合到一起了,此時(shí)兩者都處在 bound 狀態(tài)。當(dāng)用戶在使用完 PVC,將其刪除后,這個(gè) PV 就處在 released 狀態(tài),之后它應(yīng)該被刪除還是被保留呢?這個(gè)就會(huì)依賴我們剛才說的 ReclaimPolicy。

這里有一個(gè)點(diǎn)需要特別說明一下:當(dāng) PV 已經(jīng)處在 released 狀態(tài)下,它是沒有辦法直接回到 available 狀態(tài),也就是說接下來無法被一個(gè)新的 PVC 去做綁定。如果我們想把已經(jīng) released 的 PV 復(fù)用,我們這個(gè)時(shí)候通常應(yīng)該怎么去做呢?

第一種方式:我們可以新建一個(gè) PV 對象,然后把之前的 released 的 PV 的相關(guān)字段的信息填到新的 PV 對象里面,這樣的話,這個(gè) PV 就可以結(jié)合新的 PVC 了;第二種是我們在刪除 pod 之后,不要去刪除 PVC 對象,這樣給 PV 綁定的 PVC 還是存在的,下次 pod 使用的時(shí)候,就可以直接通過 PVC 去復(fù)用。K8s 中的 StatefulSet 管理的 Pod 帶存儲(chǔ)的遷移就是通過這種方式。

三、操作演示

接下來,我會(huì)在實(shí)際的環(huán)境中給大家演示一下,靜態(tài) Provisioning 以及動(dòng)態(tài) Provisioning 具體操作方式。

靜態(tài) Provisioning 例子

靜態(tài) Provisioning 主要用的是阿里云的 NAS 文件存儲(chǔ);動(dòng)態(tài) Provisioning 主要用了阿里云的云盤。它們需要相應(yīng)存儲(chǔ)插件,插件我已經(jīng)提前部署在我的 K8s 集群中了(csi-nasplugin 是為了在 K8s 中使用阿里云 NAS 所需的插件,csi-disk 是為了在 K8s 中使用阿里云云盤所需要的插件)。

從零開始入門 K8s | 應(yīng)用存儲(chǔ)和持久化數(shù)據(jù)卷:核心知識(shí)

我們接下來先看一下靜態(tài) Provisioning 的 PV 的 yaml 文件。

從零開始入門 K8s | 應(yīng)用存儲(chǔ)和持久化數(shù)據(jù)卷:核心知識(shí)

volumeAttributes 是我在阿里云 nas 控制臺(tái)預(yù)先創(chuàng)建的 NAS 文件系統(tǒng)的相關(guān)信息,我們主要需要關(guān)心的有 capacity 為 5Gi; accessModes 為多 node 讀寫訪問; reclaimPolicy:Retain,也就是當(dāng)我使用方的 PVC 被刪除之后,我這個(gè) PV 是要保留下來的;以及在使用這個(gè)卷的過程中使用的 driver。

然后我們把對應(yīng)的 PV 創(chuàng)建出來:

從零開始入門 K8s | 應(yīng)用存儲(chǔ)和持久化數(shù)據(jù)卷:核心知識(shí)

我們看一下上圖 PV 的狀態(tài),已經(jīng)處在 Available,也就是說它已經(jīng)可以被使用了。

再創(chuàng)建出來 nas-pvc:

從零開始入門 K8s | 應(yīng)用存儲(chǔ)和持久化數(shù)據(jù)卷:核心知識(shí)

我們看這個(gè)時(shí)候 PVC 已經(jīng)新創(chuàng)建出來了,而且也已經(jīng)和我們上面創(chuàng)建的 PV 綁定到一起了。我們看一下 PVC 的 yaml 里面寫的什么。

從零開始入門 K8s | 應(yīng)用存儲(chǔ)和持久化數(shù)據(jù)卷:核心知識(shí)

其實(shí)很簡單 ,就是我需要的大小以及我需要的 accessModes。提交完之后,它就與我們集群中已經(jīng)存在的 PV 做匹配,匹配成功之后,它就會(huì)做 bound。

接下來我們?nèi)?chuàng)建使用 nas-fs 的 pod:

從零開始入門 K8s | 應(yīng)用存儲(chǔ)和持久化數(shù)據(jù)卷:核心知識(shí)

上圖看到,這兩個(gè) Pod 都已經(jīng)處在 running 狀態(tài)了。

我們先看一下這個(gè) pod yaml:

從零開始入門 K8s | 應(yīng)用存儲(chǔ)和持久化數(shù)據(jù)卷:核心知識(shí)

pod yaml 里面聲明了剛才我們創(chuàng)建出來的 PVC 對象,然后把它掛載到 nas-container 容器中的 /data 下面。我們這個(gè) pod 是通過 deployment 創(chuàng)建兩個(gè)副本,通過反親和性,將兩個(gè)副本調(diào)度在不同的 node 上面。

從零開始入門 K8s | 應(yīng)用存儲(chǔ)和持久化數(shù)據(jù)卷:核心知識(shí)

上圖我們可以看一下,兩個(gè) Pod 所在的宿主機(jī)是不一樣的。

如下圖所示:我們登陸到第一個(gè)上面,findmnt 看一下它的掛載信息,這個(gè)其實(shí)就掛載在我聲明的 nas-fs 上,那我們再在下面 touch 個(gè) test.test.test 文件,我們也會(huì)登陸到另外一個(gè)容器,看一下它有沒有被共享。

從零開始入門 K8s | 應(yīng)用存儲(chǔ)和持久化數(shù)據(jù)卷:核心知識(shí)

我們退出再登陸另外一個(gè) pod(剛才登陸的是第一個(gè),現(xiàn)在登陸第二個(gè))。

如下圖所示:我們也 findmnt 一下,可以看到,這兩個(gè) pod 的遠(yuǎn)程掛載路徑一樣,也就是說我們用的是同一個(gè) NAS PV,我們再看一下剛才創(chuàng)建出來的那個(gè)是否存在。

從零開始入門 K8s | 應(yīng)用存儲(chǔ)和持久化數(shù)據(jù)卷:核心知識(shí)

可以看到,這個(gè)也是存在的,就說明這兩個(gè)運(yùn)行在不同node上的 pod 共享了同一個(gè) nas 存儲(chǔ)。

接下來我們看一下把兩個(gè) pod 刪掉之后的情況。先刪 Pod,接著再刪一下對應(yīng)的 PVC(K8s 內(nèi)部對 pvc 對象由保護(hù)機(jī)制,在刪除 pvc 對象時(shí)如果發(fā)現(xiàn)有 pod 在使用 pvc,pvc 是刪除不掉的),這個(gè)可能要稍等一下。

從零開始入門 K8s | 應(yīng)用存儲(chǔ)和持久化數(shù)據(jù)卷:核心知識(shí)

看一下下圖對應(yīng)的 PVC 是不是已經(jīng)被刪掉了。

從零開始入門 K8s | 應(yīng)用存儲(chǔ)和持久化數(shù)據(jù)卷:核心知識(shí)

上圖顯示,它已經(jīng)被刪掉了。再看一下,剛才的 nas PV 還是在的,它的狀態(tài)是處在 Released 狀態(tài),也就是說剛才使用它的 PVC 已經(jīng)被刪掉了,然后它被 released 了。又因?yàn)槲覀?RECLAIN POLICY 是 Retain,所以它這個(gè) PV 是被保留下來的。

動(dòng)態(tài) Provisioning 例子

接下來我們來看第二個(gè)例子,動(dòng)態(tài) Provisioning 的例子。我們先把保留下來的 PV 手動(dòng)刪掉,可以看到集群中沒有 PV了。接下來演示一下動(dòng)態(tài) Provisioning。

首先,先去創(chuàng)建一個(gè)生成 PV 的模板文件,也就是 storageclass??匆幌?storageclass 里面的內(nèi)容,其實(shí)很簡單。

從零開始入門 K8s | 應(yīng)用存儲(chǔ)和持久化數(shù)據(jù)卷:核心知識(shí)

如上圖所示,我事先指定的是我要?jiǎng)?chuàng)建存儲(chǔ)的卷插件(阿里云云盤插件,由阿里云團(tuán)隊(duì)開發(fā)),這個(gè)我們已經(jīng)提前部署好了;我們可以看到,parameters部分是創(chuàng)建存儲(chǔ)所需要的一些參數(shù),但是用戶不需要關(guān)心這些信息;然后是 reclaimPolicy,也就是說通過這個(gè) storageclass 創(chuàng)建出來的 PV 在給綁定到一起的 PVC 刪除之后,它是要保留還是要?jiǎng)h除。

從零開始入門 K8s | 應(yīng)用存儲(chǔ)和持久化數(shù)據(jù)卷:核心知識(shí)

如上圖所示:現(xiàn)在這個(gè)集群中是沒有 PV 的,我們動(dòng)態(tài)提交一個(gè) PVC 文件,先看一下它的 PVC 文件。它的 accessModes-ReadWriteOnce (因?yàn)榘⒗镌圃票P其實(shí)只能是單 node 讀寫的,所以我們聲明這樣的方式),它的存儲(chǔ)大小需求是 30G,它的 storageClassName 是 csi-disk,就是我們剛才創(chuàng)建的 storageclass,也就是說它指定要通過這個(gè)模板去生成 PV。

從零開始入門 K8s | 應(yīng)用存儲(chǔ)和持久化數(shù)據(jù)卷:核心知識(shí)

這個(gè) PVC 此時(shí)正處在 pending 狀態(tài),這就說明它對應(yīng)的 PV 還在創(chuàng)建過程中。

從零開始入門 K8s | 應(yīng)用存儲(chǔ)和持久化數(shù)據(jù)卷:核心知識(shí)

稍過一會(huì),我們看到已經(jīng)有一個(gè)新的 PV 生成,這個(gè) PV 其實(shí)就是根據(jù)我們提交的 PVC 以及 PVC 里面指定的storageclass 動(dòng)態(tài)生成的。之后 K8s 會(huì)將生成的 PV 以及我們提交的 PVC,就是這個(gè) disk PVC 做綁定,之后我們就可以通過創(chuàng)建 pod 來使用了。

再看一下 pod yaml:

從零開始入門 K8s | 應(yīng)用存儲(chǔ)和持久化數(shù)據(jù)卷:核心知識(shí)

pod yaml 很簡單,也是通過 PVC 聲明,表明使用這個(gè) PVC。然后是掛載點(diǎn),下面我們可以創(chuàng)建看一下。

從零開始入門 K8s | 應(yīng)用存儲(chǔ)和持久化數(shù)據(jù)卷:核心知識(shí)

如下圖所示:我們可以大概看一下 Events,首先被調(diào)度器調(diào)度,調(diào)度完之后,接下來會(huì)有個(gè) attachdetach controller,它會(huì)去做 disk 的 attach 操作,就是把我們對應(yīng)的 PV 掛載到調(diào)度器調(diào)度的 node 上,然后Pod對應(yīng)的容器才能啟動(dòng),啟動(dòng)容器才能使用對應(yīng)的盤。

從零開始入門 K8s | 應(yīng)用存儲(chǔ)和持久化數(shù)據(jù)卷:核心知識(shí)

接下來我會(huì)把 PVC 刪掉,看一下 PV 會(huì)不會(huì)根據(jù)我們的 reclaimPolicy 隨之刪掉呢?我們先看一下,這個(gè)時(shí)候 PVC 還是存在的,對應(yīng)的 PV 也是存在的。

從零開始入門 K8s | 應(yīng)用存儲(chǔ)和持久化數(shù)據(jù)卷:核心知識(shí)

然后刪一下 PVC,刪完之后再看一下:我們的 PV 也被刪了,也就是說根據(jù) reclaimPolicy,我們在刪除 PVC 的同時(shí),PV 也會(huì)被刪除掉。

從零開始入門 K8s | 應(yīng)用存儲(chǔ)和持久化數(shù)據(jù)卷:核心知識(shí)

我們的演示部分就到這里了。

四、架構(gòu)設(shè)計(jì)

PV 和 PVC 的處理流程

我們接下來看一下 K8s 中的 PV 和 PVC 體系的完整處理流程。我首先看一下這張圖的右下部分里面提到的 csi。

從零開始入門 K8s | 應(yīng)用存儲(chǔ)和持久化數(shù)據(jù)卷:核心知識(shí)

csi 是什么?csi 的全稱是 container storage interface,它是 K8s 社區(qū)后面對存儲(chǔ)插件實(shí)現(xiàn) ( out of tree ) 的官方推薦方式。csi 的實(shí)現(xiàn)大體可以分為兩部分:

  • 第一部分是由 k8s 社區(qū)驅(qū)動(dòng)實(shí)現(xiàn)的通用的部分,像我們這張圖中的 csi-provisioner和 csi-attacher controller;
  • 另外一種是由云存儲(chǔ)廠商實(shí)踐的,對接云存儲(chǔ)廠商的 OpenApi,主要是實(shí)現(xiàn)真正的 create/delete/mount/unmount 存儲(chǔ)的相關(guān)操作,對應(yīng)到上圖中的 csi-controller-server 和 csi-node-server。

接下來看一下,當(dāng)用戶提交 yaml 之后,k8s 內(nèi)部的處理流程。用戶在提交 PVCyaml 的時(shí)候,首先會(huì)在集群中生成一個(gè) PVC 對象,然后 PVC 對象會(huì)被 csi-provisioner controller watch 到,csi-provisioner 會(huì)結(jié)合 PVC 對象以及 PVC 對象中聲明的 storageClass,通過 GRPC 調(diào)用 csi-controller-server,然后,到云存儲(chǔ)服務(wù)這邊去創(chuàng)建真正的存儲(chǔ),并最終創(chuàng)建出來 PV 對象。最后,由集群中的 PV controller 將 PVC 和 PV 對象做 bound 之后,這個(gè) PV 就可以被使用了。

用戶在提交 pod 之后,首先會(huì)被調(diào)度器調(diào)度選中某一個(gè)合適的node,之后該 node 上面的 kubelet 在創(chuàng)建 pod 流程中會(huì)通過首先 csi-node-server 將我們之前創(chuàng)建的 PV 掛載到我們 pod 可以使用的路徑,然后 kubelet 開始 create && start pod 中的所有 container。

PV、PVC 以及通過 csi 使用存儲(chǔ)流程

我們接下來通過另一張圖來更加詳細(xì)看一下我們 PV、PVC 以及通過 CSI 使用存儲(chǔ)的完整流程。

從零開始入門 K8s | 應(yīng)用存儲(chǔ)和持久化數(shù)據(jù)卷:核心知識(shí)

主要分為三個(gè)階段:

  • 第一個(gè)階段(Create 階段)是用戶提交完 PVC,由 csi-provisioner 創(chuàng)建存儲(chǔ),并生成 PV 對象,之后 PV controller 將 PVC 及生成的 PV 對象做 bound,bound 之后,create 階段就完成了;
  • 之后用戶在提交 pod yaml 的時(shí)候,首先會(huì)被調(diào)度選中某一個(gè)合適的 node,等 pod 的運(yùn)行 node 被選出來之后,會(huì)被 AD Controller watch 到 pod 選中的 node,它會(huì)去查找 pod 中使用了哪些 PV。然后它會(huì)生成一個(gè)內(nèi)部的對象叫 VolumeAttachment 對象,從而去觸發(fā) csi-attacher去調(diào)用 csi-controller-server 去做真正的 attache 操作,attach 操作調(diào)到云存儲(chǔ)廠商 OpenAPI。這個(gè) attach 操作就是將存儲(chǔ) attach到 pod 將會(huì)運(yùn)行的 node 上面。第二個(gè)階段 —— attach 階段完成;
  • 然后我們接下來看第三個(gè)階段。第三個(gè)階段發(fā)生在 kubelet 創(chuàng)建 pod 的過程中,它在創(chuàng)建 pod 的過程中,首先要去做一個(gè) mount,這里的 mount 操作是為了將已經(jīng) attach 到這個(gè) node 上面那塊盤,進(jìn)一步 mount 到 pod 可以使用的一個(gè)具體路徑,之后 kubelet 才開始創(chuàng)建并啟動(dòng)容器。這就是 PV 加 PVC 創(chuàng)建存儲(chǔ)以及使用存儲(chǔ)的第三個(gè)階段 —— mount 階段。

總的來說,有三個(gè)階段:第一個(gè) create 階段,主要是創(chuàng)建存儲(chǔ);第二個(gè) attach 階段,就是將那塊存儲(chǔ)掛載到 node 上面(通常為將存儲(chǔ) load 到 node 的 /dev 下面);第三個(gè) mount 階段,將對應(yīng)的存儲(chǔ)進(jìn)一步掛載到 pod 可以使用的路徑。這就是我們的 PVC、PV、已經(jīng)通過 CSI 實(shí)現(xiàn)的卷從創(chuàng)建到使用的完整流程。

本文總結(jié)

本文內(nèi)容就到此為止了,這里為大家簡單總結(jié)一下:

  • 介紹了 K8s Volume 的使用場景,以及本身局限性;
  • 通過介紹 K8s 的 PVC 和 PV 體系,說明 K8s 通過?PVC 和 PV 體系增強(qiáng)了 K8s Volumes 在多 Pod 共享/遷移/存儲(chǔ)擴(kuò)展等場景下的能力的必要性以及設(shè)計(jì)思想;
  • 通過介紹 PV(存儲(chǔ))的不同供給模式 (static and dynamic),學(xué)習(xí)了如何通過不同方式為集群中的 Pod 供給所需的存儲(chǔ);
  • 通過 PVC&PV 在 K8s 中完整的處理流程,深入理解?PVC&PV 的工作原理 。

阿里巴巴云原生微信公眾號(hào)(ID:Alicloudnative)關(guān)注微服務(wù)、Serverless、容器、Service Mesh等技術(shù)領(lǐng)域、聚焦云原生流行技術(shù)趨勢、云原生大規(guī)模的落地實(shí)踐,做最懂云原生開發(fā)者的技術(shù)公眾號(hào)。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI