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

推荐订阅源

腾讯CDC
Hacker News: Ask HN
Hacker News: Ask HN
S
Securelist
Security Latest
Security Latest
S
Schneier on Security
T
Threat Research - Cisco Blogs
Latest news
Latest news
Cyberwarzone
Cyberwarzone
A
Arctic Wolf
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
NISL@THU
NISL@THU
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
I
Intezer
T
The Exploit Database - CXSecurity.com
N
News and Events Feed by Topic
Simon Willison's Weblog
Simon Willison's Weblog
T
Tor Project blog
Blog — PlanetScale
Blog — PlanetScale
C
Cyber Attacks, Cyber Crime and Cyber Security
C
CERT Recently Published Vulnerability Notes
The Hacker News
The Hacker News
月光博客
月光博客
WordPress大学
WordPress大学
博客园 - 叶小钗
Hugging Face - Blog
Hugging Face - Blog
美团技术团队
量子位
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
C
Cisco Blogs
博客园 - 三生石上(FineUI控件)
Google DeepMind News
Google DeepMind News
Project Zero
Project Zero
Webroot Blog
Webroot Blog
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
Application and Cybersecurity Blog
Application and Cybersecurity Blog
云风的 BLOG
云风的 BLOG
L
LINUX DO - 最新话题
Schneier on Security
Schneier on Security
Engineering at Meta
Engineering at Meta
www.infosecurity-magazine.com
www.infosecurity-magazine.com
aimingoo的专栏
aimingoo的专栏
D
Docker
有赞技术团队
有赞技术团队
Google DeepMind News
Google DeepMind News
宝玉的分享
宝玉的分享
T
Troy Hunt's Blog
L
Lohrmann on Cybersecurity
T
The Blog of Author Tim Ferriss
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
L
LangChain Blog

博客园 - 左扬

Kubernetes 编程 / Operator 专题【左扬精讲】—— Client-go 源代码分析:生产级 Controller 实践:并发安全、资源清理与高可用设计 Kubernetes 编程 / Operator 专题【左扬精讲】—— Client-go 源代码分析: Controller 调试与诊断工具:从日志分析到问题定位 Kubernetes 编程 / Operator 专题【左扬精讲】—— Client-go 源代码分析:DynamicClient 操作 CRD:无需代码生成的动态操作 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 专题【左扬精讲】—— 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开发离不开这些特殊注释 Kubernetes编程 / Operator专题【左扬精讲】—— Operator 开发实战项目1 —— Applicaion Operator(通用应用生命周期管理 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 专题【左扬精讲】—— Client-go 源代码分析:控制器与 APIServer 完整交互流程:从 Watch 到缓存同步
左扬 · 2026-06-13 · via 博客园 - 左扬

Kubernetes 编程 / Operator 专题【左扬精讲】—— Client-go 源代码分析:控制器与 APIServer 完整交互流程:从 Watch 到缓存同步

当我们深入理解 Informer、Reflector、DeltaFIFO 的工作原理后,还有一个关键问题没有解答:Controller 和 APIServer 之间的交互细节是什么?Watch 是如何工作的?ResourceVersion 有什么用?为什么有时候会收到过期的对象?Bookmark 事件又是什么?

这一篇文章,我们从源码级别深入理解 Controller 与 APIServer 的完整交互流程。

Kubernetes Watch ResourceVersion Bookmark v1.36.1

🔓 学习重点提示  — 建议先通读全文,再重点回顾标注内容

★ 重点掌握(必须)
   • Watch 分页机制:limit 和 continue 参数如何实现大数据量分页
   • ResourceVersion 传递:为什么 Watch 需要从特定的 ResourceVersion 开始
   • Bookmark 事件:定时心跳的作用和对 Controller 的影响

☆ 次重点(了解即可)
   • 早期版本兼容性问题


一、从 List 到 Watch:完整的同步流程

当我们启动一个 Informer 时,Reflector 会执行完整的 List-Watch 流程。这个流程分为两个阶段:

阶段一:List(全量同步)

List 是 Informer 启动时的第一步,目的是把 APIServer 上的所有资源一次性拉到本地。对于大数据量的资源,List 可能会分多个请求完成(分页)。

┌──────────────────────────────────────────────────────────────────────────┐
│                      List 全量同步流程                                     │
├──────────────────────────────────────────────────────────────────────────┤
│                                                                          │
│   Reflector                           APIServer                           │
│       │                                    │                              │
│       │  ──── GET /api/v1/pods?limit=500 ──→                           │
│       │                                    │                              │
│       │  ←─── 500 pods + continue token ───                            │
│       │                                    │                              │
│       │  ──── GET /api/v1/pods?           ──→                           │
│       │          continue=xxx&limit=500                                  │
│       │                                    │                              │
│       │  ←─── 更多 pods + 新 continue ────                              │
│       │                                    │                              │
│       │  ──── GET /api/v1/pods?... ──→                                  │
│       │                                    │                              │
│       │  ←─── 最后一批 pods(无 continue) ──                            │
│       │                                    │                              │
│       │  ✓ 所有数据已拉取,记录最后的 ResourceVersion                     │
│       │                                    │                              │
│       ▼  开始 Watch 阶段                                                      │
└──────────────────────────────────────────────────────────────────────────┘

阶段二:Watch(增量同步)

Watch 是基于 HTTP long-polling 的长连接。Reflector 会从 List 结束时的 ResourceVersion 开始 Watch,实时接收集群的变化事件。

┌──────────────────────────────────────────────────────────────────────────┐
│                      Watch 增量同步流程                                     │
├──────────────────────────────────────────────────────────────────────────┤
│                                                                          │
│   Reflector                           APIServer                           │
│       │                                    │                              │
│       │  ──── GET /api/v1/pods?           ──→                           │
│       │          watch=true&                                      │
│       │          resourceVersion=12345                                    │
│       │                                    │                              │
│       │                                    │  (长连接打开,等待事件)         │
│       │                                    │                              │
│       │  ←─── ADDED event ────────────────                              │
│       │  ←─── MODIFIED event ─────────────                              │
│       │  ←─── Bookmark (心跳) ─────────────                              │
│       │  ←─── DELETED event ──────────────                              │
│       │                                    │                              │
│       │  处理事件,更新 DeltaFIFO...                                        │
│       │                                    │                              │
│       │  ←─── 更多事件 ─────────────────────                              │
│       │                                    │                              │
└──────────────────────────────────────────────────────────────────────────┘

二、Watch 分页:limit 和 continue 参数

当集群中的资源数量非常大时(比如有成千上万个 Pod),一次 List 请求可能返回不了所有数据。Kubernetes 提供了分页机制解决这个问题。

List 请求的响应中会包含一个 continue token,用于获取下一页数据:

# 第一次请求:获取前 500 个 Pod
GET /api/v1/pods?limit=500

# 响应
{
  "apiVersion": "v1",
  "kind": "PodList",
  "metadata": {
    "resourceVersion": "12345",
    "continue": "eyJza2luX3ZlcnNpb24iOiJza2luX3ZlcnNpb24iLCJkaXJlY3RvcnlLZXkiOiJwb2RzI2RlZmF1bHQiLCJjb250aW51ZVRva2VuIjoiZXlKMlpXWnBjR1ZoY21Wc1BqeGphWFhwY3lnME1qQXhZakV6TURFME1USTJNZz09In0ifQ=="  // Base64 编码的游标
  },
  "items": [ /* 500 个 Pod */ ]
}

# 第二次请求:使用 continue token 获取下一页
GET /api/v1/pods?limit=500&continue=eyJza2luX3ZlcnNpb24iOiJza2luX3ZlcnNpb24iLCJkaXJlY3RvcnlLZXkiOiJwb2RzI2RlZmF1bHQiLCJjb250aW51ZVRva2VuIjoiZXlKMlpXWnBjR1ZoY21Wc1BqeGphWFhwY3lnME1qQXhZakV6TURFME1USTJNZz09In0ifQ==

Reflector 中的 List 循环会一直执行,直到没有 continue token 为止:

// Reflector 中的 List 循环伪代码

func (r *Reflector) ListAndWatch() error {
    // ... 准备 List options ...

    var resourceVersion string

    for {
        // 执行 List 请求
        listObj, err := r.listerWatcher.List(options)
        listMetaInterface, _ := meta.ListAccessor(listObj)
        resourceVersion = listMetaInterface.GetResourceVersion()

        // 将每个对象放入 DeltaFIFO
        for _, obj := range listMetaInterface.GetItems() {
            r.watchListWatermark.Record(int64(len(listMetaInterface.GetItems())))
            r.expectedGVK = r.groupVersionKind(obj)
            if err := r.store.Add(obj); err != nil {
                // 处理错误
            }
        }

        // 检查是否有更多页(continue token)
        continueToken := listMetaInterface.GetContinue()
        if continueToken == "" {
            // 没有更多数据,退出 List 阶段
            break
        }

        // 更新 options,准备下一次请求
        options.Continue = continueToken
        // 继续循环获取下一页
    }

    // List 完成,使用最后一个 ResourceVersion 开始 Watch
    r.watch(resourceVersion)
}

三、ResourceVersion:理解版本号的核心作用

ResourceVersion 是 Kubernetes 实现乐观并发的核心机制。每个资源对象都有一个 resourceVersion 字段,每次更新时这个字段都会递增。

ResourceVersion 的三个作用

  • 一致性保证:Watch 从指定的 ResourceVersion 开始,保证不会漏掉任何事件
  • 冲突检测:更新资源时携带 ResourceVersion,APIServer 会检测是否冲突
  • 分页定位:List 请求可以用 ResourceVersion 确定数据的时间点

为什么 Watch 必须指定 ResourceVersion?如果不指定,APIServer 会从"现在"开始推送事件,可能漏掉 List 和 Watch 之间发生的变更。通过从 List 结束时的 ResourceVersion 开始 Watch,可以确保不漏掉任何变更。

⚠️ 警告
ResourceVersion 不是单调递增的全局序列。不同 namespace、不同类型的资源有独立的 ResourceVersion 计数器。跨资源类型的操作(如同时 Watch Pod 和 Deployment)需要注意这一点。

四、Bookmark 事件:被忽视的心跳机制

Bookmark 是 Watch 事件中容易被忽视的一种类型。它的作用是作为心跳,确保长连接不会因为空闲超时而被代理或负载均衡器关闭。

// Bookmark 事件的示例
{
  "type": "BOOKMARK",
  "object": {
    "kind": "Pod",
    "apiVersion": "v1",
    "metadata": {
      "resourceVersion": "13000"  // 当前 APIServer 的最新 ResourceVersion
    }
  }
}

Bookmark 事件的三个作用

  1. 保持连接活跃:防止代理或负载均衡器因连接超时而关闭
  2. 更新 ResourceVersion:Reflector 会更新 lastSyncResourceVersion,用于 Watch 断线重连
  3. 无对象数据:Bookmark 的 object 字段只包含 metadata,没有实际资源数据

在 Reflector 中处理 Bookmark 事件:

// Reflector 处理 Watch 事件的部分代码
func (r *Reflector) watchHandler(...) error {
    for {
        select {
        case

五、Watch 断线重连机制

当 Watch 连接断开时(比如网络抖动),Reflector 需要自动重连。关键问题是:从哪个 ResourceVersion 开始重连?

┌──────────────────────────────────────────────────────────────────────────┐
│                        Watch 断线重连流程                                   │
├──────────────────────────────────────────────────────────────────────────┤
│                                                                          │
│   Reflector                           APIServer                           │
│       │                                    │                              │
│       │  ✓ 从 ResourceVersion=12345 开始 Watch                          │
│       │  ←─── 事件流 ──────────────────────                              │
│       │                                    │                              │
│       │  ✗ 连接断开!(网络问题)                                            │
│       │                                    │                              │
│       │  ✓ 使用 lastSyncResourceVersion=12400 重连                      │
│       │  ←─── 从 12400 之后的事件 ──────                                 │
│       │                                    │                              │
│       │  ✓ 不会漏事件,也不会重复收到                                      │
│       │                                    │                              │
└──────────────────────────────────────────────────────────────────────────┘

Reflector 维护了 lastSyncResourceVersion,每次收到 Bookmark 事件或完成 List 后都会更新它。断线重连时,就从这个版本开始。

六、Watch 请求参数详解

Watch 请求支持多种参数来控制行为:

参数说明示例
watch=true 启用 Watch 模式 必须
resourceVersion 从指定版本开始 Watch resourceVersion=12345
resourceVersionMatch 匹配模式(NotOlderThan/Exact) NotOlderThan=12345
timeoutSeconds Watch 超时时间 timeoutSeconds=600
sendInitialEvents 是否发送初始事件(BOOKMARK 特性) sendInitialEvents=true

七、常见问题与排查

问题 1:Too large resource version

这个错误表示请求的 ResourceVersion 太旧了。APIServer 默认只保留最近 5 分钟的资源版本数据。如果 Watch 断开太久,lastSyncResourceVersion 可能已经过期。

解决方案:Reflector 会自动检测到这个错误并重新执行 List,重新获取最新的数据。

问题 2:Event 顺序不一致

Kubernetes 不保证 Watch 事件的顺序。对于同一对象的事件(如 ADDED + MODIFIED),DeltaFIFO 会处理乱序问题:它会根据对象的 ResourceVersion 做判断,只保留最新版本。


八、总结

这一节我们深入理解了 Controller 与 APIServer 的完整交互流程:

  • List-Watch 两阶段:List 拉取全量数据,Watch 接收增量变更
  • 分页机制:通过 limit 和 continue token 实现大数据量分页
  • ResourceVersion:保证一致性、检测冲突、定位分页
  • Bookmark 事件:保持连接活跃,更新 lastSyncResourceVersion
  • 断线重连:从 lastSyncResourceVersion 重新开始 Watch

下一节我们将学习 DynamicClient 操作 CRD,了解如何动态操作未编译进代码的自定义资源。敬请期待!


Kubernetes 编程 / Operator 专题【左扬精讲】—— 控制器与 APIServer 完整交互流程 · 来源:Kubernetes v1.36.1 client-go 源码分析