设计理念
- Pod 是一个逻辑单位
- Kubernetes 真正处理的其实是一组共享了某些资源的的容器
- 共享network,volume, IPC, UTS等namespace
- 如果说容器是进程,那么pod就是进程组
- 在一个真正的操作系统里,进程并不是独自运行的,而是以进程组的方式组织在一起
- 例如负责日志处理的rsyslogd程序,它的主程序和它要用到的内核日志模块 imklog 等,同属于一个进程组
- 这些进程相互协作,共同完成 rsyslogd 程序的职责
- 进程组中的进程有着密切的关系,必须部署在同一个机器上
- 例如需要基于 Socket 的通信或者涉及文件交换
- 在一个真正的操作系统里,进程并不是独自运行的,而是以进程组的方式组织在一起
- 为了解决 进程组的调度, Mesos 中使用了资源囤积的机制
- 在所有资源都囤积完毕后都满足时,才开始进行调度
- 缺点在于调度效率损失和死锁以及饥饿的可能性
- 由于资源抢占还不支持
-
k8s则是把一个pod当做一个整体来调度
- 因此如果两个容器不必要在同一个机器上,则应该分为两个pod
- 例如一个后端应用和mysql数据库,最好分成两个pod,还方面水平扩容
pause容器
- 通过业务无关的
pause容器来判断pod的生存状态- 最先启动,除了等待终止信号退出外什么都不干
-
k8s.gcr.io/pause镜像是一个用汇编语言编写,大小只有 100~200 KB 左右
- 管理所有namespace并允许业务容器共享它们
- 凡是调度、网络、存储,以及安全相关的属性,基本上是 Pod 级别的
-
CNI接口给pause容器配置相关的网络,然后 Pod 中其他的容器都使用 pause 容器的网络- 所有的网络流量都经过
pause容器转发 - pod内容器共享一个IP,通过 localhost 相互通信
- 所有的网络流量都经过
initcontainer
- 主要负责初始化工作
- 准备一些变化的配置文件
- 做一些前置条件的校验,如网络是否联通
- 当所有的 initContainers运行完成后,Kubernetes才会开始初始化应用Pod
- 多个initContainer会按顺序一次运行一个,每个initContainers必须运行成功,下一个才能够运行
- 普通业务容器时并发启动的
- 通过启动前的检查可以规定普通容器的启动顺序
- InitContainer 执行成功后就退出并删除,而普通容器可能会一直在执行
- initContainers不支持Readiness Probe(就绪探针),因为它们必须在Pod就绪之前运行完成
pod启动全过程
-
kubectl首先会执行一些客户端验证操作,以确保不合法的请求将会快速失败,不会发送给apiserver- 例如创建不支持的资源或使用格式错误的镜像名称
- 通过减少不必要的负载来提高系统性能
-
kubectl查询用户信息以发送给apiserver以供身份认证- 用户凭证保存在
kubeconfig文件中,kubectl通过以下顺序来找到该文件的位置- 如果提供了
--kubeconfig参数, 就使用该参数提供的 kubeconfig 文件 - 使用
$KUBECONFIG环境变量提供的 kubeconfig 文件 - 就使用默认的 kubeconfig 文件
$HOME/.kube/config
- 如果提供了
-
kubectl会把这些信息附加到请求头中
- 用户凭证保存在
-
apiserver不仅会验证用户身份,还会进行鉴权,即用户是否有该操作的权限 -
kubectl向apiserver发送HTTP请求- Kubernetes 支持多个 API 版本,每个版本都在不同的 API 路径下
- 例如
/api/v1或者/apis/extensions/v1beta1 - 不同的 API 版本表明不同的稳定性和支持级别
- 例如
- 一旦请求发送之后获得成功的响应,
kubectl将会根据所需的输出格式打印 success message
- Kubernetes 支持多个 API 版本,每个版本都在不同的 API 路径下
-
apiserver把请求根据YAML文件里的apiVersion,kind等字段通过routes匹配指定Handler- 然后Handler构造对应的API对象,并储存到
etcd中
- 然后Handler构造对应的API对象,并储存到
-
controller manager会一直watchapiserver,如果有更新就会自动执行相应控制器 -
scheduler监听pod和node信息,选择适合的node,并将该信息写入etcd- 过滤+打分的策略
-
kubelet每隔一定时间向apiserver通过NodeName获取自身 Node 上所要运行的 Pod 清单- 会通过与自己的内部缓存进行比较来检测新增加的 Pod,如果有差异,就开始同步 Pod 列表
-
CRI调用dockershim,dockershim把K8S指令转换为具体容器API发送给Docker Daemon -
Docker Daemon首先创建
pause容器, 然后启动initcontainer,最后再启动业务容器- 首先拉取容器的镜像
- 再把容器信息发送给 Dockerd 守护进程启动容器实例
- 如果 Pod 中配置了钩子(Hook),容器启动之后就会运行这些 Hook
pod设计模式:sidecar
- 在 Pod 里面,可以定义一些专门的容器,来执行主业务容器所需要的一些辅助工作
- 例如日志收集,debug,监控等
- 这种做法一个明显的优势就是在于其实将辅助功能从我的业务容器解耦了,能够独立发布 Sidecar 容器
- 很多与 Pod 网络相关的配置和管理,也都可以交给 sidecar 完成,而完全无须干涉用户容器
- 更重要的是这个能力是可以重用的,即同样的一个监控 Sidecar 或者日志 Sidecar,可以被全公司的人共用的
- 最典型的例子莫过于 Istio 这个微服务治理项目了
- 三种典型的sidecar用法:日志收集、代理容器、适配器
-
优势:
- 基础设施可以独立与业务进行升级
- 无感知老服务改造
- 业务程序可以专注业务
pod状态
- 对于包含多个容器的 Pod,只有它里面所有的容器都进入异常状态后,Pod 才会进入
Failed状态。在此之前,Pod 都是Running状态- Pod 的
READY字段会显示正常容器的个数
- Pod 的
-
pod状态(
pod.status.phase)-
pending: API对象已经被创建并保存在ETCD中,但容器还没全部被创建成功- condition字段表述具体状态,比如
unschedulabed
- condition字段表述具体状态,比如
-
running: pod已经和节点绑定,容器都创建成功且至少有一个在运行- 未必能提供服务,可能在不断重启
- condition处于ready状态才能提供服务
-
succeeded: pod内所有容器东运行完毕- 多见于job中
-
failed: 至少有一个容器以非0返回码退出 -
unknown: pod状态未被kubelet汇报给kube-apiserver,可能是主从节点间的通信问题
-
健康检查
-
Readiness Probe
- 用来判断一个 pod 是否处在就绪状态(启动完成),当这个 pod 不处在就绪状态的时候,接入层会把相应的流量从这个 pod 上面移除
- Readiness 主要应对的是启动之后无法立即对外提供服务的这些应用
- 比如加载缓存数据,连接数据库等
- 多用于扩容和升级时
-
Liveness Probe
-
用来判断一个pod是否存活,如果不存活则直接杀掉pod,再根据重启策略判断是否重启
- 如果关心这个容器退出后的上下文环境,比如容器退出后的日志、文件和目录,请将 restartPolicy 设置为
Never
- 如果关心这个容器退出后的上下文环境,比如容器退出后的日志、文件和目录,请将 restartPolicy 设置为
-
因为一旦容器被自动重新创建,这些内容就有可能丢失掉了(被垃圾回收了)
-
Liveness 适用场景是支持那些可以重新拉起的应用(重启以实现自愈)
-
资源保障和限制
-
可以设定的资源有:CPU,Mem, 储存, GPU等
-
通过
spec.container.reousrces中的requests和limits字段设定- 最小单位为整数
- 在调度的时候,kube-scheduler 只会按照 requests 的值进行计算
- 设置 Cgroups 限制的时候,kubelet 则会按照 limits 的值来进行设置
-
QoS
- 最高优先级:
guaranteed, 中等:burstable, 最低:besteffort -
用户本身无法指定优先级,只能通过request和limit的组合确定
- 当 Pod 里的每一个 Container 都同时设置了 requests 和 limits,并且 requests 和 limits 值相等的时候,这个 Pod 就属于
Guaranteed类别- 当 Pod 仅设置了 limits 没有设置 requests 的时候,Kubernetes 会自动为它设置与 limits 相同的 requests 值
- 而当 Pod 不满足 Guaranteed 的条件,但至少有一个 Container 设置了 requests,那么这个 Pod 就会被划分到
Burstable类别 - 如果一个 Pod 既没有设置 requests,也没有设置 limits,那么它的 QoS 类别就是
BestEffort
- 当 Pod 里的每一个 Container 都同时设置了 requests 和 limits,并且 requests 和 limits 值相等的时候,这个 Pod 就属于
- 最高优先级:
-
独占CPU的核
- 类似于在使用容器的时候通过设置
cpuset把容器绑定到某个 CPU 的核上,而不是像 cpushare 那样共享 CPU 的计算能力 - 此时由于操作系统在 CPU 之间进行上下文切换的次数大大减少,容器里应用的性能会得到大幅提升
-
设置方式
- Pod 必须是
Guaranteed的 QoS 类型 - 将 Pod 的 CPU 资源的 requests 和 limits 设置为同一个相等的整数值即可
- Pod 必须是
- 类似于在使用容器的时候通过设置