为什么需要 Pod
容器的本质
- 一个视图被隔离、资源受限的进程
- 容器里 PID = 1 的进程就是应用本身
- 管理虚拟机 = 管理基础设施;管理容器 = 管理应用本身
Kubernetes
- 云时代的“操作系统”
- 容器就是这个操作系统的软件安装包
进程组
有的应用由多个进程组成,这些进程共享应用的资源,相互协作以达到应用的功能。
容器管理多进程的应用
容器是单线程模型,只能管理一个进程,容器的生命周期和这个进程关联。这会导致其他进程因为主进程的停止而成为无人管理状态,相关资源无法回收。
Pod
Pod 实际上正是 kubernetes 项目抽象出来的一个可以类比为进程组的概念。多进程的应用对应一个 Pod,每个进程对应一个容器。
应用之间往往有着密切的协作关系,使得它们必须部署在同一台机器上并且共享某些信息。
为什么 Pod 必须是原子调度单位?
为什么要把 Pod 的概念抽象出来,而不是通过调度来解决?
- 举例:两个容器紧密协作
- App:业务容器,产生日志
- LogCollector:上传日志文件到 ElasticSearch
- 内存要求
- App:1g
- LogCollector:0.5g
- 可用节点
- Node_A:1.25g
- Node_B:2g
如果调度前没有考虑整个 Pod 所需配置,就有可能将 App 放置到 Node_A,导致 LogCollector 无法调度。
其他解决方案
Mesos
- 当所有设置了 Affinity 约束的任务都达到时,才开始统一调度,这是一个非常典型的成组调度的解法。
- 调度效率会损失,可能产生死锁。
Google:Omega
- 乐观调度,不管这些冲突的异常情况,先调度,同时设置一个非常精妙的回滚机制,这样经过冲突后,通过回滚来解决问题。
- 实现复杂,悲观锁的设置一定比乐观锁要简单。
再次理解 Pod
亲密关系 - 调度解决
- 两个应用需要运行在同一台宿主机上。
超亲密关系 - Pod 解决
- 会发生直接的文件交换
- 使用localhost或者Socket文件进行本地通信(效率高)
- 发生非常频繁的RPC调用
- 共享某些Linux Namespace(比如一个容器要加入另一个容器的Network Namespace)
Pod 的实现机制
Pod 要解决的问题
如何让一个 Pod 里的多个容器之间最高效的共享某些资源和数据。
共享网络
在每个 Pod 里,额外起一个 Infra container 小容器来共享整个 Pod 的 Network Namespace。
Infra container 是一个非常小的镜像,大概 100~200KB 左右,是一个汇编语言写的、永远处于“暂停”状态的容器。由于有了这样一个 Infra container 之后,其他所有容器都会通过 Join Namespace 的方式加入到 Infra container 的 Network Namespace 中。
整个 Pod 里面,必然是 Infra container 第一个启动。并且整个 Pod 的生命周期是等同于 Infra container 的生命周期的,与容器 A 和 B 是无关的。这也是为什么在 Kubernetes 里面,它是允许去单独更新 Pod 里的某一个镜像的,即:做这个操作,整个 Pod 不会重建,也不会重启,这是非常重要的一个设计。
共享存储
把 volume 变成了 Pod level。所有同属于一个 Pod 的容器,共享所有的 volume。
详解容器设计模式
示例
WAR 包 Tomcat 的容器化
方法一
- 将WAR包和Tomcat打包到一个镜像
- 无论是WAR包和Tomcat更新都需要重新制作镜像
方法二
- 镜像只打包Tomcat,使用 Volume 把WAR包挂载到Tomcat里
- 容器所在的宿主机不固定,取决于 Scheduler,可以使用分布式存储系统,但太复杂了
InitContainer
- 比spec.containers定义的用户容器先启动,并且 InitContainer 严格按照定义的顺序执行,而用户定义的 container 是并发启动
- 可以在InitContainer中,将WAR包写到volume里,供后续启动的Tomcat容器使用
容器设计模式:Sidecar
在 Pod 里面,可以定义一些专门的容器,来执行主业务容器所需要的一些辅助工作。
Sidecar:应用与日志收集
- 前置操作,例如执行初始化脚本
- 日志收集,本身是一个进程,一个小容器,那么就可以把它打包进 Pod 里面去做这个收集工作;
- 监控 Pod 里的应用
- 还有一个非常重要的东西就是 Debug 应用,在应用 Pod 里面再次定义一个额外的小的 Container,它可以去 exec 应用 pod 的 namespace;
Sidecar:代理容器
- 访问外部服务集群,但该集群未提供负载均衡,所以访问地址有多个。
- 该负载均衡功能抽取为代理容器。
- 在 Pod 内部,同一 Network Namespace,可以使用 localhost 直接通信,没有性能损耗
Sidecar:适配器容器
- 将接口转换封装到容器里
- 适配器模式
好处
- 辅助操作和主要业务解耦
- 监控 Sidecar ,日志 Sidecar,代理容器 Sidecar 可以被复用