Kubernetes那点事儿——Pod原理剖析

文章介绍了Pod作为Kubernetes的基本调度单元的原因,它源于Linux进程组的概念,用于管理有紧密协作关系的容器。Pod内的容器共享NetworkNamespace和Volume,通过Infra容器实现网络和资源的共享。Pod的设计允许容器间直接通信和文件共享,简化了复杂应用的部署和管理。
摘要由CSDN通过智能技术生成

Kubernetes那点事儿——Pod原理剖析


前言

众所周知,Pod是K8s的原子调度单位,是 K8s 能够描述和编排各种复杂应用的基石。

1、为什么需要Pod

为什么需要Pod,或者K8s为什么要讲Pod作为最小调度单位而不是容器?

Linux进程组

操作系统里,进程是以进程组的方式组织在一起。进程树状图中,每一个进程后面括号里的数字,是它的进程组 ID(Process Group ID, PGID)

比如, rsyslogd 程序负责日志处理,主程序 main,和它要用到的内核日志模块 imklog 等,同属于一个进程组。这些进程相互协作,共同完成 rsyslogd 程序的职责。

这样的进程组更方便管理。比如,Linux 操作系统只需要将信号,比如 SIGKILL 信号,发送给一个进程组,那么该进程组中的所有进程就都会收到这个信号而终止运行。

K8s项目之所以这么做的原因

在 Borg 的开发和实践中,Google 工程师们发现,他们部署的应用,往往都存在着类似于"进程和进程组"的关系。就是这些应用之间有着密切的协作关系,使得它们必须部署在同一台机器上。而如果事先没有"组"的概念,像这样的运维关系就会非常难以处理。于是,将"进程组"的概念映射到了容器技术中,并使其成为了这个云计算"操作系统"里的"一等公民"。

还是以 rsyslogd 为例:已知 rsyslogd 由三个进程组成:一个 imklog 模块,一个 imuxsock 模块,一个 rsyslogd 自己的 main 函数主进程。这三个进程一定要运行在同一台机器上,否则,它们之间基于 Socket 的通信和文件交换,都会出现问题。现在,要把 rsyslogd 这个应用给容器化,由于受限于容器的"单进程模型",这三个模块必须被分别制作成三个不同的容器。

关于容器的"单进程模型",并不是指容器里只能运行"一个"进程,而是指容器没有管理多个进程的能力。因为容器里 PID=1 的进程就是应用本身,其他的进程都是这个进程的子进程。可是,用户编写的应用,并不能够像系统里的 init 进程或者 systemd 那样拥有进程管理的功能。比如,你的应用是一个 Java Web 程序(PID=1),然后你执行 docker exec 在后台启动了一个 Nginx 进程(PID=3)。可是,当这个 Nginx 进程异常退出的时候,你该怎么知道呢?这个进程退出后的垃圾收集,又应该由谁去做呢?

这个问题在K8s项目迎刃而解了,统一按照 Pod 而非容器的资源需求进行计算的,像 imklog、imuxsock 和 main 函数主进程这样的三个容器,正是一个典型的由三个容器组成的 Pod。

超亲密关系

像这样容器间的紧密协作,称为"超亲密关系"。具有"超亲密关系"容器的典型特征包括但不限于:互相之间会发生直接的文件交换、使用 localhost 或者 Socket 文件进行本地通信、会发生非常频繁的远程调用、需要共享某些 Linux Namespace(比如,一个容器要加入另一个容器的 Network Namespace)等等。注意,并不是所有有"关系"的容器都属于同一个 Pod。 比如,PHP 应用容器和 MySQL 虽然会发生访问关系,但并没有必要、也不应该部署在同一台机器上,它们更适合做成两个 Pod。

2、Pod的实现原理

关于 Pod 最重要的一个事实是:它只是一个逻辑概念 。k8s 真正处理的,还是宿主机上 Linux 容器的 Namespace 和 Cgroups,并不存在一个所谓的 Pod 的边界或者隔离环境。

Pod 又是怎么被"创建"出来的?Pod 其实是一组共享了某些资源的容器。 Pod 里的所有容器,共享的是同一个 Network Namespace,并且可以声明共享同一个 Volume。

一个有 A、B 两个容器的 Pod,不就是等同于一个容器(容器 A)共享另外一个容器(容器 B)的网络和 Volume 么? 好像通过 docker run --net --volumes-from 这样的命令就能实现,比如:

docker run --net=B --volumes-from=B --name=A image-A …

但是,如果真这样做,容器 B 就必须比容器 A 先启动,这样一个 Pod 里的多个容器就不是对等关系,而是拓扑关系了。

所以,在 k8s 里,Pod 的实现需要使用一个叫作 Infra 的中间容器,在这个 Pod 中,Infra 容器永远都是第一个被创建的,而其他用户定义的容器,则通过 Join Network Namespace 的方式,与 Infra 容器关联在一起。

在这里插入图片描述

如图所示,这个 Pod 里有两个容器 A 和 B,还有一个 Infra 容器。 k8s 项目里,Infra 容器的资源占用极少,它使用是一个叫作k8s.gcr.io/pause的镜像。这个镜像是用汇编语言编写的、永远处于"暂停"状态的容器,解压后的大小只有 100~200 KB。

而在 Infra 容器"Hold 住"Network Namespace 后,用户容器就可以加入到 Infra 容器的 Network Namespace 当中了。所以,如果你查看这些容器在宿主机上的 Namespace 文件(这个 Namespace 文件的路径,在前面的内容中介绍过),它们指向的值一定是完全一样的。

现在对于 Pod 里的容器 A 和容器 B 来说:

它们可以直接使用 localhost 进行通信;

它们看到的网络设备跟 Infra 容器看到的完全一样;

一个 Pod 只有一个 IP 地址,也就是这个 Pod 的 Network Namespace 对应的 IP 地址;

其他的所有网络资源,也都是一个 Pod 一份,并且被该 Pod 中的所有容器共享;

Pod 的生命周期只跟 Infra 容器一致,而与容器 A 和 B 无关。 而对于同一个 Pod 里面的所有用户容器来说,它们的进出流量,也可以认为都是通过 Infra 容器完成的。这一点很重要,因为将来如果你要为 k8s 开发一个网络插件时,应该重点考虑的是如何配置这个 Pod 的 Network Namespace,而不是每一个用户容器如何使用你的网络配置,这是没有意义的。

就意味着,如果你的网络插件需要在容器里安装某些包或者配置才能完成的话,是不可取的:Infra 容器镜像的 rootfs 里几乎什么都没有,没有你随意发挥的空间。当然,这同时也意味着你的网络插件完全不必关心用户容器的启动与否,而只需要关注如何配置 Pod,也就是 Infra 容器的 Network Namespace 即可。

3、Pod设计应用

有了这个设计,共享 Volume 就简单多了:k8s 项目只要把所有 Volume 的定义都设计在 Pod 层级即可。 这样,一个 Volume 对应的宿主机目录对于 Pod 来说就只有一个,Pod 里的容器只要声明挂载这个 Volume,就一定可以共享这个 Volume 对应的宿主机目录。比如:

apiVersion: v1
kind: Pod
metadata:
  name: two-containers
spec:
  restartPolicy: Never
  volumes:
  - name: shared-data
    hostPath:      
      path: /data
  containers:
  - name: nginx-container
    image: nginx
    volumeMounts:
    - name: shared-data
      mountPath: /usr/share/nginx/html
  - name: debian-container
    image: debian
    volumeMounts:
    - name: shared-data
      mountPath: /pod-data
    command: ["/bin/sh"]
    args: ["-c", "echo Hello from the debian container > /pod-data/index.html"]

debian-container 和 nginx-container 都声明挂载了 shared-data 这个 Volume。而 shared-data 是 hostPath 类型。所以,它对应在宿主机上的目录就是:/data。而这个目录,其实就被同时绑定挂载进了上述两个容器当中。

这就是为什么,nginx-container 可以从它的 /usr/share/nginx/html 目录中,读取到 debian-container 生成的 index.html 文件的原因。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

偷学技术的梁胖胖yo

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值