[Kubernetes] Pod 基本原理
Pod 原理
Pod 作为最小调度单位
容器——单进程模型:不是指容器只能运行一个进程(线程,轻量级进程),而是说容器没有管理多个进程的能力。因为容器内PID=1的进程就是容器本身,容器内其他进程都是这个容器进程的子进程,在容器内没有类似 init 进程和 systemd 这样执行进程管理功能的进程。
在操作系统中,应用往往不是以单个进程的形式运行,而是以进程组的形式存在(Linux 中的进程实际上是线程,或者说轻量级进程)。进程组之间的进程相互之间可能会基于 IPC(Inter Process Comunication) 进行通信。这就要求这些进程都运行在同一台宿主机上。
由于容器的单进程模型,进程组之间的各个进程各自需要运行在不同的容器中。假设某个应用进程组中有三个进程。
假设每个容器都需要 1 GB 的内存配额。集群中有两个节点,node-1 有 3 GB,node-2 有 2.5 GB。如果将应用的进程一个个单独调度,那么可能出现同一应用进程组中的各个进程被调度到了不同节点上,或者在设置了亲和性的情况下,出现节点上内存资源不足,可是却已经把部分进程调度到了该节点的状况。
在 Kubernetes 中,不是以容器作为基本调度单位,而是以 Pod 作为调度单位,应用所需要的三个容器一起组成一个 Pod,k8s 以 Pod 作为资源分配以及任务调度的单位,就可以解决上述问题。
这种容器之间的紧密联系,可以称为“超亲密关系”。
容器设计模式
除了上述的调度方面的考虑外,Pod 还有一个重要意义在于容器设计模式。
Pod 的本质是一组共享了某些资源的容器。Pod 中的容器,共享同一个Network Namespace,并且可以声明共享同一个 Volume
类似于:
$ docker run --net=B --volumes-from=B --name=A image-A ...
但是以上述方式启动容器,就注定需要容器 B 比容器 A 先启动。
因此在 kubenetes 中,Pod 的实现需要使用一个中间容器或者说 Infra 容器。在 Kubernetes 创建 Pod 时,首先启动的都是这个容器,其他用户定义的容器通过 Join Network Namesapce 的方式使用 Infra 容器的 Network Namespace,这样就实现了 Pod 内容器共享网络设备。
Infra 容器本身是一个非常特殊的镜像,叫做 k8s.gcr.io/pause
,这个进程永远处于“暂停”状态。
后续用户定义的容器通过 Join Network Namespace 实现了:
- 它们可以直接使用 localhost 进行通信
- 看到的网络设备就是 Infra 的网络设备
- 一个 Pod 只有一个 IP 地址,也就是 Pod 的 Network Namespace 对应的 IP 地址
- Pod 的生命周期只和 Infra 容器相关,和用户容器无关
那么实际上,Pod 的网络通信也是通过 Infra 容器完成的。而 Infra 容器的镜像中几乎什么都没有,那么在开发容器网络插件时,就不能使用其他的安装包,只需要关注如何通过 docker 启动接口配置 Network Namespace 即可。
同时,卷的挂载也是一个原理。一个 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"]
例如上述 yaml 文件中,ngnix-container 和 debian-container 两个容器都挂载了宿主机的 /data
文件夹,两个容器实现了数据共享。
所谓容器设计模式就是充分利用 Pod 的原理,合理地设计 Pod 内容器的功能。
比如为了实现 Pod 内的日志收集。那么我们可以将 Pod 里的一个 Volume 挂载到容器内的 /var/log
目录中,同时在 Pod 里运行另一个 sidecar 容器,它也挂载该 Volume,接下来 sidecar 容器只需要将日志文件的内容通过 MongoDB 或者 Elasticsearch 保存起来。