自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Kubernetes Storage 101: 淺談 Kubernetes 存儲概念,解鎖數據驅動的力量

云計算 云原生
Container 它本質上是無狀態(tài)的,且內容存在的時間極為短暫。一旦容器關閉,它就會回到最初的狀態(tài)。這就意味著在容器處于活動狀態(tài)時,由應用產生的數據也就丟掉了。

Kubernetes 可以說是已經成為云原生分布式操作系統(tǒng)的事實標準了,它最大的優(yōu)勢在于可擴展性,不論是計算、存儲還是網絡,它都可以根據使用者的需求來進行靈活擴展。

我曾在團隊內部就 Kubernetes Storage 主題做過分享,內容較為基礎,旨在激發(fā)大家的思考。今天我將通過文稿的形式將這些分享整理出來,重新閱讀時,我發(fā)現自己從中收獲了很多,希望對其他朋友也能有所幫助。

由于篇幅較長,我們將從 Kubernetes 存儲的基本概念和術語開始。

為什么說 Kubernetes 存儲很重要?

對于開發(fā)工程師來說,Container 想必大家都已經不陌生了。

Container 它本質上是無狀態(tài)的,且內容存在的時間極為短暫。一旦容器關閉,它就會回到最初的狀態(tài)。這就意味著在容器處于活動狀態(tài)時,由應用產生的數據也就丟掉了。

存儲可以說是支撐應用的一個基石,數據若沒了,應用必定無法正常工作,所以這種情況對于應用來說肯定是不能夠被接受的。

隨著應用逐漸往云原生轉型,讓我們來看下應用在上 Kubernetes 平臺都會碰到哪些存儲問題。

1. 應用的配置

每個應用程序都有它自己的配置,通常的做法是將配置和代碼解耦,保持其靈活性。應用代碼走鏡像,那么配置在 Kubernetes 里又是如何處理的呢?

2. 容器間數據通信

Pod 作為 Kubernetes 里的最小部署單元,Pod 里多個容器內的數據共享怎么做?又或者說微服務架構下不同節(jié)點上的 Pod 數據又是如何做通信的?

3. 容器內數據持久化

Pod 是非常脆弱的,如果 Pod 掛了,做了重新調度,應用在舊 Pod 里寫的數據還能找回嗎?

4. 集成第三方存儲服務

比方說我們有自己的存儲服務,又或者說有客戶要求必須使用國產的某個存儲系統(tǒng),這個時候我們有沒有辦法可以將它們集成到 Kubernetes 的存儲體系里?

解決方案

為了解決以上這些問題,Kubernetes 引入了 Volume 這個抽象的概念。用戶只需通過資源和聲明式 API 來描述自己的意圖,Kubernetes 自會根據用戶的需求來完成具體的操作。

Volume 一詞它最初來源于操作系統(tǒng)的術語,在 Docker 中也有類似的概念。在 Kubernetes 世界中,Volume 是指一種可插拔的抽象層,用于在 Pod 和容器之間共享和持久化數據。

本篇文章我們將一起探討 Kubernetes 中 Volume 的概念與應用。

Kubernetes 存儲體系

我們知道存儲技術的種類非常多,Kubernetes 為了盡可能多地兼容各種存儲,因此它預置了很多插件,它將選擇權交到了用戶手里,讓用戶根據自己的業(yè)務需求來選擇。

kubectl explain pod.spec.volumes

這個命令將返回一個關于 Kubernetes Pod 中 Volume 定義的詳細說明,包括其字段和用法,它可以幫助用戶了解如何在 Pod 中定義一個 Volume。

使用上述命令,我們可以發(fā)現 Kubernetes 默認支持了至少 20 多種存儲卷類型,這些存儲卷類型可以滿足各種不同的存儲需求。這個特性使得 Kubernetes 在存儲方面非常靈活,并且可以適應各種應用程序的存儲要求。

下面這張表格是根據存儲插件的種類,做的 2 個大類,表格里只列出了比較常用的卷類型,但還有其他類型可以在官方文檔的 Types of Persistent Volumes[1] 中找到。如果有對 In-Tree Volume[2] 的實現感興趣的同學,可以查看源代碼。

圖片圖片

那么 In-Tree 和 Out-Of-Tree 兩者有什么區(qū)別呢?我們可以從字面上先大致理解一下它們的區(qū)別,后續(xù)我會詳細介紹它們的不同之處。

可以將 In-Tree 理解為將實現代碼放在 Kubernetes 代碼倉庫中;而 Out-Of-Tree 則表示代碼實現與 Kubernetes 本身解耦,存放在 Kubernetes 代碼倉庫之外。

小結

乍一看,Kubernetes 支持的存儲卷類型太多了,對于不熟悉的同學可能感到無從下手。然而,總結起來其實主要有兩種類型:

  1. 非持久化的 Volume(Non-Persistent Volume or also called ephemeral storage)
  2. 持久化的 PersistentVolume

Non-Persistent Volume vs Persistent Volume

在介紹這兩種 Volume 的主要區(qū)別之前,我覺得有必要聲明一下它們的共同點:

1. 使用方式

  • 首先都是通過在 .spec.volumes[] 里定義使用哪一種 Volume 類型
  • 然后將其掛載到容器的指定位置 .spec.containers[].volumeMounts[]

2. 宿主機目錄

不論是哪種 Volume 類型,Kubelet 都會為調度到當前 HOST 上的 Pod, 創(chuàng)建它的 Volume 目錄,格式如下

/var/lib/kubelet/pods/<Pod-UID>/volumes/kubernetes.io~<Volume-Type>/<Volume-Name>

#1. Non-Persistent Volume

設計 Non-Persistent Volume 的目的并非為了持久的保存數據,它是為同一個 Pod 中多個 Container 之間提供可共享的存儲資源。

VolumeVolume

從上面的架構圖可以看出,Volume 是包在 Pod 內的,因此其生命周期與掛載它的 Pod 是一致的。當 Pod 因某種原因被銷毀時,Volume 也會隨之刪除,但至少它的生命周期要比 Pod 中運行的任何一個容器的存活時間長。

思考

以下是 Kubernetes 官方文檔對 Pod 生命周期的描述

Pods are only scheduled once in their lifetime. Once a Pod is scheduled (assigned) to a Node, the Pod runs on that Node until it stops or is terminated.

對于配置了 restartPolicy != Never 的 Pod,即使發(fā)生異常導致 Pod 重啟,Pod 本身不會重新調度,因此這些 Volume 的數據其實都還在,因為 Pod 的 UID 并未發(fā)生變化,它并沒有被刪除。

?? 另外這里有必要說明一下,重啟后 Pod 背后的 Container 雖然是全新的,但是舊的 Container 仍然存在于當前的 HOST 上,它只是處于 Exited 狀態(tài)而已,想必大家也明白我的意思了吧?這就意味著在舊 Container 處于活動狀態(tài)時,用戶在普通目錄下寫入的數據,我們仍然有機會可以通過容器讀寫層將數據找回。

除非被 Kubelet GC 回收機制清理掉了。

2. Persistent Volume

設計 Persistent Volume 的目的是為了持久的保存數據,且它能為不同節(jié)點上的 Pod 中多個 Container 提供可共享的存儲資源。

Persistent VolumePersistent Volume

很明顯,從上面的架構中看到 Persistent Volume 是獨立于 Pod 存在的,所以它的生命周期與 Pod 是無關的。

ConfigMap/Secret & EmtpyDir & HostPath

下面簡單過一下 Kubernetes 平臺經常用到的都有哪些存儲卷,我們先站在使用者的角度去看這些存儲卷都是怎么使用的?

這三種類型我在前面的文章里也都分享過,只是沒有特意強調存儲這個概念?,F在我將它們放在一起,或許可以讓大家更深刻理解到它們的適用場景。

ConfigMap/Secret 適用場景

ConfigMap 和 Secret 這兩個資源通常用來存儲應用程序的配置,是 Kubernetes 平臺作為解耦配置和代碼的一種常用手段。

最佳實踐

圖片圖片

一份基準代碼(Codebase),多份部署(deploy)

我們可以將它們當做一個存儲卷來使用,下面是一個比較常見的用法,詳細內容可以點擊圖片直接跳轉查看。

ConfigMap 常見用法ConfigMap 常見用法

EmtpyDir 適用場景

EmtpyDir 可以說是一種很常用的 Volume 了,顧名思義,它剛被創(chuàng)建出來的時候是一個空目錄,通常被用于 Pod 內多個容器之間的數據共享。它屬于 Non-Persistent Volume,Pod 被刪除時,在 EmtpyDir 內生成的數據也一并會被清除。

最佳實踐

我們以下面這個架構舉例,其中 Init Container 用來作數據的準備容器,負責寫內容;Application Container 作為主應用的容器,負責數據的讀取。

該例子不僅詮釋了 Kubernetes 容器編排的魅力,同時它也是富容器的一種解決方案,具體可以點擊圖片查看詳細內容 Scenario 03

容器間利用 EmtpyDir Volume 實現數據的通信

我們再來看另外一個比較常見的例子,sidecar 容器通過 EmtpyDir 來讀取另外一個容器的日志文件。

cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
  name: counter
spec:
  containers:
  - name: count
    image: lqshow/busybox-curl:1.28
    args:
    - /bin/sh
    - -c
    - >
      i=0;
      while true;
      do
        echo "$i: $(date)" >> /var/log/1.log;
        i=$((i+1));
        sleep 1;
      done
    volumeMounts:
    - name: varlog
      mountPath: /var/log
  - name: count-log-sidecar
    image: lqshow/busybox-curl:1.28
    args: [/bin/sh, -c, 'tail -n+1 -f /var/log/1.log']
    volumeMounts:
    - name: varlog
      mountPath: /var/log
  volumes:
  - name: varlog
    emptyDir: {}
EOF

正如大家所見,Pod 里 count 容器的日志并未以 stdout 的方式輸出,我們若想用以下命令其實是抓不到日志的。

kubectl logs -f counter -c count

但是我們可以通過一個 sidecar 容器,將日志文件讀出來重新以 stdout 的方式輸出來。

當然這種處理日志的方式肯定不是最佳的,因為它會導致宿主機上會形成 2 份一樣的日志,其實完全沒有必要的。但是這里舉這么一個例子用來幫助理解 emptyDir 的適用場景,我是覺得還蠻合適的。

kubectl logs -f counter -c count-log-sidecar

HostPath 適用場景

HostPath 它是將宿主機節(jié)點上的文件系統(tǒng)上的文件或目錄,直接掛載到 Pod 中的,另外需要注意的是卷數據是持久化在宿主機節(jié)點的文件系統(tǒng)內的。這種 Volume 類型其實不適合大部分的應用,因為 Volume 里的內容它只保留在某個特定的節(jié)點上,一旦 Pod 做了重新分配,調度到了其他節(jié)點,原數據是不會被帶過來的。

所以一般情況下不建議使用 hostpath,除非你有一個非常具體的需求,并且了解你在做什么,是否 make sense?

最佳實踐

HostPath 通常和 DaemonSet 搭配使用,我們繼續(xù)以上面提到的日志收集為例,每個節(jié)點上會跑一個 Logging agent(Fluentd),它會掛載宿主機上的容器日志目錄,來達到收集當前主機日志的目的。

還有我們本次分享的主題之一,各種存儲插件的 Agent 組件(CSI),它也必須運行在每一個節(jié)點上,用來在這個節(jié)點上掛載遠程存儲目錄,操作容器的 Volume 目錄。

當然它還有很多其他用法,大家自己去發(fā)現啦,我就不在多做介紹了。

思考

HostPath + NodeAffinity 雖然能做到偽持久化,看似有了 PV 的能力,但是我們不建議這么使用,因為這種方式很容易會將宿主機上的磁盤寫滿,最終會導致當前節(jié)點 NotReady。

另外出于對集群安全的考慮,我們通常都會限制 HostPath 掛載,畢竟 HostPath Volume 存在許多安全風險,如果不加以限制,用戶真的有可能對 Node 做任何事情。

PV,PVC & Storage Class

PV 和 PVC 是 Kubernetes 存儲體系里非常重要的兩個資源,它們的引入主要是為了實現職責分離和解耦。

這么說吧,對于應用開發(fā)者來說,他們無需關心存儲設施的細節(jié),只需關注應用對存儲資源的需求。

其實還有一個原因,作為開發(fā)者的我們并不知道集群里有哪些 volume 類型可用,而且存儲相關的配置也確實非常的復雜。存儲涉及的知識領域很專業(yè),俗話說讓專業(yè)人做專業(yè)事,可以更好的提高做事效率。

我們先來看下官方對這兩個資源的解讀吧。

名詞解釋

1. Persistent Volume (PV)

Cluster 級別的資源,它由集群管理員或者是External Provisioner組件創(chuàng)建。

它描述的是持久化存儲數據卷。

2. Persistent Volume Claim (PVC)

Namespace 級別的資源,它由開發(fā)者或者是StatefulSet控制器(根據 VolumeClaimTemplate) 創(chuàng)建,另外通用臨時卷(Generic ephemeral volume)也會創(chuàng)建其生命周期與 Pod 一致的臨時存儲卷。

它描述的是 Pod 對持久化存儲的需求屬性,如 Volume 的容量、訪問模式等,提供了對底層存儲資源的抽象表述。

通過使用 PVC,可以使 Pod 跨集群移植成為可能。

3. Storage Class

Cluster 級別的資源,它由集群管理員創(chuàng)建,它定義了動態(tài)供應和配置 PV 的能力。Storage Class 提供了一種動態(tài)分配 PV 的機制,根據 PVC 的請求自動創(chuàng)建 PV,并實現了存儲的動態(tài)供應和管理。

這里有必要提一下 StorageClass 中的 parameters 字段,它雖然是一個可選的字段,用于指定與存儲類相關的參數,但是在開發(fā) CSI Driver 的時候,這個參數非常的有用,具體參見:Secrets and Credentials[3]。這些參數的具體含義取決于所使用的存儲插件和存儲供應商。

source: docker.com

怎么理解呢?

對于剛接觸 Kubernetes 不久的同學來說,僅從名詞解釋上可能不大好理解。

一開始我也感到困惑,不知道這些資源是什么,如何使用,以及它們適用的場景。

沒關系,接下來我們將從不同角度對這些資源進行解讀,希望能幫助大家建立概念。最后,我會介紹它們的具體用法。

#1. 從資源維度

以下是一個恰當的比喻,雖然出處未知,但我覺得很適合,所以直接引用了

PV 資源的 .Spec 中保存了存儲設備的詳細信息,我們可以把 PVC 想象成 Pod。

  1. Pod 消耗 Node 資源,而 PVC 消耗的是 PV 資源
  2. Pod 可以請求特定級別的資源(比如 CPU 和內存),而 PVC 可以請求特定存儲卷的大小及訪問模式
  3. PVC 將應用程序與它背后特定的存儲做解耦

2. 從關注點分離維度

應用開發(fā)者

首先需要明確的是,對應用開發(fā)者來說,我們只會跟 PVC 這個資源打交道,因為只有我們開發(fā)者才知道自己的應用大概需要多少存儲空間。

在 Kubernetes 中,PVC 可以被視為持久化存儲的抽象接口。它描述了對特定持久化存儲的需求和屬性,但并不負責實際的存儲實現。PV 負責實現具體的存儲,并與 PVC 進行綁定。

我們只需要明確自己的需求,即存儲需要的大小以及訪問模式就夠了,而不必關心存儲背后具體是 NFS 還是 Ceph 等實現方式,這并不是我們需要關心的事情。

這種分離的設計讓我們能夠專注于應用開發(fā),無需關心底層存儲技術的選擇和實現,從而提高我們的效率和靈活性。

運維人員

PV 資源通常是由運維人員來創(chuàng)建的,因為集群內會提供哪些存儲,也只有集群運維人員才知道,這個應該比較好理解。

Provisioning

集群內提供 PV 一般有兩種方式,下面我們來分別看下這兩種方式的優(yōu)缺點

1. Static Provisioning

Static Provisioning

使用流程
  1. 首先由集群管理員基于一些底層網絡存儲資源創(chuàng)建一個靜態(tài)持久化卷
  2. 然后用戶創(chuàng)建一個 PVC 聲明,用來申請第一步的特定持久化卷
  3. 最后創(chuàng)建一個 Pod 并同時使用這個 PVC
思考

這種方式好處是非常明顯的,可以說集群內所有的存儲資源都在運維的掌控中。

但是,供給靜態(tài) PV 這種流程,PV 資源必須是由運維人員參與創(chuàng)建的,它也帶來一些問題:

  1. 小規(guī)模的集群還好,如果在大規(guī)模的生產環(huán)境下,用戶應用的實例是不可控的,可能需要成千上萬個 PV,無法預測。這種手動且重復的工作,效率不僅低下,且這種操作對管理員也是一種痛苦。
  2. 另外這種方式也很容易導致存儲使用效率低下
  • 要么配置少了,限制程序的運行
  • 要么過度配置了,導致存儲的浪費

那么如何實現自動化呢?這就得依賴 Storage Class 這個概念了,它引入了動態(tài)存儲配置的流程。

2. Dynamic Provisioning

Dynamic Provisioning

使用流程

從上面的流程圖來看,很明顯整個流程少了管理員的介入。

  1. 用戶創(chuàng)建一個 PVC 的時候,由對應的 Provision 自動創(chuàng)建一個 PV 與它進行一一綁定。怎么對應呢?就是通過 storageClassName 來尋找。
  2. 創(chuàng)建一個 Pod 并同時使用這個 PVC,這個流程兩者都是一致的。
思考

自動創(chuàng)建 PV 這種模式,它可以說是完全改變了部署的工作方式。

它其實不僅僅是解放了運維人員這么簡單,對 DevOps 的推進也提供了很大的幫助。

PV 和 PVC 綁定條件

?? StorageClass: PV 和 PVC 必須屬于相同的 StorageClass。?? 訪問模式(Access Modes): PV 和 PVC 的訪問模式必須兼容,訪問模式定義了 Pod 如何訪問存儲卷。?? 容量(Capacity):PVC 的請求容量不能超過 PV 的容量。PVC 可以請求一個特定大小的存儲卷,而 PV 必須具備足夠的容量來滿足 PVC 的請求。?? Selector(選擇器): PV 可以定義一個或多個 Label Selector,而 PVC 可以通過 Label Selector 來選擇匹配的 PV。PVC 的 Selector 必須匹配 PV 的 Label Selector 才能進行綁定。

當滿足這些綁定條件時,Kubernetes 將會自動將 PVC 綁定到相應的 PV 上,從而實現持久化存儲的分配和使用。

思考

在 Kubernetes 中,PV 與 PVC 是一對一的關系,一個 PV 只能綁定一個 PVC。然而,多個 Pod 可以共享同一個 PVC,從而使用該 PVC 提供的持久化存儲。事實上,我們在生產環(huán)境并不會直接使用 Pod,因為它會因為各種原因被關閉而不再提供服務,比如被節(jié)點驅逐。

一般情況下我們使用 Deployment, 它可以管理多個副本(replicas)的 Pod,在以下場景中,我們有 3 個 Pod 副本,它們掛載同一個 PVC,如果該 PVC 只有只讀權限的話,那么不會出現任何問題。

DeploymentDeployment

但是,如果該 PVC 具有讀寫權限,然后又被 3 個 Pod 副本共享,這可能會導致數據不一致的問題。好比說會碰到 Race Condition:如果兩個或更多的 Pod 同時寫入相同的文件,可能會發(fā)生數據覆蓋或者數據不一致的情況。

那么有什么辦法可以解決這個問題呢?那就是 StatefulSet 工作負載,它也有多副本的概念。

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: web
spec:
  ...
  replicas: 3
  template:
    ...
  volumeClaimTemplates: # 定義創(chuàng)建與該 StatefulSet 相關的 PVC 的模板
  - metadata:
      name: www
    spec:
      accessModes: ["ReadWriteOnce"]
      storageClassName: "my-storage-class"
      resources:
        requests:
          storage: 1Gi

不同之處我們可以通過定義 volumeClaimTemplates,以便自動為每個副本創(chuàng)建一個新的 PVC,如下圖所示

StatefulSetStatefulSet

當然,實際場景往往會更加的復雜,尤其是分布式應用,如主從關系需要考慮 StatefulSet 提供的 Pod 標識與順序等特性。

我們再來看一下 PVC 最后一種創(chuàng)建方式:它就是通用臨時卷(Generic ephemeral volume)。

需要注意的是,Generic Ephemeral Volume 中的數據只會在容器的生命周期內保留,當 Pod 被刪除或重啟時,一并生產的 PVC 同時也被刪除。因此,Generic Ephemeral Volume 適用于需要在容器生命周期內保留一些臨時數據的場景,例如緩存文件或臨時配置文件。

kind: Pod
apiVersion: v1
metadata:
  name: my-app
spec:
  containers:
    - name: my-frontend
      volumeMounts:
      - mountPath: "/scratch"
        name: scratch-volume
      ...
  volumes:
    - name: scratch-volume
      ephemeral:
        volumeClaimTemplate:
          metadata:
            labels:
              type: my-frontend-volume
          spec:
            accessModes: [ "ReadWriteOnce" ]
            storageClassName: "scratch-storage-class"
            resources:
              requests:
                storage: 1Gi

Default Storage Class

管理員可以將集群內的某個存儲類打上 annotation,作為 Default Storage Class,它是預定義的一種特殊存儲類,用于指定在未顯式指定存儲類的情況下所使用的默認存儲類。

kubectl patch storageclass <STORAGE-CLASS-NAME> -p \
'{"metadata": {"annotations":{"storageclass.kubernetes.io/is-default-class":"true"}}}'

Container Storage Interface

盡管 Kubernetes 平臺本身支持多種存儲插件,但由于用戶需求不斷增長,這些插件往往無法滿足所有要求。比方說,當客戶要求我們的 PaaS 平臺必須與國產某個存儲集成時,我們該如何應對?

此外,這些存儲插件基本上是 In-tree的,如果插件需要做 patch,當前的模式會給測試和維護帶來不便。

這時就不得不提到容器存儲接口(CSI),它本質上只是定義了一套協(xié)議標準,第三方存儲插件只要實現這些統(tǒng)一的接口,就能對接 Kubernetes,用戶無需接觸核心的 Kubernetes 代碼。

適配工作由容器編排系統(tǒng)(如 Kubernetes)和存儲提供商(SP)共同完成,CO 通過 gRPC 與 CSI 插件進行通信。

圖片圖片

CSI 其實蠻復雜的,涉及到的組件相當多,后面我會專門寫一篇文章介紹 CSI 的工作原理以及遇到的挑戰(zhàn)。

寫在最后

通過對這些基本概念的理解,我們能夠在 Kubernetes 中正確配置和管理存儲資源,滿足應用程序對持久化存儲的需求。

參考資料

[1]Types of Persistent Volumes: https://kubernetes.io/docs/concepts/storage/persistent-volumes/#types-of-persistent-volumes

[2]In-Tree Volume: https://github.com/kubernetes/kubernetes/tree/master/pkg/volume

[3]Secrets and Credentials: https://kubernetes-csi.github.io/docs/secrets-and-credentials.html

責任編輯:武曉燕 來源: Cloud Native 101
相關推薦

2022-11-29 08:05:48

KubernetesPVCSI

2018-11-21 10:36:29

Kubernetes存儲Docker

2023-12-18 08:23:12

CSI插件Kubernetes

2024-09-29 13:53:58

數據飛輪數據中臺數字化轉型

2021-04-14 09:33:58

Kubernetes通信網絡模型

2023-04-28 08:11:46

Kubernetes容器

2021-02-19 08:38:36

Kubernetes容器化分布式

2024-09-29 18:49:39

2021-04-13 05:38:35

Kubernetes存儲數據庫

2021-01-12 14:46:34

Kubernetes開發(fā)存儲

2023-11-20 20:45:38

2024-02-22 15:35:05

2018-06-21 15:14:51

Kubernetes存儲容器

2021-11-22 14:54:36

Kubernetes存儲

2018-07-19 10:56:16

Kubernetes存儲架構

2019-01-15 17:50:18

存儲技術容器

2022-01-27 13:47:10

Kubernete命令Linux

2025-04-29 10:00:00

Kubernete云原生Helm

2020-07-01 08:05:46

Kubernetes容器開發(fā)

2024-09-29 18:31:16

解鎖數據在線教育飛輪效應
點贊
收藏

51CTO技術棧公眾號