存儲卷
同一個pod內的所有容器共享存儲 網絡 用戶等空間 pod內的容器都是以pause鏡像為基礎鏡像來運行的
k8s.gcr.io/pause 3.1 da86e6ba6ca1 17 mon 742 kB
emptyDir
臨時存儲目錄 pod刪除 存儲卷也隨即會被刪除
hostPath 主機目錄
pod所在主機上的目錄
搭建NFS網絡存儲
nfs搭建 yum install nfs-utils mkdir -pv /data/volumes vi /etc/exports /data/volumes 192.168.11.0/16(rw,no_root_squash) systemctl start nfs 1.確保k8s集群中的所有節點都能驅動nfs yum install nfs-utils 2.[root@node2 ~]# mount -t nfs 192.168.11.157:/data/volumnes/ /mnt mount.nfs: access denied by server while mounting 192.168.11.157:/data/volumnes/ 3.在nfs服務器查看/var/log/messages查看 volumnes單詞拼寫錯誤 [root@node2 ~]# mount -t nfs 192.168.11.157:/data/volumes /mnt [root@localhost ~]# vi /etc/exports /data/volumes/ 192.168.11.0/16(insecure,rw,async,no_root_squash) [root@localhost ~]# exportfs -r exportfs: Invalid prefix `24*' for 192.168.11.0/24* [root@localhost ~]# showmount -e Export list for localhost.localdomain: /data/volumes 192.168.11.0/16
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
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.11.157
nfs遠程文件存儲並沒有一個“存儲設備”需要掛載在宿主機上 所以不需要attach階段
直接將遠端 NFS 服務器的目錄(比如:“/”目錄)掛載到Volume的宿主機目錄上即可
mount -t nfs <NFS服務器地址>:/ /var/lib/kubelet/pods/<Pod的ID>/volumes/kubernetes.io~<Volume類型>/<Volume名字>
通過掛載操作,Volume的宿主機目錄就成為了一個遠程NFS目錄的掛載點,后面你在這個目錄里寫入的所有文件,都會被保存在遠程NFS服務器
所以,我們也就完成了對這個Volume宿主機目錄的“持久化”
pv和pvc的使用
存儲工程師把分布式存儲系統上的總空間划分成一個一個小的存儲塊
k8s管理員根據存儲塊創建與之一一對應的pv資源
pv屬於集群級別資源 不屬於任何名稱空間 定義的時候不能指定名稱空間
用戶在創建pod的時候同時創建與pv一一對應的pvc資源
創建Pod的時候,系統里並沒有合適的PV跟它定義的PVC綁定 也就是說此時容器想要使用的Volume不存在.這時候Pod的啟動就會報錯
[root@localhost volumes]# mkdir v{1,2,3,4,5} [root@localhost volumes]# ls index.html v1 v2 v3 v4 v5 [root@localhost volumes]# vi /etc/exports /data/volumes/v1 192.168.11.0/16(insecure,rw,async,no_root_squash) /data/volumes/v2 192.168.11.0/16(insecure,rw,async,no_root_squash) /data/volumes/v3 192.168.11.0/16(insecure,rw,async,no_root_squash) /data/volumes/v4 192.168.11.0/16(insecure,rw,async,no_root_squash) /data/volumes/v5 192.168.11.0/16(insecure,rw,async,no_root_squash) [root@localhost volumes]# exportfs -r [root@localhost volumes]# showmount -e Export list for localhost.localdomain: /data/volumes/v5 192.168.11.0/16 /data/volumes/v4 192.168.11.0/16 /data/volumes/v3 192.168.11.0/16 /data/volumes/v2 192.168.11.0/16 /data/volumes/v1 192.168.11.0/16
apiVersion: v1 kind: PersistentVolume metadata: name: pv001 labels: name: pv001 spec: nfs: path: /data/volumes/v1 server: 192.168.11.158 accessModes: ["ReadWriteMany","ReadWriteOnce"] capacity: storage: 2Gi --- apiVersion: v1 kind: PersistentVolume metadata: name: pv002 labels: name: pv002 spec: nfs: path: /data/volumes/v2 server: 192.168.11.158 accessModes: ["ReadWriteMany","ReadWriteOnce"] capacity: storage: 5Gi --- apiVersion: v1 kind: PersistentVolume metadata: name: pv003 labels: name: pv003 spec: nfs: path: /data/volumes/v3 server: 192.168.11.158 accessModes: ["ReadWriteMany","ReadWriteOnce"] capacity: storage: 2Gi --- apiVersion: v1 kind: PersistentVolume metadata: name: pv004 labels: name: pv004 spec: nfs: path: /data/volumes/v4 server: 192.168.11.158 accessModes: ["ReadWriteMany","ReadWriteOnce"] capacity: storage: 2Gi --- apiVersion: v1 kind: PersistentVolume metadata: name: pv005 labels: name: pv005 spec: nfs: path: /data/volumes/v5 server: 192.168.11.158 accessModes: ["ReadWriteMany","ReadWriteOnce"] capacity: storage: 2Gi
apiVersion: v1 kind: PersistentVolumeClaim metadata: name: mypvc namespace: default spec: accessModes: ["ReadWriteMany"] resources: requests: storage: 4Gi --- apiVersion: v1 kind: Pod metadata: name: pod-vol-pvc namespace: default spec: containers: - name: myapp image: ikubernetes/myapp:v1 volumeMounts: - name: html mountPath: /usr/share/nginx/html volumes: - name: html persistentVolumeClaim: claimName: mypvc
[root@k8s-master ~]# kubectl get pv NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE pv001 2Gi RWO,RWX Retain Available 8m pv002 5Gi RWO,RWX Retain Bound default/mypvc 8m pv003 2Gi RWO,RWX Retain Available 8m pv004 2Gi RWO,RWX Retain Available 8m pv005 2Gi RWO,RWX Retain Available 8m [root@k8s-master ~]# kubectl get pvc NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE mypvc Bound pv002 5Gi RWO,RWX 8m [root@k8s-master ~]# kubectl get pods NAME READY STATUS RESTARTS AGE myapp-deploy-67f6f6b4dc-2986w 1/1 Running 0 4h myapp-deploy-67f6f6b4dc-czvq4 1/1 Running 0 4h myapp-deploy-67f6f6b4dc-tpggj 1/1 Running 0 10d pod-vol-pvc 1/1 Running 0 9m tomcat-deploy-588c79d48d-mdgml 1/1 Running 0 10d tomcat-deploy-588c79d48d-mvttj 1/1 Running 0 4h tomcat-deploy-588c79d48d-w2mxb 1/1 Running 0 10d 在創建pvc的時候如果后端沒有合適的pv 那么掛載此pvc的pod將會一致處於等待狀態直到pvc匹配到一個符合條件的pv
pv和pvc的機制
pv和pvc綁定要求
1.PV和PVC的spec字段.比如PV的存儲(storage)大小
2.PV和PVC的storageClassName字段必須一樣
3. PV描述的是持久化存儲卷 這個API對象主要定義的是一個持久化存儲在宿主機上的目錄,如一個NFS的掛載目錄
PVC可以理解為持久化存儲的 它提供了對某種持久化存儲的描述,但不提供具體的實現
4. 持久化存儲的實現部分則由PV負責完成
5.PV與PVC進行綁定,其實就是將這個PV對象的名字,填在了PVC對象的spec.volumeName字段上
當PV和PVC成功綁定后 Pod就能像使用hostPath等常規類型的Volume一樣 在YAML文件中使用PVC
pv對象轉換成持久化volume的原理
所謂容器的Volume,其實就是將一個宿主機上的目錄跟一個容器里的目錄綁定掛載在了一起
持久化宿主機目錄
遠程存儲服務,比如:遠程文件存儲(比如,NFS、GlusterFS),遠程塊存儲(比如,公有雲提供的遠程磁盤)
“持久化”宿主機目錄的過程,我們可以形象地稱為“兩階段處理”
先把遠程存儲設備附加到指定節點上 然后在節點上格式化存儲設備再掛載到節點上的具體目錄下 這樣才能通過節點目錄訪問遠程存儲設備
Attach階段
為虛擬機掛載遠程磁盤的操作
當一個Pod調度到一個節點上之后,kubelet就要負責為這個Pod創建它的 Volume 目錄.默認情況下,kubelet為Volume創建的目錄是一個宿主機上的路徑如
/var/lib/kubelet/pods/volumes/kubernetes.io~/ 這個目錄是宿主機后面用來和遠程存儲服務mount的關聯目錄
Kubernetes提供的可用參數是nodeName,即宿主機的名字
Mount階段
將磁盤設備格式化並掛載到Volume宿主機目錄的操作
把格式化的磁盤mount到/var/lib/kubelet/pods/volumes/kubernetes.io~/
Kubernetes提供的可用參數是dir 即Volume的宿主機目錄
經過了“兩階段處理” 我們就得到了一個“持久化”的 Volume 宿主機目錄 但是還沒有關聯到docker容器
接下來 kubelet 只要把這個 Volume目錄通過 CRI 里的 Mounts 參數,傳遞給 Docker 然后就可以為 Pod 里的容器掛載這個“持久化”的 Volume
docker run -v /var/lib/kubelet/pods/<Pod的ID>/volumes/kubernetes.io~<Volume類型>/<Volume名字>:/<容器內的目標目錄> 我的鏡像
上面是Kubernetes 處理 PV 的具體原理
StorageClass自動創建pv
Kubernetes只會將StorageClass相同的PVC和PV綁定起來
自動創建PV的機制Dynamic Provisioning
人工管理PV的方式就叫作Static Provisioning
StorageClass對象就是創建PV的模板
定義StorageClass一般包含Name,后端使用存儲插件類型,存儲插件需要使用到的參數等信息 定義好之后相當於定義了一塊巨大的存儲磁盤
當pvc中定義的storageClassName和StorageClass的Name相同的時候那么StorageClass會自動從巨大的存儲磁盤創建一個指定storage大小的pv,創建的pv和pvc進行綁定
StorageClass對象會定義如下兩個部分內容
第一 PV的屬性. 比如,存儲類型
第二 創建這種PV需要用到的存儲插件. 比如Ceph等等
k8s根據用戶提交的PVC中指定的storageClassName的屬性值找到對應的StorageClass.然后調用該StorageClass聲明的存儲插件創建出需要的PV
有了Dynamic Provisioning機制,運維人員只需要在Kubernetes集群里創建出數量有限的StorageClass對象就可以了
當開發人員提交了包含StorageClass字段的PVC之后,Kubernetes就會根據這個StorageClass創建出對應的PV
在沒有StorageClass的情況下運維人員對開發人員定義的每一個pvc都要手動創建一個對應的pv與其進行綁定 這樣就會大大的增加創建pv的工作量
