3.1 介绍pod
![](https://bookbk.img.zhangyue01.com/group61/M00/77/93/CmQUOF8KrwOEGX6fAAAAAB76RfE908976629.png?v=ywDc_OF-&t=CmQUOF8KrwM.)
我们已经了解到,pod是一组并置的容器,代表了Kubernetes中的基本构建模块。在实际应用中我们并不会单独部署容器,更多的是针对一组pod的容器进行部署和操作。然而这并不意味着一个pod总是要包含多个容器——实际上只包含一个单独容器的pod也是非常常见的。值得注意的是,当一个pod包含多个容器时,这些容器总是运行于同一个工作节点上——一个pod绝不会跨越多个工作节点,如图3.1所示。
![](https://bookbk.img.zhangyue01.com/group61/M00/77/AE/CmQUOV8Kru-EJLxAAAAAABzgf6Q016034542.jpg?v=ewPvImmO&t=CmQUOV8KrvA.)
图3.1 一个pod的所有容器都运行在同一个节点上;一个pod绝不跨越两个节点
3.1.1 为何需要pod
关于为何需要pod这种容器?为何不直接使用容器?为何甚至需要同时运行多个容器?难道不能简单地把所有进程都放在一个单独的容器中吗?接下来我们将一一回答上述问题。
为何多个容器比单个容器中包含多个进程要好
想象一个由多个进程组成的应用程序,无论是通过ipc(进程间通信)还是本地存储文件进行通信,都要求它们运行于同一台机器上。在Kubernetes中,我们经常在容器中运行进程,由于每一个容器都非常像一台独立的机器,此时你可能认为在单个容器中运行多个进程是合乎逻辑的,然而在实践中这种做法并不合理。
容器被设计为每个容器只运行一个进程(除非进程本身产生子进程)。如果在单个容器中运行多个不相关的进程,那么保持所有进程运行、管理它们的日志等将会是我们的责任。例如,我们需要包含一种在进程崩溃时能够自动重启的机制。同时这些进程都将记录到相同的标准输出中,而此时我们将很难确定每个进程分别记录了什么。
综上所述,我们需要让每个进程运行于自己的容器中,而这就是Docker和Kubernetes期望使用的方式。
3.1.2 了解pod
由于不能将多个进程聚集在一个单独的容器中,我们需要另一种更高级的结构来将容器绑定在一起,并将它们作为一个单元进行管理,这就是pod背后的根本原理。
在包含容器的pod下,我们可以同时运行一些密切相关的进程,并为它们提供(几乎)相同的环境,此时这些进程就好像全部运行于单个容器中一样,同时又保持着一定的隔离。这样一来,我们便能全面地利用容器所提供的特性,同时对这些进程来说它们就像运行在一起一样,实现两全其美。
同一pod中容器之间的部分隔离
在上一章中,我们已经了解到容器之间彼此是完全隔离的,但此时我们期望的是隔离容器组,而不是单个容器,并让每个容器组内的容器共享一些资源,而不是全部(换句话说,没有完全隔离)。Kubernetes通过配置Docker来让一个pod内的所有容器共享相同的Linux命名空间,而不是每个容器都有自己的一组命名空间。
由于一个pod中的所有容器都在相同的network和UTS命名空间下运行(在这里我们讨论的是Linux命名空间),所以它们都共享相同的主机名和网络接口。同样地,这些容器也都在相同的IPC命名空间下运行,因此能够通过IPC进行通信。在最新的Kubernetes和Docker版本中,它们也能够共享相同的PID命名空间,但是该特征默认是未激活的。
注意 当同一个pod中的容器使用单独的PID命名空间时,在容器中执行ps aux就只会看到容器自己的进程。
但当涉及文件系统时,情况就有所不同。由于大多数容器的文件系统来自容器镜像,因此默认情况下,每个容器的文件系统与其他容器完全隔离。但我们可以使用名为Volume的Kubernetes资源来共享文件目录,关于这一概念将在第6章进行讨论。
容器如何共享相同的IP和端口空间
这里需强调的一点是,由于一个pod中的容器运行于相同的Network命名空间中,因此它们共享相同的IP地址和端口空间。这意味着在同一pod中的容器运行的多个进程需要注意不能绑定到相同的端口号,否则会导致端口冲突,但这只涉及同一pod中的容器。由于每个pod都有独立的端口空间,对于不同pod中的容器来说则永远不会遇到端口冲突。此外,一个pod中的所有容器也都具有相同的loopback网络接口,因此容器可以通过localhost与同一pod中的其他容器进行通信。
介绍平坦pod间网络
Kubernetes集群中的所有pod都在同一个共享网络地址空间中(如图3.2所示),这意味着每个pod都可以通过其他pod的IP地址来实现相互访问。换句话说,这也表示它们之间没有NAT(网络地址转换)网关。当两个pod彼此之间发送网络数据包时,它们都会将对方的实际IP地址看作数据包中的源IP。
![](https://bookbk.img.zhangyue01.com/group61/M00/77/8F/CmQUOF8Kru-EHtrMAAAAANvibss491496527.jpg?v=AL7irDGW&t=CmQUOF8KrvA.)
图3.2 每个pod获取可路由的IP地址,其他pod都可以在该IP地址下看到该pod
因此,pod之间的通信其实是非常简单的。不论是将两个pod安排在单一的还是不同的工作节点上,同时不管实际节点间的网络拓扑结构如何,这些pod内的容器都能够像在无NAT的平坦网络中一样相互通信,就像局域网(LAN)上的计算机一样。此时,每个pod都有自己的IP地址,并且可以通过这个专门的网络实现pod之间互相访问。这个专门的网络通常是由额外的软件基于真实链路实现的。
总结本节涵盖的内容:pod是逻辑主机,其行为与非容器世界中的物理主机或虚拟机非常相似。此外,运行在同一个pod中的进程与运行在同一物理机或虚拟机上的进程相似,只是每个进程都封装在一个容器之中。
3.1.3 通过pod合理管理容器
将pod视为独立的机器,其中每个机器只托管一个特定的应用。过去我们习惯于将各种应用程序塞进同一台主机,但是pod不是这么干的。由于pod比较轻量,我们可以在几乎不导致任何额外开销的前提下拥有尽可能多的pod。与将所有内容填充到一个pod中不同,我们应该将应用程序组织到多个pod中,而每个pod只包含紧密相关的组件或进程。
说到这里,对于一个由前端应用服务器和后端数据库组成的多层应用程序,你认为应该将其配置为单个pod还是两个pod呢?下面我们将对该问题做进一步探讨。
将多层应用分散到多个pod中
虽然我们可以在单个pod中同时运行前端服务器和数据库这两个容器,但这种方式并不值得推荐。前面我们已经讨论过,同一pod的所有容器总是运行在一起,但对于Web服务器和数据库来说,它们真的需要在同一台计算机上运行吗?答案显然是否定的,它们不应该被放到同一个pod中。那假如你非要把它们放在一起,有错吗?某种程度上来说,是的。
如果前端和后端都在同一个容器中,那么两者将始终在同一台计算机上运行。如果你有一个双节点Kubernetes集群,而只有一个单独的pod,那么你将始终只会用一个工作节点,而不会充分利用第二个节点上的计算资源(CPU和内存)。因此更合理的做法是将pod拆分到两个工作节点上,允许Kubernetes将前端安排到一个节点,将后端安排到另一个节点,从而提高基础架构的利用率。
基于扩缩容考虑而分割到多个pod中
另一个不应该将应用程序都放到单一pod中的原因就是扩缩容。pod也是扩缩容的基本单位,对于Kubernetes来说,它不能横向扩缩单个容器,只能扩缩整个pod。这意味着如果你的pod由一个前端和一个后端容器组成,那么当你扩大pod的实例数量时,比如扩大为两个,最终会得到两个前端容器和两个后端容器。
通常来说,前端组件与后端组件具有完全不同的扩缩容需求,所以我们倾向于分别独立地扩缩它们。更不用说,像数据库这样的后端服务器,通常比无状态的前端web服务器更难扩展。因此,如果你需要单独扩缩容器,那么这个容器很明确地应该被部署在单独的pod中。
何时在pod中使用多个容器
将多个容器添加到单个pod的主要原因是应用可能由一个主进程和一个或多个辅助进程组成,如图3.3所示。
![](https://bookbk.img.zhangyue01.com/group61/M00/77/8F/CmQUOF8KrvCEX85BAAAAAHoamsQ804617605.jpg?v=F8NWmxN0&t=CmQUOF8KrvA.)
图3.3 pod应该包含紧密耦合的容器组(通常是一个主容器和支持主容器的其他容器)
例如,pod中的主容器可以是一个仅仅服务于某个目录中的文件的Web服务器,而另一个容器(所谓的sidecar容器)则定期从外部源下载内容并将其存储在Web服务器目录中。在第6章中,我们将看到在这种情况下需要使用Kubernetes Volume,并将其挂载到两个容器中。
sidecar容器的其他例子包括日志轮转器和收集器、数据处理器、通信适配器等。
决定何时在pod中使用多个容器
回顾一下容器应该如何分组到pod中:当决定是将两个容器放入一个pod还是两个单独的pod时,我们需要问自己以下问题:
它们需要一起运行还是可以在不同的主机上运行?
它们代表的是一个整体还是相互独立的组件?
它们必须一起进行扩缩容还是可以分别进行?
基本上,我们总是应该倾向于在单独的pod中运行容器,除非有特定的原因要求它们是同一pod的一部分。图3.4将有助于我们记忆这一点。
![](https://bookbk.img.zhangyue01.com/group61/M00/77/AF/CmQUOV8KrvCEIFohAAAAAOw0-dU220666593.jpg?v=CBCi3ocY&t=CmQUOV8KrvA.)
图3.4 容器不应该包含多个进程,pod也不应该包含多个并不需要运行在同一主机上的容器
尽管pod可以包含多个容器,但为了保持现在的简单性,本章将仅讨论单容器pod有关的问题。稍后我们将在第6章看到如何在一个pod中使用多个容器。