本文只介绍道,不写术;即试图介绍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到弹性容器上,并通过容器收集获得结果返回给调用方