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