溫馨提示×

溫馨提示×

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

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

從零開始入門 K8s | 應(yīng)用存儲和持久化數(shù)據(jù)卷:存儲快照與拓?fù)湔{(diào)度

發(fā)布時間:2020-04-06 05:06:30 來源:網(wǎng)絡(luò) 閱讀:277 作者:阿里系統(tǒng)軟件技術(shù) 欄目:云計(jì)算

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

一、基本知識

存儲快照產(chǎn)生背景

在使用存儲時,為了提高數(shù)據(jù)操作的容錯性,我們通常有需要對線上數(shù)據(jù)進(jìn)行 snapshot ,以及能快速 restore 的能力。另外,當(dāng)需要對線上數(shù)據(jù)進(jìn)行快速的復(fù)制以及遷移等動作,如進(jìn)行環(huán)境的復(fù)制、數(shù)據(jù)開發(fā)等功能時,都可以通過存儲快照來滿足需求,而 K8s 中通過 CSI Snapshotter controller 來實(shí)現(xiàn)存儲快照的功能。

存儲快照用戶接口-Snapshot

我們知道,K8s 中通過 pvc 以及 pv 的設(shè)計(jì)體系來簡化用戶對存儲的使用,而存儲快照的設(shè)計(jì)其實(shí)是仿照? pvc & pv 體系的設(shè)計(jì)思想。當(dāng)用戶需要存儲快照的功能時,可以通過 VolumeSnapshot 對象來聲明,并指定相應(yīng)的 VolumeSnapshotClass 對象,之后由集群中的相關(guān)組件動態(tài)生成存儲快照以及存儲快照對應(yīng)的對象 VolumeSnapshotContent 。如下對比圖所示,動態(tài)生成 VolumeSnapshotContent 和動態(tài)生成 pv 的流程是非常相似的。

從零開始入門 K8s | 應(yīng)用存儲和持久化數(shù)據(jù)卷:存儲快照與拓?fù)湔{(diào)度cdn.com/acb0479b57c985f69d45f6d5062471cc62e1abac.png">

存儲快照用戶接口-Restore

有了存儲快照之后,如何將快照數(shù)據(jù)快速恢復(fù)過來呢?如下圖所示:

從零開始入門 K8s | 應(yīng)用存儲和持久化數(shù)據(jù)卷:存儲快照與拓?fù)湔{(diào)度

如上所示的流程,可以借助 PVC 對象將其的 dataSource 字段指定為 VolumeSnapshot 對象。這樣當(dāng) PVC 提交之后,會由集群中的相關(guān)組件找到 dataSource 所指向的存儲快照數(shù)據(jù),然后新創(chuàng)建對應(yīng)的存儲以及 pv 對象,將存儲快照數(shù)據(jù)恢復(fù)到新的 pv 中,這樣數(shù)據(jù)就恢復(fù)回來了,這就是存儲快照的 restore 用法。

Topolopy-含義

首先了解一下拓?fù)涫鞘裁匆馑迹哼@里所說的拓?fù)涫?K8s 集群中為管理的 nodes 劃分的一種“位置”關(guān)系,意思為:可以通過在 node 的 labels 信息里面填寫某一個 node 屬于某一個拓?fù)洹?lt;br />?<br />常見的有三種,這三種在使用時經(jīng)常會遇到的:

  • 第一種,在使用云存儲服務(wù)的時候,經(jīng)常會遇到?region,也就是地區(qū)的概念,在 K8s 中常通過 label?failure-domain.beta.kubernetes.io/region 來標(biāo)識。這個是為了標(biāo)識單個 K8s 集群管理的跨 region 的 nodes 到底屬于哪個地區(qū);

  • 第二種,比較常用的是可用區(qū),也就是?available?zone,在 K8s 中常通過 label failure-domain.beta.kubernetes.io/zone 來標(biāo)識。這個是為了標(biāo)識單個 K8s 集群管理的跨 zone 的 nodes 到底屬于哪個可用區(qū);

  • 第三種,是?hostname,就是單機(jī)維度,是拓?fù)溆驗(yàn)?node 范圍,在 K8s 中常通過 label?kubernetes.io/hostname 來標(biāo)識,這個在文章的最后講 local pv 的時候,會再詳細(xì)描述。

上面講到的三個拓?fù)涫潜容^常用的,而拓?fù)淦鋵?shí)是可以自己定義的。可以定義一個字符串來表示一個拓?fù)溆?,這個 key 所對應(yīng)的值其實(shí)就是拓?fù)溆蛳虏煌耐負(fù)湮恢谩?/p>

舉個例子:可以用?rack,也就是機(jī)房中的機(jī)架這個緯度來做一個拓?fù)溆?。這樣就可以將不同機(jī)架 ( rack ) 上面的機(jī)器標(biāo)記為不同的拓?fù)湮恢茫簿褪钦f可以將不同機(jī)架上機(jī)器的位置關(guān)系通過 rack 這個緯度來標(biāo)識。屬于 rack1 上的機(jī)器,node label 中都添加 rack 的標(biāo)識,它的 value 就標(biāo)識成 rack1,即 rack=rack1;另外一組機(jī)架上的機(jī)器可以標(biāo)識為 rack=rack2,這樣就可以通過機(jī)架的緯度就來區(qū)分來 K8s 中的 node 所處的位置。

接下來就一起來看看拓?fù)湓?K8s 存儲中的使用。

存儲拓?fù)湔{(diào)度產(chǎn)生背景

上一節(jié)課我們說過,K8s 中通過 PV 的 PVC 體系將存儲資源和計(jì)算資源分開管理了。如果創(chuàng)建出來的 PV有"訪問位置"的限制,也就是說,它通過 nodeAffinity 來指定哪些 node 可以訪問這個 PV。為什么會有這個訪問位置的限制?

因?yàn)樵?K8s 中創(chuàng)建 pod 的流程和創(chuàng)建 PV 的流程,其實(shí)可以認(rèn)為是并行進(jìn)行的,這樣的話,就沒有辦法來保證 pod 最終運(yùn)行的 node 是能訪問到 有位置限制的 PV 對應(yīng)的存儲,最終導(dǎo)致 pod 沒法正常運(yùn)行。這里來舉兩個經(jīng)典的例子:

首先來看一下?Local PV 的例子,Local PV 是將一個 node 上的本地存儲封裝為 PV,通過使用 PV 的方式來訪問本地存儲。為什么會有 Local PV 的需求呢?簡單來說,剛開始使用 PV 或 PVC 體系的時候,主要是用來針對分布式存儲的,分布式存儲依賴于網(wǎng)絡(luò),如果某些業(yè)務(wù)對 I/O 的性能要求非常高,可能通過網(wǎng)絡(luò)訪問分布式存儲沒辦法滿足它的性能需求。這個時候需要使用本地存儲,刨除了網(wǎng)絡(luò)的 overhead,性能往往會比較高。但是用本地存儲也是有壞處的!分布式存儲可以通過多副本來保證高可用,但本地存儲就需要業(yè)務(wù)自己用類似 Raft 協(xié)議來實(shí)現(xiàn)多副本高可用。

接下來看一下 Local PV 場景可能如果沒有對 PV 做“訪問位置”的限制會遇到什么問題?

從零開始入門 K8s | 應(yīng)用存儲和持久化數(shù)據(jù)卷:存儲快照與拓?fù)湔{(diào)度

當(dāng)用戶在提交完 PVC 的時候,K8s PV controller可能綁定的是 node2 上面的 PV。但是,真正使用這個 PV 的 pod,在被調(diào)度的時候,有可能調(diào)度在 node1 上,最終導(dǎo)致這個 pod 在起來的時候沒辦法去使用這塊存儲,因?yàn)?pod 真實(shí)情況是要使用 node2 上面的存儲。

第二個(如果不對 PV 做“訪問位置”的限制會出問題的)場景:

從零開始入門 K8s | 應(yīng)用存儲和持久化數(shù)據(jù)卷:存儲快照與拓?fù)湔{(diào)度

如果搭建的 K8s 集群管理的 nodes 分布在單個區(qū)域多個可用區(qū)內(nèi)。在創(chuàng)建動態(tài)存儲的時候,創(chuàng)建出來的存儲屬于可用區(qū) 2,但之后在提交使用該存儲的 pod,它可能會被調(diào)度到可用區(qū) 1 了,那就可能沒辦法使用這塊存儲。因此像阿里云的云盤,也就是塊存儲,當(dāng)前不能跨可用區(qū)使用,如果創(chuàng)建的存儲其實(shí)屬于可用區(qū) 2,但是 pod 運(yùn)行在可用區(qū) 1,就沒辦法使用這塊存儲,這是第二個常見的問題場景。

接下來我們來看看 K8s 中如何通過存儲拓?fù)湔{(diào)度來解決上面的問題的。

存儲拓?fù)湔{(diào)度

首先總結(jié)一下之前的兩個問題,它們都是 PV 在給 PVC 綁定或者動態(tài)生成 PV 的時候,我并不知道后面將使用它的 pod 將調(diào)度在哪些 node 上。但 PV 本身的使用,是對 pod 所在的 node 有拓?fù)湮恢玫南拗频模?Local PV 場景是我要調(diào)度在指定的 node 上我才能使用那塊 PV,而對第二個問題場景就是說跨可用區(qū)的話,必須要在將使用該 PV 的 pod 調(diào)度到同一個可用區(qū)的 node 上才能使用阿里云云盤服務(wù),那 K8s 中怎樣去解決這個問題呢?

簡單來說,在 K8s 中將 PV 和 PVC 的 binding 操作和動態(tài)創(chuàng)建 PV 的操作做了 delay,delay 到 pod 調(diào)度結(jié)果出來之后,再去做這兩個操作。這樣的話有什么好處?

  • 首先,如果要是所要使用的 PV 是預(yù)分配的,如 Local PV,其實(shí)使用這塊 PV 的 pod 它對應(yīng)的 PVC 其實(shí)還沒有做綁定,就可以通過調(diào)度器在調(diào)度的過程中,結(jié)合 pod 的計(jì)算資源需求(如 cpu/mem) 以及 pod 的 PVC 需求,選擇的 node 既要滿足計(jì)算資源的需求又要 pod 使用的 pvc 要能 binding 的 pv 的 nodeaffinity 限制;
  • 其次對動態(tài)生成 PV 的場景其實(shí)就相當(dāng)于是如果知道 pod 運(yùn)行的 node 之后,就可以根據(jù) node 上記錄的拓?fù)湫畔韯討B(tài)的創(chuàng)建這個 PV,也就是保證新創(chuàng)建出來的 PV 的拓?fù)湮恢门c運(yùn)行的 node 所在的拓?fù)湮恢檬且恢碌模缟厦嫠龅陌⒗镌圃票P的例子,既然知道 pod 要運(yùn)行到可用區(qū) 1,那之后創(chuàng)建存儲時指定在可用區(qū) 1 創(chuàng)建即可。

為了實(shí)現(xiàn)上面所說的延遲綁定和延遲創(chuàng)建 PV,需要在 K8s 中的改動涉及到的相關(guān)組件有三個:

  • PV Controller 也就是 persistent volume controller,它需要支持延遲 Binding 這個操作。
  • 另一個是動態(tài)生成 PV 的組件,如果 pod 調(diào)度結(jié)果出來之后,它要根據(jù) pod 的拓?fù)湫畔砣討B(tài)的創(chuàng)建 PV。
  • 第三組件,也是最重要的一個改動點(diǎn)就是 kube-scheduler。在為 pod 選擇 node 節(jié)點(diǎn)的時候,它不僅要考慮 pod 對 CPU/MEM 的計(jì)算資源的需求,它還要考慮這個 pod 對存儲的需求,也就是根據(jù)它的 PVC,它要先去看一下當(dāng)前要選擇的 node,能否滿足能和這個 PVC 能匹配的 PV 的 nodeAffinity;或者是動態(tài)生成 PV 的過程,它要根據(jù) StorageClass 中指定的拓?fù)湎拗苼?check 當(dāng)前的 node 是不是滿足這個拓?fù)湎拗?,這樣就能保證調(diào)度器最終選擇出來的 node 就能滿足存儲本身對拓?fù)涞南拗啤?/li>

這就是存儲拓?fù)湔{(diào)度的相關(guān)知識。

二、用例解讀

接下來通過 yaml 用例來解讀一下第一部分的基本知識。

Volume Snapshot/Restore示例

從零開始入門 K8s | 應(yīng)用存儲和持久化數(shù)據(jù)卷:存儲快照與拓?fù)湔{(diào)度

下面來看一下存儲快照如何使用:首先需要集群管理員,在集群中創(chuàng)建 VolumeSnapshotClass 對象,VolumeSnapshotClass 中一個重要字段就是 Snapshot,它是指定真正創(chuàng)建存儲快照所使用的卷插件,這個卷插件是需要提前部署的,稍后再說這個卷插件。

接下來用戶他如果要做真正的存儲快照,需要聲明一個 VolumeSnapshotClass , VolumeSnapshotClass 首先它要指定的是 VolumeSnapshotClassName,接著它要指定的一個非常重要的字段就是 source,這個 source 其實(shí)就是指定快照的數(shù)據(jù)源是啥。這個地方指定 name 為 disk-pvc,也就是說通過這個 pvc 對象來創(chuàng)建存儲快照。提交這個 VolumeSnapshot 對象之后,集群中的相關(guān)組件它會找到這個 PVC 對應(yīng)的 PV 存儲,對這個 PV 存儲做一次快照。

有了存儲快照之后,那接下來怎么去用存儲快照恢復(fù)數(shù)據(jù)呢?這個其實(shí)也很簡單,通過聲明一個新的 PVC 對象并在它的 spec 下面的 DataSource 中來聲明我的數(shù)據(jù)源來自于哪個 VolumeSnapshot,這里指定的是 disk-snapshot 對象,當(dāng)我這個 PVC 提交之后,集群中的相關(guān)組件,它會動態(tài)生成新的 PV 存儲,這個新的 PV 存儲中的數(shù)據(jù)就來源于這個 Snapshot 之前做的存儲快照。

Local PV 的示例

如下圖看一下 Local PV 的 yaml 示例:

從零開始入門 K8s | 應(yīng)用存儲和持久化數(shù)據(jù)卷:存儲快照與拓?fù)湔{(diào)度

Local PV 大部分使用的時候都是通過靜態(tài)創(chuàng)建的方式,也就是要先去聲明 PV 對象,既然 Local PV 只能是本地訪問,就需要在聲明 PV 對象的,在 PV 對象中通過 nodeAffinity 來限制我這個 PV 只能在單 node 上訪問,也就是給這個 PV 加上拓?fù)湎拗?。如上圖拓?fù)涞?key 用 kubernetes.io/hostname 來做標(biāo)記,也就是只能在 node1 訪問。如果想用這個 PV,你的 pod 必須要調(diào)度到 node1 上。

既然是靜態(tài)創(chuàng)建 PV 的方式,這里為什么還需要 storageClassname 呢?前面也說了,在 Local PV 中,如果要想讓它正常工作,需要用到延遲綁定特性才行,那既然是延遲綁定,當(dāng)用戶在寫完 PVC 提交之后,即使集群中有相關(guān)的 PV 能跟它匹配,它也暫時不能做匹配,也就是說 PV controller 不能馬上去做 binding,這個時候你就要通過一種手段來告訴 PV controller,什么情況下是不能立即做?binding。這里的 storageClass 就是為了起到這個副作用,我們可以看到 storageClass 里面的 provisioner 指定的是?no-provisioner,其實(shí)就是相當(dāng)于告訴 K8s 它不會去動態(tài)創(chuàng)建 PV,它主要用到 storageclass 的VolumeBindingMode 字段,叫 WaitForFirstConsumer,可以先簡單地認(rèn)為它是延遲綁定。

當(dāng)用戶開始提交 PVC 的時候,pv controller 在看到這個 pvc 的時候,它會找到相應(yīng)的 storageClass,發(fā)現(xiàn)這個 BindingMode 是延遲綁定,它就不會做任何事情。

之后當(dāng)真正使用這個 pvc 的 pod,在調(diào)度的時候,當(dāng)它恰好調(diào)度在符合 pv nodeaffinity 的 node 的上面后,這個 pod 里面所使用的 PVC 才會真正地與 PV 做綁定,這樣就保證我 pod 調(diào)度到這臺 node 上之后,這個 PVC 才與這個 PV 綁定,最終保證的是創(chuàng)建出來的 pod 能訪問這塊 Local PV,也就是靜態(tài) Provisioning 場景下怎么去滿足 PV 的拓?fù)湎拗啤?/p>

限制 Dynamic Provisioning PV 拓?fù)涫纠?/h4>

再看一下動態(tài) Provisioning PV 的時候,怎么去做拓?fù)湎拗频模?/p>

從零開始入門 K8s | 應(yīng)用存儲和持久化數(shù)據(jù)卷:存儲快照與拓?fù)湔{(diào)度

動態(tài)就是指動態(tài)創(chuàng)建 PV 就有拓?fù)湮恢玫南拗疲窃趺慈ブ付ǎ?/p>

首先在 storageclass 還是需要指定 BindingMode,就是 WaitForFirstConsumer,就是延遲綁定。

其次特別重要的一個字段就是?allowedTopologies,限制就在這個地方。上圖中可以看到拓?fù)湎拗剖强捎脜^(qū)的級別,這里其實(shí)有兩層意思:

  1. 第一層意思就是說我在動態(tài)創(chuàng)建 PV 的時候,創(chuàng)建出來的 PV 必須是在這個可用區(qū)可以訪問的;
  2. 第二層含義是因?yàn)槁暶鞯氖茄舆t綁定,那調(diào)度器在發(fā)現(xiàn)使用它的 PVC 正好對應(yīng)的是該 storageclass 的時候,調(diào)度 pod 就要選擇位于該可用區(qū)的 nodes。

總之,就是要從兩方面保證,一是動態(tài)創(chuàng)建出來的存儲時要能被這個可用區(qū)訪問的,二是我調(diào)度器在選擇 node 的時候,要落在這個可用區(qū)內(nèi)的,這樣的話就保證我的存儲和我要使用存儲的這個 pod 它所對應(yīng)的 node,它們之間的拓?fù)溆蚴窃谕粋€拓?fù)溆?,用戶在?PVC 文件的時候,寫法是跟以前的寫法是一樣的,主要是在 storageclass 中要做一些拓?fù)湎拗啤?/p>

三、操作演示

本節(jié)將在線上環(huán)境來演示一下前面講解的內(nèi)容。

首先來看一下我的阿里云服務(wù)器上搭建的 K8s 服務(wù)。總共有 3 個 node 節(jié)點(diǎn)。一個 master 節(jié)點(diǎn),兩個 node。其中 master 節(jié)點(diǎn)是不能調(diào)度 pod 的。

從零開始入門 K8s | 應(yīng)用存儲和持久化數(shù)據(jù)卷:存儲快照與拓?fù)湔{(diào)度

再看一下,我已經(jīng)提前把我需要的插件已經(jīng)布好了,一個是 snapshot 插件? ( csi-external-snapshot ) ,一個是動態(tài)云盤的插件? ( csi-disk ) 。

從零開始入門 K8s | 應(yīng)用存儲和持久化數(shù)據(jù)卷:存儲快照與拓?fù)湔{(diào)度

現(xiàn)在開始 snapshot 的演示。首先去動態(tài)創(chuàng)建云盤,然后才能做 snapshot。動態(tài)創(chuàng)建云盤需要先創(chuàng)建? storageclass,然后去根據(jù) PVC 動態(tài)創(chuàng)建 PV,然后再創(chuàng)建一個使用它的 pod 了。

從零開始入門 K8s | 應(yīng)用存儲和持久化數(shù)據(jù)卷:存儲快照與拓?fù)湔{(diào)度

有個以上對象,現(xiàn)在就可以做 snapshot 了,首先看一下做 snapshot 需要的第一個配置文件:snapshotclass.yaml。

從零開始入門 K8s | 應(yīng)用存儲和持久化數(shù)據(jù)卷:存儲快照與拓?fù)湔{(diào)度

其實(shí)里面就是指定了在做存儲快照的時候需要使用的插件,這個插件剛才演示了已經(jīng)部署好了,就是 csi-external-snapshot-0 這個插件。

從零開始入門 K8s | 應(yīng)用存儲和持久化數(shù)據(jù)卷:存儲快照與拓?fù)湔{(diào)度

接下來創(chuàng)建 volume-snapshotclass 文件,創(chuàng)建完之后就開始了 snapshot。

從零開始入門 K8s | 應(yīng)用存儲和持久化數(shù)據(jù)卷:存儲快照與拓?fù)湔{(diào)度

然后看 snapshot.yaml,Volumesnapshot 聲明創(chuàng)建存儲快照了,這個地方就指定剛才創(chuàng)建的那個 PVC 來做的數(shù)據(jù)源來做 snapshot,那我們開始創(chuàng)建。

從零開始入門 K8s | 應(yīng)用存儲和持久化數(shù)據(jù)卷:存儲快照與拓?fù)湔{(diào)度

我們看一下 Snapshot 有沒有創(chuàng)建好,如下圖所示,content 已經(jīng)在 11 秒之前創(chuàng)建好了。

從零開始入門 K8s | 應(yīng)用存儲和持久化數(shù)據(jù)卷:存儲快照與拓?fù)湔{(diào)度

可以看一下它里面的內(nèi)容,主要看 volumesnapshotcontent 記錄的一些信息,這個是我 snapshot 出來之后,它記錄的就是云存儲廠商那邊返回給我的 snapshot 的 ID。然后是這個 snapshot 數(shù)據(jù)源,也就是剛才指定的 PVC,可以通過它會找到對應(yīng)的 PV。

從零開始入門 K8s | 應(yīng)用存儲和持久化數(shù)據(jù)卷:存儲快照與拓?fù)湔{(diào)度

snapshot 的演示大概就是這樣,把剛才創(chuàng)建的 snapshot 刪掉,還是通過 volumesnapshot 來刪掉。然后看一下,動態(tài)創(chuàng)建的這個 volumesnapshotcontent 也被刪掉。

從零開始入門 K8s | 應(yīng)用存儲和持久化數(shù)據(jù)卷:存儲快照與拓?fù)湔{(diào)度

接下來看一下動態(tài) PV 創(chuàng)建的過程加上一些拓?fù)湎拗疲紫葘⒌?storageclass 創(chuàng)建出來,然后再看一下 storageclass 里面做的限制,storageclass 首先還是指定它的 BindingMode 為 WaitForFirstConsumer,也就是做延遲綁定,然后是對它的拓?fù)湎拗?,我這里面在 allowedTopologies 字段中配置了一個可用區(qū)級別的限制。

從零開始入門 K8s | 應(yīng)用存儲和持久化數(shù)據(jù)卷:存儲快照與拓?fù)湔{(diào)度

來嘗試創(chuàng)建一下的 PVC,PVC 創(chuàng)建出來之后,理論上它應(yīng)該處在 pending 狀態(tài)??匆幌拢F(xiàn)在因?yàn)樗鲅舆t綁定,由于現(xiàn)在沒有使用它的 pod,暫時沒辦法去做綁定,也沒辦法去動態(tài)創(chuàng)建新的 PV。

從零開始入門 K8s | 應(yīng)用存儲和持久化數(shù)據(jù)卷:存儲快照與拓?fù)湔{(diào)度

接下來創(chuàng)建使用該 pvc 的 pod 看會有什么效果,看一下 pod,pod 也處在 pending了。

從零開始入門 K8s | 應(yīng)用存儲和持久化數(shù)據(jù)卷:存儲快照與拓?fù)湔{(diào)度

那來看一下 pod 為啥處在 pending 狀態(tài),可以看一下是調(diào)度失敗了,調(diào)度失敗原因:一個 node 由于 taint 不能調(diào)度,這個其實(shí)是 master,另外兩個 node 也是沒有說是可綁定的 PV。

從零開始入門 K8s | 應(yīng)用存儲和持久化數(shù)據(jù)卷:存儲快照與拓?fù)湔{(diào)度

為什么會有兩個 node 出現(xiàn)沒有可綁定的 pv 的錯誤?不是動態(tài)創(chuàng)建么?

我們來仔細(xì)看看 storageclass 中的拓?fù)湎拗?,通過上面的講解我們知道,這里限制使用該 storageclass 創(chuàng)建的 PV 存儲必須在可用區(qū) cn-hangzhou-d 可訪問的,而使用該存儲的 pod 也必須調(diào)度到?cn-hangzhou-d 的 node 上。

從零開始入門 K8s | 應(yīng)用存儲和持久化數(shù)據(jù)卷:存儲快照與拓?fù)湔{(diào)度

那就來看一下 node 節(jié)點(diǎn)上有沒有這個拓?fù)湫畔?,如果它沒有當(dāng)然是不行了。

看一下第一個 node 的全量信息吧,主要找它的 labels 里面的信息,看 lables 里面的確有一個這樣的 key。也就是說有一個這樣的拓?fù)?,但是這指定是?cn-hangzhou-b,剛才 storageclass 里面指定的是?cn-hangzhou-d。

從零開始入門 K8s | 應(yīng)用存儲和持久化數(shù)據(jù)卷:存儲快照與拓?fù)湔{(diào)度

那看一下另外的一個 node 上的這個拓?fù)湫畔懙囊彩?hangzhou-b,但是我們那個 storageclass 里面限制是 d。

從零開始入門 K8s | 應(yīng)用存儲和持久化數(shù)據(jù)卷:存儲快照與拓?fù)湔{(diào)度

這就導(dǎo)致最終沒辦法將 pod 調(diào)度在這兩個 node 上?,F(xiàn)在我們修改一下 storageclass 中的拓?fù)湎拗疲瑢?cn-hangzhou-d 改為 cn-hangzhou-b。

從零開始入門 K8s | 應(yīng)用存儲和持久化數(shù)據(jù)卷:存儲快照與拓?fù)湔{(diào)度

改完之后再看一下,其實(shí)就是說我動態(tài)創(chuàng)建出來的 PV 要能被 hangzhou-b 這個可用區(qū)訪問的,使用該存儲的 pod 要調(diào)度到該可用區(qū)的 node 上。把之前的 pod 刪掉,讓它重新被調(diào)度看一下有什么結(jié)果,好,現(xiàn)在這個已經(jīng)調(diào)度成功了,就是已經(jīng)在啟動容器階段。

從零開始入門 K8s | 應(yīng)用存儲和持久化數(shù)據(jù)卷:存儲快照與拓?fù)湔{(diào)度

說明剛才把 storageclass 它里面的對可用區(qū)的限制從 hangzhou-d 改為 hangzhou-b 之后,集群中就有兩個 node,它的拓?fù)潢P(guān)系是和 storageclass 里要求的拓?fù)潢P(guān)系是相匹配的,這樣的話它就能保證它的 pod 是有 node 節(jié)點(diǎn)可調(diào)度的。上圖中最后一點(diǎn) Pod 已經(jīng) Running 了,說明剛才的拓?fù)湎拗聘膭又罂梢?work 了。

四、處理流程

kubernetes 對 Volume Snapshot/Restore 處理流程

接下來看一下 K8s 中對存儲快照與拓?fù)湔{(diào)度的具體處理流程。如下圖所示:

從零開始入門 K8s | 應(yīng)用存儲和持久化數(shù)據(jù)卷:存儲快照與拓?fù)湔{(diào)度

首先來看一下存儲快照的處理流程,這里來首先解釋一下 csi 部分。K8s 中對存儲的擴(kuò)展功能都是推薦通過 csi out-of-tree 的方式來實(shí)現(xiàn)的。

csi 實(shí)現(xiàn)存儲擴(kuò)展主要包含兩部分:

  • 第一部分是由 K8s 社區(qū)推動實(shí)現(xiàn)的 csi controller 部分,也就是這里的 csi-snapshottor controller 以及 csi-provisioner controller,這些主要是通用的 controller 部分;
  • 另外一部分是由特定的云存儲廠商用自身 OpenAPI 實(shí)現(xiàn)的不同的 csi-plugin 部分,也叫存儲的 driver 部分。

兩部分部件通過 unix domain socket 通信連接到一起。有這兩部分,才能形成一個真正的存儲擴(kuò)展功能。

如上圖所示,當(dāng)用戶提交 VolumeSnapshot 對象之后,會被 csi-snapshottor controller watch 到。之后它會通過 GPPC 調(diào)用到 csi-plugin,csi-plugin 通過 OpenAPI 來真正實(shí)現(xiàn)存儲快照的動作,等存儲快照已經(jīng)生成之后,會返回到 csi-snapshottor controller 中,csi-snapshottor controller 會將存儲快照生成的相關(guān)信息放到 VolumeSnapshotContent 對象中并將用戶提交的 VolumeSnapshot 做 bound。這個 bound 其實(shí)就有點(diǎn)類似 PV 和 PVC 的 bound 一樣。

有了存儲快照,如何去使用存儲快照恢復(fù)之前的數(shù)據(jù)呢?前面也說過,通過聲明一個新的 PVC 對象,并且指定他的 dataSource 為 Snapshot 對象,當(dāng)提交 PVC 的時候會被 csi-provisioner watch 到,之后會通過 GRPC 去創(chuàng)建存儲。這里創(chuàng)建存儲跟之前講解的 csi-provisioner 有一個不太一樣的地方,就是它里面還指定了 Snapshot 的 ID,當(dāng)去云廠商創(chuàng)建存儲時,需要多做一步操作,即將之前的快照數(shù)據(jù)恢復(fù)到新創(chuàng)建的存儲中。之后流程返回到 csi-provisioner,它會將新創(chuàng)建的存儲的相關(guān)信息寫到一個新的 PV 對象中,新的 PV 對象被 PV controller watch 到它會將用戶提交的 PVC 與 PV 做一個 bound,之后 pod 就可以通過 PVC 來使用 Restore 出來的數(shù)據(jù)了。這是 K8s 中對存儲快照的處理流程。

kubernetes 對 Volume Topology-aware Scheduling 處理流程

接下來看一下存儲拓?fù)湔{(diào)度的處理流程:

從零開始入門 K8s | 應(yīng)用存儲和持久化數(shù)據(jù)卷:存儲快照與拓?fù)湔{(diào)度

第一個步驟其實(shí)就是要去聲明延遲綁定,這個通過 StorageClass 來做的,上面已經(jīng)闡述過,這里就不做詳細(xì)描述了。

接下來看一下調(diào)度器,上圖中紅色部分就是調(diào)度器新加的存儲拓?fù)湔{(diào)度邏輯,我們先來看一下不加紅色部分時調(diào)度器的為一個 pod 選擇 node 時,它的大概流程:

  • 首先用戶提交完 pod 之后,會被調(diào)度器 watch 到,它就會去首先做預(yù)選,預(yù)選就是說它會將集群中的所有 node 都來與這個 pod 它需要的資源做匹配;
  • 如果匹配上,就相當(dāng)于這個 node 可以使用,當(dāng)然可能不止一個 node 可以使用,最終會選出來一批 node;
  • 然后再經(jīng)過第二個階段優(yōu)選,優(yōu)選就相當(dāng)于我要對這些 node 做一個打分的過程,通過打分找到最匹配的一個 node;
  • 之后調(diào)度器將調(diào)度結(jié)果寫到 pod 里面的 spec.nodeName 字段里面,然后會被相應(yīng)的 node 上面的 kubelet watch 到,最后就開始創(chuàng)建 pod 的整個流程。

那現(xiàn)在看一下加上卷相關(guān)的調(diào)度的時候,篩選 node(第二個步驟)又是怎么做的?

  • 先就要找到 pod 中使用的所有 PVC,找到已經(jīng) bound 的 PVC,以及需要延遲綁定的這些 PVC;
  • 對于已經(jīng) bound 的 PVC,要 check 一下它對應(yīng)的 PV 里面的 nodeAffinity 與當(dāng)前 node 的拓?fù)涫欠衿ヅ?。如果不匹配, 就說明這個 node 不能被調(diào)度。如果匹配,繼續(xù)往下走,就要去看一下需要延遲綁定的 PVC;
  • 對于需要延遲綁定的 PVC。先去獲取集群中存量的 PV,滿足 PVC 需求的,先把它全部撈出來,然后再將它們一一與當(dāng)前的 node labels 上的拓?fù)渥銎ヅ?,如果它?存量的 PV)都不匹配,那就說明當(dāng)前的存量的 PV 不能滿足需求,就要進(jìn)一步去看一下如果要動態(tài)創(chuàng)建 PV 當(dāng)前 node 是否滿足拓?fù)湎拗?,也就是還要進(jìn)一步去 check StorageClass 中的拓?fù)湎拗?,如?StorageClass 中聲明的拓?fù)湎拗婆c當(dāng)前的 node 上面已經(jīng)有的 labels 里面的拓?fù)涫窍嗥ヅ涞?,那其?shí)這個 node 就可以使用,如果不匹配,說明該 node 就不能被調(diào)度。

經(jīng)過這上面步驟之后,就找到了所有即滿足 pod 計(jì)算資源需求又滿足 pod 存儲資源需求的所有 nodes。<br />?<br />當(dāng) node 選出來之后,第三個步驟就是調(diào)度器內(nèi)部做的一個優(yōu)化。這里簡單過一下,就是更新經(jīng)過預(yù)選和優(yōu)選之后,pod 的 node 信息,以及 PV 和 PVC 在 scheduler 中做的一些 cache 信息。

第四個步驟也是重要的一步,已經(jīng)選擇出來 node 的 Pod,不管其使用的 PVC 是要 binding 已經(jīng)存在的 PV,還是要做動態(tài)創(chuàng)建 PV,這時就可以開始做。由調(diào)度器來觸發(fā),調(diào)度器它就會去更新 PVC 對象和 PV 對象里面的相關(guān)信息,然后去觸發(fā) PV controller 去做 binding 操作,或者是由 csi-provisioner 去做動態(tài)創(chuàng)建流程。

總結(jié)

  1. 通過對比 PVC&PV 體系講解了存儲快照的相關(guān) K8s 資源對象以及使用方法;
  2. 通過兩個實(shí)際場景遇到的問題引出存儲拓?fù)湔{(diào)度功能必要性,以及 K8s 中如何通過拓?fù)湔{(diào)度來解決這些問題;
  3. 通過剖析 K8s 中存儲快照和存儲拓?fù)湔{(diào)度內(nèi)部運(yùn)行機(jī)制,深入理解該部分功能的工作原理。

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

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

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

AI