惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Hugging Face - Blog
Hugging Face - Blog
量子位
H
Help Net Security
Microsoft Azure Blog
Microsoft Azure Blog
MongoDB | Blog
MongoDB | Blog
小众软件
小众软件
爱范儿
爱范儿
博客园 - 【当耐特】
Vercel News
Vercel News
S
SegmentFault 最新的问题
M
MIT News - Artificial intelligence
F
Fortinet All Blogs
Apple Machine Learning Research
Apple Machine Learning Research
GbyAI
GbyAI
博客园 - 叶小钗
博客园_首页
V
Visual Studio Blog
宝玉的分享
宝玉的分享
B
Blog
MyScale Blog
MyScale Blog
C
Check Point Blog
博客园 - 三生石上(FineUI控件)
L
LangChain Blog
V
V2EX

蛮荆

如何获取更多的免费服务器 Kubernetes 调度器队列 - 设计与实现 Kubernetes 调度器 - 核心流程 Kubernetes Networking Model & CNI Kubernetes CronJob 设计与实现 Kubernetes Job 设计与实现 Kubernetes HPA 设计与实现 Kubernetes Deployment 滚动更新实现原理 Kubernetes GC 设计与实现 Kubernetes Pod 驱逐 - 设计与实现 Kubernetes Daemonset 设计与实现 Kubernetes ReplicaSet 设计与实现 Kubernetes EndPoint 设计与实现 Kubernetes Informer 设计与实现 降本增效之应用优化 (三) 日志存储与检索 Kubernetes Pod 设计与实现 - 创建流程 Kubernetes 探针设计与实现 Unix 编程艺术名句摘录 Kubernetes - CRI 概述 Golang 编译速度为什么这么快? Kubernetes Pod 设计与实现 - Pause 容器 Kubernetes - kube-proxy 代理模式工程优化 Kubernetes 应用最佳实践 - 优雅关闭长连接 Kubernetes Service 类型和会话亲和性 Kubernetes 为什么需要 Ingress Kubernetes 架构 - 控制平面和数据平面 降本增效之应用优化 (二) 大报表 Go 语言如何获取 CPU 利用率 降本增效之应用优化 (一) Redis 业务规则引擎演变过程简述
Kubernetes 控制器管理总结
2024-01-07 · via 蛮荆

2024-01-07 Cloud Native Kubernetes 读代码

控制器管理逻辑

Kubernetes 中,控制器通过监控集群的资源状态,保证资源的当前状态向期望值收敛,单个控制器至少管理一种类型资源。

如果单从逻辑上划分,每个控制器应该是一个独立的进程,但是为了降低管理复杂度和代码的实现难度,Kubernetes 中所有的控制器会在同一个进程中运行。 当然,不同的控制器主体执行逻辑位于单独的 goroutine 中,每个控制器的内部可以被简化为一个无限循环,不断执行自身相关的控制逻辑,使系统资源收敛于期望状态。

控制器之间不会直接通信,它们甚至不知道其他控制器的存在,每个控制器内部的处理逻辑都是独立且类似的: 通过订阅 Informer 资源事件,并根据事件来执行对应的资源操作

通过 API Server 控制

需要注意的是: 所有控制器并不会主动创建并运行资源, 而是通过创建对应的资源 声明并发送到 API Server, 最后由控制面板其余组件配合完成剩下的操作, 例如 Deployment 控制器创建 Pod 时,由调度器分配 Node 并由 Node 上面的 kubelet 创建并运行指定的 Pod。

下面以经常使用的任务控制器 JobController 为例来进行说明。

Job 是集群中用于运行任务的资源,它通过一个或多个 Pod 来执行一个具体的任务,当 JobController 获取到新的运行任务时,保证一组节点上面的 kubelet 可以运行正确的 Pod 来完成任务。 JobController 自身不会去运行 Pod 或者容器,而是通过请求告知 API Server 具体的操作 (例如创建或删除 Pod), 当 API Server 执行完对应的请求操作后会返回消息, 控制面板中的其他组件会根据返回消息做出对应操作。

Controller Manager

顾名思义,Controller Manager 的角色就是作为一个大管家,管理集群中的各类控制器运行。

初始化

在控制器管理器执行 初始化操作 时,可以看到 Kubernetes 中常见的控制器全部会被注册:

func NewControllerInitializers(loopMode ControllerLoopMode) map[string]InitFunc {
	// 注册表结构
	// 控制器名称 => 启动方法
	controllers := map[string]InitFunc{}

	// 注册方法会逐个注册控制器
	// 注册时需要保证每个控制器的名称是唯一的
	//   如果出现重复的名称,直接 panic
	register := func(name string, fn InitFunc) {
		if _, found := controllers[name]; found {
			panic(fmt.Sprintf("controller name %q was registered twice", name))
		}
		controllers[name] = fn
	}

	// 注册各种控制器
	register(names.EndpointsController, startEndpointController)
	register(names.DaemonSetController, startDaemonSetController)
	register(names.JobController, startJobController)
	register(names.DeploymentController, startDeploymentController)
	register(names.HorizontalPodAutoscalerController, startHPAController)
	
	...

	return controllers
}

Informer 存在的必然性

如果从交互流程简单来看,控制器完全可以绕开 Informer, 直接和 API Server 进行通信获取资源的变更情况,然后根据结果执行对应的操作, 但是这种通信模式下为了保证即时获取资源变化情况,控制器的访问方式必须是类似轮询方式,那么所有控制器都采用这个方式的话,API Server 将会面对极大的负载压力, 因此通过使用 Informer 提供的 List & Watch 方式缓存增量事件数据,可以非常有效地降低 API Server 等控制面板相关组件的压力。

此外,通过增加一个 Informer “中间缓存层”,开发者只需要订阅 Informer 事件本身,然后专注于处理控制器的具体业务逻辑,而无需考虑事件数据源访问和并发等问题。

执行框架和流程

API Server 的工作只是负责存储资源对象到 etcd, 并将资源对象的变化以事件形式通知到 Informer,控制器初始化后启动时会从 Informer 订阅相关的事件, 根据事件创建对应的资源声明并发送到 API Server, 最后由控制面板其余组件配合完成剩下的操作。

每个控制器内部的处理逻辑都是独立且类似的: 通过订阅 Informer 资源事件, 注册资源对应的 Add/Update/Delete 等事件回调方法. 资源事件会被放入控制器关联的具体的队列中,然后控制器会开启多个 goroutine 从队列中获取资源事件并消费 (执行相关的操作),最后将期望状态声明回填到 API Server。

控制器执行框架流程图