k8s十一 | 持久化存儲(chǔ) PV&PVC&StorageClass

在kubernetes中Pod的生命周期是很短暫的,會(huì)被頻繁地銷毀和創(chuàng)建。容器銷毀時(shí),保存在容器內(nèi)部文件系統(tǒng)中的數(shù)據(jù)都會(huì)被清除。為了持久化保存容器的數(shù)據(jù),需要使用 Kubernetes Volume數(shù)據(jù)卷,下面文章分別以本地卷,網(wǎng)絡(luò)數(shù)據(jù)卷的方式來(lái)實(shí)踐集群的持久化存儲(chǔ)。

一、hostPath/emptyDir


  • hostPath:用于將目錄從宿主機(jī)節(jié)點(diǎn)的文件系統(tǒng)掛載到pod中,屬于單節(jié)點(diǎn)集群中的持久化存儲(chǔ),刪除pod后,卷里面的文件會(huì)繼續(xù)保持,在同一節(jié)點(diǎn)運(yùn)行的Pod可以繼續(xù)使用數(shù)據(jù)卷中的文件,但pod被重新調(diào)度到其他節(jié)點(diǎn)時(shí),就無(wú)法訪問(wèn)到原數(shù)據(jù)。不適合作為存儲(chǔ)數(shù)據(jù)庫(kù)數(shù)據(jù)的目錄。

  • emptyDir: 用于存儲(chǔ)臨時(shí)數(shù)據(jù)的簡(jiǎn)單空目錄,生命周期是和pod捆綁的,隨著pod創(chuàng)建而創(chuàng)建;刪除而銷毀,卷的內(nèi)容將會(huì)丟失。emptyDir卷適用于同一個(gè)pod中運(yùn)行的容器之間共享文件。

hostPath定義如下:

....
    volumeMounts:
    - name: data
      mountPath: /data
  volumes:
  - name: data
    hostPath:
      path: /opt/data
      type: Directory

emptyDir定義如下:

....
    volumeMounts:
      - name: data
        mountPath: /data
  volumes:
  - name: data
    emptyDir: {}

二、PV/PVC


前面使用的 hostPath 和 emptyDir 類型的 Volume 并不具備持久化特征,它們既有可能被 kubelet 清理掉,也不能被“遷移”到其他節(jié)點(diǎn)上。所以,大多數(shù)情況下,持久化 Volume 的實(shí)現(xiàn),往往依賴于一個(gè)遠(yuǎn)程存儲(chǔ)服務(wù),比如:遠(yuǎn)程文件存儲(chǔ)(比如,NFS、GlusterFS)、遠(yuǎn)程塊存儲(chǔ)(比如,公有云提供的遠(yuǎn)程磁盤)等等。而 Kubernetes 需要做的工作,就是使用這些存儲(chǔ)服務(wù),來(lái)為容器準(zhǔn)備一個(gè)持久化的宿主機(jī)目錄,以供將來(lái)進(jìn)行綁定掛載時(shí)使用。而所謂“持久化”,指的是容器在這個(gè)目錄里寫入的文件,都會(huì)保存在遠(yuǎn)程存儲(chǔ)中,從而使得這個(gè)目錄具備了“持久性”。為了屏蔽底層的技術(shù)實(shí)現(xiàn)細(xì)節(jié),讓用戶更加方便的使用,Kubernetes 便引入了 PV 和 PVC 兩個(gè)重要的資源對(duì)象來(lái)實(shí)現(xiàn)對(duì)存儲(chǔ)的管理。

  • PV:持久化存儲(chǔ)數(shù)據(jù)卷,全稱為PersistentVolume,PV其實(shí)是對(duì)底層存儲(chǔ)的一種抽象,通常是由集群的管理員進(jìn)行創(chuàng)建和配置 ,底層存儲(chǔ)可以是Ceph,GlusterFS,NFS,hostpath等,都是通過(guò)插件機(jī)制完成與共享存儲(chǔ)的對(duì)接。

  • PVC: 持久化數(shù)據(jù)卷聲明,全稱為PersistentVolumeClaim,PVC 對(duì)象通常由開(kāi)發(fā)人員創(chuàng)建,描述 Pod 所希望使用的持久化存儲(chǔ)的屬性。比如,Volume 存儲(chǔ)的大小、可讀寫權(quán)限等等。PVC綁定PV,消耗的PV資源。

下面創(chuàng)建一個(gè)NFS類型的PV,首先節(jié)點(diǎn)部署NFS,共享/data/k8s目錄

$ systemctl stop firewalld.service
$ yum -y install nfs-utils rpcbind
$ mkdir -p /data/k8s
$ chmod 755 /data/k8s
$ vim /etc/exports
/data/k8s  *(rw,sync,no_root_squash)
$ systemctl start rpcbind.service
$ systemctl start nfs.service
$ mount 192.168.16.173:/data/k8s  /data/k8s

創(chuàng)建nfspv.yaml

apiVersion: v1
kind: PersistentVolume
metadata:
  name: nfs
spec:
  storageClassName:  manual
  capacity:
    storage: 5Gi
  accessModes:
    - ReadWriteMany
  nfs:
    server: 192.168.16.173
    path: "/data/k8s"

文件的內(nèi)容定義如下:

  • spec.storageClassName:定義了名稱為 manual 的 StorageClass,該名稱用來(lái)將 PVC請(qǐng)求綁定到該 PV。
  • spec.Capacity:定義了當(dāng)前PV的存儲(chǔ)空間為storage=10G。
  • spec.nfs: 定義了PV的類型為NFS,并指定了該卷位于節(jié)點(diǎn)上的 /data/k8s/目錄。
  • spec.accessModes:定義了當(dāng)前的訪問(wèn)模式,可定義的模式如下:
    • ReadWriteMany(RWX):讀寫權(quán)限,可以被多個(gè)節(jié)點(diǎn)掛載。
    • ReadWriteOnce(RWO):讀寫權(quán)限,但是只能被單個(gè)節(jié)點(diǎn)掛載;
    • ReadWriteMany(ROX):只讀權(quán)限,可以被多個(gè)節(jié)點(diǎn)掛載。

下圖是一些常用的 Volume 插件支持的訪問(wèn)模式:


image.png

創(chuàng)建資源對(duì)象:

$ kubectl  create -f  nfspv.yaml 
persistentvolume/nfs created
$ kubectl  get pv
NAME          CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM                  STORAGECLASS    REASON   AGE
nfs           5Gi        RWX            Retain           Available                          manual                   4s

可以看到創(chuàng)建后的PV的Status為Available 可用狀態(tài),意味著當(dāng)前PV還沒(méi)有被PVC綁定使用。
PV生命周期的不同狀態(tài)如下:

  • Available(可用):表示可用狀態(tài),還未被任何 PVC 綁定
  • Bound(已綁定):表示 PVC 已經(jīng)被 PVC 綁定
  • Released(已釋放):PVC 被刪除,但是資源還未被集群重新聲明
  • Failed(失敗): 表示該 PV 的自動(dòng)回收失敗

另外還有一個(gè)RECLAIM POLICY字段輸出內(nèi)容為Retain,這個(gè)字段輸出的其實(shí)是PV的回收策略,目前 PV 支持的策略有三種:

  • Retain(保留):保留數(shù)據(jù),需要管理員手工清理數(shù)據(jù)
  • Recycle(回收):清除 PV 中的數(shù)據(jù),效果相當(dāng)于執(zhí)行 rm -rf /thevoluem/*
  • Delete(刪除):與 PV 相連的后端存儲(chǔ)完成 volume 的刪除操作,當(dāng)然這常見(jiàn)于云服務(wù)商的存儲(chǔ)服務(wù),比如 ASW EBS。

注意:目前只有 NFS 和 HostPath 兩種類型支持回收策略。

現(xiàn)在我們創(chuàng)建一個(gè)PVC來(lái)綁定上面的PV

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: nfs-pvc
spec:
  storageClassName:  manual
  accessModes:
  - ReadWriteMany
  resources:
    requests:
      storage: 3Gi

創(chuàng)建PVC

$ kubectl  get pvc
  NAME      STATUS   VOLUME   CAPACITY   ACCESS MODES   STORAGECLASS   AGE
  nfs-pvc   Bound    nfs      5Gi        RWX            manual         3m59s
$ kubectl   get pv
  NAME   CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                 STORAGECLASS   REASON   AGE 
  nfs    5Gi        RWX            Retain           Bound    default/nfs-pvc   manual                  20m

可以看到PV和PVC的狀態(tài)已經(jīng)為Bound綁定狀態(tài),其中綁定需要檢查的條件,包括兩部分:

  1. 第一個(gè)條件,當(dāng)然是 PV 和 PVC 的 spec 字段。比如,PV 的存儲(chǔ)(storage)大小,權(quán)限等就必須滿足 PVC 的要求。
  2. 而第二個(gè)條件,則是 PV 和 PVC 的 storageClassName 字段必須一樣。

創(chuàng)建一個(gè)Pod,PVC使用方式和hostpath類似

apiVersion: v1
kind: Pod
metadata:
  name: web-front
spec:
  containers:
  - name: web
    image: nginx
    ports:
      - name: web
        containerPort: 80
    volumeMounts:
        - name: nfs
          mountPath: "/usr/share/nginx/html"
  volumes:
  - name: nfs
    persistentVolumeClaim:
      claimName: nfs-pvc

Pod將PVC掛載到容器的html目錄,我們?cè)赑VC的目錄下創(chuàng)建一個(gè)文件用來(lái)驗(yàn)證

$ echo  "hello nginx" > /data/k8s/index.html

創(chuàng)建Pod,并驗(yàn)證是否將文件掛載至容器

$ kubectl  create -f nfs-nginxpod.yaml
$ kubectl exec -it  web-front  -- /bin/bash
  root@web-front:/# curl localhost
  hello nginx

可以看到輸出結(jié)果是我們前面寫到PVC卷中的index.html 文件內(nèi)容。

三、StorageClass


在上面PV對(duì)象創(chuàng)建的方式為Static Provisioning(靜態(tài)資源調(diào)配),在大規(guī)模的生產(chǎn)環(huán)境里,面對(duì)集群中大量的PVC,需要提前手動(dòng)創(chuàng)建好PV來(lái)與之綁定,這其實(shí)是一個(gè)非常麻煩的工作。還好kubernetes提供了Dynamic Provisioning(動(dòng)態(tài)資源調(diào)配)的機(jī)制,即:StorageClass對(duì)象, 它的作用其實(shí)就是創(chuàng)建 PV 的模板。

StorageClass 對(duì)象會(huì)定義如下兩個(gè)部分內(nèi)容:

  1. PV 的屬性。比如,存儲(chǔ)類型、Volume 的大小等等。
  2. 創(chuàng)建這種 PV 需要用到的存儲(chǔ)插件。比如,Ceph 等等。

Kubernetes 有了這樣兩個(gè)信息之后,就能夠根據(jù)用戶提交的 PVC,找到一個(gè)對(duì)應(yīng)的StorageClass,然后Kubernetes 就會(huì)調(diào)用該 StorageClass 聲明的存儲(chǔ)插件,自動(dòng)創(chuàng)建出需要的 PV。

現(xiàn)在我們創(chuàng)建一個(gè)NFS類型的StorageClass,首先需要?jiǎng)?chuàng)建nfs-client-provisioner(存儲(chǔ)插件):nfs-client 的自動(dòng)配置程序

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nfs-client-provisioner
  labels:
    app: nfs-client-provisioner
spec:
  replicas: 1
  strategy:
    type: Recreate
  selector:
    matchLabels:
      app: nfs-client-provisioner
  template:
    metadata:
      labels:
        app: nfs-client-provisioner
    spec:
      serviceAccountName: nfs-client-provisioner
      containers:
        - name: nfs-client-provisioner
          image: quay.io/external_storage/nfs-client-provisioner:latest
          volumeMounts:
            - name: nfs-client-root
              mountPath: /persistentvolumes
          env:
            - name: PROVISIONER_NAME
              value: fuseim.pri/ifs
            - name: NFS_SERVER
              value: 192.168.16.173 # 修改成自己的 IP
            - name: NFS_PATH
              value: /data/k8s
      volumes:
        - name: nfs-client-root
          nfs:
            server: 192.168.16.173 # 修改成自己的 IP
            path: /data/k8s

為nfs-client程序綁定相應(yīng)的集群操作權(quán)限 :

apiVersion: v1
kind: ServiceAccount
metadata:
  name: nfs-client-provisioner

---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: nfs-client-provisioner-runner
rules:
  - apiGroups: [""]
    resources: ["persistentvolumes"]
    verbs: ["get", "list", "watch", "create", "delete"]
  - apiGroups: [""]
    resources: ["persistentvolumeclaims"]
    verbs: ["get", "list", "watch", "update"]
  - apiGroups: ["storage.k8s.io"]
    resources: ["storageclasses"]
    verbs: ["get", "list", "watch"]
  - apiGroups: [""]
    resources: ["events"]
    verbs: ["list", "watch", "create", "update", "patch"]
  - apiGroups: [""]
    resources: ["endpoints"]
    verbs: ["create", "delete", "get", "list", "watch", "patch", "update"]

---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: run-nfs-client-provisioner
subjects:
  - kind: ServiceAccount
    name: nfs-client-provisioner
    namespace: default
roleRef:
  kind: ClusterRole
  name: nfs-client-provisioner-runner
  apiGroup: rbac.authorization.k8s.io

創(chuàng)建StorageClass

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: es-data-db
provisioner: fuseim.pri/ifs

provisioner 字段的值是:fuseim.pri/ifs,這個(gè)是NFS提供的分配器,kubernetes也內(nèi)置了一些存儲(chǔ)的分配器:https://kubernetes.io/zh/docs/concepts/storage/storage-classes/

創(chuàng)建資源對(duì)象

$ kubectl  create -f  nfs-client-Provisioner.yaml
$ kubectl  create -f  nfs-client-sa.yaml
$ kubectl  create -f  nfs-storageclass.yaml 
$ kubectl  get   storageclass
NAME              PROVISIONER                  RECLAIMPOLICY   VOLUMEBINDINGMODE   ALLOWVOLUMEEXPANSION   AGE
nfs-data-db       fuseim.pri/ifs               Delete          Immediate           false                  136m

StorageClass創(chuàng)建完成后,開(kāi)發(fā)人員只需要在 PVC 里指定要使用的 StorageClass 名字即可,PV則會(huì)根據(jù)PVC的屬性定義自動(dòng)創(chuàng)建。

創(chuàng)建nfs-pvc02.yaml

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: nfs-pvc02
spec:
  accessModes:
    - ReadWriteOnce
  storageClassName: nfs-data-db
  resources:
    requests:
      storage: 1Gi

創(chuàng)建PVC并查看是否綁定相應(yīng)的PV

$ kubectl  create  -f  nfs-pvc02.yaml 
$ kubectl  get pvc
  NAME        STATUS   VOLUME                                     CAPACITY     ACCESS MODES   STORAGECLASS   AGE
  nfs-pvc     Bound    nfs                                        5Gi        RWX            manual         7h49m
  nfs-pvc02   Bound    pvc-9df576f0-b2d4-41cf-9d92-27958f68e7e0   1Gi        RWO            nfs-data-db    7s
$ kubectl    get pv 
  NAME                                       CAPACITY   ACCESS MODES   RECLAIM  POLICY   STATUS   CLAIM               STORAGECLASS   REASON   AGE
  nfs                                        5Gi        RWX            Retain           Bound    default/nfs-pvc     manual                  8h
  pvc-9df576f0-b2d4-41cf-9d92-27958f68e7e0   1Gi        RWO            Delete           Bound    default/nfs-pvc02   nfs-data-db             17s
$  ls /data/k8s
  default-nfs-pvc02-pvc-9df576f0-b2d4-41cf-9d92-27958f68e7e0  index.html

可以看到創(chuàng)建完P(guān)VC后,StorageClass自動(dòng)為PVC創(chuàng)建并綁定了對(duì)應(yīng)的PV,而且PV的屬性是和PVC相同的,在共享卷中也創(chuàng)建了相關(guān)的PV目錄,這樣我們創(chuàng)建Pod時(shí)只需要指定PVC的名字即可,不用再去手動(dòng)的為PVC創(chuàng)建PV。

注意:Kubernetes 只會(huì)將StorageClass 定義相同的 PVC 和 PV 綁定起來(lái)


總結(jié):

  1. hostPath:屬于單節(jié)點(diǎn)集群中的持久化存儲(chǔ),Pod需要綁定集群節(jié)點(diǎn)。刪除pod后,卷里面的文件會(huì)繼續(xù)保持,但pod被重新調(diào)度到其他節(jié)點(diǎn)時(shí),就無(wú)法訪問(wèn)到原數(shù)據(jù)。不適合作為存儲(chǔ)數(shù)據(jù)庫(kù)數(shù)據(jù)的目錄。

  2. emptyDir: 用于存儲(chǔ)臨時(shí)數(shù)據(jù)的簡(jiǎn)單空目錄,生命周期是和pod捆綁的,隨著pod創(chuàng)建而創(chuàng)建;刪除而銷毀,卷的內(nèi)容將會(huì)丟失。emptyDir卷適用于同一個(gè)pod中運(yùn)行的容器之間共享文件。

  3. PVC 描述的,是 Pod 想要使用的持久化存儲(chǔ)的屬性,比如存儲(chǔ)的大小、讀寫權(quán)限等。

  4. PV 描述的,則是一個(gè)具體的 Volume 的屬性,比如 Volume 的類型、掛載目錄、遠(yuǎn)程存儲(chǔ)服務(wù)器地址等。

  5. StorageClass 的作用,則是充當(dāng) PV 的模板。并且,只有同屬于一個(gè) StorageClass 的 PV 和 PVC,才可以綁定在一起。當(dāng)然,StorageClass 的另一個(gè)重要作用,是指定 PV 的 Provisioner(存儲(chǔ)插件)。這時(shí)候,如果你的存儲(chǔ)插件支持 Dynamic Provisioning 的話,Kubernetes 就可以自動(dòng)為你創(chuàng)建 PV 了。


參考資料:
深入剖析Kubernetes-張磊


關(guān)注公眾號(hào)回復(fù)【k8s】獲取視頻教程及更多資料:


image.png
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,967評(píng)論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,273評(píng)論 3 415
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事。” “怎么了?”我有些...
    開(kāi)封第一講書人閱讀 175,870評(píng)論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書人閱讀 62,742評(píng)論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,527評(píng)論 6 407
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書人閱讀 55,010評(píng)論 1 322
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,108評(píng)論 3 440
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書人閱讀 42,250評(píng)論 0 288
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,769評(píng)論 1 333
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,656評(píng)論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,853評(píng)論 1 369
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,371評(píng)論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,103評(píng)論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 34,472評(píng)論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 35,717評(píng)論 1 281
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,487評(píng)論 3 390
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,815評(píng)論 2 372