Pod在K8中的定义

Pod是一个或一个以上的 容器(例如Docker容器)组成的,且具有共享存储/网络/UTS/PID的能力,以及运行容器的规范。并且在Kubernetes中,Pod是最小的可被调度的原子单位。
在Kubernetes集群中,Pod是所有业务类型的基础,它是一个或多个容器的组合。这些容器共享存储、网络和命名空间,以及如何运行的规范。在Pod中,所有容器都被同一安排和调度,并运行在共享的上下文中。对于具体应用而言,Pod是它们的逻辑主机,Pod包含业务相关的多个应用容器。Kubernetes不只是支持Docker容器,它也支持其他容器。Pod 的上下文可以理解成多个linux命名空间的联合:

PID 命名空间(同一个Pod中应用可以看到其它进程)
网络 命名空间(同一个Pod的中的应用对相同的IP地址和端口有权限)
IPC 命名空间(同一个Pod中的应用可以通过VPC或者POSIX进行通信)
UTS 命名空间(同一个Pod中的应用共享一个主机名称)

引入Pod的好处

在学习的过程中,一直对为什么需要Pod,这样搞有啥好处产生着疑问,其实在上面对Pod的定义和描述就可以窥见其中的一些根因了。

1. 解决成组调度问题。

Kubernetes 项目之所以要这么做的原因:在 Borg 项目的开发和实践过程中,Google 公司的工程师们发现,他们部署的应用,往往都存在着类似于“进程和进程组”的关系。更具体地说,就是这些应用之间有着密切的协作关系,使得它们必须部署在同一台机器上, 而如果事先没有“组”的概念,像这样的运维关系就会非常难以处理。
像这种关联关系非常紧密的容器就可以规整为同一个Pod。对这种关系特别紧密的容器进行调度的场景就是典型的典型的成组调度(gang scheduling)。
以容器A,B,C为例,做如下假设

  • 假设一:容器A,B,C必须要同时运行在一个node节点上,才能正常提供服务。容器A,B,C所需的内存配额都是1G。
  • 假设二:现有集群有两个节点:node-1 剩余内存资源3G, node-2剩余内存资源2G。

然后开始调度拉起容器我们借助Docker Swarm来运行容器A, 为了让容器B,容器C也可以和容器A一起运行在同一台主机上。

  1. 我们必须在容器BC上设置affinity=A(与容器A有亲密特性)的约束。然后拉起容器,此时三个容器都会进入Swarm的待调度队列,
  2. 然后A容器和B容器先后出队并被调度到node-2上。
  3. 此时当容器C出队的时候,由于node-2上已经没有内存资源了,不能运行容器C,但是根据affinity=A的约束,容器C又只能运行到node-2上。

这就是一个典型的承租调度没有被妥善处理的例子。

Mesos对这种情况的解决方案:
Mesos 中就有一个资源囤积(resource hoarding)的机制,会在所有设置了 Affinity 约束的任务都达到时,才开始对它们统一进行调度。而在 Google Omega 论文中,则提出了使用乐观调度处理冲突的方法,即:先不管这些冲突,而是通过精心设计的回滚机制在出现了冲突之后解决问题。
可是这些方法都谈不上完美。资源囤积带来了不可避免的调度效率损失和死锁的可能性;而乐观调度的复杂程度,则不是常规技术团队所能驾驭的。
到了 Kubernetes 项目里,这样的问题就迎刃而解了Kubernetes中的解决方案:
由于Pod 是 Kubernetes 里的原子调度单位。这就意味着,Kubernetes 项目的调度器,是统一按照 Pod 而非容器的资源需求进行计算的。
将容器A,B,C规整到同一个Pod上,由于Pod是Kubernetes里的原子调度单位,所以Kubernetes在调度的时候根本就不会考虑剩余2G内存的node-2.

2. 使一组容器共享存储、网络和命名空间。

还是以容器A,B,C为例,做如下假设:

  • 假设一:B,C共享A的网络。
  • 假设二:B,C共享A的存储。

一般情况下为了达成这两个目的,我们通过 docker run --net --volumes-from 这样的命令就可以使B,C共享A的网络。但是这样做有一个问题,那就是A,B,C三个容器之间并不再是对等关系,而是层级拓扑关系了,B,C必须晚于A启动。
而Kubernetes通过Pod中的Infra 容器来做到上面的两个要求。
在 Kubernetes 项目里,Pod 的实现需要使用一个中间容器,这个容器叫作 Infra 容器。在这个 Pod 中,Infra 容器永远都是第一个被创建的容器,而其他用户定义的容器,则通过 Join Network Namespace 的方式,与 Infra 容器关联在一起。Infra 容器一定要占用极少的资源,所以它使用的是一个非常特殊的镜像,叫作:k8s.gcr.io/pause。这个镜像是一个用汇编语言编写的、永远处于“暂停”状态的容器,解压后的大小也只有 100~200 KB 左右。
Kubernetes以Pod层级来定义Volume和命名空间。

一个典型的例子就是:WAR包和Web服务器
现在有一个 Java Web 应用的 WAR 包,它需要被放在 Tomcat 的 webapps 目录下运行起来。

现在又两种实现思路:

  • 思路一:把 WAR 包直接放在 Tomcat 镜像的 webapps 目录下,做成一个新的镜像运行起来。可是,这时候,如果你要更新 WAR
    包的内容,或者要升级 Tomcat 镜像,就要重新制作一个新的发布镜像,非常麻烦。
  • 思路二:不管 WAR 包,永远只发布一个 Tomcat 容器。不过,这个容器的 webapps 目录,就必须声明一个 hostPath类型的 Volume,从而把宿主机上的 WAR 包挂载进 Tomcat容器当中运行起来。不过,这样你就必须要解决一个问题,即:如何让每一台宿主机,都预先准备好这个存储有 WAR包的目录。这样来看,你只能独立维护一套分布式存储系统了。

而Kubernetes是如何的呢?
K8S学习笔记之为什么需要Pod?

Kubernetes将War包和Tocat包分别作出镜像,然后组合到一个Pod下面。
这个War包的容器是一个Init Container 类型的容器,而Pod中所有init Container容器都会比 spec.containers 定义的用户容器先启动,Init Container 类型的 WAR 包容器启动后,把应用的 WAR 包拷贝到 /app 目录下,然后退出。而后这个 /app 目录,就挂载了一个名叫 app-volume 的 Volume,之后Tomcat容器,同样声明挂载 app-volume 到自己的 webapps 目录下。
像这样,我们就用一种“组合”方式,解决了 WAR 包与 Tomcat 容器之间耦合关系的问题。
实际上,这个所谓的“组合”操作,正是容器设计模式里最常用的一种模式,它的名字叫:sidecar。
顾名思义,sidecar 指的就是我们可以在一个 Pod 中,启动一个辅助容器,来完成一些独立于主进程(主容器)之外的工作。
这样做的好处在于,不需要再维护一套分布式共享存储文件系统。单个Pod作为一个调度单元可以在node节点上飘来飘去,而不用受限于主机是否有这个挂载目录资源。

3. 使得Kubernetes不依赖于底层某一种具体的容器运行时实现技术

大家最熟悉的容器运行时软件当然是Docker,然而Docker只是Kubernetes支持的容器运行时技术的一种。为了让Kubernetes不和某种特定的容器运行时技术绑死,而是能无需重新编译源代码就能够支持多种容器运行时技术的替换,和我们面向对象设计中引入接口作为抽象层一样,在Kubernetes和容器运行时之间我们引入了一个抽象层,即容器运行时接口。
这就是Kubernetes的CRI技术,关于这个技术,后面再做详细的讨论学习。


附:Linux 支持的7种namespace:

  1. cgroup用于隔离cgroup根目录;
  2. IPC用于隔离系统消息队列;
  3. Network隔离网络;
  4. Mount隔离挂载点;
  5. PID隔离进程;
  6. User隔离用户和用户组;
  7. UTS隔离主机名nis域名。

相关文章:

  • 2022-01-31
  • 2021-08-07
  • 2022-01-14
  • 2021-12-20
  • 2022-12-23
  • 2021-06-16
  • 2021-09-03
  • 2022-01-05
猜你喜欢
  • 2021-10-22
  • 2021-11-05
  • 2021-09-14
  • 2022-12-23
  • 2021-10-20
  • 2021-12-04
  • 2021-10-14
相关资源
相似解决方案