概述
摘要:本文通过实践描述并验证了pod内容器如何实现网络、文件、PID、UTC、mount的共享。
pod实战之容器内资源共享与隔离
container容器之间的共享实战
从实际场景说起:有2个容器nginx与wordpress分别运行了紧密耦合且需要共享资源的应用程序。我们希望2两个容器共享网络命名空间,IPC 命名空间,PID 命名空间。
实现这个需求的可以使用2个解决方案:
解决方案一
- 先创建一个名为nginx的业务容器,根据docker的底层原理,linux内核中的namespaces组件会为nginx的容器创建独立与宿主的网络、IPC、PID、UTC命名空间。
- 再创建业务容器wordpress,同时将wordpress“加入”,属于nginx容器的命名空间。
该方案实现简单,但是存在一个问题就是,wordpress容器依赖nginx业务容器,一旦nginx容器fail,wordpress容器也将fail.
解决方案二
为了解决方案一存在的问题,我们可以利用pause容器来解决。pause容器不执行任何业务逻辑,仅仅是为了创建一个独立的命名空间而生,之后再将业务容器
“加入”到pause容器已经创建的命名空。具体步骤如下:
- 先创建一个容器:pause的容器,根据docker的底层原理,linux内核中的namespaces组件会为pause的容器创建独立于宿主的网络、IPC、PID、UTC命名空间。pause是一个特殊的容器,里面运行着一个非常简单的进程,它不执行任何功能,基本上是永远“睡觉”的。
- 创建业务容器nginx,同时将nginx”加入“,属于pause容器的命名空间。
- 再创建业务容器wordpress,同时将wordpress”加入“,属于pause容器的命名空间。
解决方案二的实践
root@ ubuntu1604:~# docker run -d --name pause pause:3.1
3fd25a34f1b92e3799bbef61b13f88258ea9633b1ffef512e8e7d2f6b5fc199aroot@ ubuntu1604:~# docker ps |grep pause
3fd25a34f1b9 pause:3.1 "/pause" 26 seconds ago Up 25 seconds pause
- –name:指定 pause 容器的名字,pause
root@ ubuntu1604:~# docker run -d --name nginx --net=container:pause --ipc=container:pause --pid=container:pause --ipc=shareable ubuntu1604
3d043d9a692cb8e3ed1e7f522c8222cc99d0e0c1a44b8b2ca0c4083c308f073froot@ ubuntu1604:~# docker ps |grep nginx
3d043d9a692c ubuntu1604 "/start.sh" 25 seconds ago Up 24 seconds nginx
- –net=container:pause:表示创建的容器与另一个容器共享网络命名空间。在这种情况下,容器 “nginx” 会与名为 “pause” 的容器共享网络命名空间,它们可以使用相同的网络配置和接口。
- –ipc=container:pause:表示创建的容器与另一个容器共享 IPC 命名空间。IPC 命名空间允许容器之间进行进程间通信(Inter-Process Communication),在这里,容器 “nginx” 与名为 “pause” 的容器共享 IPC 命名空间。
- –pid=container:pause:表示创建的容器与另一个容器共享 PID 命名空间。PID 命名空间允许容器查看和管理其他容器的进程。
- –ipc=shareable:指示 IPC 命名空间是可共享的,以便其他容器也可以加入到这个共享命名空间中。
root@ ubuntu1604:~# docker run -d --name wordpress --net=container:pause --ipc=container:pause --pid=container:pause --ipc=shareable ubuntu16.04
efaedd09438fdc2eb903cc2b57301088abf6cd6ee35fcc37f8d0b341802c28b5
- 查看网络命名空间是否共享
root@ ubuntu1604:~# docker exec -it nginx /bin/bash
root@3fd25a34f1b9:/work# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00inet 127.0.0.1/8 scope host lovalid_lft forever preferred_lft forever
5: eth0@if6: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group defaultlink/ether 02:42:c0:a8:01:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0inet 192.168.1.2/24 brd 192.168.1.255 scope global eth0valid_lft forever preferred_lft forever
root@3fd25a34f1b9:/work# ip route show
default via 192.168.1.1 dev eth0
192.168.1.0/24 dev eth0 proto kernel scope link src 192.168.1.2root@ ubuntu1604:~# docker exec -it wordpress /bin/bash
root@3fd25a34f1b9:/work# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00inet 127.0.0.1/8 scope host lovalid_lft forever preferred_lft forever
5: eth0@if6: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group defaultlink/ether 02:42:c0:a8:01:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0inet 192.168.1.2/24 brd 192.168.1.255 scope global eth0valid_lft forever preferred_lft forever
root@3fd25a34f1b9:/work# ip route show
default via 192.168.1.1 dev eth0
192.168.1.0/24 dev eth0 proto kernel scope link src 192.168.1.2
可以看到nginx与wordpress两个容器,IP地址和路由表是一样的,说明它们共用一个网络协议栈(网络命名空间)。
- 查看PID命名空间是否共享
root@ ubuntu1604:~# docker exec -it nginx ps aux
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.0 0.0 1024 4 ? Ss 17:26 0:00 /pause
root 6 0.0 0.0 19696 3160 ? Ss 17:28 0:00 /bin/bash /sta
root 25 0.0 0.0 65512 3128 ? Ss 17:28 0:00 /usr/sbin/sshd
root 34 0.0 0.0 27728 2748 ? Ss 17:28 0:00 /usr/sbin/cron
root 39 0.0 0.0 6008 664 ? S 17:28 0:00 sleep 600
root 40 0.0 0.0 19696 3368 ? Ss 17:29 0:00 /bin/bash /sta
root 68 0.0 0.0 27728 2620 ? Ss 17:29 0:00 /usr/sbin/cron
root 73 0.0 0.0 6008 668 ? S 17:29 0:00 sleep 600
root 126 0.0 0.0 36084 3224 pts/0 Rs+ 17:34 0:00 ps auxroot@ ubuntu1604:~# docker exec -it wordpress ps aux
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.0 0.0 1024 4 ? Ss 17:26 0:00 /pause
root 6 0.0 0.0 19696 3160 ? Ss 17:28 0:00 /bin/bash /sta
root 25 0.0 0.0 65512 3128 ? Ss 17:28 0:00 /usr/sbin/sshd
root 34 0.0 0.0 27728 2748 ? Ss 17:28 0:00 /usr/sbin/cron
root 39 0.0 0.0 6008 664 ? S 17:28 0:00 sleep 600
root 40 0.0 0.0 19696 3368 ? Ss 17:29 0:00 /bin/bash /sta
root 68 0.0 0.0 27728 2620 ? Ss 17:29 0:00 /usr/sbin/cron
root 73 0.0 0.0 6008 668 ? S 17:29 0:00 sleep 600
root 121 0.0 0.0 36084 3208 pts/0 Rs+ 17:33 0:00 ps aux
可以看到nginx与wordpress两个容器,可以相互看到多方的进程pid。
- 查看文件系统挂载是否共享
root@ ubuntu1604:~# docker exec -it nginx df
Filesystem 1K-blocks Used Available Use% Mounted on
overlay 309505024 38063364 258331752 13% /
tmpfs 65536 0 65536 0% /dev
tmpfs 16336452 0 16336452 0% /sys/fs/cgroup
/dev/vda1 309505024 38063364 258331752 13% /etc/hosts
shm 65536 0 65536 0% /dev/shm
tmpfs 16336452 0 16336452 0% /proc/acpi
tmpfs 16336452 0 16336452 0% /proc/scsi
tmpfs 16336452 0 16336452 0% /sys/firmwareroot@ ubuntu1604:~# docker exec -it wordpress df
Filesystem 1K-blocks Used Available Use% Mounted on
overlay 309505024 38063460 258331656 13% /
tmpfs 65536 0 65536 0% /dev
tmpfs 16336452 0 16336452 0% /sys/fs/cgroup
/dev/vda1 309505024 38063460 258331656 13% /etc/hosts
shm 65536 0 65536 0% /dev/shm
tmpfs 16336452 0 16336452 0% /proc/acpi
tmpfs 16336452 0 16336452 0% /proc/scsi
tmpfs 16336452 0 16336452 0% /sys/firmware
可以看到nginx与wordpress两个容器,挂载的目录是一样的。
- 查看UTC命名空间是否共享
root@ ubuntu1604:~# docker exec -it wordpress hostname
3fd25a34f1b9
root@ ubuntu1604:~# docker exec -it nginx hostname
3fd25a34f1b9
可以看到nginx与wordpress两个容器,挂载的主机名是一样的。
总结:
-
方案二中,由于pause是不运行业务逻辑代码,永远处于”睡觉“状态。就可以"维护"一套独立的命名空间,再将业务容器加入pause的命名空间。
-
细心的读者可能观察到,这套方案和pod就很类似,恩,没错。kubernetes就是利用pause容器+业务容器来实现一个POD的。
-
其实pause除了实现 Pod 中多个容器的共享网络与资源隔离的功能之外,pause 容器也会负责管理容器之间的生命周期,保证在 Pod 删除与更新时,容器和资源可以被正确地释放。如果 Pod 中运行的所有其他容器都终止,则 pause 容器自动终止并删除。
关于pause容器的具体描述后面再展开。
pod内容器之间的共享与隔离
我们知道kubernetes管理的最小单位是pod,而不是容器。一个pod中可以包括一个或多个容器。在实践中,通常一个pod中运行多个容器,将多个紧密耦合且需要共享资源的应用程序封装到一个pod.
实践中根据业务场景的不同,需要考虑pod中多个容器对共享与隔离的需求:
- pod内的多个容器,使用的网络是共享的,而且也是必须的。
- pod内的多个容器,对于文件系统是有共享的需求的,默认情况下容器之间对文件访问是隔离的。
- pod内的多个容器,对于pid也是有共享需求的,默认情况下容器的pid是隔离的,不能相关看到对方容器的pid。
- pod内的多个容器,对于hostname(UTC)也是有共享需求的,默认情况下容器的UTC是隔离的,容器有不同的hostname。
- pod需要和宿主共享PID,默认pod是无法看到宿主的PID,也是处于安全考虑,默认情况下pod无法与宿主共享PID命名空间。
pod内容器之间网络的共享
当Pod创建多个容器时,容器之间会通过namespace实现了隔离的,之间的网络是隔离的。Pod要解决内部容器之间网络通信的问题其实就要打破namespace的隔离。
那么Kubernetes是怎么解决两个容器之间通信的问题呢?
也即是容器之间看到的网络协议栈是一样的,在创建pod的时候会先创建infra container这个容器(也叫sandbox或pause容器),启动好之后然后再将实际创建的容器加入到infra container容器当中,这个infra容器实现的主要目的是维护了pod的网络,没有跑任何的业务逻辑,只是启动了一个容器罢了。其他容器的创建都让其连接至该容器的网络命名空间当中。这样一来其他容器看到的网络视图就是infra container的网络视图了。一个pod当中所有的容器看到的网络设备,网卡,ip,mac地址都看到的是同一个了,因为在一个网络命名空间。这样就解决了网络共享的问题。实际上pod的ip就是infra container的ip。(类似上面方案二的实践)
记住: 同一个pod内的所有容器共享一个协议栈。
pod内容器之间文件的共享
pod内的容器之间的文件系统,默认是相互隔离的。如果需要实现共享文件或目录,可以两个容器共享一个卷用于他们之间的通信。
root@ ubuntu1604:~# cat pod-blog.yaml
---
apiVersion: v1
kind: Pod
metadata:name: bloglabels:app: blog
spec:restartPolicy: Alwaysvolumes:- name: shared-dataemptyDir: {}containers:containers:- name: nginxvolumeMounts:- name: shared-datamountPath: /sharedirimage: ubuntu16.04- name: wordpressvolumeMounts:- name: shared-datamountPath: /sharedirimage: ubuntu16.04
在配置文件中,你可以看到 Pod 有一个共享卷,名为 shared-data
。
配置文件中的第一个容器运行了一个 nginx 服务器,第二个容器是运行 wordpress 服务器。他们共享卷的挂载路径是 /sharedir
。
先在第一个容器中共享的目录中创建一个文件/sharedir/blog.txt
root@ ubuntu1604:~# kubectl get pod -owide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
blog 2/2 Running 0 10s 192.168.26.100 10.234.12.77 <none> <none>
root@ ubuntu1604:~# kubectl exec -it blog -c nginx -- bash
root@blog:/work# df -h
Filesystem Size Used Avail Use% Mounted on
overlay 296G 84G 199G 30% /
tmpfs 64M 0 64M 0% /dev
tmpfs 16G 0 16G 0% /sys/fs/cgroup
/dev/vda1 296G 84G 199G 30% /sharedir
shm 64M 0 64M 0% /dev/shm
tmpfs 16G 12K 16G 1% /run/secrets/kubernetes.io/serviceaccount
root@blog:/work# echo "myblog" >> /sharedir/blog.txt
root@blog:/work# cat /sharedir/blog.txt
myblog
第二个容器读取/sharedir/blog.txt
的内容。
root@ ubuntu1604:~# kubectl exec -it blog -c wordpress -- cat /sharedir/blog.txt
myblog
pod内容器之间的PID的共享
POD内的容器之前,默认的PID是隔离的,无法相互看到或操控。但可以使用 Pod .spec
中的 shareProcessNamespace
字段可以启用进程命名空间共享。shareProcessNamespace的值默认为false,即容器之间不是共享进程命名空间的。调整shareProcessNamespace为true,可以实现容器之间共享进程命名空间
创建POD,指定shareProcessNamespace: true
root@ ubuntu1604:~# cat pod-blog.yaml
---
apiVersion: v1
kind: Pod
metadata:name: bloglabels:app: blog
spec:### 使用 pod.spec.shareProcessNamespace 控制Pod内容器之间的PID共享。shareProcessNamespace的值默认为falseshareProcessNamespace: truerestartPolicy: Alwaysvolumes:- name: shared-dataemptyDir: {}containers:containers:- name: nginxvolumeMounts:- name: shared-datamountPath: /sharedirimage: ubuntu16.04- name: wordpressvolumeMounts:- name: shared-datamountPath: /sharedirimage: ubuntu16.04
观察2个业务容器观察到的PID是一样的。
root@ ubuntu1604:~# kubectl exec -it blog -c nginx -- ps aux
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.0 0.0 1024 4 ? Ss 18:13 0:00 /pause
root 6 0.0 0.0 19708 3344 ? Ss 18:13 0:00 /bin/bash /sta
root 25 0.0 0.0 65512 3080 ? Ss 18:13 0:00 /usr/sbin/sshd
root 34 0.0 0.0 27728 2704 ? Ss 18:13 0:00 /usr/sbin/cron
root 40 0.0 0.0 19708 3328 ? Ss 18:13 0:00 /bin/bash /sta
root 68 0.0 0.0 27728 2700 ? Ss 18:13 0:00 /usr/sbin/cron
root 102 0.0 0.0 6008 800 ? S 18:23 0:00 sleep 600
root 103 0.0 0.0 6008 660 ? S 18:23 0:00 sleep 600
root 119 0.0 0.0 36084 3216 pts/0 Rs+ 18:28 0:00 ps aux
root@ ubuntu1604:~# kubectl exec -it blog -c wordpress -- ps aux
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.0 0.0 1024 4 ? Ss 18:13 0:00 /pause
root 6 0.0 0.0 19708 3344 ? Ss 18:13 0:00 /bin/bash /sta
root 25 0.0 0.0 65512 3080 ? Ss 18:13 0:00 /usr/sbin/sshd
root 34 0.0 0.0 27728 2704 ? Ss 18:13 0:00 /usr/sbin/cron
root 40 0.0 0.0 19708 3328 ? Ss 18:13 0:00 /bin/bash /sta
root 68 0.0 0.0 27728 2700 ? Ss 18:13 0:00 /usr/sbin/cron
root 102 0.0 0.0 6008 800 ? S 18:23 0:00 sleep 600
root 103 0.0 0.0 6008 660 ? S 18:23 0:00 sleep 600
root 124 0.0 0.0 36084 3152 pts/0 Rs+ 18:29 0:00 ps aux
注意:这里有一个隐含的信息,是pause, nginx,wordpress三个容器共享进程命名空间。PID 1是pause容器
pod内容器之间共享主机名
POD内的容器之前,默认的UTC是独立的,也就是容器的主机名都不通。但可以使用 Pod .spec
中的 hostname
实现UTC命名空间共享,也就是多个容器共用同样的主机名。
创建yaml文件pod-blog.yaml
,添加spec.hostname: sharedhostname
实现容器之间共用同样的主机名
---
apiVersion: v1
kind: Pod
metadata:name: bloglabels:app: blog
spec:### 使用 pod.spec.hostname 实现容器共用同一个主机名hostname: sharedhostname### 使用 pod.spec.shareProcessNamespace 控制Pod内容器之间的PID共享。shareProcessNamespace的值默认为falseshareProcessNamespace: truerestartPolicy: Alwaysvolumes:- name: shared-dataemptyDir: {}containers:containers:- name: nginxvolumeMounts:- name: shared-datamountPath: /sharedirimage: ubuntu1604- name: wordpressvolumeMounts:- name: shared-datamountPath: /sharedirimage: ubuntu1604
root@ ubuntu1604:~# kubectl exec -it blog -c nginx -- hostname
sharedhostname
root@ ubuntu1604:~# kubectl exec -it blog -c wordpress -- hostname
sharedhostname
pod与宿主的共享网络命名空间
我们知道pod如果需要使用宿主的网络协议栈,可以使用hostNetwork: true
来实现。 由于该场景非常有,所以本次实践略。
pod与宿主的进程命名空间
我们知道pod如果需要使用宿主的网络协议栈,可以使用hostNetwork: true
来实现。 有时场景下一个管理性的Pod,也需要与宿主进程命名空间,即看到和控制宿主的进程。这时就可以使用hostPID: true
来实现。
创建yaml文件指定hostPID: true
---
apiVersion: v1
kind: Pod
metadata:name: mgr
spec:restartPolicy: AlwayshostPID: truecontainers:- name: nginximage: ubuntu16.04
从容器中查看宿主的内核进程kworker
root@ ubuntu1604:~# kubectl exec -it mgr -- ps aux |grep worker |head
root 4 0.0 0.0 0 0 ? I< 16:28 0:00 [kworker/0:0H]
root 18 0.0 0.0 0 0 ? I< 16:28 0:00 [kworker/1:0H]
root 24 0.0 0.0 0 0 ? I< 16:28 0:00 [kworker/2:0H]
root 30 0.0 0.0 0 0 ? I< 16:28 0:00 [kworker/3:0H]
root 36 0.0 0.0 0 0 ? I< 16:28 0:00 [kworker/4:0H]
root 42 0.0 0.0 0 0 ? I< 16:28 0:00 [kworker/5:0H]
root 48 0.0 0.0 0 0 ? I< 16:28 0:00 [kworker/6:0H]
root 54 0.0 0.0 0 0 ? I< 16:28 0:00 [kworker/7:0H]
root 60 0.0 0.0 0 0 ? I< 16:28 0:00 [kworker/8:0H]
root 66 0.0 0.0 0 0 ? I< 16:28 0:00 [kworker/9:0H]
参考文档
同 Pod 内的容器使用共享卷通信
在 Pod 中的容器之间共享进程命名空间
https://cloud.tencent.com/developer/article/2332162
https://blog.csdn.net/qq_34556414/article/details/110083541