k8s控制面相关学习
k8s控制面相关学习
informer
informer:https://zhuanlan.zhihu.com/p/391465614 https://www.zhihu.com/question/463207052/answer/2377261278
(注意Informer最后也有个controller,跟这个不一样)
对于Informer而言
- 大步骤1: Reflector 将资源对象的事件添加进 Delta FIFO queue 中
这里先提前介绍一下 Delta FIFO queue。 所谓 Delta 就是变化的意思,什么的变化呢?就是资源对象的变化。
即 资源对象的变化都会被添加到 Delta FIFO queue 中!这样是不是就很好理解了。
- 大步骤2: Informer 将 Delta FIFO queue 中的对象数据 添加到本地 cache 中。
补充一下这个本地 cache 缓存的就是监听资源对象的最新版。就是缓存的当前集群里面的资源信息。
- 大步骤3: 使用 workqueue 处理业务逻辑。
Kubernetes 控制面controller基础概念解析
1. 控制面的基本组件
控制器 (Controller)
从 pkg/controller/controller.go
可以看到,控制面主要由多个控制器组成:
- 主控制器:协调所有子控制器
- EgressGateway 控制器:处理出口网关策略
- IPAMPool 控制器:管理 IP 地址池
- Operator 控制器:处理网格配置
每个控制器都遵循相同的模式:
1 | type controller struct { |
缓存系统 (Cache)
从 pkg/controller/cache/interfaces.go
可以看到缓存系统的作用:
- 资源监听:监听 Kubernetes 资源变化
- 数据同步:保持本地缓存与集群状态同步
- 数据查询:提供高效的资源查询接口
处理器 (Processors)
每个控制器包含多个处理器,如 egressgateway 控制器中的:
- IPPoolProcessor:处理 IP 池
- DeploymentProcessor:处理部署
- StatusProcessor:更新状态
2. 控制面设计原则
声明式 API
从 pkg/controller/apis/egressgateway_policy.go
可以看到声明式设计:
1 | type EgressGatewayPolicyInfo struct { |
用户声明期望状态,控制器负责将当前状态调整为期望状态。
协调循环 (Reconcile Loop)
每个控制器都有 Reconcile
方法:
1 | func (c *controller) Reconcile(ctx context.Context, key string, keyLogger logman.Logman) error { |
领导者选举
从 pkg/controller/controller.go
可以看到高可用设计:
1 | runner, err := runnable.NewLeaseLeaderElection( |
确保只有一个控制器实例处于活跃状态。
3. 事件驱动架构
资源监听
从 WatchResources
方法可以看到:
1 | func (c *controller) WatchResources() []controllerruntime.WatchResourceDefination { |
控制器监听感兴趣的 Kubernetes 资源变化。
事件处理
从 AddEventHandlers
方法可以看到事件处理机制:
1 | func (c *controller) AddEventHandlers(obj runtime.Object, handlers cache.ResourceEventHandlerFuncs) { |
4. 错误处理和重试机制
优雅的错误处理
从 Reconcile
方法可以看到:
1 | if errors.IsNotFound(err) { |
速率限制
从 RateLimiter
方法可以看到:
1 | func (c *controller) RateLimiter() workqueue.RateLimiter { |
5. 学习要点总结
-
控制器模式:Kubernetes 控制面的核心是控制器模式,通过协调循环不断调整当前状态到期望状态。
-
声明式API:用户声明期望状态,系统负责实现和维护这个状态。
-
事件驱动:基于资源变化事件触发协调操作。
-
高可用性:通过领导者选举确保只有一个活跃实例。
-
缓存优化:使用本地缓存减少对 API Server 的压力。
-
模块化设计:控制器、处理器、缓存等组件职责分离。
这个 grid 项目展示了典型的 Kubernetes 控制器实现模式,是学习控制面设计的优秀示例。
CRD controller/informer生成?
基于对代码的详细分析,这个项目基于自定义CRD生成informer、controller等的实现机制如下:
1. 核心实现机制
API定义阶段
- 在
pkg/apis/
目录下定义自定义资源类型(如EgressGatewayPolicy
) - 使用Kubernetes代码生成标记:
+genclient
:生成客户端代码+k8s:deepcopy-gen:interfaces
:生成deepcopy方法+kubebuilder
注解:定义CRD元数据
代码生成流程
-
CRD生成:通过
hack/gencrd.sh
使用controller-gen
生成CRD YAML文件到manifests/
目录1
2
3
4
5
6
7
8
9
10
11
12# gencrd.sh
#!/bin/bash
set -o errexit
go install sigs.k8s.io/controller-tools/cmd/controller-gen@v0.15.0
gobin="${GOBIN:-$(go env GOPATH)/bin}"
rm -rf ./manifests/*
${gobin}/controller-gen crd:generateEmbeddedObjectMeta=true paths=./pkg/apis/... output:crd:dir=./manifests -
客户端代码生成:通过
hack/gencode.sh
调用Kubernetes的代码生成器:-
client-gen
:生成客户端接口和实现 -
lister-gen
:生成Lister用于缓存访问 -
informer-gen
:生成Informer用于监听资源变化1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59# gencode.sh
#!/bin/bash
set -o errexit
set -o nounset
set -o pipefail
# 获取脚本根目录
SCRIPT_ROOT=$(dirname "${BASH_SOURCE[0]}")/..
pushd "${SCRIPT_ROOT}"
# 获取 kubernetes code-generator 的特定版本
echo "Fetching code-generator..."
go get k8s.io/code-generator@v0.30.14
# 定义生成代码的输入根包和输出根包
INPUT_PKG="./pkg/apis"
OUTPUT_PKG="gitlab.infini-ai.com/mizar/apis/pkg/client"
# 检查并导出 GOPATH
GOPATH=$(go env GOPATH)
echo "GOPATH=${GOPATH}"
# 设置 CODEGEN_PKG 连接到正确定义的路径
CODEGEN_PKG="$GOPATH/pkg/mod/k8s.io/code-generator@v0.30.14"
if [ ! -d "$CODEGEN_PKG" ]; then
echo "Unable to find code-generator at $CODEGEN_PKG"
exit 1
fi
source "${CODEGEN_PKG}/kube_codegen.sh"
echo "Using code-generation package at: $CODEGEN_PKG"
# 生成 Deepcopy、Defaulter、Conversion 代码
echo "Generating deepcopy, defaulter, and conversion code..."
kube::codegen::gen_helpers \
--boilerplate "./hack/boilerplate.go.txt" \
"${INPUT_PKG}"
# 生成 Client、Lister、Informer 代码
echo "Generating client, lister, and informer code..."
kube::codegen::gen_client \
--output-dir "${SCRIPT_ROOT}/pkg/client" \
--output-pkg "${OUTPUT_PKG}" \
--boilerplate "${SCRIPT_ROOT}/hack/boilerplate.go.txt" \
--with-watch \
"${INPUT_PKG}"
# 检查生成的代码是否存在于预期的位置
echo "Checking generated code..."
echo "Contents of ${SCRIPT_ROOT}/${INPUT_PKG}/pkg/client/:"
# 整理 go 模块
echo "Tidying up go modules..."
go mod tidy
echo "Code generation complete."
-
生成的代码结构
客户端代码 (pkg/client/
)
- Clientset (
clientset/versioned/clientset.go
):统一的客户端接口 - 类型化客户端 (
typed/networking/v1alpha1/
):针对具体资源的客户端 - 资源操作接口 (
egressgatewaypolicy.go
):完整的CRUD操作
Informer系统 (pkg/client/informers/
)
- SharedInformerFactory (
factory.go
):管理所有informer的工厂 - 资源特定Informer (
egressgatewaypolicy.go
):监听特定资源变化 - ListWatch机制 (
egressgatewaypolicy.go:44-55
):实现资源的列表和监听
Lister缓存 (pkg/client/listers/
)
- Lister接口 (
egressgatewaypolicy.go:14
):提供从缓存读取资源的接口 - 索引器操作 (
egressgatewaypolicy.go:34-51
):基于cache.Indexer的实现
2. 关键特性
自动代码生成
- 基于Go结构体定义和注解自动生成全套客户端代码
- 支持watch功能,实时监听资源变化
- 内置缓存机制,减少API服务器压力
类型安全
- 强类型接口,编译时检查
- 自动生成的深拷贝方法确保数据一致性
完整的CRUD操作
- Create、Get、List、Update、Delete、Patch、Watch等完整操作
- 支持status子资源更新
3. 使用流程
- 定义API:在pkg/apis下定义资源结构体和注解
- 生成代码:运行
make generate
生成客户端、informer、lister代码 - 部署CRD:应用manifests目录下的CRD定义
- 使用客户端:通过生成的Clientset访问自定义资源
这种实现方式充分利用了Kubernetes的代码生成生态系统,确保了类型安全和开发效率,同时保持了与原生Kubernetes资源相同的使用体验。
CRD controller/informer生成具体的实现?
1. CRD生成 (hack/gencrd.sh
)
1 | # 使用Kubebuilder的controller-gen工具 |
- 工具: Kubebuilder的controller-gen
- 功能: 解析API定义中的Kubebuilder注解,生成完整的CRD YAML
- 输出:
manifests/
目录下的CRD文件
2. 客户端代码生成 (hack/gencode.sh
)
1 | # 使用Kubernetes原生代码生成器 |
- 工具: Kubernetes的code-generator (client-gen, informer-gen, lister-gen)
- 功能: 生成客户端、informer、lister等运行时组件
- 输出:
pkg/client/
目录下的完整客户端代码
3. 与Kubebuilder的关系
使用Kubebuilder的部分:
- 注解系统:
+kubebuilder:
注解定义CRD元数据和验证规则 - CRD生成: 使用controller-gen工具生成CRD YAML
- 丰富特性: 支持验证规则、打印列、子资源等高级CRD特性
使用Kubernetes原生工具的部分:
- 客户端生成: 使用client-gen生成类型安全的客户端接口
- Informer系统: 使用informer-gen生成高效的watch机制
- Lister缓存: 使用lister-gen生成缓存访问接口
4. 生成的关键组件
客户端层 (pkg/client/clientset/
)
- Clientset: 统一的客户端入口,支持所有API组版本
- 类型化客户端: 针对每个资源的强类型操作接口
Informer层 (pkg/client/informers/
)
- SharedInformerFactory: 管理所有informer的工厂模式
- 资源特定Informer: 实现ListWatch机制,实时监听资源变化
Lister层 (pkg/client/listers/
)
- 缓存访问: 提供从本地缓存读取资源的接口
- 索引支持: 基于cache.Indexer的高效数据检索
5. 优势总结
这种混合架构结合了:
- Kubebuilder的CRD定义能力 - 丰富的注解系统和验证规则
- Kubernetes原生的客户端生态 - 与k8s完全兼容的客户端模式
- 灵活性 - 可以独立使用客户端而不依赖完整的controller框架
- 性能 - 高效的informer/watch机制和本地缓存
这种实现确保了自定义资源拥有与Kubernetes原生资源相同的开发体验和性能特性。