写点什么

云原生:详解|K8s 核心对象技术

作者:息之
  • 2021 年 12 月 01 日
  • 本文字数:10666 字

    阅读完需:约 35 分钟

云原生:详解|K8s核心对象技术

云世

续前篇:详解|K8s核心技术栈解析

K8s 与虚拟机部署的区别、高可用部署的命令、对象如何定义、对象分组原则、Pod、service、deployment 等核心对象工作流程、架构、原理解析。


K8s 应用的好处

假设你是一个开发人员,写了一个 web 服务器,想把它发布出去。在虚拟机世界里面,这是很困难的:

首先要去建这些虚拟机,要有一个 provision 的过程:需要去设定什么样的操作系统,要多少 CPU,多少 memory,然后要想办法把你的应用程序,包括它所依赖的中间件,(比如你的应用是 Java 程序,就需要想办法把 Tom cat 装上去,要把你的 war 包放在固定的目录,需要去重启 Tom cat,让你的应用起起来)。


对于一两个节点,这个手工操作 ok,但是当面对成千上万甚至几十万的实例的时候,这个操作几乎就不可能,就失去人为去管理整个平台的能力了,成本和出错的几率会非常大。

那么在容器世界里怎么做呢?

在前面一篇:容器核心技术解析 中讲过:

  1. 首先可以通过 go build 把源代码编译成一个可执行的文件,

  2. 然后通过 docker build,把编译好的文件放到容器镜像里面,

  3. 接下来在定义容器镜像启动的时候,定义它执行哪条命令,把所有编译的任务都放到了这个文件里面。


这个文件有几个目标:

1. 把源代码编译成可执行文件,

2. 通过 docker build 把可执行文件塞到一个容器镜像里面去,

3. 通过 docker push 把这个容器镜像推到镜像仓库。默认的镜像仓库是 docker hub。

作为应用开发人员,通过以上操作就把应用打包好,并且上传到镜像仓库了。要去做部署的时候,就去另外一个环境,用镜像把它部署出来。


K8s 的部署跟虚拟机不一样


回顾前面讲的 K8s 的核心对象:Namespace、Pod、Node 等。

这些核心对象及相互之间在 K8s 是如何运作的,有哪些通用属性呢?

以及这些核心对象间世怎么联动的?


其中 Pod 就是用来起镜像的容器实例的。


先定义一个 K8s 对象,deployment,它是用来干嘛的?它跟 Pod 之间有什么关系?


举个例子:deployment 其中有个属性叫 replica,如果它=3,就是有 3 个实例。(设定多个实例的原因是要做应用的高可用,做冗余部署)。每个实例分别提供了 IP。思考对于冗余部署的服务来说,要提供一个服务出去,能把这三个 IP 都给出去吗?


当多实例部署的时候,把每个实例的 IP 都给出去,是有问题的。因为把 IP list 给出去,要依赖客户端来决定连哪个。客户端盯着一个,只把 request 发给其中一个 IP,而背后有三个实例,它的负载是不均衡的。当请求很大的时候,会出现所有 request 都连到一个实例上去,这个实例就可能没法响应了,但是其他的实例又都是空闲的。


所以为了保证应用高可用率,要有负载负载均衡。


K8s 提供了一个对象叫做 service,预先定义好了 service,叫做 Nginx service,是为服务配负载均衡规则的。service 有 plus IP 作为这个服务的虚 IP,VIP(virtual IP)。访问虚 IP 就会把请求转到后面的三个 Pod。每次访问的时候,它连的 backend 就已经做了负载均衡。这种情况下,服务就有了高可用的能力。


如果把其中一个 Pod 删掉,再通过访问 VIP,用户的访问是不受影响的,它把请求指向了其他两个 Pod 中的一个,保证了出现故障时的应用高可用。


在 K8s 中,应用高可用部署可以简化到敲三个命令完成


  1. 第一条命令:create -f +文件,创建了一个对象 deployment

    通过建 deployment,达到的目标是把应用部署到 K8s 平台上。

  2. 第二条命令:scale,把建好的对象里面其中一个副本数的属性进行扩展到 n,获得的结果是同时起了 n 个 Pod。

  3. 第三条命令,集群里面有 service,提供虚 IP,访问虚 ip,就把所有的请求转到 backend 的 Pod 里面去,就完成了应用的高可用部署。

K8s 的对象是如何设计的

K8s 之所以厉害,成为业界云计算的统一标准、事实标准,是因为它是一个声明式系统。

通过声明式系统,也就是它的架构原则,把所有要管控的对象都用统一的方法抽象成了一个个对象。

K8s 的统一属性


第一部分是 Typemeta


里面又有 TypeMeta,用来声明这个对象的类型。


Type meta 里面有三个重要的属性,第一个属性是叫做 group,第二个属性叫 kind,第三个属性叫 version。我们常说的 GKV。


1,K8s 有很多很多的对象,会按照不同的业务目的,把这些对象放到不同的 group 里面去,所以可以把 group 就认为它是 Java 里面的 package。


2,Kind 代表这个对象是什么。映射到 Java 里面的 class。


3,第三个是 version, K8s 里面任何对象都有一个 version,而且很多对象在中间态的时候会有不同的多种版本同时存在。


比如装了某一个 K8s 版本,你用不同的版本都能 get 出来, version 干嘛用的呢?社区在不断的迭代过程中,不断的设计和演化出来。K8s 在每年有 4 个版本,基本上每个季度一次,它是按照既定的周期去发新版本的。(后面一年会调整为三个版本,因为 release 太多,整个业界都要跟着他走,都跟得很累)


我们讲究敏捷,讲究迭代,任何社区的 API 定义,对象的定义,并不是一定义出来就是一个 final 版本了,社区会 follow 一定的原则做版本的演进。从 alpha,到 beta,然后再上生产。

跟做系统类似,开始先做 poc,一个最初的版本,然后随着需求不断增加,把功能部署出去,不停的使用,会觉得有一个设计是不合理的,要做一些调整。所以 K8s 在做演进的时候,非生产版本的变化是不保证的。


处在版本升级,迁移中,如果用的是 alpha 功能或 beta 功能,可能它的属性会发生变化,那么怎么减少迁移成本?K8s 设计了一种向前兼容的能力。向前兼容三个版本,实现就是通过 version。任何对象,会分 external version 和 internal version。


例如:


v1:说明 Pod 本身已经毕业了,它基本上不需要做向前兼容,想的已经很清楚了。但是有些对象还不是这个版本,很有可能在下一个时期它的结构就会发生变化,所以设计了 internal version 和 external version。


version 的作用

External version 和 Internal version 之间的转换是怎样的?

是客户端连 API server 的时候,告诉 API server,它是哪个版本的,API server 内部会维护 internal version,在 API server 这一端,它会有 conversion 的方法,把 external version 转成 internal version,然后会把 internal version 再转成 external version 的模式。这个能力支持到当不同的客户端以不同的版本去连 api server 的时候,不同的版本的数据都可以按照既定的 conversion 的方法转成 internal version。


Internal version 也知道,这个版本要通过 alpha 1 写进来的数据,你要通过 alpha 2 去读,它知道怎么样相互 convert,所以这样就实现了多个版本的转换和兼容。这就是 version 的作用。


GKV 就代表了对象是哪个组的,哪种类型的,什么版本。其实就是定义对象是什么,不是什么。


K8s 核心对象第二个大的部分,所有对象都有的通用属性,叫做 metadata。



比如:deployment 对象,metal data 里面有 namespace。


Namespace 把 DB 分成一个个的 namespace,可以按照用户去划分的,或者按照项目去划分。可以把不同应用的对象或者不同用户的对象放在这些 namespace,然后通过权限控制来控制能不能看,能不能改,这样可以做安全性的隔离,相当于对 DB 做隔离,建了一些目录,这些目录里面放了一些文件,这些文件就是这些 spec,比如 deployment 就是 spike。Name 和 namespace 约定了对象放在哪个 namespace,它的名字是什么。

就唯一确定了我是谁,上面的 GKV 确定了我是啥,Metadata 确定了我是谁。这几个要素组合起来就变成了这个对象在集群范围的一个唯一标识。


第三个重要的属性是 label 和 annotation


label 是什么?可以为对象加上一些标签,用这个标签去做过滤查询。

标签查询有什么意义?

K8s 在做对象和对象组合的时候,鼓励用松耦合,有另外一个对象叫 service 用来定义负载均衡配置。在 service 里面有一个属性叫 selector,做过滤查询,查询 namespace 里面打了这个标签的 Pod。所以 label 的意义就是跟一些 selector 去合作,去完成一个查询过滤的业务目标。


Annotation 是什么?

annotation 是不支持过滤查询的。


它的意义是什么呢?

把它当成对象定义的扩展。对象一旦定型了以后,对象的本身的 struct 就不会再发生变化了,比如 Pod,想加一个属性是加不上去的,那么 annotation 这个时候就能体现它的作用了。另外一个重要的属性是 Finalizer,把它理解为资源的锁。如果一个对象要配一个外部的设备,假设说你建一个 service 要配一个外部的负载均衡器,如果把这个对象删除,控制器正在出现故障,这个对象消失了,从 etcd 里面被删除了,那么负载均衡上那个配置就丢在那里了,没人管了。这个情况,应用 finalizer 锁,可以往里面塞任何的值,一旦塞了这个值,这个对象就不会被删除,直到你把 Finalizer 解除掉。


接下来还有一个 resource version,是做版本控制的。resource version 就相当于给对象加一个版本号。

K8s 本身是一个微服务的平台,任何对象都可能被多个控制器或者用户同时修改,那么就涉及到了多线程或者多进程去修改同一个对象资源的场景。


我们做数据库或者做多线程开发的时候,这种场景下应该怎么处理加锁?

有两种,一种叫乐观锁,一种叫悲观锁。


  • 悲观锁在做写之前,先把对象 lock 住,如有其他人也要改,需先获取锁,如没获取到,需等锁。

    对于分布式系统,如果这样加锁整个系统的性能就会降低。因为很多线程在等锁,所以 resource version 就是给对象加一个版本号。


  • resource version 是一个乐观锁。它的工作机制是:某个 Pod 的 resource version 假如是 1 版本,这时如果有两个线程来读取对象,那么两个线程都读取到的是 1 版本,两个线程都去更新对象,同时发起写操作到 API server,那么微观上 API server 一定会先处理一个后处理一个,先处理的完成了以后,对象就会变成版本 2,后处理的会去比较:要操作的对象最新的版本是多少,请求过来的时候,resource work 版本是多少。如果当前的最新版本超出请求的版本,说明请求版本是旧的,就会触发写冲突,给客户端报错,409,conflict,后处理的写操作失败。这是客户端有责任去做的操作。


    这是 resource version 的乐观锁作用,MVCC(multiple version concurrency control)它依赖于 Etcd 的 mvcc 保证的。


以上是 K8s 对象定义的解析。这些通用的 type meta 和 metadata 在任何 K8s 对象里面有。

K8s 特定对象及分组

K8s 按照业务目的,将不同的对象放在不同的分组里面,分组的方式可能是这个版本在这个分组,下个版本它在另外一个分组更合理,就会放到别的分组去。


K8s 对象如何分组



  • 首先最核心的对象,是让平台或者让你的业务能跑起来的一个最小级。

  • 在最小级之上要做一些应用管理,应用管理可能要管理多副本,要管理有状态,要管理无状态,就分了一个 application 的组,APPS 组。在这个组,可能跑一些短时的 Job,有 Job 和 CronJob。

  • 在应用管理的上面可能有一些自动化,比如要做扩缩容,horizontal port,autoscaler,做扩缩容,要控制一个应用最多能有多少个实例不工作,可以通过 pdb 去定义。


按不同的业务目的,把所有的 K8s 对象组织起来了。


最核心的对象就是 Pod。是连接应用层和结构层的核心对象。

基础架构这边的核心对象是 node,计算节点。应用的核心对象是 Pod,pod 和 node 之间产生绑定关系,就让二者之间的边界去除了。

Pod 不是孤立运行的。可以把它理解为一堆容器的松耦合的容器集合的对象。Pod 就像 Borg 里面的 task 一样,是 K8s 里面调度的基本单元。当有一个应用,通过源代码构建成了一个容器,容器镜像,想运行在 K8s 上面怎么办?就去定义一个 Pod。


Pod 定义原则

Pod 里面定义镜像仓库镜像的地址,需要的资源,还有更多依赖等。K8s 的 12 factor 其中有一条原则是代码和配置分离,应用的可执行文件或者依赖包都在镜像仓库里面,但配置文件是不在镜像仓库里面的,遵循 12 fector 原则,需要把它的配置文件通过另外的方式灌进去,K8s 提供了一些其他的对象,比如 config map,secret,把配置放在 config map 和 secret 里面。这两个对象定义好了以后,通过 volume 外挂存储的方式,把它 mount 给这个 Pod。


Pod 启动的时候会需要一堆依赖对象,它本身是要有个账户身份的,叫 service count。它的作用是 Pod 要跟 API server 通信, API server 能够认识 Pod 是谁。


Pod 需要一些配置,这些配置可以从 configmap 或 secret 里面去读。

Pod 需要一些外挂存储,K8s 提供了 pv 和 PVC 的这样一些对象,这些东西都可以挂载到 Pod, Pod 可以把它们里面所包含的内容当成本地文件来读。


Pod 应用高可用策略

Pod 是应用事例,当部署应用的时候,Pod 一旦创建,会被调度到某一个节点上面。如果定义了一个孤立的 Pod 被删除,它就消失了,没有办法确保这个应用的高可用。怎么样去确保应用的高可用?要去定义一个更高级的跟 application 相关的高级对象,来确保 Pod 永远存在。


更高级的对象有好多种:

  • 无状态的应用,用 replicaset 和 deployment 去创建

  • 有状态的应用,用 statefulset 创建

  • 在每个节点上跑一个实例的应用,用 Deamonset 创建


K8s 核心对象工作架构

从 deployment 到 Pod 这一条线是如何工作的


service:Pod 起来以后有了应用实例,要发布服务,通过 service 去 expose 出去,暴露服务。


ingress:对于一个 web 应用,整个集群有一个统一的 API 网关,K8s 提供 ingress 对象,定义 API 网关。



以 Pod 为核心,发散出来跟它相关密切相关的重要对象。这个架构里涵盖 K8s 大部分的知识。

容器在 Pod 创建的时候到底如何工作的?

定义了一个容器的 pod,假设是 Nginx 的 Pod,这个 Pod 里面就只有一个 image,是 ngx 的 image。K8s 在起这个 Pod 的时候,它并不是只起一个容器,它是起两个容器,其中一个容器叫 sandbox。比如 CRI 接口,事实上是两部分,一部分是对 sandbox 的操作,一部分是对 user container 的操作。这个容器是 K8s 自带叫做 pause 的 image, 里面就一条命令,这个镜像一旦运行起来,它不做什么任何事情就直接 sleep 掉了。

这意味着这个镜像非常小,不需要 CPU 资源,极度稳定,它不会 crash,不会 oam,也不会有空指针。小而精的不消耗资源的基础镜像,这个基础镜像的作用是去创建一个独立的网络 namespace,把 sandbox 进程跟网络 name space 相关,然后所有的网络配置是配在这个 sandbox container 的 namespace 的。

当所有网络配置完成以后,才会去起主 container。为什么需要这样?

因为主 container 启动的时候就需要网络,主 container 启动之前,网络要先配好的,所以一定是网络预加载。


Configmap 支持动态更改。

POd 启动后,把 configmap Mount 到 pod 里面。会把 configmap 从 API server 下下来,放在本地 docker 的 dir 里面,只要应用进程支持本地文件刷新的时候,它重新去 load,就可以动态更改。

service 的虚 IP 是某个机器的 IP 吗?外部可访问吗?会不会和其中一个 replica 的重复?Pod 的 IP 和 service IP 是不同的 range,


POd IP 是怎么做的呢?是通过 CNI 的 plugin 去配置的。

service IP 的或者叫 class IP,是 API server 在启动的时候会配一个 class IP range。所有的 service IP 是从那个 range 去配的。


外部访问集群内的一个服务有两种方式:

  • 第一种方式、通过负载均衡器访问,负载均衡器跟 K8s 做集成,标准的 cloud provide 提供集成,也有可能如果不是标准的 cloud,要自己去切 service controller;

  • 第二种方式、就是通过 noteport,就是节点端口。起一个 service 的时候,它在会在机器上开一个高端口,访问这个高端口的时候,会把请求转到 backend pod 里面去。


K8s 核心对象的工作机制

K8s 是一个声明式系统,除了 metatype,metadata 这些通用属性,还有 Spec 和 status。Spec 就是用户的声明,就是期望这个对象长得什么样。status 是 Controller 回填的,是对象的一个真实状态。


Controller 在这两个属性上的作用是什么?就是把真正的状态跟期望的状态保持一致。这是 K8s 对象设计的精华,每一个对象的 spec 和 status 长的是不一样的。


对象的 Spec 工作机制以计算节点 NOde 为例,如 spec 是空的,有很多 status,填这个节点的 IP,节点的主机名,节点的可分配资源。如节点一共有多少资源,20G 硬盘,10G 内存,6 个 CPU。用 eligible 表明有多少可用的,这个信息是给 scheduler 消费的,scheduler 会读取这个信息,知道每个节点的资源使用情况,它做调度的时候就可以做正确决策。


以 Pod 为例,它的 spec 部分就是一堆一组容器定义的集合,每个 container 会定义 image 是什么,name 是什么,还有一些资源的使用需求。资源需求不填,这个 Pod 就是随便可以调到任何的节点上,它不受资源管控。


Pod 是一组紧密关联的容器集合,有些应用可能一个容器不能满足业务需求,可定义多个容器,每个容器有自己的镜像和自己的资源需求,在 Pod 这个 spec 里面,容器的部分是个数组,可以把多个容器组合成一个 Pod,它本身是 K8s 的调度基本单位。不同的容器会运行在不同的 PID,所以当一个 Pod 里面有多个容器的时候,它和主机的 IP 信息是共享的,网络是共享的。但是像 PID IPC IMP 这些东西是不共享的,所以 Pod 是一堆容器的松耦合的一个组合对象。


Pod 为什么不鼓励被独立创建呢?因为 Pod 是短生命周期的,假设说建一个 Pod,然后把它删掉,它就消失了。如果作为服务的提供商,万一 Pod 被建出来以后又被人误删了,我的服务就当了,所以应用要确保高可用。


  • Replicaset 对象

    确保高可用怎么操作呢?要去定义另外一个对象。为了不同的业务场景,应该组合不同对象来完成一个目标。通过副本级来实现。所谓的副本级,它本质上就是在 Pod 的 spec 上面包了一层,它把 Pod spec 作为 Pod 的 template,模板,在上面就定义要几个 replica,K8s 会去确保总会有这么多副本能够满足你的需求。


  • Deployment 对象

    做应用开发或者应用部署的时候,经常会涉及到版本的替换。一个应用已经变成高可用的了,升级的时候希望是滚动升级,就是一部分一部分升级,最终的目的是什么?不影响用户的使用。那么通过这种方式,希望做一个滚动升级,就需要约定一些滚动升级的策略,策略通过 Deployment 这个对象来定义。


    对于无状态应用,最常用的对象就是 deployment,它确保了什么?第一,定义好了滚动升级的策略,在做版本升级的时候,可以做对用户没有影响,不会让 service down 掉的滚动升级。

    第二,会建一个副本级,确保应用一定要多少个实例。当某些实例被删掉的时候,会自动补一个新的出来,这是 deployment 的作用。

对象的 Status 工作机制 K8s 对象都有 status,status 是让 controller 去干活,然后 Controller 填上去的,是对象的真实状态。


Status 里面有个 condition,是对象的一些状态的细节。

协同工作原理

Controller manager 是一堆控制器的集合,其中有一个控制器叫做 deployment controller,它 watch 所有 deployment 的变化,一旦一个 deployment 被创建了,或者被更新了,它就要去做配置,去建了一个 replicaset。



控制器是怎么工作的


  1. 创建 deployment,然后 Controller 监听到了创建事件,

  2. 就去读取 deployment,Deployment controller 去解析这个 department,把 deployment 里面的 port template 部分读出来,并计算哈希。

  3. 根据哈希去判断模板创建 POd 的副本级存不存在?如果不存在,就要创建一个副本级叫做 Replicaset,

  4. 这个 Replicaset 副本数是 deployment 里面那个 Replicate 是几,复制过来,它会把 template 也复用过来。

  5. 副本级被创建好了以后,在 Controller Manager 里面同时 replicaset controller,会去 watch API server,看有 Replicaset 被创建了,那所对应的 Pod 是否存在了,如 POd 不存在,Replica Controller 会去创建 Pod。创建原则是在后面加 uid,保证 Pod 名字唯一性。

  6. 此时 scheduller 调度器看到新的 POd 创建出来了,会看有没有被调度过,如没有,就进行调度,并且把 Pod 和 node 完成绑定。

  7. 绑定到 node 节点以后,节点上的 kubelet 会去 watch API server,因为所有的组件都是跟 API server 通信的,组件和组件之间是不通信的,通过长链接监听,大家都在监听 API server。

  8. Kubelet 监听到某个 POd 被调度到节点,看本地 POd 起起来没有,如果没有,会去调用 CRI 接口,CNI 接口,CSI 接口去挂载 volumn,去起容器进程,去加载网络,这样整个容器就起来了,就能够被访问了。

其中,replica 更新的数据会被存到 API server,API server 有 Watch 机制,通过 event 进行通信,API server 里面发生的变化,会推送给 replicaset,那么 Replica Controller 看到这个事件变化了以后,就看这个 Replicaset 对应的 Pold 有几个,是否跟 deployment 申明一致,不一致就更新到一致。


K8s 核心对象的应用

集群的故障转移

场景:一个应用被调到了不同的节点,其中的一个节点出现了硬件故障,网络不通了,这时 kubelet 会把这个节点上的所有 Pod 做驱逐,因为那个节点已经坏了。意味着上面所有应用都会出问题。此时 Node controller 会去驱逐这个坏 node 上所有 Pod,结果相当于把这个 Pod 删除掉了。立刻,K8s 又补了一个 Pod 出来。通过这种方式就完成了一次故障转移。

这其实是由 Replicaset 的 Controller 来实现了用户期望的值和真实的值的一致性。另外,因为节点故障了,在新的 pod 被创建出来了以后,调度器再去调度的时候,会找一个健康的 node 把 pod 调度上去,再由 kubelet 起起来,这样就实现了一个 fail over,实现了故障转移。

从用户的感受来说,一个节点坏了,他什么都没做,这个实例就在另外一个节点上起来了。这些就是 replicaset controller 去控制的。


如上 scal out,fail over,在 K8s 上都是用户无感知的,就被自然实现了。

工作原理就是有一个个的 Controller,Controller 会不停的监控你的期望值和当前的现实值是不是一致,不一致就去调整成一致,智能化自动化了。这是 K8s 最核心的功能。


版本升级

对于任何新建的 deployment,会给一个默认的 rolling update strategy,每次去升级 25%的实例,如果是三个的话,25%就一个一个去做。如果有一个不 available,它就会停在那里。这是升级策略的理解。


版本升级是 deployment controller 去做。它会计算 Pod 模板的哈希值,当哈希字串发生变化了,对于 development controller 来说,意味着用户要做一次升级,controller 就会算一个新的哈希,建一个以新的哈希副本级,逐渐把新版本的副本级的 replicaset 数量往上调,把老版本的 replicaset 的副本数往下调,直到所有的版本,pod 版本都变成新的。如果中间有任何 pod 出现问题没有 ready 的话,就会卡在那里。


这样基于 deployment 就完成了一次版本的滚动升级。

在开发实践中应用,就是构建一个新的镜像,这个新的镜像打了 V1.1 新的 tag,只要把原来在 deployment 的 V1.0 tag 改成新的版本,那么它的哈希就发生了变化, deployment 的 rolling update strategy 就会生效,就会按照既定策略去完成升级。

Tips:最核心的对象就是 deployment,Replicaset 和 port 这条线。

怎么样设计一个合理系统,想一想 deployment 怎么做的,做设计的时候经常会参考这个模式去思考。


Pod 的重要属性 owner reference

 owner reference 是 metadata 里面的一个属性,用来声明对象的父子关系。意味着对象不是独立存在的。

所谓的 owner reference 就相当于在建对象的时候,如果这个对象有一个父对象,比如 Replicaset 建 Pod,会把 owner reference 填上,说这个 pod 的 owner 是我。而 replicaset 是被 deployment controller 建出来的,那么也会被填上一个 owner 是那个 deployment。


在 GC 中的应用原理

在 K8s 的 controller manager 里面有一个控制器叫 garbage collector,GC,垃圾回收,一旦有 owner reference,对象的父子关系就有了,GC 会去监控当前集群里面的所有对象,GC 有两个组件,一个组件叫 graph builder,扫这些对象的 owner reference,有 ownership,graph builder 就会记录当前这个集群里面的这个对象关系图,会知道刚才的那个 deployment 和这个 replicaset 和 Pod 是有从属关系的。


当把主对象,也就是 deployment 删除的时候,GC 就会触发工作,根据 owner reference,会删除掉有关的 Replicaset,再发现其下有 Pod,也会删除掉。


所以 owner reference 是灌给 GC 的,为什么要做 GC 这样的操作?就是为了统一提高用户感受,建了一个 deployment,要清的时候,不管建了多少对象,就统一给你清掉。这就是 GC 的作用。


总结


不同的控制器如何协作来完成一个业务流,然后如何去保证这个无状态应用的高可用,然后如何保证它的扩缩容,如何保证它的故故障转移,如何去做版本升级滚动升级。


尽量去把这个应用构建成无状态的。那么无状态的意义就是,一个实例出现故障的时候,可以把它随便替换掉,不 care 里面的状态,只要坏了就换一个,通过这种方式,我们的真正的这种故障转移才能工作。


如果你每个应用都是有状态的,万一节点出现故障,就要花很高的成本去做计划,所以这里面是我们去构建应用的一个指导原则,就是:能不依赖某一状态就不依赖,尽量让它是一个独立的应用,无状态的应用,这样我们才很好的去做替换或扩缩容等动作。


结合上面的对象原理,看 K8s 控制器内部的工作流程



控制器的工作流程是怎样的


控制器是生产者消费者模型。对于任何 K8s 对象,本身有一个项目是 code generation,帮你把代码框架生成出来,会有 informer,会有 Lister。所谓的 lister,允许去遍历某一个对象,所谓的 informer 是去监听某一个对象。


你需要做的事情是注册这些对象 informer 的 event handle,然后说明如果发生了 edit 事件,应该做什么?发生 delete 事件,应该做什么,发生 update 事件,做什么。


把对象的 key 放在某一个 Queue 里面。会有一堆 worker 为并发度需要存在,worker 都是死循环,是 sick loop。worker 有多少个看实际情况,看需要有多大的并发度。性能要求高的控制器可能要 1000 个 worker。这些 worker 不断的去看 Q 里面有没有对象(对象在 Q 里面是记录了它的 key,这个 K 就是 namespace+name 的组合),当 worker 获取对象的 Key 以后,会去 client cache 里面去把对象的完整信息取出来,针对对象去做配置。


比如 deployment 会去看对应的 replicaset 存不存在,如果不存在就把它建出来,如果存在那么就去看哈希变了没,副本数变了没,如果变了的话,就需去做对应的更新操作。


如果对象配置成功了,线程就结束,如果配置不成功,基于 K8s 的最终一致性,某一次配置失败,要把失败的 Key 加回到 Q 里面,让 Controller 继续去重试。所以这个就完全依赖你的控制逻辑。


从实现层面理解,可以把 K8s 理解为 Linux 的一个扩展,很多的设计的思想或者是代码的结构跟 Linux 非常像。

延伸阅读

关注 云世 微信公众号。

最全云厂商及云方案能力报告【建议收藏】

最新Serverless服务研究报告【收藏参考】

云原生:一文读懂K8s架构

云原生:一文读懂Docker核心技术

云原生:一文读懂K8s架构原则和对象设计》

云原生:一文读懂DevOps工具链


如果你觉得这个系列有价值,也欢迎转发给有需要的朋友。


云世 专注职场人的硬实力|软技能提升。为企业应用上云和云原生前沿技术及落地实践布道,分享微服务、容器、K8s、Service Mesh、面向企业的 ToB 服务能力的体系化建设等方案、最佳实践、免费课程资料;持续发布职场做事方法论、管理提效等软技能。350 篇原创内容

公众号



喜欢就|关注|转发|点赞|订阅专题吧

公号专题

云原生」「微服务」「Service Mesh」「K8s」「Docker

职场养分」「职场软实力」「认知跨越


上一篇云原生:云原生应用的构建之路

下一篇云原生:详解|K8s 技术栈解析 —————— 一文读懂 K8s 工作原理

发布于: 4 小时前阅读数: 5
用户头像

息之

关注

微信公众号【云世】,云厂商产品从业者 2021.08.06 加入

【云世】专注职场人的硬实力和软技能提升。为产品上云,微服务和云原生前沿技术和落地实践布道。

评论

发布
暂无评论
云原生:详解|K8s核心对象技术