与容器相关的那些事——Kubernetes带你飞

Updated on with 0 views and 0 comments

1 Kubernetes核心设计理念

  • 首先,通过一个编排对象,比如Pod、Job、CronJob等,来描述你试图管理的应用

  • 然后,再为编排对象定义一些服务对象,比如Service、Secret、Horizontal Pod Autoscaler(自动水平扩展器)等,服务对象会负责具体的平台级功能

编排对象和服务对象是Kubernetes项目中的API对象(API Object),即面向API对象编程。

一个API对象在Etcd里的完整资源路径是由 Group(API组)、Version(API版本)和 Resource(API资源类型)三个部分组成的。整个Kubernetes里的所有API对象可以用如下的树形结构表示出来:

46603fcc0c3c44778c664333c1688e91.png

2 Kubernetes控制器模型

控制器模型(Controller Pattern)是Kubernetes进行容器编排的核心原理。

上篇文章有一张Kubernetes架构图,里面有一个组件叫 kube-controller-manager,这个组件就是一系列控制器的集合。每一个控制器,都以独有的方式负责某种编排功能。例如,Deployment和Job就是Kubernetes内置控制器的典型例子。

这些控制器都遵循一个通用编排模式,即控制循环(Control Loop),说白了就是一个死循环,其伪代码如下:

for {
  实际状态 := 获取集群中对象 X 的实际状态(Actual State)
  期望状态 := 获取集群中对象 X 的期望状态(Desired State)
  if 实际状态 == 期望状态{
    什么都不做
  } else {
    执行编排动作,将实际状态调整为期望状态
  }
}

简单来说,一个控制器对象是由 控制器定义(包括期望状态)被控制对象的模板 组成的,如下图所示:

0be9c8f5455d41ce93974e5d217e5cdb.png

3 Kubernetes声明式API

kube-apiserver 在处理命令式请求时,一次只能处理一个写请求,否则会有产生冲突的可能;而对于声明式请求,一次能处理多个写操作,并且具备Merge能力。

# 命令式
kubectl replace -f nginx.yaml
# 声明式
kubectl apply -f nginx.yaml

上面两条命令本质是不一样的,你可以简单地理解为,kubectl replace 的执行过程是使用新的YAML文件中的API对象替换原有的API对象,而 kubectl apply 则是执行了一个对原有API对象的 PATCH 操作。

可能大家看到这里对声明式API的重要性还是无感,那么请看下面这个更有说明力的栗子:

e91f23dcc43747718f8920464eaef22b.png

这个栗子来源于微服务治理框架Istio,而Envoy项目则是一个高性能C++网络代理。简单来说,Istio项目会为每一个被治理的Pod动态添加一个Envoy容器(以 sidecar 方式运行),Envoy容器通过配置Pod里的iptables规则,把整个Pod的进出流量接管下来,从而实现微服务治理。

要实现动态无感添加容器的功能,需要两方面支持:

  • Dynamic Admission Control,也称为Initializer,是一种Kubernetes支持的热插拔式的Admission机制,简单来说,可以在API对象提交后做一些初始化工作,比方说加上某些标签
  • 在Initializer更新用户的Pod对象的时候,必须使用类似于 git merge 这样的操作,也就是说,需要用到声明式API最主要的功能 PATCH API

4 Kubernetes调度器

Kubernetes默认调度器的主要职责,就是为一个新创建出来的Pod寻找一个最合适的Node。

9545f3e4bfca4942a14729afbbcaefea.png

如上图所示,调度器的核心就是两个独立的控制循环:

  • 第1个控制循环称为 Informer Path:在这个控制循环中启动了一系列的Informer监听Etcd中Pod、Node、Service等与调度相关的API对象的变化;这里需要重点关注的是 调度队列Scheduler Cache

  • 第2个控制循环称为 Scheduling Path:这个控制循环是控制Pod调度的主循环,循环里不断地从调度队列pop出来一个Pod,然后调用 Predicates 进行过滤,得到一组Node,接着调用 Priorities打分,选出得分最高的Node

关于默认调度器有3个设计思想需要重点关注:

  • Cache化,比方说,Predicates算法需要的Node信息是从 Scheduler Cache 里直接拿的

  • 乐观绑定,也称为 Assume,Kubernetes的默认调度器在Bind阶段,只会更新 Scheduler Cache 里的Pod和Node的信息,正因为如此,当Pod被调度到某个Node上时,该Node上的kubelet还会再做一次 Admit 的校验

  • 无锁化,从图中可以看到,Kubernetes调度器只在操作 调度队列 和 Scheduler Cache 时才需要加锁,而这两部分操作都不在 Scheduling Path 的执行路径上

调度一个Pod分为两个阶段,即 调度周期绑定周期。在Kubernetes调度框架中定义了一些扩展点,如下图所示:

aa2275713791400395791efccd4e2823.png

骚年,看到这里对Kubernetes有点感觉了吗,更多精彩内容下期分享!


标题:与容器相关的那些事——Kubernetes带你飞
作者:yanghao
地址:http://solo.fancydigital.com.cn/articles/2022/04/08/1649415445519.html