1. 程式人生 > >kubernetes-儲存卷(十二)

kubernetes-儲存卷(十二)

為了保證資料的永續性,必須保證資料在外部儲存在docker容器中,為了實現資料的永續性儲存,在宿主機和容器內做對映,可以保證在容器的生命週期結束,資料依舊可以實現永續性儲存。但是在k8s中,由於pod分佈在各個不同的節點之上,並不能實現不同節點之間永續性資料的共享,並且,在節點故障時,可能會導致資料的永久性丟失。為此,k8s就引入了外部儲存卷的功能。

Volume

https://kubernetes.io/docs/concepts/storage/volumes/

Kubernetes中的Volume提供了在容器中掛載外部儲存的能力

Pod需要設定捲來源(spec.volume)和掛載點(spec.containers.volumeMounts)兩個資訊後才可以使用相應的Volume

k8s的儲存型別

[[email protected] ~]# kubectl explain pod.spec.volumes #檢視k8s支援的儲存型別
KIND:     Pod
VERSION:  v1

常用分類:
emptyDir(臨時目錄):Pod刪除,資料也會被清除,這種儲存成為emptyDir,用於資料的臨時儲存。
hostPath(宿主機目錄對映):
本地的SAN(iSCSI,FC)、NAS(nfs,cifs,http)儲存
分散式儲存(glusterfs,rbd,cephfs)
雲端儲存(EBS,Azure Disk)
emptyDir

一個emptyDir 第一次建立是在一個pod被指定到具體node的時候,並且會一直存在在pod的生命週期當中,正如它的名字一樣,它初始化是一個空的目錄,pod中的容器都可以讀寫這個目錄,這個目錄可以被掛在到各個容器相同或者不相同的的路徑下。當一個pod因為任何原因被移除的時候,這些資料會被永久刪除。注意:一個容器崩潰了不會導致資料的丟失,因為容器的崩潰並不移除pod.

emptyDir 磁碟的作用:
(1)普通空間,基於磁碟的資料儲存
(2)作為從崩潰中恢復的備份點
(3)儲存那些那些需要長久儲存的資料,例web服務中的資料
預設的,emptyDir 磁碟會儲存在主機所使用的媒介上,可能是SSD,或者網路硬碟,這主要取決於你的環境。當然,我們也可以將emptyDir.medium的值設定為Memory來告訴Kubernetes 來掛在一個基於記憶體的目錄tmpfs,因為
tmpfs速度會比硬碟塊度了,但是,當主機重啟的時候所有的資料都會丟失。

建立emptyDir示例

[[email protected] volume]# vim pod-vol-demo.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: pod
-demo namespace: default labels: app: myapp tier: frontend spec: containers: - name: myapp image: ikubernetes/myapp:v1 imagePullPolicy: IfNotPresent ports: - name: http containerPort: 80 volumeMounts: - name: html mountPath: /usr/share/nginx/html/ - name: busybox image: busybox:latest imagePullPolicy: IfNotPresent volumeMounts: - name: html mountPath: /data/ command: ['/bin/sh','-c','while true;do echo $(date) >> /data/index.html;sleep 2;done'] volumes: - name: html emptyDir: {} [[email protected]-master1 volume]# kubectl apply -f pod-vol-demo.yaml pod/pod-demo configured [[email protected]-master1 volume]# kubectl get pod -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES pod-demo 2/2 Running 0 15m 172.17.32.3 192.168.0.125 <none> <none>

在上面,我們定義了2個容器,其中一個容器是輸入日期到index.html中,然後驗證訪問nginx的html是否可以獲取日期。以驗證兩個容器之間掛載的emptyDir實現共享。如下訪問驗證:

[[email protected] ~]# curl 172.17.73.3
Wed Dec 26 09:03:21 UTC 2018
Wed Dec 26 09:03:23 UTC 2018
Wed Dec 26 09:03:25 UTC 2018
Wed Dec 26 09:03:27 UTC 2018
Wed Dec 26 09:03:29 UTC 2018
Wed Dec 26 09:03:31 UTC 2018
Wed Dec 26 09:03:33 UTC 2018
Wed Dec 26 09:03:35 UTC 2018
hostPath

hostPath宿主機路徑,就是把pod所在的宿主機之上的脫離pod中的容器名稱空間的之外的宿主機的檔案系統的某一目錄和pod建立關聯關係,在pod刪除時,儲存資料不會丟失。

[[email protected] volume]# vim pod-hostpath-vol.yaml
apiVersion: v1
kind: Pod
metadata:
  name: pod-vol-hostpath
  namespace: default
spec:
  containers:
  - name: myapp
    image: ikubernetes/myapp:v1
    volumeMounts:
    - name: html
      mountPath: /usr/share/nginx/html
  volumes:
    - name: html
      hostPath:
        path: /data/pod/volume1
        type: DirectoryOrCreate

[[email protected]-master1 volume]# kubectl apply -f pod-hostpath-vol.yaml
pod/pod-vol-hostpath created
[[email protected]-master1 volume]# kubectl get pod -o wide
NAME               READY   STATUS    RESTARTS   AGE   IP            NODE            NOMINATED NODE   READINESS GATES
pod-vol-hostpath   1/1     Running   0          39s   172.17.73.3   192.168.0.126   <none>           <none>

訪問測試

[[email protected] ~]# echo "test hostpath" > /data/pod/volume1/index.html
[[email protected]-node02 ~]# curl 172.17.73.3
test hostpath

nfs共享儲存卷

nfs使的我們可以掛在已經存在的共享到的我們的Pod中,和emptyDir不同的是,emptyDir會被刪除當我們的Pod被刪除的時候,但是nfs不會被刪除,僅僅是解除掛在狀態而已,這就意味著NFS能夠允許我們提前對資料進行處理,而且這些資料可以在Pod之間相互傳遞.並且,nfs可以同時被多個pod掛在並進行讀寫

準備nfs服務
[[email protected] ~]# yum install -y nfs-utils
[[email protected] ~]# mkdir /data/volumes -p
[[email protected] ~]# vim /etc/exports
/data/volumes 192.168.0.0/24(rw,no_root_squash)
[[email protected] ~]# systemctl start nfs
[[email protected] ~]# showmount -e
Export list for localhost.localdomain:
/data/volumes 192.168.0.0/24

node測試掛載
[[email protected]-node01 ~]# yum install -y nfs-utils
[[email protected]-node01 ~]# mount -t nfs 192.168.0.122:/data/volumes /mnt
[[email protected]-node01 ~]# mount
. . . . .
192.168.0.122:/data/volumes on /mnt type nfs4 (rw,relatime,vers=4.1,rsize=524288,wsize=524288,namlen=255,hard,proto=tcp,timeo=600,retrans=2,sec=sys,clientaddr=192.168.0.125,local_lock=none,addr=192.168.0.122)
[[email protected]-node01 ~]# umount /mnt
使用nfs儲存卷示例
[[email protected] volume]# vim pod-nfs-vol.yaml

apiVersion: v1
kind: Pod
metadata:
  name: pod-vol-nfs
  namespace: default
spec:
  containers:
  - name: myapp
    image: ikubernetes/myapp:v1
    volumeMounts:
    - name: html
      mountPath: /usr/share/nginx/html
  volumes:
    - name: html
      nfs:
        path: /data/volumes
        server: 192.168.0.122

[[email protected]-master1 volume]# kubectl apply -f pod-nfs-vol.yaml
pod/pod-vol-nfs created
[[email protected]-master1 volume]# kubectl get pod -o wide
NAME               READY   STATUS    RESTARTS   AGE   IP            NODE            NOMINATED NODE   READINESS GATES
pod-vol-hostpath   1/1     Running   0          29m   172.17.73.3   192.168.0.126   <none>           <none>
pod-vol-nfs        1/1     Running   0          13s   172.17.73.4   192.168.0.126   <none>           <none>

在nfs建立index.html,訪問測試
[[email protected] ~]# cat /data/volumes/index.html 
<h1>nfs test</h1>
[[email protected]-node01 ~]# curl 172.17.73.4
<h1>nfs test</h1>

PVC和PV的概念

Kubernetes支援持久卷的儲存外掛:https://kubernetes.io/docs/concepts/storage/persistent-volumes/

kubernetes提供那麼多儲存介面,但是首先kubernetes的各個Node節點能管理這些儲存,但是各種儲存引數也需要專業的儲存工程師才能瞭解,由此我們的kubernetes管理變的更加複雜的。由此kubernetes提出了PV和PVC的概念,這樣開發人員和使用者就不需要關注後端儲存是什麼,使用什麼引數等問題。如下圖:

PersistentVolume(PV)是叢集中已由管理員配置的一段網路儲存。 叢集中的資源就像一個節點是一個叢集資源。 PV是諸如卷之類的卷外掛,但是具有獨立於使用PV的任何單個pod的生命週期。 該API物件捕獲儲存的實現細節,即NFS,iSCSI或雲提供商特定的儲存系統。

PersistentVolumeClaim(PVC)是使用者儲存的請求。PVC的使用邏輯:在pod中定義一個儲存卷(該儲存卷型別為PVC),定義的時候直接指定大小,pvc必須與對應的pv建立關係,pvc會根據定義去pv申請,而pv是由儲存空間創建出來的。pv和pvc是kubernetes抽象出來的一種儲存資源。

PV是叢集中的資源。 PVC是對這些資源的請求,也是對資源的索賠檢查。 PV和PVC之間的相互作用遵循這個生命週期:

Provisioning(配置)---> Binding(繫結)--->Using(使用)---> Releasing(釋放) ---> Recycling(回收)
Provisioning

這裡有兩種PV的提供方式:靜態或者動態

靜態-->直接固定儲存空間:
    叢集管理員建立一些 PV。它們攜帶可供叢集使用者使用的真實儲存的詳細資訊。 它們存在於Kubernetes API中,可用於消費。

動態-->通過儲存類進行動態建立儲存空間:
    當管理員建立的靜態 PV 都不匹配使用者的 PVC 時,叢集可能會嘗試動態地為 PVC 配置卷。此配置基於 StorageClasses:PVC 必須請求儲存類,並且管理員必須已建立並配置該類才能進行動態配置。 要求該類的宣告有效地為自己禁用動態配置。

Binding

在動態配置的情況下,使用者建立或已經建立了具有特定數量的儲存請求和特定訪問模式的PersistentVolumeClaim。 主機中的控制迴路監視新的PVC,找到匹配的PV(如果可能),並將 PVC 和 PV 繫結在一起。 如果為新的PVC動態配置PV,則迴圈將始終將該PV繫結到PVC。 否則,使用者總是至少得到他們要求的內容,但是卷可能超出了要求。 一旦繫結,PersistentVolumeClaim繫結是排他的,不管用於繫結它們的模式。

如果匹配的卷不存在,PVC將保持無限期。 隨著匹配卷變得可用,PVC將被繫結。 例如,提供許多50Gi PV的叢集將不匹配要求100Gi的PVC。 當叢集中新增100Gi PV時,可以繫結PVC。

Using

Pod使用PVC作為卷。 叢集檢查宣告以找到繫結的卷並掛載該卷的卷。 對於支援多種訪問模式的卷,使用者在將其宣告用作pod中的卷時指定所需的模式。

一旦使用者有宣告並且該宣告被繫結,繫結的PV屬於使用者,只要他們需要它。 使用者通過在其Pod的卷塊中包含PersistentVolumeClaim來安排Pods並訪問其宣告的PV。

Releasing

當用戶完成卷時,他們可以從允許資源回收的API中刪除PVC物件。 當宣告被刪除時,卷被認為是“釋放的”,但是它還不能用於另一個宣告。 以前的索賠人的資料仍然保留在必須根據政策處理的捲上.

Reclaiming

PersistentVolume的回收策略告訴叢集在釋放其聲明後,該卷應該如何處理。 目前,卷可以是保留,回收或刪除。 保留可以手動回收資源。 對於那些支援它的卷外掛,刪除將從Kubernetes中刪除PersistentVolume物件,以及刪除外部基礎架構(如AWS EBS,GCE PD,Azure Disk或Cinder卷)中關聯的儲存資產。 動態配置的卷始終被刪除

Recycling

如果受適當的卷外掛支援,回收將對卷執行基本的擦除(rm -rf / thevolume / *),並使其再次可用於新的宣告。

靜態建立nfs型別的pv示例
建立pv
[[email protected]-master1 volume]# vim pv.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
  name: my-pv
spec:
  capacity:
    storage: 5Gi
  accessModes:
    - ReadWriteMany
  nfs:
    path: /data/volumes/wwwroot
    server: 192.168.0.122

[[email protected]-master1 volume]# kubectl apply -f pv.yaml
persistentvolume/my-pv created
[[email protected]-master1 volume]# kubectl get pv
NAME    CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM   STORAGECLASS   REASON   AGE
my-pv   5Gi        RWX            Retain           Available                                   7s

建立容器應用
[[email protected]-master1 volume]# cat  pod-nfs-pvc.yaml
apiVersion: apps/v1beta1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  replicas: 3
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx
        volumeMounts:
        - name: wwwroot
          mountPath: /usr/share/nginx/html
        ports:
        - containerPort: 80
      volumes:
      - name: wwwroot
        persistentVolumeClaim:
          claimName: my-pvc

---
定義pvc  卷需求模板
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: my-pvc
spec:
  accessModes:
    - ReadWriteMany
  resources:
    requests:
      storage: 5Gi

[[email protected]-master1 volume]# kubectl get pv,pvc    #檢視pvc繫結
NAME                     CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM            STORAGECLASS   REASON   AGE
persistentvolume/my-pv   5Gi        RWX            Retain           Bound    default/my-pvc                           4m21s

NAME                           STATUS   VOLUME   CAPACITY   ACCESS MODES   STORAGECLASS   AGE
persistentvolumeclaim/my-pvc   Bound    my-pv    5Gi        RWX                           9s
[[email protected]-master1 volume]# kubectl get pod -o wide
NAME                                READY   STATUS    RESTARTS   AGE     IP            NODE            NOMINATED NODE   READINESS GATES
nginx-deployment-57fbd964dc-g9xw9   1/1     Running   0          2m44s   172.17.73.3   192.168.0.126   <none>           <none>
nginx-deployment-57fbd964dc-m9wjq   1/1     Running   0          2m44s   172.17.32.3   192.168.0.125   <none>           <none>
nginx-deployment-57fbd964dc-p4zgk   1/1     Running   0          2m44s   172.17.32.4   192.168.0.125   <none>           <none>
訪問測試,多個pod使用的同一個pv
[[email protected] ~]# cat /data/volumes/wwwroot/index.html
hello world!
[[email protected]-node01 ~]# curl 172.17.32.4
hello world!
[[email protected]-node01 ~]# curl 172.17.73.3
hello world!
[[email protected]-node01 ~]# curl 172.17.32.3
hello world!

 動態生成nfs型別的pv示例

Dynamic Provisioning機制工作的核心在於StorageClass的API物件。
StorageClass宣告儲存外掛,用於自動建立PV。

參考文件:https://github.com/kubernetes-incubator/external-storage/tree/master/nfs-client/deploy/objects

建立storageclass
[[email protected] volume]# vim storageclass-nfs.yaml 
apiVersion: storage.k8s.io/v1beta1
kind: StorageClass
metadata:
  name: managed-nfs-storage
provisioner: fuseim.pri/ifs

[[email protected]-master1 volume]# kubectl apply -f storageclass-nfs.yaml
storageclass.storage.k8s.io/managed-nfs-storage created
 建立rbac授權
[[email protected] volume]# cat rbac.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
  name: nfs-client-provisioner

---

kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1beta1
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"]

---

kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1beta1
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

[[email protected]-master1 volume]# kubectl apply -f rbac.yaml
serviceaccount/nfs-client-provisioner created
clusterrole.rbac.authorization.k8s.io/nfs-client-provisioner-runner created
clusterrolebinding.rbac.authorization.k8s.io/run-nfs-client-provisioner created

 執行nfs-client-provisioner

[[email protected] volume]# cat deployment-nfs.yaml
apiVersion: apps/v1beta1 
kind: Deployment
metadata:
  name: nfs-client-provisioner
spec:
  replicas: 1
  strategy:
    type: Recreate
  template:
    metadata:
      labels:
        app: nfs-client-provisioner
    spec:
      imagePullSecrets:
        - name: registry-pull-secret
      serviceAccount: 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.0.122
            - name: NFS_PATH
              value: /data/volumes
      volumes:
        - name: nfs-client-root
          nfs:
            server: 192.168.0.122
            path: /data/volumes

[[email protected]-master1 volume]# kubectl apply -f deployment-nfs.yaml
deployment.apps/nfs-client-provisioner created
[[email protected]-master1 volume]# kubectl get storageclass
NAME                  PROVISIONER      AGE
managed-nfs-storage   fuseim.pri/ifs   5m18s
[[email protected]-master1 volume]# kubectl get pod
NAME                                     READY   STATUS    RESTARTS   AGE
nfs-client-provisioner-d46679749-mdndn   1/1     Running   0          67s
建立StatefulSet示例
[[email protected] volume]# cat nginx-demo.yaml 
apiVersion: v1
kind: Service
metadata:
  name: nginx
  labels:
    app: nginx
spec:
  ports:
  - port: 80
    name: web
  clusterIP: None
  selector:
    app: nginx
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: web
spec:
  selector:
    matchLabels:
      app: nginx
  serviceName: "nginx"
  replicas: 3
  template:
    metadata:
      labels:
        app: nginx
    spec:
      terminationGracePeriodSeconds: 10
      containers:
      - name: nginx
        image: nginx 
        ports:
        - containerPort: 80
          name: web
        volumeMounts:
        - name: www
          mountPath: /usr/share/nginx/html
  volumeClaimTemplates:
  - metadata:
      name: www
    spec:
      accessModes: [ "ReadWriteOnce" ]
      storageClassName: "managed-nfs-storage"
      resources:
        requests:
          storage: 1Gi

[[email protected]-master1 volume]# kubectl apply -f nginx-demo.yaml 
service/nginx created
statefulset.apps/web created
[[email protected]-master1 volume]# kubectl get pod
NAME                                      READY   STATUS    RESTARTS   AGE
nfs-client-provisioner-86c69c768f-rhvwt   1/1     Running   0          6m56s
web-0                                     1/1     Running   0          11s
web-1                                     1/1     Running   0          8s
web-2                                     1/1     Running   0          5s
[[email protected]-master1 volume]# kubectl get pv,pvc    檢視自動生成pv
NAME                                                                          CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM               STORAGECLASS          REASON   AGE
persistentvolume/default-my-pvc-pvc-34427ad1-09a6-11e9-b58a-000c298a2b5f      5Gi        RWX            Delete           Bound    default/my-pvc      managed-nfs-storage            9m31s
persistentvolume/default-www-web-0-pvc-cea5df66-09a1-11e9-b58a-000c298a2b5f   1Gi        RWO            Delete           Bound    default/www-web-0   managed-nfs-storage            28m
persistentvolume/default-www-web-1-pvc-a16abaa6-09a3-11e9-b58a-000c298a2b5f   1Gi        RWO            Delete           Bound    default/www-web-1   managed-nfs-storage            27m
persistentvolume/default-www-web-2-pvc-a37b3a1a-09a3-11e9-b58a-000c298a2b5f   1Gi        RWO            Delete           Bound    default/www-web-2   managed-nfs-storage            27m

NAME                              STATUS   VOLUME                                                       CAPACITY   ACCESS MODES   STORAGECLASS          AGE
persistentvolumeclaim/my-pvc      Bound    default-my-pvc-pvc-34427ad1-09a6-11e9-b58a-000c298a2b5f      5Gi        RWX            managed-nfs-storage   9m31s
persistentvolumeclaim/www-web-0   Bound    default-www-web-0-pvc-cea5df66-09a1-11e9-b58a-000c298a2b5f   1Gi        RWO            managed-nfs-storage   40m
persistentvolumeclaim/www-web-1   Bound    default-www-web-1-pvc-a16abaa6-09a3-11e9-b58a-000c298a2b5f   1Gi        RWO            managed-nfs-storage   27m
persistentvolumeclaim/www-web-2   Bound    default-www-web-2-pvc-a37b3a1a-09a3-11e9-b58a-000c298a2b5f   1Gi        RWO            managed-nfs-storage   27m