在虛擬化的一系列解決方案中,數據的持久化都是須要咱們很是關心的問題,dokcer是這樣,Kubernetes也是這樣。不過在Kubernetes中,有一個數據卷的概念。node
咱們常常都會說:容器、Pod都是很短暫的!其含義就是容器和Pod的生命週期都是很短暫的,會被頻繁地銷燬和建立。容器銷燬時,保存在容器內部文件系統中的數據都會被清除。mysql
Volume的生命週期獨立於容器,Pod中的容器可能被銷燬和重啓,但Volume會被保留。sql
Kubernetes Volume主要解決了如下兩個問題:
1)數據持久性:一般狀況下,容器運行起來後,寫到其文件系統的文件是暫時性的。當容器崩潰後,kubelet會將這個容器不斷的重啓,當達到重啓的次數後,容器仍然不可用,那麼就會將這個容器kill掉,從新生成新的容器。此時,新運行的容器並無原容器中的數據,由於容器是由鏡像建立的;
2)數據共享:同一個Pod中運行的容器之間,常常會存在共享文件/共享文件夾的需求;docker
從根本上來講,一個數據卷僅僅是一個能夠被Pod訪問的目錄或文件。這個目錄是怎麼來的,取決於該數據卷的類型。同一個Pod中的兩個容器能夠將一個數據卷掛載到不一樣的目錄下。數據庫
Volume 提供了對各類 backend 的抽象,容器在使用 Volume 讀寫數據的時候不須要關心數據究竟是存放在本地節點的文件系統中呢仍是雲硬盤上。對它來講,全部類型的 Volume 都只是一個目錄。vim
emptyDir 是最基礎的 Volume 類型。正如其名字所示,一個 emptyDir Volume 是 Host 上的一個空目錄。api
emptyDir Volume 對於容器來講是持久的,對於 Pod 則不是。當 Pod 從節點刪除時,Volume 的內容也會被刪除。但若是隻是容器被銷燬而 Pod 還在,則 Volume 不受影響。相似於docker數據持久化中的docker manager volume方式!bash
[root@master yaml]# vim emptyDir.yaml apiVersion: v1 kind: Pod metadata: name: producer-consumer #定義Pod的名稱 spec: containers: - image: busybox name: producer #定義容器的名稱 volumeMounts: - mountPath: /producer_dir #指定容器內的路徑 name: shared-volume #表示把shared-volume掛載到容器中 args: #當容器運行完成後,執行如下的寫操做 - /bin/sh - -c - echo "hello k8s" > /producer_dir/hello; sleep 30000 - image: busybox name: consumer #定義容器的名稱 volumeMounts: - mountPath: /consumer_dir name: shared-volume #與上一個容器同樣 args: - /bin/sh - -c - cat /consumer_dir/hello; sleep 30000 volumes: - name: shared-volume #定義數據卷的名稱,必須與以上掛載的數據卷名稱一致 emptyDir: {} #定義一個類型爲emptyDir的數據卷,名稱爲shared-volume [root@master yaml]# kubectl apply -f emptyDir.yam #生成所需的Pod資源 [root@master yaml]# kubectl exec -it producer-consumer -c producer /bin/sh #進入第一個容器進行驗證 / # cat /producer_dir/hello hello k8s [root@master yaml]# kubectl exec -it producer-consumer -c consumer /bin/sh #進行第二個容器進行驗證 / # cat /consumer_dir/hello hello k8s
到此能夠看出這個pod中的兩個容器指定的目錄內容都是同樣的,具體是本地的那個目錄還需進一步進行驗證。app
[root@master yaml]# kubectl get pod -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES producer-consumer 2/2 Running 0 7m58s 10.244.2.2 node02 <none> <none> #能夠看出這個pod是運行在node02上的 [root@node02 ~]# docker ps | grep busybox #因爲容器較多,根據使用的鏡像名稱進行篩選 4fbd734e1763 busybox "/bin/sh -c 'cat /co…" 8 minutes ago Up 8 minutes k8s_consumer_producer-consumer_default_003a002d-caec-4202-a020-1ae8d6ff7eba_0 b441c2ff2217 busybox "/bin/sh -c 'echo \"h…" 8 minutes ago Up 8 minutes k8s_producer_producer-consumer_default_003a002d-caec-4202-a020-1ae8d6ff7eba_0 [root@node02 ~]# docker inspect 4fbd734e1763 #根據容器的ID查看容器的詳細信息 #找到Mounts字段,以下: "Mounts": [ { "Type": "bind", "Source": "/var/lib/kubelet/pods/003a002d-caec-4202-a020-1ae8d6ff7eba/volumes/kubernetes.io~empty-dir/shared-volume", #此處指定的即是docker host本地的目錄 "Destination": "/consumer_dir", #容器中的目錄 "Mode": "", "RW": true, "Propagation": "rprivate" }, [root@node02 ~]# docker inspect b441c2ff2217 "Mounts": [ { "Type": "bind", "Source": "/var/lib/kubelet/pods/003a002d-caec-4202-a020-1ae8d6ff7eba/volumes/kubernetes.io~empty-dir/shared-volume", "Destination": "/producer_dir", "Mode": "", "RW": true, "Propagation": "rprivate" }, #能夠看出這兩個容器的源目錄是同樣,掛載的是docker host本地的同一個目錄 [root@node02 ~]# cd /var/lib/kubelet/pods/003a002d-caec-4202-a020-1ae8d6ff7eba/volumes/kubernetes.io~empty-dir/shared-volume [root@node02 shared-volume]# cat hello hello k8s #驗證內容
因爲是Kubernetes集羣的環境,刪除一個容器比較麻煩,直接將pod刪除,查看docker host本地的數據是否存在!ide
[root@master yaml]# kubectl delete -f emptyDir.yaml #master節點將pod刪除 [root@node02 ~]# cd /var/lib/kubelet/pods/003a002d-caec-4202-a020-1ae8d6ff7eba/volumes/kubernetes.io~empty-dir/shared-volume -bash: cd: /var/lib/kubelet/pods/003a002d-caec-4202-a020-1ae8d6ff7eba/volumes/kubernetes.io~empty-dir/shared-volume: 沒有那個文件或目錄 #node02進行驗證,發現目錄已經消失
emptyDir 是Docker Host 上建立的臨時目錄,其優勢是可以方便地爲 Pod 中的容器提供共享存儲,不須要額外的配置。但它不具有持久性,若是 Pod 不存在了,emptyDir 也就沒有了。根據這個特性,emptyDir 特別適合 Pod 中的容器須要臨時共享存儲空間的場景!
簡單來講就是,若是容器被刪除,數據依然存在;若是Pod被刪除,數據將不會存在!
hostPath Volume 的做用是將 Docker Host 文件系統中已經存在的目錄 mount 給 Pod 的容器。大部分應用都不會使用 hostPath Volume,由於這實際上增長了 Pod 與節點的耦合,限制了 Pod 的使用。不過那些須要訪問 Kubernetes 或 Docker 內部數據(配置文件和二進制庫)的應用則須要使用 hostPath。相似於docker數據持久化中的bind mount方式!
固然也能夠進行建立,這裏就偷個懶,使用Kubernetes集羣自帶的YAML文件進行介紹!
[root@master yaml]# kubectl edit --namespace=kube-system pod kube-apiserver-master #查看apiserver組件的yaml文件
如圖:
若是 Pod 被銷燬了,hostPath 對應的目錄也還會被保留,從這點看,hostPath 的持久性比 emptyDir 強。不過一旦 Host 崩潰,hostPath 也就無法訪問了。
因爲使用場景較少,以上兩種方式這裏就不詳細介紹了!
Persistent Volume概述
普通Volume和使用它的Pod之間是一種靜態綁定關係,在定義Pod的文件裏,同時定義了它使用的Volume。Volume 是Pod的附屬品,咱們沒法單首創建一個Volume,由於它不是一個獨立的K8S資源對象。
而Persistent Volume 簡稱PV是一個K8S資源對象,因此咱們能夠單首創建一個PV。它不和Pod直接發生關係,而是經過Persistent Volume Claim,簡稱PVC來實現動態綁定。Pod定義裏指定的是PVC,而後PVC會根據Pod的要求去自動綁定合適的PV給Pod使用。
既然有了PV這個概念,那麼PVC(PersistentVolumeClaim)這個概念也不得不說一下,PVC表明用戶使用存儲的請求,應用申請PV持久化空間的一個申請、聲明。K8s集羣可能會有多個PV,你須要不停的爲不一樣的應用建立多個PV。
如圖:
1)PV是集羣中的存儲資源,一般由集羣管理員建立和管理;
2)StorageClass用於對PV進行分類,若是配置正確,Storage也能夠根據PVC的請求動態建立PV;
3)PVC是使用該資源的請求,一般由應用程序提出請求,並指定對應的StorageClass和需求的空間大小;
4)PVC能夠做爲數據卷的一種,被掛載到Pod中使用;
PV與PVC的管理過程以下: 1)在主機上劃分出一個單獨的目錄用於PV使用,而且定義其可用大小; 2)建立PVC這個資源對象,便於申請PV的存儲空間; 3)Pod中添加數據卷,數據卷關聯到PVC; 4)Pod中包含容器,容器掛載數據卷;
下面經過一個案例來詳細瞭解一下Persistent Volume!
案例實現過程:
1)底層採用NFS存儲,而後再NFS的目錄下劃分1G的容量供PV調度;
2)建立PVC來申請PV的存儲空間;
3)建立Pod,使用PVC申請的存儲空間來實現數據的持久化;
本次案例直接在master節點上建立NFS存儲!
[root@master ~]# yum -y install nfs-utils rpcbind [root@master ~]# vim /etc/exports /nfsdata *(rw,sync,no_root_squash) [root@master ~]# systemctl start nfs-server [root@master ~]# systemctl start rpcbind [root@master ~]# showmount -e Export list for master: /nfsdata *
[root@master ~]# vim test-pv.yaml apiVersion: v1 kind: PersistentVolume metadata: name: test-pv spec: capacity: storage: 1Gi #指定該PV資源分配的容器爲1G accessModes: #指定訪問模式 - ReadWriteOnce persistentVolumeReclaimPolicy: Recycle #指定回收策略(實驗環境,實際環境不多會這樣作) storageClassName: nfs #指定存儲類名字 nfs: #須要與存儲類名字一致 path: /nfsdata/test-pv //指定NFS的目錄 server: 192.168.1.4 //指定NFS的IP地址
上述yaml文件中,主要字段的解釋:
1)accessModes(訪問模式):
- ReadWriteOnce:以讀寫的方式掛載到單個節點;
- ReadWriteMany:以讀寫的方式掛載到多個節點 ;
- ReadOnlyMany:以只讀的方式掛載到多個節點;
2)persistentVolumeReclaimPolicy(PV的回收策略):- Recycle:自動清除PV中的數據,自動回收;
- Retain:須要手動回收;
- Delete:刪除雲存儲資源(雲存儲專用);
[root@master ~]# kubectl apply -f test-pv.yaml [root@master ~]# kubectl get pv #查看PV的狀態 NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE test-pv 1Gi RWO Recycle Available nfs 21s #注意其PV的狀態必須是 Available纔可正常使用
[root@master ~]# vim test-pvc.yaml apiVersion: v1 kind: PersistentVolumeClaim metadata: name: test-pvc spec: accessModes: #定義訪問模式,必須與PV定義的訪問模式一致 - ReadWriteOnce resources: requests: storage: 1Gi #直接請求i使用最大的容量 storageClassName: nfs #定義的名稱需與PV定義的名稱一致 [root@master ~]# kubectl apply -f test-pvc.yaml [root@master ~]# kubectl get pvc #查看PVC的狀態 NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE test-pvc Bound test-pv 1Gi RWO nfs 102s [root@master ~]# kubectl get pv #查看PV的狀態 NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE test-pv 1Gi RWO Recycle Bound default/test-pvc nfs 14m #注意PV與PVC的狀態都是Bound,表示PV與PVC的關聯成功
PV和PVC能夠經過storageClassName或accessModes進行關聯的!
常見的狀態有:
1)Available——>閒置狀態,沒有被綁定到PVC;
2)Bound——>綁定到PVC;
3)Released——>PVC被刪除,資源沒有被利用;
4)Failed——>自動回收失敗;
[root@master ~]# vim test-pod.yaml apiVersion: v1 kind: Pod metadata: name: test-pod spec: containers: - name: test-pod image: busybox args: - /bin/sh - -c - sleep 300000 volumeMounts: - mountPath: /testdata #定義容器中的目錄 name: volumedata #保證與卷的名稱一致 volumes: - name: volumedata #定義卷的名稱 persistentVolumeClaim: claimName: test-pvc #指定邏輯卷對應的PVC名稱 [root@master ~]# kubectl apply -f test-pod.yaml [root@master ~]# kubectl get pod #查看pod的狀態 NAME READY STATUS RESTARTS AGE test-pod 0/1 ContainerCreating 0 6m26s #注意其狀態爲 ContainerCreating,表示容器正在建立,可是查看時間,這麼長時間沒有建立好,不太正常
當pod狀態不正常時,通常咱們能夠採用如下三種方式進行排錯:
1)使用「 kubectl describe pod pod名稱」查看pod的詳細信息;
2)使用「kubectl logs pod名稱「查看pod的日誌信息;
3)使用「cat /var/log/messages」查看系統日誌;
本次採用第一種方式排錯!
[root@master ~]# kubectl describe pod test-pod #最後的一條的信息以下: mount.nfs: mounting 192.168.1.4:/nfsdata/test-pv failed, reason given by server: No such file or directory #根據消息提示,指定本地須要掛載的目錄不存在 [root@master ~]# mkdir -p /nfsdata/test-pv #建立完成目錄後,建議查看pod生成的容器運行的節點,將節點上的kubelet服務進行重啓,重啓完成後,再次查看Pod的狀態 [root@master ~]# kubectl get pod //再次查看pod的狀態 NAME READY STATUS RESTARTS AGE test-pod 1/1 Running 0 32m
[root@master ~]# kubectl exec -it test-pod /bin/sh / # echo "test pv pvc" > /testdata/test.txt #進入容器,建立文件進行測試 [root@master ~]# cat /nfsdata/test-pv/test.txt test pv pvc #確認這個文件本地是存在的 [root@master ~]# kubectl delete -f test-pod.yaml #將pod進行刪除 [root@master ~]# cat /nfsdata/test-pv/test.txt test pv pvc #再次查看發現pod的測試文件依然存在 [root@master ~]# kubectl delete -f test-pvc.yaml #將PVC進行刪除 [root@master ~]# cat /nfsdata/test-pv/tes cat: /nfsdata/test-pv/tes: 沒有那個文件或目錄 #再次查看發現pod的測試文件不見了,由於將PVC刪除了
可能經過步驟四並不能真正的理解Persistent volume的做用,下面經過建立mysql容器的方式來驗證Persistent volume的做用!
本次案例直接在master節點上建立NFS存儲!
[root@master ~]# yum -y install nfs-utils rpcbind [root@master ~]# vim /etc/exports /nfsdata *(rw,sync,no_root_squash) [root@master ~]# systemctl start nfs-server [root@master ~]# systemctl start rpcbind [root@master ~]# showmount -e Export list for master: /nfsdata *
[root@master ~]# vim mysql-pv.yaml apiVersion: v1 kind: PersistentVolume metadata: name: mysql-pv spec: capacity: storage: 1Gi accessModes: - ReadWriteOnce persistentVolumeReclaimPolicy: Retain #注意指定的回收策略爲手動回收 storageClassName: nfs nfs: path: /nfsdata/mysql-pv server: 192.168.1.4 [root@master ~]# kubectl apply -f mysql-pv.yaml [root@master ~]# kubectl get pv NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE test-pv 1Gi RWO Retain Available nfs 15s [root@master ~]# mkdir -p /nfsdata/mysql-pv
[root@master ~]# vim mysql-pvc.yaml apiVersion: v1 kind: PersistentVolumeClaim metadata: name: mysql-pvc spec: accessModes: - ReadWriteOnce resources: requests: storage: 1Gi storageClassName: nfs [root@master ~]# kubectl apply -f mysql-pvc.yaml [root@master ~]# kubectl get pvc NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE mysql-pvc Bound mysql-pv 1Gi RWO nfs 13s [root@master ~]# kubectl get pv NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE mysql-pv 1Gi RWO Retain Bound default/mysql-pvc nfs 8m14s
[root@master ~]# vim mysql-pod.yaml apiVersion: extensions/v1beta1 kind: Deployment metadata: name: mysql-pod spec: selector: #設置給予等值的標籤選擇器(也可省略) matchLabels: app: mysql template: metadata: labels: app: mysql spec: containers: - image: mysql:5.6 name: mysql env: #設置環境變量,數據庫root用戶的密碼 - name: MYSQL_ROOT_PASSWORD value: 123.com volumeMounts: - name: mysql-storage mountPath: /var/lib/mysql #這個目錄是數據庫存放數據的目錄(指定的是容器中的目錄) volumes: - name: mysql-storage persistentVolumeClaim: claimName: mysql-pvc [root@master ~]# kubectl apply -f mysql-pod.yaml [root@master ~]# kubectl get pod NAME READY STATUS RESTARTS AGE mysql-pod-6cc889468b-gq4qz 1/1 Running 0 3s
[root@master ~]# kubectl exec -it mysql-pod-6cc889468b-gq4qz -- mysql -u root -p123.com #直接登陸運行mysql數據庫的pod中的mysql #插入數據進行測試 mysql> create database lzj; mysql> use lzj; mysql> create table my_id( id int(4) ); mysql> insert my_id values (9527); mysql> select * from my_id; +------+ | id | +------+ | 9527 | +------+ [root@master ~]# ls /nfsdata/mysql-pv/ auto.cnf ibdata1 ib_logfile0 ib_logfile1 lzj mysql performance_schema #查看pod對應的NFS的目錄,確實有了數據 [root@master ~]# kubectl get pod -o wide #查看pod的詳細信息 NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES mysql-pod-6cc889468b-gq4qz 1/1 Running 0 23m 10.244.2.6 node02 <none> <none> #查看到pod是運行在node02節點上的 #模擬node02宕機,步驟省略,關機、掛起均可以! [root@node01 ~]# systemctl restart kubelet #重啓node01的kubelet服務 #接下來耐心等待pod的轉移,可能須要差很少5分鐘 ^C[root@master ~]# kubectl get pod -o wide #能夠看到pod已經運行在node01上 NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES mysql-pod-6cc889468b-gdf7k 1/1 Running 0 66s 10.244.1.6 node01 <none> <none> mysql-pod-6cc889468b-gq4qz 1/1 Terminating 0 32m 10.244.2.6 node02 <none> <none> [root@master ~]# kubectl exec -it mysql-pod-6cc889468b-gdf7k -- mysql -u root -p123.com #再次登陸到pod中運行的mysql(注意:pod的名稱) mysql> select * from lzj.my_id; #再次數據是否存在 +------+ | id | +------+ | 9527 | +------+ 1 row in set (0.01 sec) #數據依舊存在
——————————————本次到此結束,感謝閱讀——————————————