前言说明
- 容器的本质是什么: 进程,Namespace做隔离,Cgroup做限制,rootfs做文件系统
- k8s 则相当于操作系统
- 那么,Pod是 k8s 项目中最小的API对象,也可以说 Pod 是 k8s 调度的最小单位
- Pod 只是一种逻辑概念
问题?
k8s 调度的最小单位,为什么不能是容器,而需要搞出一个Pod的概念?
为什么需要Pod?
在操作系统中,进程不是"孤苦伶仃"的运行,而是以进程组的方式,"有原则的"组织到一起运行。而 k8s 需要做的工作就是将 "进程组" 的概念映射到容器技术中。
以 rsyslogd 为例子。已知 rsyslogd 有三个进程组成,一个 imklog 模块,一个 imuxsock 模块, 一个 rsyslogd 自己的 main 函数主进程。这三个进程一定要运行在同一台机器上,否则,他们之间基于 Socket 的通信和文件交换,都会出现问题。
如果要将 rsyslogd 服务容器化,受限于容器的"单进程模式",那么就需要制作三个不同的容器,最明显的问题,在调度的时候,这三个容器可能被调度到不同的 node 上。
容器单进程模式:并不是指容器里只能运行"一个"进程而是指容器没有管理多进程的能力。这是因为容器里 PID=1 的进程就是应用本身,其他的进程都是 PID=1 进程的子进程。
而如果存在 Pod,想 imklog、imudock 和 main 函数主进程这样的三个容器,这是一个典型的由三个容器组成的 Pod。像这样容器间的紧密协作,我们可以称为 "超亲密关系"。 这些具有 "超亲密关系" 容器的典型特征包括但不限于:
- 互相之间发生直接的文件交换
- 使用 localhost 或者 Socket 文件进行本地通信
- 会发生非常频繁的远程调用
- 需要共性某些 Linux Namespace
如果仅仅是为了解决上面 "超亲密" 容器的调度问题,也没有必要额外增加一个Pod,通过调度层面也能解决。那就是 Pod 在 k8s 中还有更重要的意义,那就是:容器设计模式
,为了理解这些内容,需要介绍下 Pod的实现原理
首先, 关于一个 Pod 最重要的事实是: 它只是一个逻辑概念。
也就是说,k8s 真正处理,还是宿主机操作系统上 Linux
容器的 Namespace 和 Cgroups,而并不存在一个所谓的 Pod 的边界或者隔离环境。
那么,Pod 又是怎么被 "创建" 出来的呢?
具体来说,Pod里的所有容器,共享是同一个 Network Namespace, 并且可以共享同一个 Volume。
那这么来看的话,一个有A、B两个容器的Pod,不就是等同于一个容器 (容器A) 共享另外一个容器 (容器B) 的网络和 Volume 的玩儿法么?
但是,这里设计到的问题是,如果是这样的话,容器B 就必须必容器 A 先启动起来,这样 Pod 里面的容器就不是对等关系了,而是拓扑关系了。
所以,在 k8s 项目中, Pod 的实现需要使用一个中间容器,这个容器叫做 Infra 容器,在 Pod 中, Infra 容器永远都是第一个被创建的容器,而其他用户定义的容器通过 Join Netword Namespace 的方式,与 Infra 容器关联在一起。
infra 容器占用极少的资源,他使用一个特殊的镜像,叫做 k8s.gcr.io/pause
。
在 Infra 容器 "Hold住" Network Namespace后,用户容器也可以加入到 Infra 的 Network Namespace 中了,这意味着,对于 Pod 里的容器 A 和 容器B 来说:
- 他们可以直接使用 localhost 进行通信
- 他们看到的网络设备跟 infra 容器看到的完全一样
- 一个 Pod 只有一个 IP 地址,也就是这个 Pod 的 Network Namespace 对应的 IP 地址
- 当前,其他的所有网络资源,都是一个 Pod 一份,并且被该 Pod 中的所有容器共享
- Pod 的生命周期只跟 Infra 容器一致,而与容器 A 和 B 无关。
总结
Pod本质:
Pod, 实际上是在扮演传统基础设施里 "虚拟机" 的角色,而容器则是这个虚拟机里运行的用户进程。
Pod 是一个小家庭,它把密不可分的家庭成员(Container)聚在一起,而 Infra 则是这个家庭的家长,长官家中共用资源。
网友评论