容器已经能运行了,为什么 Kubernetes 还要用 Pod?

文章来源声明: 原文作者:韩振方; 来源站点:掘金; 原文链接:https://juejin.cn/post/7694888649713221641; 本文基于上述来源整理/加工,觅优补充点评,仅供技术学习交流。版权归原作者所有。
觅优短评

用办公楼、房间和人的类比把 Node、Pod、容器三层关系讲得很清楚,适合刚接触 Kubernetes、分不清容器与 Pod 边界的人,读完能判断哪些容器该同放一个 Pod。

**前言:** 上一篇刚把“多台机器上的服务由谁持续照看”讲清楚,我接着画一张部署图,却在“应用容器放到哪台机器”这一步卡住了。按直觉,我只要把镜像交给某台 **Node**,让它启动容器就行。可翻开 [**Kubernetes** 的 **Pod** 文档](https://link.juejin.cn?target=https%3A%2F%2Fkubernetes.io%2Fdocs%2Fconcepts%2Fworkloads%2Fpods%2F "https://kubernetes.io/docs/concepts/workloads/pods/"),发现集群先管理的是 **Pod**,容器反而装在它里面。于是问题来了:没有这层 **Pod** 时,我原来想怎样安排应用?有了它以后,哪些东西必须放在一起,哪些又不能硬塞进去?

这篇先围绕“一个应用到底放在哪里”把关系捋顺,不讲调度算法,也不把所有工作负载对象一次塞进来。

一. 我以为把容器交给机器就够了

  1. 假设团队要部署一个图片预览服务。一个容器提供网页,另一个小程序定期生成预览文件。最初我画得特别直接:挑一台机器,把两个容器都启动。反正它们在同一台机器上,应该就能互相配合了吧?
  2. 这张草图在单机上说得通,但放进集群以后,马上冒出一个问题:集群如果把两个容器分别安排到不同机器,网页去哪儿读刚生成的文件?就算这次碰巧在一起,下次重建时还在一起吗?我真正想表达的不是“启动两个容器”,而是“这两个东西属于同一个运行单元”。

图 1:只写两个容器,表达不出它们必须待在一起

这张图重点看右侧的问号:机器可以承载容器,却不能仅凭两个容器的名字猜出它们应该结伴运行。

二. 最容易想到的办法,是把机器地址写死

  1. 最粗暴的做法是手工挑一台 Node,把网页容器和生成程序都放过去,再让它们约定好端口和目录。小项目试一下完全可以,但这份“要在一起”的约定只存在于人的操作里。换机器、重建或交给别人维护时,还得把这套步骤重新讲一遍。
  2. 稍微好一点,可以写脚本或文档固定启动顺序。可脚本解决的是“这次怎么启动”,还没有把“它们应被当成一个整体”交给集群。最怕的是其中一个被单独迁走,另一个还在原处等它。问题不是命令少了一行,而是缺少一个边界。
安排方式谁保证两者在一起换机器时要做什么
手工启动操作者记住约定重新选机器、检查连接
启动脚本脚本固化部分步骤仍要处理落点和两者关系
同一个 **Pod**集群把它们作为一个单元安排由更高层工作负载创建新的 **Pod**

图 2:手工同机和共同调度,差别在约定交给谁

两条路径的目标都是“待在一起”,绿色那条把这个要求写进了集群认识的对象里。

三. Pod 把“一起运行”变成明确的边界

  1. 官方把 Pod 称为 Kubernetes 中能够创建和管理的最小计算单元。它可以只装一个容器,也可以装几个紧密协作的容器;同一个 Pod 里的容器会被安排在同一台 Node 上。这里才是那层“盒子”的意义:集群决定把盒子放哪,而不是分别为盒子里的每个容器挑机器。
  2. 回到图片预览服务,只有当网页和生成程序确实需要紧贴着运行,才考虑把它们放进同一个 Pod。它们可以通过 localhost 通信,也可以在显式配置共享卷后读写同一批文件。注意,同一个 Pod 不等于自动共享整个文件系统;共享文件仍要配置卷。反过来,两个可以独立扩缩容的服务就别因为“有关系”而硬放进一个 Pod。
  3. 文档还提醒了一句很容易被忽略的话:扩成多份时,通常是创建多个 Pod,每个 Pod 承载一份应用实例,而不是往一个 Pod 里不停塞同类容器。上一篇讲的“期望三份”,到了这里可以落成“三个 Pod”。我们今天只认识单个 Pod 的边界,副本由谁创建和维护,留到后面再讲。

图 3:一个 Pod 把紧密协作的容器安排在同一台 Node

重点看外层的绿色框:它是一组容器共同的调度边界;共享目录则是框内额外声明的资源。

四. 把 Node 当大楼,Pod 就像一间房

  1. 我后来用“办公楼”理解这三个名字:Node 是整栋楼,Pod 是其中一间房,容器是在房里干活的人。楼里可以有很多房间;一个房间里通常只有一个人,也可以有需要紧密配合的同事。管理员安排的是“这间房去这栋楼”,不会把同一间房里的两个人拆到两栋楼。
  2. 这个类比还能解释网络:同一间房共用一个门牌,Pod 里的容器共享网络命名空间和 IP、端口空间,因此可以走 localhost;两个不同房间即便在同一栋楼,也不能把对方的 localhost 当成自己。共享文件更像房里放一个双方都能打开的柜子——柜子得先装好,对应到技术上就是显式挂载共享卷。
  3. 类比有边界。Pod 不是永久房产,某台 Node 出故障后,原来的 Pod 不会带着原身份搬去另一台机器。官方生命周期说明讲得很清楚:需要时是由更高层控制器创建新的 Pod 来替代它。这也是为什么不能把临时写在 Pod 内的文件当作天然持久的数据。

图 4:Node、Pod 和容器分别对应楼、房间与房里的人

这张图重点看三个层级和那只“共享柜子”:位置一起安排,数据共享仍需要单独准备。

五. 一个容器也有 Pod,该怎么观察它?

  1. 看到“Pod 可以装多个容器”,我第一反应是:那是不是每个 Pod 都得凑两个?其实不是。官方文档明确说,一个 Pod 装一个容器是最常见的用法。我们为了理解边界举了双容器例子,日常一个网页服务通常就从单容器 Pod 开始。

  2. 如果你手边已有测试集群,可以先看集群正在运行的 Pod,再看它们落在哪台 Node:

    kubectl get pods -o wide
    
    

    kubectl 官方参考说明,-o wide 会包含 Pod 所在的节点名。接着挑一个名字查看详情:

    kubectl describe pod demo-pod
    
    

    运行第二条命令时,把示例名 demo-pod 换成上一条输出里的真实名称。这些命令是给已有集群的读者练习观察用的。我当前没有连接 Kubernetes 集群,所以不展示伪造的终端输出;不同集群的名称、状态也会不同。

  3. 看结果时别急着背全部字段,先对上三个问题:这个 Pod 在哪台 Node?里面有几个容器?如果它被替换,谁负责再建一个?前两个问题今天已经能读懂,第三个会自然带我们走向 Deployment 这类工作负载对象。

图 5:从 Pod 名称、所在 Node 和容器组成读懂运行位置

这张图把观察顺序压成三步:先找运行单元,再找落点,最后看盒子里装了什么。

六. 总结:下次看到 Pod,先问它装了谁

  1. 下次再画部署图,或者看到报错里出现 Pod,我们可以先把三个层级分开:容器负责运行程序,Pod 把一份应用实例及紧密协作的容器放在一起,Node 提供实际运行的机器。先分清这层关系,后面的配置项才有位置可放。

    看容器:程序本身跑得怎样
    看 Pod:哪些容器作为一个单元运行,能共享什么
    看 Node:这个单元实际落在哪台机器
    
    
  2. 图片预览服务如果只有网页容器,就用单容器 Pod;如果生成程序确实需要与网页同机、通过本地网络或共享卷紧密配合,再考虑多容器 Pod。独立扩缩容的程序应保留各自的运行边界。Pod 本身也会消失和被替换;想长期维持份数,就需要下一层工作负载对象来照看它。

图 6:判断一组容器是否该放进同一个 Pod

最后回到开头那张部署图:不要直接把镜像画到机器上,先问“这一份应用需要谁和它一起运行”,再画 Pod 的边界。