1 卷的介紹
1.1 卷的概念
在搞容器的時候,我們在處理完應用如何起,如何運行,最終落實到數據的時候,我們又要考慮2個問題:容器是如何訪問外部磁盤存儲的?容器之間如何共享存儲空間?在一些場景下,我們經常希望新起的容器可以在之前容器over的那個卡點處繼續運行下去。
怎么做?怎么能解決上面的問題?這個時候k8s中的卷,也就是存儲卷應運而生。卷不是獨立的k8s對象,它是pod的一部分,和pod同生命周期(共享),不能單獨創建和銷毀,即跟隨pod啟動時創建,刪除時銷毀。當重啟容器時,卷可以保持不變,新的容器可以識別上一個容器寫入卷的所有文件,這樣就可以卡點繼續工作。
所有容器使用卷的前提是將卷掛載在每個需要訪問它的容器中,當然,這個操作可以在容器文件系統的任意位置掛載。不過,容器可以裝載也可以選擇不裝載卷。
k8s對於有狀態的容器應用或者數據需要持久化存儲的應用,不僅僅需要將容器內的目錄掛載到宿主機的目錄或者通過emptyDir臨時存儲卷,同樣需要更加可靠的持久卷和持久卷聲明這兩個資源對象進行應用重要數據的存儲,這樣可以對用戶屏蔽底層存儲實現的細節,讓其更好的關注pod這些資源的維護。
2 卷的分類
2.1 卷的常用分類
這邊主要列舉一些常見的分類
- emptyDir——用於存儲臨時數據的簡單空目錄;
- hostPath——用於將目錄從工作節點的文件系統掛載到pod中;
- gitRepo——通過Git倉庫的內容來初始化的卷;
- nfs——掛載到pod中的NFS共享卷;
- configMap、secret、downwardAPI——用於將k8s部分資源和集群信息等元數據公開給運行在pod中應用程序的特殊類型的卷,它們不用於存儲數據;
- persistentVolumeClaim——一種使用預置或者動態配置的持久存儲類型。
2.2 emptyDir卷
emptyDir卷,就是從一個空目錄開始,運行在pod內的應用程序可以寫入它需要的任何文件。其生命周期是和pod捆綁,隨着pod創建而創建;刪除而銷毀,卷的內容將會丟失。emptyDir卷適用於同一個pod中運行的容器之間共享文件。
舉例:
volumes:
- name: emptyDir1
emptyDir: {}
兩個容器掛載同一個卷進行共享:
spec:
template:
containers:
- name: container1
image: test/image1
volumeMounts:
- name: emptyDir1
mountPath: /var/dir1
- name: container2
image: test/image2
volumeMounts:
- name: emptyDir1
mountPath: /dev/doc/dir2
readOnly: true
ports:
- containerPort: 80
protocol: TCP
volumes:
- name: emptyDir1
emptyDir: {}
第一個容器為container1,運行鏡像test/image1,名為emptyDir1的卷掛載在容器的/var/dir1路徑中;第二個容器胃container2,運行鏡像test/image2,與第一個容器相同的卷掛載在/dev/doc/dir2路徑上,且為只讀。
2.3 gitRepo卷
gitRepo卷是一種emptyDir卷,通過克隆Git倉庫並在pod啟動時,檢查出特定版本來填充數據(注意:pod啟動時,是在創建容器前)。
原理如下:
- 用戶創建帶有gitRepo volume的pod;
- k8s創建一個空目錄並將指定的git倉庫克隆到其中;
- pod中的容器啟動(卷掛載在路徑上)
gitRepo volume有一個缺陷:每次將更改推送到gitRepo時,都需要刪除pod才可以拉取到最新版本的信息,即本地目錄和git倉庫無法同步。當然,這個缺陷可以通過同步容器來實現。
2.4 hostPath卷
hostPath卷指向節點文件系統上的特定文件或目錄,某些系統級別的pod可以通過掛載hostPath卷去讀取節點的文件或使用節點文件系統對節點進行訪問。在同一個節點上運行並在其hostPath卷中使用相同路徑的pod可以看到相同的文件。
hostPath卷屬於持久性存儲,刪除pod1后,卷里面的文件繼續保持,不丟失,新的pod2如果使用了指向主機相同路徑的hostPath卷,則pod2就能夠發現pod1留下的文件和數據(前提,pod2能夠被調度到pod1相同的節點)。
pod對預定規划的節點敏感, 基於這種前提,因為卷的內容存儲在特定節點的文件系統,所以pod被重新調度到其他節點時,就無法訪問到原數據,hostPath卷不適合作為存儲數據庫數據的目錄。hostPath卷通常用於嘗試單節點集群中的持久化存儲,僅當需要在節點上讀取或寫入系統文件時才會使用hostPath,不用做持久化跨pod的數據。
2.5 持久化存儲
帶有單個運行db1的容器的pod,容器掛載引用外部的GCE持久磁盤。這種方式需要pod的開發人員了解這個環境集群中的可用真實網絡存儲的基礎結構,這就違背了k8s的基本理念(向應用程序及開發人員隱藏集群環境的真實基礎設施,這種基礎設施應該由集群管理員來控制)
2.6 持久卷PV/持久卷聲明PVC
持久卷: pv,persistentVolume,不屬於任何命名空間,和節點一樣屬於集群層面的資源,管理員通過k8s API Server創建pv時,需要告知k8s:容量需求、訪問模式(RWO/ROX/RWX)、處理ov邏輯(pvc綁定刪除后的邏輯)、存儲類型、存儲位置等屬性。
持久卷聲明: pvc,persistentVolumeClaim,pvc可以當做pod中的一個卷來使用,其他用戶不能使用相同的持久卷pv,但可以通過刪除pvc綁定來釋放出pv。雖然pv不在特定的命名空間下創建,但pvc只能在特定的命名空間下創建,所以pvc和pv只能被同一個命名空間內的pod創建使用。
通過持久化存儲,研發人員需要了解底層的基礎設施實際情況,為了能夠順應k8s的理念,通過pvc和pv來向研發人員屏蔽掉底層存儲的架構。集群管理員設置底層存儲,通過k8s API Server指定其大小和所支持的訪問模式,創建持久卷並注冊。研發人員只需要創建持久卷聲明PVC清單,指定所需要的最低容量要求和訪模式就可以了,剩下的pvc會提交到k8s API Server端,k8s找到可以匹配的pv,並將該pv綁定到pvc。
流程:
- 集群管理員創建某種類型的網絡存儲;
- 集群管理員通過k8s API Server創建持久卷pv,指定大小和訪問模式;
- 用戶創建持久卷聲明pvc;
- k8s尋找一個具有足夠容量的pv,將其設置為訪問模式,將pvc綁定到該pv上;
- 用戶創建一個pod並通過卷配置spec.template.spec.volumes.persistentVolumeClaim.claimName來引用pvc
附:pv訪問模式
- RWO:ReadWriteOnce,僅允許當個節點掛載進行讀寫;
- ROX:ReadOnlyMany,允許多個節點掛載且只讀;
- RWX:ReadWriteMany,允許多個節點掛載進行讀寫;