Pod
中文直译为豆荚,一个豆荚中可能存在多个豆子,他们长相可能不尽相同,但是都是为了发芽做准备,类似地,在 Kubernetes 中,Pod 是容器组的概念,一个 Pod 可以包含多个容器,这些容器共享网络、存储等资源,虽然每个容器可能功能不尽相同,通过组合最终体现为某一个完整的能力对外服务。
那它是如何做到资源共享的呢?这得从容器的实现开始讲起,容器的本质是一个特殊的进程,特殊在为其创建了 NameSpace 隔离运行环境,并用 Cgroups 控制资源开销,还借助了一些 Linux 网络虚拟化技术解决了网络通信的问题。
Pod 所做的则是让多个容器加入同一个 NameSpace 以实现资源共享。
Linux Namespace
什么是Namespace?Namespace中文直译为命名空间,它是操作系统内核在不同进程间实现的一种「环境隔离机制」。
举例来说:现在有两个进程A,B。他们处于两个不同的 PID Namespace 下:ns_A / ns_B。在ns_A下,A 进程的 PID 可以被设置为1,在 ns_B 下,B 进程的 PID 也可以设置为1。但是它们两个并不会冲突,因为 Linux PID Namespace 对 PID 这个资源在进程 A,B 之间做了隔离。A 进程在 ns_A 下是不知道 B 进程在 ns_B 下面的 PID 的。
这种环境隔离机制是实现容器技术的基础。因为在整个操作系统的视角下,一个容器表现出来的就是一个进程。
Linux 一共构建了 6 种不同的 Namespace,用于不同场景下的隔离:
- Mount - isolate filesystem mount points
(隔离文件系统挂载点 --> 每个进程都存在于一个mount Namespace里面,mount Namespace为进程提供了一个文件层次视图。如果不设定这个flag,子进程和父进程将共享一个mount Namespace,其后子进程调用mount或umount将会影响到所有该Namespace内的进程, 如果子进程在一个独立的mount Namespace里面,就可以调用mount或umount建立一份新的文件层次视图。)
- UTS - isolate hostname and domainname(隔离主机名和域名信息)
- IPC - isolate interprocess communication (IPC) resources
(隔离进程间通信 -->用于隔离进程间通讯所需的资源( System V IPC, POSIX message queues),PID命名空间和IPC命名空间可以组合起来用,同一个IPC名字空间内的进程可以彼此看见,允许进行交互,不同空间进程无法交互)
- PID - isolate the PID number space
(隔离进程的ID --> linux通过命名空间管理进程号,同一个进程,在不同的命名空间进程号不同,进程命名空间是一个父子结构,子空间对于父空间可见。)
- Network - isolate network interfaces
(隔离网络资源 --> Network Namespace为进程提供了一个完全独立的网络协议栈的视图。包括网络设备接口,IPv4和IPv6协议栈,IP路由表,防火墙规则,sockets等等。一个Network Namespace提供了一份独立的网络环境,就跟一个独立的系统一样。)
- User - isolate UID/GID number spaces (隔离用户和用户组的ID)
这些东西我们都可以在/proc/$pid/ns
这个目录下找到(图我就不放了,自己去看看)。
k8s的Pod实现
共享的基础实现
假设我们要创建两个容器,一个容器作为web应用(nginx),另外一个作为调试容器,正常来说两个容器分别属于不同的Namespace,我是查不到另外的容器的进程的,我们应该如何做才能实现这个需求呢?
- 启动一个nginx容器(a: 允许Namespace被共享,b: 挂载文件系统)
- 再启动一个容器加入上一个容器的NameSpace,并且挂载同一个文件树。
具体操作如下:
- 启动一个web容器
|
|
- 接下来启动 busybox 容器,并加入到 nginx 容器的 NET、IPC、PID NameSpace 中,同时,我们共享 nginx 容器的 Volume ,以便可以访问 nginx 的日志文件
|
|
两个容器都启动后,就可以在 busybox 容器中直接调试 nginx 容器的资源了。
[root@vm ~]# echo “hello pod” > $PWD/html/index.html
[root@vm ~]# docker exec -it busybox ps
PID USER TIME COMMAND
1 root 0:00 nginx: master process nginx -g daemon off;
29 101 0:00 nginx: worker process
30 101 0:00 nginx: worker process
31 root 0:00 /bin/sh -c while true; do sleep 1h; done;
37 root 0:00 sleep 1h
38 root 0:00 ps
[root@vm ~]# docker exec -it busybox curl localhost
hello pod
[root@vm ~]# docker exec -it busybox tail /var/log/nginx/access.log
127.0.0.1 - - [30/Mar/2023:08:07:58 +0000] “GET / HTTP/1.1” 200 10 “-” “curl/7.81.0” “-”
这个我们就实现了一个简单版本的pod,但是有一个问题:由于 Namespace 是由 nginx 容器创建的,如果 nginx 意外崩溃,那么所有 Namespace 都会一同被删除,busybox 容器也会被终止。
parse容器
显然让业务容器充当共享基础容器是不可取的,必须保证每个容器都是对等的关系,而不是父子关系,所以k8s引入了parse容器。
Pause 容器,又叫 Infra 容器,为了解决共享基础容器的安全问题, Kubernetes 会在每个 Pod 里,额外起一个 Infra 容器来共享整个 Pod 的 Namespace 。
Pause 容器会在 Pod 创建时首先启动,并创建 Namespace 、配置网络 IP 地址及路由等相关信息,等 Pause 容器启动完成后,其它容器才接着启动,并与 Pause 容器共享 Namespace,这样,每个容器就都可以访问 Pod 中其他容器的资源了。
Pod结束时,这个容器会最后退出,可以说,Pause 容器的生命周期就相当于是整个 Pod 的生命周期。