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

推荐订阅源

博客园 - 三生石上(FineUI控件)
Martin Fowler
Martin Fowler
月光博客
月光博客
AI
AI
B
Blog
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
C
CXSECURITY Database RSS Feed - CXSecurity.com
WordPress大学
WordPress大学
GbyAI
GbyAI
L
Lohrmann on Cybersecurity
O
OpenAI News
Schneier on Security
Schneier on Security
P
Palo Alto Networks Blog
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
T
Troy Hunt's Blog
V2EX - 技术
V2EX - 技术
W
WeLiveSecurity
L
LINUX DO - 最新话题
人人都是产品经理
人人都是产品经理
S
Security Affairs
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
A
Arctic Wolf
Recorded Future
Recorded Future
Microsoft Security Blog
Microsoft Security Blog
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
G
GRAHAM CLULEY
N
Netflix TechBlog - Medium
TaoSecurity Blog
TaoSecurity Blog
C
Check Point Blog
Scott Helme
Scott Helme
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
Apple Machine Learning Research
Apple Machine Learning Research
PCI Perspectives
PCI Perspectives
www.infosecurity-magazine.com
www.infosecurity-magazine.com
Vercel News
Vercel News
The Hacker News
The Hacker News
Y
Y Combinator Blog
Latest news
Latest news
SecWiki News
SecWiki News
Hugging Face - Blog
Hugging Face - Blog
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
Google Online Security Blog
Google Online Security Blog
Webroot Blog
Webroot Blog
Google DeepMind News
Google DeepMind News
Recent Commits to openclaw:main
Recent Commits to openclaw:main
C
Cisco Blogs
博客园_首页
H
Hackread – Cybersecurity News, Data Breaches, AI and More
宝玉的分享
宝玉的分享
L
LINUX DO - 热门话题

博客园 - 左扬

VictoriaMetrics 1.146.0 源码专题【左扬精讲】—— VictoriaLogs 协同:Metrics 到 Logs 的一体化监控 VictoriaMetrics 1.146.0 源码专题【左扬精讲】—— 源码阅读路线图:如何高效阅读 VM 源码 VictoriaMetrics 1.146.0 源码专题【左扬精讲】—— 开源生态:VM 在 CNCF 生态中的位置 VictoriaMetrics 1.146.0 源码专题【左扬精讲】—— 与其他 TSDB 对比:Prometheus/InfluxDB/Thanos/VM VictoriaMetrics 1.146.0 源码专题【左扬精讲】—— 写入吞吐/查询延迟/内存占用的数学模型 VictoriaMetrics 1.146.0 源码专题【左扬精讲】—— 模块依赖图——从 import 语句看组件关系 VictoriaMetrics 1.146.0 源码专题【左扬精讲】—— Goroutine 池/atomic/零拷贝/sync.Pool VictoriaMetrics 1.146.0 源码专题【左扬精讲】—— 多租户架构——accountID/projectID 与 tenant 隔离 VictoriaMetrics 1.146.0 源码专题【左扬精讲】—— 版本演进:1.146.0 LTS 重大更新解析 VictoriaMetrics 1.146.0 源码专题【左扬精讲】—— 整体数据流:一条监控数据的完整生命周期 VictoriaMetrics 1.146.0 源码专题【左扬精讲】—— 架构演进:从 TSDB 到 MergeSet 的设计取舍 VictoriaMetrics 1.146.0 源码专题【左扬精讲】—— Single-Node vs Cluster 模式本质区别 VictoriaMetrics 1.146.0 源码【左扬精讲】—— 开篇总览 Rust 专题【左扬精讲】—— 从语法到灵魂:Ownership、Borrowing 与多语言对比 kubernetes 源码【左扬精讲】—— kube-scheduler 启动流程源码分析 Rust 专题【左扬精讲】—— 选择控制语句、运算符与格式化输出 Rust 专题【左扬精讲】—— 所有权详解 Rust 专题【左扬精讲】—— 作用域详解 Rust 专题【左扬精讲】—— 变量、常量与标量数据类型 kubernetes 源码 / Operator 专题【左扬精讲】—— Deployment Controller 源码分析:从对象创建到滚动更新 kubernetes 源码 / Operator 专题【左扬精讲】—— Operator 开发中的 Webhook:从准入控制到生产部署 Kubernetes源码 / Operator 专题【左扬精讲】—— 实现 Application Controller:从零构建生产级控制器 Kubernetes 编程 / Operator 专题【左扬精讲】—— 定义 Application 资源 + 添加自定义新 API 完整指南 Kubernetes 源码【左扬精讲】—— kube-scheduler(调度专题 · 八):内部架构与核心组件 Kubernetes 源码【左扬精讲】—— kube-scheduler(调度专题 · 八): —— 从入口到调度的全链路源码剖析(k8s v1.36.1) DeepSeek-R1 多模态 R1 / VLM-GRPO【左扬精讲】—— Qwen2-VL 微调与视觉推理强化学习实战 DeepSeek-R1 工业 RAG + 微调混合系统【左扬精讲】—— R1 系列收官之作:从 Prompt → RAG → 微调 选型决策树 DeepSeek-R1 推理时扩展【左扬精讲】—— o1 / R1 慢思考机制:Self-Consistency + ToT + PRM 详解 DeepSeek-R1 端侧 LLM 工程【左扬精讲】—— llama.cpp 调参与 Apple Silicon / 国产 NPU / Android 端侧落地全攻略 DeepSeek-R1 vLLM + k8s 生产部署【左扬精讲】—— 从单卡 7B 到 100 卡 671B MoE 集群的工业化部署实战 DeepSeek-R1 评估与系统(Evaluation & Systems)【左扬精讲】—— 从 GSM8K/MMLU 到 LLM-as-Judge 的工业级评估方法论 DeepSeek-R1 模型训练与算法【左扬精讲】—— GRPO 进阶算法:DAPO / PRIME / RLVR / PRM 四大 2025 前沿改进 DeepSeek-R1 模型训练与算法【左扬精讲】—— 数据蒸馏:用 DeepSeek-R1-671B 生成 800K 高质量 CoT 样本的完整流水线 DeepSeek-R1 优化与微调实战【左扬精讲】—— 从 R1 强化学习新范式到 GRPO 微调一站式入门 Kubernetes 源码【左扬精讲】—— kube-scheduler(调度专题 · 七):自定义插件开发实战 —— 手写一个 Score 插件并注册到集群 Kubernetes 源码【左扬精讲】—— kube-scheduler(调度专题 · 六):Scheduler Profile 与多调度器 —— 如何配置多个 profile 实现多租户、Coordinated LeaderElection Kubernetes 源码【左扬精讲】—— kube-scheduler(调度专题 · 五):SchedulingQueue 与 QueueingHint —— 三段队列的细节、v1.36 新引入的 QueueingHint 工作机制 Kubernetes 源码【左扬精讲】—— kube-scheduler(调度专题 · 四):抢占(Preemption)算法剖析 —— DefaultPreemption 如何选 victim、PodDisruptionBudget 如何约束 Kubernetes 源码【左扬精讲】—— kube-scheduler(调度专题 · 二):内置插件逐个精读 — NodeResourcesFit / NodeAffinity / TaintToleration / PodTopologySpread / VolumeBinding / InterPodAffinity Kubernetes 源码 / Operator 专题【左扬精讲】——kube-scheduler(调度专题):调度器内置插件 逐个精读 k8s 源码级精讲(二十六):调度器内置插件逐个精读 Kubernetes 源码 / Operator 专题【左扬精讲】——kube-scheduler(调度专题):调度器内置插件精读 — NodeResourcesFit / NodeAffinity / TaintToleration / PodTopologySpread / VolumeBinding / InterPodAffinity Kubernetes 源码 / Operator 专题【左扬精讲】——kube-scheduler(调度专题):Scheduling Framework 扩展点逐个源码拆解 Kubernetes 源码 / Operator 专题【左扬精讲】——kube-scheduler(调度专题):初识调度模型、内部架构与事件驱动机制 Kubernetes 编程 / client-go 专题【左扬精讲】—— 四种客户端:为什么、怎么选、怎么用 Kubernetes 编程 / Operator 专题【左扬精讲】—— controller-runtime、kubebuilder、operator-sdk 三大框架深度对比 Kubernetes 编程 / Operator 专题【左扬精讲】—— 深入理解 ManagedFields 字段冲突协调机制 Kubernetes 编程 / Operator 专题【左扬精讲】—— k8s Finalizers 深度解析:对象的生命周期与删除控制 Kubernetes 编程 / Operator 专题【左扬精讲】—— OwnerReference 字段与级联删除机制 Kubernetes 编程 / Operator 专题【左扬精讲】—— 深入学习 Server-Side Apply:managedFields 替代 last-applied-configuration 的演进方向 Kubernetes 编程 / Operator 专题【左扬精讲】—— k8s Annotations 与元数据体系(Operator 专题) Kubernetes 编程 / Operator 专题【左扬精讲】—— RESTMapper:把 Group / Version / Kind / Resource 四元组翻译成 REST 路径的"查字典"大师 Kubernetes 编程 / Operator 专题【左扬精讲】—— Converter 资源版本转换器 Kubernetes 编程 / Operator 专题【左扬精讲】—— Application 业务扩展:从单 Deployment 到多 Workload 的复合 Operator 演进 Kubernetes 编程 / Operator 专题【左扬精讲】—— OwnerReference / Finalizer / 准入控制:k8s 资源生命周期的三大支柱 Kubernetes 编程 / Operator 专题【左扬精讲】—— controller-runtime 框架内幕:从 Manager 到 Reconcile 的全栈拆解 Kubernetes 编程 / Operator 专题【左扬精讲】—— 生产级 Operator 最佳实践:并发安全、资源清理与高可用设计 Kubernetes 编程 / Operator 专题【左扬精讲】—— application-operator Reconcile 循环源码精讲:从 client-go Informer 到 workqueue 的全链路解剖 Kubernetes 编程 / Operator 专题【左扬精讲】—— 从零搭建一个 application-operator 新项目:脚手架、API 设计与基于原生 DeploymentStatus/ServiceStatus 的状态建模 Kubernetes 编程 / Operator 专题【左扬精讲】—— Client-go 源代码分析:生产级 Controller 实践:并发安全、资源清理与高可用设计 Kubernetes 编程 / Operator 专题【左扬精讲】—— Client-go 源代码分析: Controller 调试与诊断工具:从日志分析到问题定位 Kubernetes 编程 / Operator 专题【左扬精讲】—— Client-go 源代码分析:DynamicClient 操作 CRD:无需代码生成的动态操作 Kubernetes 编程 / Operator 专题【左扬精讲】—— Client-go 源代码分析:控制器与 APIServer 完整交互流程:从 Watch 到缓存同步 Kubernetes 编程 / Operator 专题【左扬精讲】—— Client-go 源代码分析:错误处理与重试机制:WorkQueue 限速器详解 Kubernetes 编程 / Operator 专题【左扬精讲】—— Client-go 源代码分析:Leader 选举机制:高可用控制器的必备技能 Kubernetes 编程 / Operator 专题【左扬精讲】—— Client-go 源代码分析:Controller 开发模式完整实战 Kubernetes 编程 / Operator 专题【左扬精讲】—— Client-go 源代码分析:SharedInformerFactory 与等待缓存同步 Kubernetes 编程 / Operator 专题【左扬精讲】—— Client-go 源代码分析:从认证配置到 Deployment 操作 Kubernetes 编程 / Operator 专题【左扬精讲】—— Client-go 源代码分析:版本对应、架构组件与组件关系 Kubernetes 编程 / Operator 专题【左扬精讲】—— Client-go 源代码分析:Informer 源码深度解析:从底层原理到实战应用 Kubernetes 编程 / Operator 专题【左扬精讲】—— Client-go 源代码分析:Reflector 源码深度解析 Kubernetes 编程 / Operator 专题【左扬精讲】—— Client-go 源代码分析:ListWatcher 源码深度解析 Kubernetes 编程 / Operator 专题【左扬精讲】—— Client-go 源代码分析:Indexer 与 ThreadSafeStore 核心原理与源码深度剖析 Kubernetes 编程 / Operator 专题【左扬精讲】—— Client-go 源代码分析:DeltaFIFO 核心原理与源码深度剖析 Kubernetes 编程 / Operator 专题【左扬精讲】—— Client-go 源代码分析:workqueue 核心原理与实战 Kubernetes 编程 / Operator 专题【左扬精讲】—— runtime.Codec 资源编解码:serializer 与 codec 差异、编解码数据结构、codec 核心调用链路 Kubernetes 编程 / Operator 专题【左扬精讲】—— Scheme 资源注册机制全解 Kubernetes 编程 / Operator 专题【左扬精讲】—— Kubernetes 自定义资源的内部版本与外部版本:从源码看版本定义机制 Kubernetes 编程 / Operator 专题【左扬精讲】—— Kubernetes 1.36.1 核心 API 数据结构全解 Kubernetes 编程 / Operator 专题【左扬精讲】—— Kubernetes 构建过程 【AIOPS】一文读懂LLM【左扬精讲】:从诞生到普及,解锁大语言模型的核心密码 【AIOPS】AI Agent 专题【左扬精讲】核心功能篇:MCP-VictoriaMetrics Hooks 源码精讲:Hooks 可观测性的无侵入式实现 【AIOPS】AI Agent 专题【左扬精讲】核心功能篇:MCP-VictoriaMetrics Golang 配置解析源码精讲 ——SRE 自定义 Agent 核心技巧 【AIOPS】AI Agent 专题【左扬精讲】核心功能篇:MCP-VictoriaMetrics Golang 并发模型解析 ——SRE 应对高并发采集的调优思路 【AIOPS】AI Agent 专题【左扬精讲】基础架构篇:MCP-VictoriaMetrics Golang 源码整体架构拆解 ——SRE 必懂的核心模块与数据流 OpenTelemetry 开发实战【左扬精讲】—— 云原生可观测体系构建与分布式追踪二次开发 Kubernetes 编程 / Operator 专题【左扬精讲】—— Operator 开发实战项目 7 —— 基于流量预测模型的智能弹性扩缩容 Operator 实战(AIOps 模型训练与智能扩容(下篇)—— 预测式弹性扩缩容 Operator 落地实现) Kubernetes 编程 / Operator 专题【左扬精讲】—— Operator 开发实战项目 7 —— 基于流量预测模型的智能弹性扩缩容 Operator 实战(AIOps 模型训练与智能扩容(上篇)—— 时序预测模型构建与离线训练) Kubernetes 编程 / Operator 专题【左扬精讲】—— Operator 开发实战项目 6 —— 基于运维专家知识库的智能故障诊断与排查 Operator 实战 Kubernetes 编程 / Operator 专题【左扬精讲】—— Operator 开发实战项目 5 —— 基于大语言模型(LLM)的实时日志流智能监测 Operator 实现 Kubernetes 编程 / Operator 专题【左扬精讲】—— Operator 开发实战项目 4 —— 基于 Operator 实现大模型私有化部署与管理 Kubernetes 编程 / Operator 专题【左扬精讲】—— Operator 开发实战项目 3(上篇)—— 面向 AI / 算力调度场景:GPU 竞价实例资源池统一调度管理 Operator 开发 Kubernetes编程 / Operator专题【左扬精讲】—— Operator 开发实战项目 2 —— 面向零售 / 电商潮汐流量难题:多云多集群数据中心级全链路弹性伸缩 DataCenter Scaler Operator 从 0 到 1 全链路开发 Kubernetes编程 / Operator专题【左扬精讲】—— 深入理解Kubebuilder注解:为什么Operator开发离不开这些特殊注释 Pod 镜像拉取失败?kubectl edit pods修改镜像地址的底层原理与实操 (该方法仅为临时应急方案,并非长期解决方案) Kubernetes编程/Operator专题精讲—— 理解控制器模式 —— 控制器模式的核心原理与实现逻辑(从原理到实践) 【AIOPS】AI Agent 专题【左扬精讲】模型微调实战:一站式平台 LLaMA-Factory 【AIOPS】AI Agent 专题【左扬精讲】基于 k8s+vLLM+Ray 分布式部署全指南:架构设计、资源调度与性能优化 【AIOPS】AI Agent专题【左扬精讲】非量化版DeepSeek分布式部署全指南:精度保障、显存规划与Ollama/vLLM选型 【AIOPS】AI Agent 专题【左扬精讲】零开发框架实现 ReAct Agent(Go SRE友好)
Kubernetes编程 / Operator专题【左扬精讲】—— Operator 开发实战项目1 —— Applicaion Operator(通用应用生命周期管理 Operator 实战)
左扬 · 2026-02-09 · via 博客园 - 左扬

        Controller Operator 的 大脑,核心逻辑在 Reconcile() 方法 中。Reconcile 的中文意思是 调和其核心作用是:监听 CR 的变化,对比 期望状态(Spec) 实际状态(Status),如果不一致,就执行相应的操作(比如创建 Pod),直到实际状态和期望状态一致。
        结合我们的场景,Controller 的核心逻辑是:当用户提交 AppService CR 后,Reconcile 方法会被触发,然后根据 CR 的 Spec 字段(镜像、副本数、端口),自动创建对应的 Pod,并且维护 Pod 的数量(如果 CR 中修改了 replicas,Controller 会自动扩容/缩容)

Reconcile 方法是一个 循环执行 的逻辑,每次触发后,都会重新检查期望状态和实际状态,直到两者一致。

7.2、修改 Controller 逻辑(实现 Reconcile 方法)

打开 controllers/appservice_controller.go 文件,这是 Kubebuilder 自动生成的 Controller 模板,我们需要修改 Reconcile() 方法,添加 创建 Pod 的核心逻辑。

完整的修改后的代码如下(重点关注 Reconcile 方法 中的逻辑,注释详细说明每一步):

package controllers

import (
	"context"
	corev1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/types"
	ctrl "sigs.k8s.io/controller-runtime"
	"sigs.k8s.io/controller-runtime/pkg/client"
	"sigs.k8s.io/controller-runtime/pkg/log"

	appv1 "github.com/your-name/application-operator/api/v1"
)

// AppServiceReconciler reconciles a AppService object
type AppServiceReconciler struct {
	client.Client
	Scheme *runtime.Scheme
}

//+kubebuilder:rbac:groups=app.example.com,resources=appservices,verbs=get;list;watch;create;update;patch;delete
//+kubebuilder:rbac:groups=app.example.com,resources=appservices/status,verbs=get;update;patch
//+kubebuilder:rbac:groups=app.example.com,resources=appservices/finalizers,verbs=update
//+kubebuilder:rbac:groups=core,resources=pods,verbs=get;list;watch;create;update;patch;delete  // 新增:允许 Controller 操作 Pod
//+kubebuilder:rbac:groups=core,resources=pods/status,verbs=get  // 新增:允许 Controller 查看 Pod 状态

// Reconcile is part of the main kubernetes reconciliation loop which aims to
// move the current state of the cluster closer to the desired state.
// TODO(user): Modify the Reconcile function to compare the state specified by
// the AppService object against the actual cluster state, and then
// perform operations to make the cluster state reflect the state specified by
// the user.
//
// For more details, check Reconcile and its Result here:
// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.16.3/pkg/reconcile
func (r *AppServiceReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
	log := log.FromContext(ctx)

	// 1. 获取当前触发 Reconcile 的 AppService CR 实例
	var appService appv1.AppService
	if err := r.Get(ctx, req.NamespacedName, &appService); err != nil {
		log.Error(err, "unable to fetch AppService")
		// 如果 CR 不存在(比如被删除),直接返回,忽略错误
		return ctrl.Result{}, client.IgnoreNotFound(err)
	}

	// 2. 定义期望创建的 Pod 模板(根据 CR 的 Spec 字段)
	pod := &corev1.Pod{
		ObjectMeta: metav1.ObjectMeta{
			Name:      appService.Name + "-pod",  // Pod 名称 = CR 名称 + "-pod"(自定义,保证唯一)
			Namespace: appService.Namespace,      // Pod 命名空间和 CR 一致
			Labels: map[string]string{
				"app": appService.Name,  // 给 Pod 打标签,方便后续管理
			},
		},
		Spec: corev1.PodSpec{
			Containers: []corev1.Container{
				{
					Name:  "app-container",  // 容器名称(自定义)
					Image: appService.Spec.Image,  // 容器镜像 = CR Spec 中的 Image
					Ports: []corev1.ContainerPort{
						{
							ContainerPort: appService.Spec.Port,  // 容器端口 = CR Spec 中的 Port
						},
					},
				},
			},
		},
	}

	// 3. 关联 Pod 和 CR(让 Kubernetes 知道这个 Pod 属于这个 CR)
	if err := ctrl.SetControllerReference(&appService, pod, r.Scheme); err != nil {
		log.Error(err, "unable to set controller reference on Pod")
		return ctrl.Result{}, err
	}

	// 4. 检查 Pod 是否已经存在
	var existingPod corev1.Pod
	if err := r.Get(ctx, types.NamespacedName{Name: pod.Name, Namespace: pod.Namespace}, &existingPod); err != nil {
		// 4.1 如果 Pod 不存在,创建 Pod
		if errors.IsNotFound(err) {
			log.Info("creating new Pod", "pod", pod.Name)
			if err := r.Create(ctx, pod); err != nil {
				log.Error(err, "unable to create Pod")
				return ctrl.Result{}, err
			}
			// Pod 创建成功,返回 Requeue,重新检查状态
			return ctrl.Result{Requeue: true}, nil
		}
		// 4.2 如果检查 Pod 时出现其他错误,返回错误
		log.Error(err, "unable to fetch existing Pod")
		return ctrl.Result{}, err
	}

	// 5. 如果 Pod 已经存在,更新 CR 的 Status 字段(记录实际状态)
	appService.Status.CurrentReplicas = 1  // 这里简化处理,实际场景中需要统计 Pod 数量
	appService.Status.Status = "Running"   // 标记服务状态为 Running
	if err := r.Status().Update(ctx, &appService); err != nil {
		log.Error(err, "unable to update AppService status")
		return ctrl.Result{}, err
	}

	// 6. 期望状态和实际状态一致,返回不重新触发 Reconcile
	return ctrl.Result{}, nil
}

// SetupWithManager sets up the controller with the Manager.
func (r *AppServiceReconciler) SetupWithManager(mgr ctrl.Manager) error {
	return ctrl.NewControllerManagedBy(mgr).
		For(&appv1.AppService{}).  // 监听 AppService 类型的 CR
		Owns(&corev1.Pod{}).       // 监听和 CR 关联的 Pod 资源
		Complete(r)
}

// 重点说明:
// 1. 这里我们简化了逻辑,只创建 1 个 Pod(实际场景中可根据 replicas 字段创建多个 Pod,或结合 Deployment 管理副本)
// 2. 实际 SRE 场景中,还可以添加:Pod 故障重启、镜像更新、副本数伸缩等逻辑
// 3. Reconcile 方法的返回值:ctrl.Result{Requeue: true} 表示重新触发 Reconcile,ctrl.Result{} 表示不重新触发

7.3、关键细节说明(作为 SRE 的人都重点关注下吧)

1、RBAC 权限:Controller 代码的注释中,我们新增了 //+kubebuilder:rbac:groups=core,resources=pods,verbs=get;list;watch;create;update;patch;delete,这是因为 Controller 需要操作 Pod 资源,必须授予对应的 RBAC 权限,否则会报错(权限不足)。

2、ControllerReference 关联:通过 ctrl.SetControllerReference 方法,将 Pod 和 CR 关联起来,这样当 CR 被删除时,Kubernetes 会自动删除对应的 Pod(避免资源泄漏)这是 SRE 开发 Operator 时必须关注的资源清理细节

3、Reconcile 循环:Reconcile 方法 不是执行一次就结束,而是会根据返回值决定是否重新触发——比如 Pod 创建成功后,我们返回 Requeue: true,让 Controller 重新检查 Pod 状态,确保 Pod 正常运行。

八、第七步:启动 Controller(直接运行 make run)—— 本地调试

Controller 逻辑实现完成后,我们可以先在本地启动 Controller,进行调试(无需部署到 Kubernetes 集群),查看 Controller 是否能正常监听 CR,自动创建 Pod
Kubebuilder 已经帮我们生成了自动化脚本(Makefile),直接执行 make run 即可启动 Controller

8.1、本地启动 Controller

# 本地启动 Controller(依赖本地 kubectl 配置,需确保 kubectl 能连接到 Kubernetes 集群)
[root in ~/application-operator k8s_current_context:dev-ack k8s_server_version:v1.20.11-aliyun.1 2026.02.16-17:16:57]
# make run
"/root/application-operator/bin/controller-gen" rbac:roleName=manager-role crd webhook paths="./..." output:crd:artifacts:config=config/crd/bases
"/root/application-operator/bin/controller-gen" object:headerFile="hack/boilerplate.go.txt" paths="./..."
go fmt ./...
go vet ./...
go run ./cmd/main.go
2026-02-16T17:17:07+08:00	ERROR	setup	unable to start manager	{"error": "error listening on :8081: listen tcp :8081: bind: address already in use"}
main.main
	/root/application-operator/cmd/main.go:177
runtime.main
	/usr/local/go/current/src/runtime/proc.go:285
exit status 1
make: *** [run] Error 1 

执行 make run 时,程序启动失败了,核心原因是 8081 端口已经被其他进程占用,导致控制器管理器无法绑定该端口监听请求。

解决办法:Operator 项目中,端口通常配置在 cmd/main.go 文件中(错误信息指向了该文件的 177 行),或者在 config/default/manager_auth_proxy_patch.yaml 等配置文件中。我是在打开 cmd/main.go,找到类似以下的端口配置代码(8081 -> 8089):

image

再次运行 make run 命令:

[root in ~/application-operator k8s_current_context:dev-ack k8s_server_version:v1.20.11-aliyun.1 2026.02.16-17:25:26]
# make run
"/root/application-operator/bin/controller-gen" rbac:roleName=manager-role crd webhook paths="./..." output:crd:artifacts:config=config/crd/bases
"/root/application-operator/bin/controller-gen" object:headerFile="hack/boilerplate.go.txt" paths="./..."
go fmt ./...
go vet ./...
go run ./cmd/main.go
2026-02-16T17:25:41+08:00	INFO	setup	starting manager
2026-02-16T17:25:41+08:00	INFO	starting server	{"name": "health probe", "addr": "[::]:8089"}
2026-02-16T17:25:41+08:00	INFO	Starting EventSource	{"controller": "appservice", "controllerGroup": "app.example.com", "controllerKind": "AppService", "source": "kind source: *v1.Pod"}
2026-02-16T17:25:41+08:00	INFO	Starting EventSource	{"controller": "appservice", "controllerGroup": "app.example.com", "controllerKind": "AppService", "source": "kind source: *v1.AppService"}
2026-02-16T17:25:41+08:00	INFO	Starting Controller	{"controller": "appservice", "controllerGroup": "app.example.com", "controllerKind": "AppService"}
2026-02-16T17:25:41+08:00	INFO	Starting workers	{"controller": "appservice", "controllerGroup": "app.example.com", "controllerKind": "AppService", "worker count": 1}
2026-02-16T17:25:41+08:00	INFO	creating new Pod	{"controller": "appservice", "controllerGroup": "app.example.com", "controllerKind": "AppService", "AppService": {"name":"demo-appservice","namespace":"monitoring"}, "namespace": "monitoring", "name": "demo-appservice", "reconcileID": "212a495f-53b3-4055-84c5-9ba26a560b93", "pod": "demo-appservice-pod"}

此时,我们打开另一个终端,执行以下命令,查看 Pod 是否被创建: 

# 查看 Pod 状态
# kubectl get pods -n monitoring
NAME                                   READY   STATUS    RESTARTS   AGE
demo-appservice-pod                    1/1     Running   0          5m42s
kube-state-metrics-957fd6c75-b57xd     3/3     Running   0          474d
node-exporter-2rxrz                    2/2     Running   0          474d
node-exporter-7vckf                    2/2     Running   0          474d
node-exporter-8tthn                    2/2     Running   0          474d
node-exporter-qr8z7                    2/2     Running   0          474d
node-exporter-wh5rb                    2/2     Running   0          474d
prometheus-adapter-5949969998-894q6    1/1     Running   0          474d
prometheus-k8s-0                       4/4     Running   1          59d
prometheus-operator-574fd8ccd9-f692x   2/2     Running   0          474d

# 查看 CR 的状态(此时 Status 应该已经更新)
# kubectl get appservices.app.example.com demo-appservice -n monitoring -o yaml
apiVersion: app.example.com/v1
kind: AppService
metadata:
  annotations:
    kubectl.kubernetes.io/last-applied-configuration: |
      {"apiVersion":"app.example.com/v1","kind":"AppService","metadata":{"annotations":{},"name":"demo-appservice","namespace":"monitoring"},"spec":{"image":"nginx:1.25","port":80,"replicas":2}}
  creationTimestamp: "2026-02-16T09:03:50Z"
  generation: 1
  managedFields:
  - apiVersion: app.example.com/v1
    fieldsType: FieldsV1
    fieldsV1:
      f:metadata:
        f:annotations:
          .: {}
          f:kubectl.kubernetes.io/last-applied-configuration: {}
      f:spec:
        .: {}
        f:image: {}
        f:port: {}
        f:replicas: {}
    manager: kubectl
    operation: Update
    time: "2026-02-16T09:03:50Z"
  - apiVersion: app.example.com/v1
    fieldsType: FieldsV1
    fieldsV1:
      f:status:
        .: {}
        f:currentReplicas: {}
        f:status: {}
    manager: main
    operation: Update
    time: "2026-02-16T09:25:41Z"
  name: demo-appservice
  namespace: monitoring
  resourceVersion: "433314260"
  uid: 4fdeb14b-1c48-4fc2-815b-9b32f71c73ba
spec:
  image: nginx:1.25
  port: 80
  replicas: 2
status:
  currentReplicas: 1
  status: Running 

注意:本地启动 Controller 时,依赖本地的 kubectl 配置(~/.kube/config),确保本地能正常连接到 Kubernetes 集群(比如 Kind、Minikube)

九、第八步:部署 Controller —— 部署到 Kubernetes 集群(生产级操作)

本地调试通过后,我们需要将 Controller 部署到 Kubernetes 集群中,让它能长期运行,持续监听 CR 的变化。部署 Controller 的核心是将 Controller 打包成 Docker 镜像,然后通过 Deployment 部署到集群中。

 9.1、构建 Controller 镜像

Kubebuilder 已经生成了 Dockerfile,我们直接执行以下命令,构建 Docker 镜像(需确保本地 Docker 能正常使用,且集群能拉取到镜像——如果是 Kind 集群,可使用 kind load docker-image 将镜像加载到集群中):

# 构建 Docker 镜像(替换镜像名称为自己的,格式:仓库地址/镜像名:标签)
# 示例:docker build -t application-operator:v0.1 .
docker build -t application-operator:v0.1 .

# 如果是 Kind 集群,将镜像加载到集群中(避免拉取失败)
kind load docker-image application-operator:v0.1

9.2、修改部署配置(指定镜像)

打开 config/manager/manager.yaml 文件,找到 spec.template.spec.containers[0].image,将其修改为我们刚才构建的镜像名称:

spec:
  replicas: 1
  selector:
    matchLabels:
      control-plane: controller-manager
  template:
    spec:
      containers:
      - command:
        - /manager
        args:
        - --health-probe-addr=:8081
        - --metrics-addr=127.0.0.1:8080
        - --leader-elect
        image: application-operator:v0.1  # 修改为自己构建的镜像名称
        name: manager
        ...

9.3、部署 Controller 到集群

执行以下命令,部署 Controller(Kubebuilder 生成的脚本会自动部署 Manager、RBAC 等资源)

# 部署 Controller(包括 Manager Deployment、RBAC 权限等)
make deploy IMG=application-operator:v0.1

9.4、验证 Controller 部署成功

执行以下命令,查看 Controller 的 Pod 是否正常运行:

# 查看 Controller 的 Pod(在 default 命名空间,名称以 controller-manager 开头)
kubectl get pods -l control-plane=controller-manager

# 查看 Controller 的日志(确认是否正常启动)
kubectl logs -f <controller-pod-name>

如果 Controller Pod 的 STATUS 为 Running,且日志中没有报错,说明 Controller 部署成功,此时它会持续监听 AppService 类型的 CR,自动执行 Reconcile 逻辑。  

十、第九步:资源清理 —— 避免集群资源泄漏(SRE 必做)

需要清理部署到集群中的资源(CR、CRD、Controller、Pod 等),避免资源泄漏,这是 SRE 日常运维的良好习惯。清理顺序很重要:先删除 CR,再删除 Controller,最后删除 CRD。

10.1、删除 CR(自定义资源)

[root in ~/application-operator k8s_current_context:dev-ack k8s_server_version:v1.20.11-aliyun.1 2026.02.16-18:20:25]
# ls -l
total 96
-rw------- 1 root root 10817 Feb  9 15:11 AGENTS.md
drwx------ 3 root root  4096 Feb  9 15:18 api
drwxr-xr-x 2 root root  4096 Feb 16 17:25 bin
drwx------ 2 root root  4096 Feb 16 17:25 cmd
drwx------ 9 root root  4096 Feb  9 15:18 config
-rw------- 1 root root  1232 Feb  9 15:11 Dockerfile
-rw------- 1 root root  4665 Feb  9 15:12 go.mod
-rw-r--r-- 1 root root 22710 Feb  9 15:12 go.sum
drwx------ 2 root root  4096 Feb  9 15:11 hack
drwx------ 3 root root  4096 Feb  9 15:18 internal
-rw------- 1 root root 11074 Feb  9 15:11 Makefile
-rw------- 1 root root   582 Feb  9 15:18 PROJECT
-rw------- 1 root root  3824 Feb  9 15:11 README.md
drwx------ 4 root root  4096 Feb  9 15:11 test

# 删除我们部署的 AppService CR
[root in ~/application-operator k8s_current_context:dev-ack k8s_server_version:v1.20.11-aliyun.1 2026.02.16-18:20:25]
# kubectl delete -f config/samples/app_v1_appservice.yaml
appservice.app.example.com "demo-appservice" deleted
[root in ~/application-operator k8s_current_context:dev-ack k8s_server_version:v1.20.11-aliyun.1 2026.02.16-18:20:31]

# 验证 CR 是否删除成功
# kubectl get appservices.app.example.com -n monitoring
No resources found in monitoring namespace.

10.2、删除 Controller(包括 Manager Deployment、RBAC 等)

# 执行 Kubebuilder 生成的清理脚本,删除 Controller 相关资源
make undeploy

10.3、删除 CRD(自定义资源定义)

# 删除 CRD
kubectl delete -f config/crd/bases/app.example.com_appservices.yaml

# 验证 CRD 是否删除成功
kubectl get crd | grep appservices

10.4、额外清理:删除 Pod 和镜像(可选) 

# 删除残留的 Pod(根据标签筛选)
kubectl delete pods -l app=appservice-demo

# 如果是 Kind 集群,可删除镜像(可选)
kind delete images application-operator:v0.1

image

 结束!