详解 Kubernetes 中的 Pod
2022-04-05 18:33:51 最后更新: 2022-04-05 18:33:51 访问数量:646
2022-04-05 18:33:51 最后更新: 2022-04-05 18:33:51 访问数量:646
前面的文章中,我们相信介绍了 Kubernetes 的组成和架构,并且搭建出了一个基础的 Kubernetes 集群。
但我们对于 Kubernetes 最基础的 Pod 的了解仍然十分有限,本文我们就来详细介绍和讲解一下 Kubernetes 最核心的抽象 -- Pod。
在操作系统中,程序往往并非是单兵作战的,如果我们执行 pstree 命令,就可以看到进程是以成组的方式运行的,这才是最常见的状态。
想想我们的线上服务,各个服务之间也有着复杂的种种关系,即便是在单机上,也不乏这样需要成组调度的进程,这些进程间错综复杂的“关系”,对于一个进程即一个镜像的 Docker 抽象来说,是很难去处理的,这就需要在此之上进一步的抽象,来解决成组调度的问题。于是 Pod 应运而生。
事实上,Pod 只是 Kubernetes 中的一层逻辑概念,Kubernetes 调度的仍然是基础的容器,只是经过我们的配置,Kubernetes 将一些容器看作一个 Pod,从而能够统一调度,进而让他们处于同一个 Linux Namespace 中。
传统通过 docker 镜像部署的方法是很难处理 Linux Namespace 的共享问题的。
假设有 A、B 两个容器,我们想让他们共享网络和 Volume Namespace,那么就需要县启动其中的一个:
$ docker run B
然后再在另一个启动时加入参数:
$ docker run --net=B --volumes-from=B --name=A image-A
这样一来,docker B 必须先启动,docker A 后启动,这两个本是一组的两个容器却不得不有了启动的先后,让他们的关系从平等关系变成了拓扑关系,如果规模扩大,这样的问题就会变得无比复杂而难以处理。
Kubernetes 解决上述问题靠的是引入 Infra 容器:
Infra 容器是 Pod 中隐式声明的容器,它先于其他容器的启动,它的主要工作就是分配并占用 Pod 容器中需要共享的 Linux Namespace,于是,容器 A 与容器 B 就可以真正做到平等了,他们都共享 Infra 容器的 Linux Namespace 即可,于是:
例如下面的 Pod 配置:
在这个配置中,声明了共享的 Volume Namespace:位于宿主机 /data 路径的 shared-data,在 Pod 启动之初,Infra 容器先行启动,并且分配了该 Volume Namespace,而在这之后启动的两个容器只要加入这个 Volume Namespace 并且挂载到镜像指定目录即可。Infra 容器的存在成功让两个容器得以解耦。
如果在 Pod 中配置和开启了 PID Namespace 的共享,就可以在容器中看到一个名为“/pause”的进程,它就是 Infra 的容器进程。
考虑一个 java 应用应该如何被部署到云服务器上的呢?在传统的 Docker 部署模式下,我们可以看到 java 程序是以 tomcat 进程的方式运行起来的。而打包有 java 代码的 war 包则仅仅是 tomcat webapps 目录下的一部分。
于是,基于 Docker 的部署方案有两种选择:
方案 1 看起来很方便,但一来我们会感觉到显然每次上线前都要进行的镜像打包工作是不必要的,二来,在遇到问题需要回滚前,首先需要将要回滚到的 war 包放到 Tomcat 镜像的 webapps 目录然后重新打镜像,之后再部署,这对于线上问题的处理也太过繁琐。
而方案 2 虽然免去了反复打包的困扰,但我们不得不维护一个分布式存储系统,看起来也毫无必要。
但在 Kubernetes 看来,问题却很容易解决:
在 Pod 的 spec 定义中,initContainers 是先于 containers 中定义的镜像先行启动的镜像列表。同时,不同于 containers 中不保证启动顺序的启动,initContainers 中定义的容器是保证按照列表顺序顺次启动的。
基于上述定义,我们的 war 包只需要每次放到宿主机的固定位置然后被复制到容器的指定路径即可,再也不用反复执行打镜像的操作了,世界是不是都清爽了呢?
这个例子中的 sample 镜像就充当了一个 sidecar 的角色,所谓的 sidecar,就是一种将应用程序的功能划分为单独进程的设计模式。类似的,我们也可以将日志收集、上报等功能划分出来作为一个 sidecar 容器单独启动起来,从而让整个系统更为清晰。
Pod 是 Kubernetes 的最小调度单位,而 Container 是 Pod 的最小组成单位。我们知道,容器其实就是单个启动的进程,而 Pod 则是要统一部署和调度的一组进程,因此,配置 Pod 时,需要考虑什么属性是进程组维度的,什么属性是进程维度的。
以下字段在 Pod 配置中是非常重要的:
供用户将 Pod 与 Node 绑定的字段:
这样一来,containers 中只有打了这个指定的键值对 “disktype: ssd” 的容器才会在节点上运行。
用来在 Pod 的 hosts 文件(/etc/hosts)中添加的内容:
在 pod 启动后,/etc/hosts 文件中便会出现:
想要在 Pod 中开启共享哪种 Linux Namespace 资源,只要在 spec 中配置相应的开关即可,例如:
镜像拉取策略,可以选择:
Pod 允许用户定义容器状态变化时触发的钩子,例如:
它定义了容器启动前与结束前要做的事。
一个 Pod 的生命周期也就是这个 API 对象的 status,有以下五种:
当需要进一步排查引起相应状态的原因时,我们需要关注 Pod 的 Events 以及细分的状态字段 Conditions,它包括: