理解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 辅助组。
感谢您的阅读,欢迎关注个人微信公众号: