k8s那些事之万法之源Pod
本文只介绍道,不写术;即试图介绍Kubernetes的思想,并不去罗列概念和操作方式
此Pod者, 乃Kubernetes万法之源, 抽象模型之根本, 调度之要门, 资源模型之宗, 容器设计思想之祖
为什么需要Pod
我们已经有了容器,为什么需要Pod?
首先我们要明白容器的本质是进程,Kubernetes本质就是操作系统。
为什么这么说,因为在容器本质上是一个单进程模型,init进程在容器内部就是Pid为1的根进程,如果死掉整个容器就要重启,它的子进程并不能拥有进程管理能力,子进程死掉就无法被感知,所以整个模型就不能表示复杂的多进程应用模型。
而在操作系统中,应用进程不是单独存在的,是根据进程组来组织在一起的。比如rsyslogd程序。
rsyslogd 由三个进程组成:一个 imklog 模块,一个 imuxsock 模块,一个 rsyslogd 自己的 main 函数主进程。这三个进程一定要运行在同一台机器 上,否则,它们之间基于 Socket 的通信和文件交换,都会出现问题
Kubernetes就是把“进程组”的概念映射到了容器技术中,包装成Pod。
Google 公司的工程师们发现,他们部署的应用,往往都存 在着类似于“进程和进程组”的关系。更具体地说,就是这些应用之间有着密切的协作关系,使得 它们必须部署在同一台机器上。
有了Pod,我们才能表示复杂的运维关系,比如一个Java Web应用我们要容器化,怎么做呢?
如果只有Docker,只能把Tomcat或者Nginx和应用包打在一个镜像里面,但是如果需要升级Tomcat或者修改配置的时候,也需要重新做镜像,可能需要中断来发布应用,十分不灵活。
而这只是复杂的应用进程关系中的一部分,比如上面的rsyslogd需要多进程模型,只能做成多个容器,使用Mesos或者Swarm这种没有“进程组”概念的调度,都会面临一系列问题,比如资源占用和怎么把三个容器调度到同一个节点上?启动先后怎么处理?这些复杂的问题。
而Kubernetes通过Pod来作为作为最小调度单元,这就是Kubernetes的容器设计哲学和思想。
Pod本质
Pod只是一个逻辑概念,这表明宿主机上并不存在一个所谓的 Pod 的边界或者隔离环境。它其实是一组共享了共享存储、网络、以及怎样运行这些容器的声明的容器。
共享网络和存储,Docker能做吗?当然可以,通过 docker run –net –volumes-from 这样的命令也能实现
docker run --net=B --volumes-from=B --name=A image-A ...
但是如果真这样做的话,容器 B 就必须比容器 A 先启动,这样一个 Pod 里的 多个容器就不是对等关系,而是拓扑关系了。
在Kubernetes中,通过Pause或者叫Infa容器来实现。在一个 Pod 中,Infra 容器永远都是第一个被创建的容器,而其他用户定义的容器,则通过 Join Network Namespace 的方式,与 Infra容器关联在一起。
┌──────────────────────────────────────────────────┐
│ Pod │
│ │
│ /proc/{pid}/ns/net->net:[xxxxxxxx] │
│ ┌────────────────┐ ┌────────────────┐ │
│ │ ContainerA │ │ ContainerB │ │
│ └────────────────┘ └────────────────┘ │
│ │ │ │
│ │ │ │
│ │ │ │
│ │ ┌───────────────────┐ │ │
│ │ │ pause │ │ │
│ └─▶│ Container │◀─┘ │
│ └───────────────────┘ │
│ ▲ │
└─────────────────────────┼────────────────────────┘
│
│
│
- Pod生命周期取决于pause容器生命周期
- pause容器大小200K以内,只做无限循环,资源占用极少,本质是一个资源占位符
- 一个Pod只有一个IP,也就是这个 Pod 的 Network Namespace 对应的 IP 地址
Pod是一种编排思想,可以把Pod理解成虚拟机,容器理解成虚拟机中的进程,但是两者不是一回事。
从具体的实现原理,还是从使用方法、特性、功能等方面,容器与虚拟机几乎没 有任何相似的地方;也不存在一种普遍的方法,能够把虚拟机里的应用无缝迁移到容器中。因为, 容器的性能优势,必然伴随着相应缺陷,即:它不能像虚拟机那样,完全模拟本地物理机环境中的 部署方法。
容器设计模式
上面介绍Pod使用方式的时候,已经包含了容器设计模式。Pod就是“容器设计模式”的核心机制
所有“设计模式”的本质都是:解耦和重用。
实际上,Kubernetes社区已经整理了“容器设计模式”这个论文:Brendan Burns and David Oppenheimer 发布的: 《Design Patterns for Container-based Distributed Systems》,简单来说,就是教你怎么把应用在容器里面用好的
┌──────────────┐
│ Sidecar │
┌────▶│ pattern │
│ └──────────────┘
│
│
┌──────────────────┐ │ ┌──────────────┐
│ Single Node │ │ │ Ambassador │
│ Multi-Container │─────┼────▶│ pattern │
└──────────────────┘ │ └──────────────┘
│
│
│ ┌──────────────┐
│ │ Adapter │
└────▶│ pattern │
└──────────────┘
┌────────────────┐
│Leader election │
┌────▶│ pattern │
│ └────────────────┘
│
│
┌──────────────────┐ │ ┌────────────────┐
│ Multi-node │ │ │ Work queue │
│ application │─────┼────▶│ pattern │
└──────────────────┘ │ └────────────────┘
│
│
│ ┌────────────────┐
│ │ Scatter/gather │
└────▶│ pattern │
└────────────────┘
Single Node
Single-node, multi-container application patterns,单节点模式,由一组共生容器组成,并且共同调度到一个节点上。容器管理系统会将这些容器作为一个原子单位共同调度。
Sidecar pattern
利用同一Pod中容器可以共享存储空间的能力。将应用容器与工具容器解耦。
比如使用Sidecar部署的Filebeat,应用容器写日志到共享目录,Filebeat容器读日志;
或者前面说的java web应用容器,一个tomcat工具容器,应用容器先将war包cp到共享的emptyDir中,tomcat运行共享目录war包
Ambassador pattern
利用同一Pod中容器可以共享网络栈,类似反向代理。例如业务容器通过twemproxy ambassador
访问memcache。应用容器以为自己访问的是本地的memcache,但是其实访问外面分布式部署的memcache集群。为什么要这么做呢?
Pod访问外部的服务,外部服务往往是一个集群,这个时候如何通过一个统一的、简单的方式,用一个 IP 地址,就把这些集群都访问到?解耦方式就是通过代理容器。在业务容器连接代理容器,代理容器中处理这部分逻辑,业务容器和代理容器通过localhost没有损耗,而且代理容器还能复用。
Adapter pattern
和设计模式中的适配器模式类似,无需修改应用容器,仅仅通过适配器容器就可以对外界提供统一的接口。业务暴露出来的 API,比如说有个 API 的一个格式是 A,但是现在有一个外部系统要去访问我的业务容器,它只知道的一种格式是 API B,这样API不用修改就能适配很多外部系统。
典型场景:监控。监控容器对外提供统一的监控接口,监控容器内部对应用容器的各个指标做收集,聚合等等操作
multi-node application patterns
分布式场景下会有很多场景涉及到多个节点协同问题,这些问题可以使用容器的方式解决。因此衍生出如下几类模式:
这几种模式的想法都是把复杂的三方系统从业务容器中剥离出来,通过简单接口调用来获取结果,也可以达到复用的目的。
Leader election pattern
利用容器来解决有状态场景下的选主问题。比如多个节点对应不同的pod,每个pod下有一个选主容器,这样选主容器进行选主行为,业务容器只需要关注业务开发,从选主容器读取选举结果即可。
这样做的原因在于选主系统实现很多,但是如果自己集成,需要业务自己去理解,而且受语言限制。
Work queue pattern
工作队列模式主要利用容器解决基于队列的计算资源调度问题
由于容器天然具有弹性,可以利用多个节点的容器协同来实现动态计算资源调度
Scatter/gather pattern
分散收集模式主要利用容器解决弹性计算问题。
根容器接受到来自客户端的服务请求,将服务请求fan-out到弹性容器上,并通过容器收集获得结果返回给调用方