理解OpenShift(1):網絡之 Router 和 Routehtml
理解OpenShift(2):網絡之 DNS(域名服務)node
理解OpenShift(5):從 Docker Volume 到 OpenShift Persistent Volumegithub
** 本文基於 OpenShift 3.11,Kubernetes 1.11 進行測試 ***docker
Docker 鏡像是不可修改的。使用一Docker 鏡像啓動一個容器實例後,Docker 會在鏡像層之上添加一個可讀寫的容器層(Container layer)。容器中全部新增或修改的數據都保存在該容器層之中。在容器實例被刪除後,該層也會隨之被自動刪除,所以全部寫入的或修改的數據都會丟失。具體可閱讀Docker 相關文檔,好比 https://docs.docker.com/v17.09/engine/userguide/storagedriver/imagesandcontainers/。ubuntu
在容器的可寫層中保存數據是可能的,可是有一些缺點:後端
爲了解決以上問題,Docker 提供了 Volume (卷)功能。本質上,一個數據卷(data volume)是 Docker 容器所在宿主機上的一個目錄或文件,它被掛載(mount)進容器。Docker 卷具備本身獨立的生命週期,可使用 Docker volume 命令獨立地被建立和管理。在容器實例被刪除後,卷依然存在,所以卷中的數據會被保留,從而實現數據持久化。並且,數據卷直接將數據寫入宿主機文件系統,性能相比容器的可寫層有提升。centos
Docker 提供三種方式將宿主機文件或文件夾掛載到容器中:bash
tmpfs
volume:數據保存在宿主機內存中,而不寫入磁盤。
三種方式各自有合適的場景,一般建議使用 Docker Volume。Docker Volume 還支持經過各類卷插件(volume plugin),接入各類外置存儲。本質上,都是存儲插件將存儲的卷掛載到Docker宿主機上的某個目錄,而後Docker 將目錄在掛載給容器。
更詳細信息,請閱讀 https://docs.docker.com/v17.09/engine/admin/volumes/#good-use-cases-for-tmpfs-mounts 等官方文檔。
OpenShift 利用 Kubernetes 的存儲機制來實現其 Volume 功能。和Docker volume 概念相似,本質上,一個 K8S Volume 也是一個能被Pod 中的容器訪問的目錄。至於該目錄是怎麼來的,後端介質是什麼,內容是什麼,則是由所使用的具體卷類型(volume type)決定的。Kubernetes Volume 支持多種存儲類型:
關於 K8S Volume 概念的更多信息,請閱讀相關文檔。
下面以 Glusterfs Volume 爲例介紹 K8S Volume 的使用:
(1)OpenShift 管理員在集羣中建立一個 endpoints 對象,指向 Glusterfs 服務器的 IP 地址。在個人測試環境中,由兩臺服務器提供Glusterfs服務。
172.20.80.7:glusterfsvol1 on /var/lib/origin/openshift.local.volumes/pods/bd8914b5-00d9-11e9-a6cf-fa163eae8505/volumes/kubernetes.io~glusterfs/glustervol1 type fuse.glusterfs (rw,relatime,user_id=0,group_id=0,default_permissions,allow_other,max_read=131072)
(5)而後,宿主機上的這個目錄會經過 Docker bind mounted 掛載進容器
從上面過程能夠看出,使用卷的過程須要至少有存儲工程師和開發人員。要使用某種卷,開發人員須要瞭解後端存儲的具體配置信息。
可是實際上,存儲信息對於應用開發人員來講,實際上是不須要可見的。他們只關心有沒有知足要求的存儲可用,而不須要關心後端是什麼存儲。
爲了解耦存儲供給和存儲使用(pod中的存儲定義),Kubernetes 建立了兩個概念:PV (Persistent Volume)和 PVC (Persistent Volume Claim)這些概念。
以 Glusterfs 爲例,這是各類概念之間對照圖(來源: http://blog.leifmadsen.com/blog/2017/09/19/persistent-volumes-with-glusterfs/):
根據 PV 的不一樣建立方式,又能夠分爲靜態建立PV 和 動態建立PV兩種方式。前面一種PV由OpenShift 管理員手工建立,後者一種的PV由系統自動建立。具體可參考後面的兩個例子。
(1)存儲管理員準備 NFS 環境
網上有不少關於NFS安裝步驟的文章,這裏再也不重複。個人測試環境上,NFS 服務器的IP 地址爲 172.20.80.4,它暴露了三個文件夾供客戶端使用:
(2)OpenShift 管理員建立 PV, 後端使用上述 NFS 存儲的
(3)開發人員建立一個 PVC,使用上一步驟中建立的PV。該 PVC實例會存在於某個project 之中,而PV則是在集羣範圍內共享的。
(4)NFS folder4 文件夾被掛載到Pod 所在的宿主機上。
172.20.80.4:/mnt/folder4 on /var/lib/origin/openshift.local.volumes/pods/863e9b2d-01a0-11e9-a6cf-fa163eae8505/volumes/kubernetes.io~nfs/pv-folder4-2 type nfs4 (rw,relatime,vers=4.1,rsize=1048576,wsize=1048576,namlen=255,hard,proto=tcp,port=0,timeo=600,retrans=2,sec=sys,clientaddr=172.22.122.8,local_lock=none,addr=172.20.80.4)
(5)宿主機上的目錄被 bind mounted 給容器,成爲其 /var/volume 文件夾。
每種存儲後端都有其本身的權限管理方式。在NFS 中,在 /etc/exports 文件中國年,可使用如下原語來設置每一個將被共享出來的文件夾的權限:
NFS 用戶認證及權限控制基於 RPC。在 NFS 3 和 4 版本中,最經常使用的認證機制是 AUTH_Unix。客戶端系統上的 uid 和 gid 經過 RPC 調用傳到 NFS 端,而後這些 id 所擁有的權限會被校驗,以肯定可否訪問目標資源。所以,客戶端和服務器端上的 uid 和 gid 必須相同。同時,可使用一些設置來作特定處理:
在咱們當前的例子中,folder4 的文件夾權限爲 /mnt/folder4 172.22.122.0/24(insecure,rw,sync,no_root_squash,no_all_squash)。這表示它:
NFS 上的 folder4 的目錄權限爲:drwxr--r-x 2 nfsnobody nfsnobody 4096 Dec 17 10:11 folder4。這意味着 nfsnobody 用戶能夠對它作讀寫,其它用戶(包括nfsnobody組內的用戶和其它用戶)都只能讀。
Pod 中的用戶 id 爲:uid=1001(default) gid=0(root) groups=0(root)。
查詢共享目錄OK。
寫入失敗:Permission denied。這是由於本地用戶 uid 1001 在 NFS 服務器上有匹配的用戶 (cloud-user2:x:1001:1001::/home/cloud-user2:/bin/bash),而該用戶並無 folder4 文件夾寫權限。
從上面對 NFS 權限控制原理的分析能夠看出有幾種方式來保證Pod 中的用戶的寫入成功。
(1)將 NFS 暴露出來的文件夾的全部者修改成 nfsnobody:nfsnobody,而後在文件夾上設置 all_squash,這會將全部客戶端 uid 和 gid 映射爲NFS服務器端的 nfsnobody 用戶和 nfsnobdy 組。
在 pod 中的 id: uid=1001(default) gid=0(root) groups=0(root)
在 pod 中寫入文件,而後在 NFS 上查看:
可見是uid 和 gid 都是映射成功了的。
(2)上述方法一將全部客戶端的用戶都映射爲 nfsnobody:nfsnobody,這有了統一性,可是也消滅了獨特性。有時候還須要保留客戶端上的已知uid。此時會在 NFS 共享的文件夾上設置 no_all_squash,這樣會先作匹配找到兩地都有的user,匹配不成功則走步驟(1)中的作法。
這種狀況下,若是匹配成功,則NFS 會對服務器端的同 uid 和 gid 的用戶的權限進行校驗。一般狀況下,NFS 服務器端匹配到的用戶不會是 nfsnobdy,根據文件夾上的權限設置,此時Pod 中是沒法寫入文件的。這就是 2.2.1 中說描述的場景的結果。此時有兩種處理方式:
(a)將文件夾上 other user 加上寫權限。這種作法比較簡單粗暴,權限暴露過大,不推薦使用。
chmod o+w folder5 -R
(b)使用 supplemental group id
Linux 系統中, supplemental group ID 是進程所擁有的附加組的一個集合。在 Linux 上,文件系統的用戶(user)、組(group)的 ID,連同輔助組(supplementary group)的ID,一塊兒肯定對文件系統的操做權限,包括打開(open)、修改全部者(change ownership)、修改權限(permission)。具體請閱讀相關 Linux 文檔。
首先修改NFS 文件夾的 gid 爲某個數值,好比下面的命令修改gid爲 2000(這裏其實是 gid,不是 supplemental gid。gid 對文件夾有意義,而 supplemental gid 對文件夾無心義而對進程有意義)。
chown :2000 folder4 -R
而後在 pod 上進行配置,使得 Pod 中的主進程的輔助組id 爲這裏所設置的gid。
(1)Pod 的 uid,gid 和 supplemental gid
Kubernets 目前還不支持設置 gid,所以全部 pod 中運行主進程的 gid 都是 0。
對一個非 cluster admin role 用戶啓動的 pod,它的默認 service account 爲 restricted。它要求 uid 必須在指定的區間內,而它本身並無指定用戶id 區間:
此時 pod 的 uid 區間受pod 所在的 project 上的定義的相應 annotation 限制:
此時pod 中的 uid 和 suppenmental gid 以下圖所示:(備註:與前面的例子中的 uid 不一樣,是由於前面的 pod 是 cluster admin user 啓用的,所以 pod 的 scc 爲 anyuid):
在不顯式指定 uid 和 supplemental gid 的狀況下,會使用區間的最小值做爲默認值。
(2)修改 Pod 的 uid
根據前面對 NFS 權限管理的分析,能夠將 Pod 中的 uid 修改成 nfsnobody 對應的 uid,這樣Pod 就會具備 NFS 共享目錄的寫入權限。可是,默認的 nfsnobdy 的 uid 爲 65534,這個 uid 並不在service account restricted 容許的 uid 區間 [1000000000, 1000009999] 以內,所以沒法將 uid 設置爲 65534.
此時,能夠基於 restricted scc 建立一個新的 scc,別的配置不變,除了將 RunAsUser 策略修改成 RunAsAny 之外。此時,就能夠在 Pod 中指定 uid 爲 65534 了。
新的scc:
pod 中指定 uid:
pod 的 uid:
掛載的文件夾可寫。操做成功。
(3)修改 supplementantal gid
由於 uid 會和太多因素關聯,因此直接修改 uid 這種作法比較重。除了 uid 外,Pod 中還能夠:
由於 Glusterfs 是共享文件存儲,所以需設置輔助組id。具體步驟包括:
這兩,在NFS客戶端(pod)和服務器端(文件夾)上經過 group id 將把權限打通了。
更詳細說明,請閱讀 OpenShift 官方文檔 https://docs.okd.io/latest/install_config/persistent_storage/pod_security_context.html。
下圖展現了從 OpenShift 角度看的動態建立PV的流程。在步驟 3.2,當開發人員建立好PVC之後,OpenShift 會在當前StorageClass中查找知足要求的 StorageClass。一旦找到,就會根據PVC中的配置自動建立一個PV,並調用StorageClass中的 storage provisioner 自動建立一個存儲volume。在開發人員建立使用該 PVC 的 Pod 後,存儲卷就會被掛載給Pod 所在的宿主機,而後經過 bind mounted 被掛載給Pod。
這麼作的好處是顯而易見的,好比:
另外一方面,OpenShift 會爲每一個PVC 在後端存儲上建立一個卷。這樣,在有大量PVC時,存儲中將出現大量的小容量卷,這對某些存儲會產生至關大的壓力,特別是對於一些傳統存儲。這些存儲可能就不能知足現代容器雲平臺對存儲的要求了。
由於 Glusterfs 自己不提供 REST API,所以須要在它前面部署一個Proxy。Heketi 就是一種開源的這種Proxy,它的項目地址是 https://github.com/heketi/heketi。它暴露Gluster Volume 操做的REST API,並經過 SSH 來運行 Glusterfs 命令,完成各類卷相關的操做,好比建立,映射等。OpenShift 經過調用 Heketi API 來實現 Gluesterfs 卷的動態建立和管理。
(1)OpenShift 管理員建立 StorageClass
每一個 StorageClass 會包含幾個屬性:
關於StorageClass的詳細說明,請閱讀 https://kubernetes.io/docs/concepts/storage/storage-classes/。
(2)開發人員建立一個PVC
其中關鍵的一項是在 storageClassName 中制定 StorageClass 的名稱。
(3)OpenShfit 自動建立一個PV,以及其它資源。
OpenShfit 會根據 StorageClass 及 PVC 中的有關屬性,動態建立一個 PV。
以及 Service:
及其 Endpoints:
OpenShift 是經過該 service 調用 storage provisioner 的。
(4)Volume plugin 會自動地建立存儲卷
Heketi 在 Glusterfs 中建立改卷的過程大體以下:
(a)Glusterfs 系統初始化時會爲每一個物理磁盤建立一個 Volume Group:
pvcreate --metadatasize=128M --dataalignment=256K '/dev/vde' vgcreate --autobackup=n vg_c04281d30edfa285bb51f0f323ab7690 /dev/vde
gluster --mode=script volume create vol_e22dc22f335de8f8c90f7c66028edf37 172.20.80.7:/var/lib/heketi/mounts/vg_c04281d30edfa285bb51f0f323ab7690/brick_97d37975df78714e2e0bfea850a9e4aa/brick mkdir -p /var/lib/heketi/mounts/vg_c04281d30edfa285bb51f0f323ab7690/brick_97d37975df78714e2e0bfea850a9e4aa lvcreate --autobackup=n --poolmetadatasize 8192K --chunksize 256K --size 1048576K --thin vg_c04281d30edfa285bb51f0f323ab7690/tp_97d37975df78714e2e0bfea850a9e4aa --virtualsize 1048576K --name brick_97d37975df78714e2e0bfea850a9e4aa mkfs.xfs -i size=512 -n size=8192 /dev/mapper/vg_c04281d30edfa285bb51f0f323ab7690-brick_97d37975df78714e2e0bfea850a9e4aa mount -o rw,inode64,noatime,nouuid /dev/mapper/vg_c04281d30edfa285bb51f0f323ab7690-brick_97d37975df78714e2e0bfea850a9e4aa /var/lib/heketi/mounts/vg_c04281d30edfa285bb51f0f323ab7690/brick_97d37975df78714e2e0bfea850a9e4aa
#這個目錄是在 Glusterfs 節點上實際保存數據的目錄
mkdir /var/lib/heketi/mounts/vg_c04281d30edfa285bb51f0f323ab7690/brick_97d37975df78714e2e0bfea850a9e4aa/brick
#該命令會目錄的 gid 修改成前述第(3)步中的 gid
chown :2000 /var/lib/heketi/mounts/vg_c04281d30edfa285bb51f0f323ab7690/brick_97d37975df78714e2e0bfea850a9e4aa/brick chmod 2775 /var/lib/heketi/mounts/vg_c04281d30edfa285bb51f0f323ab7690/brick_97d37975df78714e2e0bfea850a9e4aa/brick
(5)開發人員建立一個使用上述PVC的 Pod
(6)Pod 啓動時,系統會
[root@node2 cloud-user]# mount | grep gluster 172.20.80.7:vol_e22dc22f335de8f8c90f7c66028edf37 on /var/lib/origin/openshift.local.volumes/pods/5d97c7db-ff75-11e8-8b3e-fa163eae8505/volumes/kubernetes.io~glusterfs/pvc-10438bac-ff75-11e8-8b3e-fa163eae8505 type fuse.glusterfs (rw,relatime,user_id=0,group_id=0,default_permissions,allow_other,max_read=131072)
而後該宿主機目錄做爲一個 mountpoint 被掛載給容器:
172.20.80.7:vol_e22dc22f335de8f8c90f7c66028edf37 on /var/volume type fuse.glusterfs (rw,relatime,user_id=0,group_id=0,default_permissions,allow_other,max_read=131072)
查看用戶,它有id 爲 2000 輔助組。
感謝您的閱讀,歡迎關注個人微信公衆號: