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

推荐订阅源

腾讯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 源代码分析:控制器与 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 专题【左扬精讲】—— 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 源代码分析:DynamicClient 操作 CRD:无需代码生成的动态操作
左扬 · 2026-06-13 · via 博客园 - 左扬

Kubernetes 编程 / Operator 专题【左扬精讲】—— Client-go 源代码分析:DynamicClient 操作 CRD:无需代码生成的动态操作

当我们需要操作 Kubernetes 内置资源(如 Pod、Deployment)时,可以使用 typed client(Clientset),因为这些资源的 Go 类型已经定义好了。

但当我们需要操作自定义资源(CRD)时,如果每次都等代码生成,会非常麻烦。DynamicClient 就是来解决这个问题的:它可以动态地操作任意 Kubernetes 资源,包括 CRD,无需提前知道资源的具体类型。这一篇文章,我们来深入理解 DynamicClient 的设计和使用。

Kubernetes DynamicClient CRD Unstructured v1.36.1

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

★ 重点掌握(必须)
   • DynamicClient vs Clientset:什么时候用哪个,为什么
   • Unstructured 对象:如何用 map[string]interface{} 表示任意资源
   • GroupVersionResource:如何用 GVR 定位任意资源

☆ 次重点(了解即可)
   • DynamicInformer 与 DynamicClient 的配合


一、为什么需要 DynamicClient?

假设我们开发了一个通用工具,需要操作用户的 CRD,但用户可能有任意类型的 CRD,不可能每次都等 CRD 定义好之后重新生成代码。

另一个场景是 Operator Framework 的早期版本,它们需要在运行时动态发现和操作 CRD。

DynamicClient 的核心思想:用通用的数据结构(map/list)来表示资源,而不是用编译时已知的 Go struct。这样就可以操作任意类型的资源了。

二、DynamicClient 的数据结构

DynamicClient 的结构非常简单:

// staging/src/k8s.io/client-go/dynamic/simple.go(行 34-36)

// DynamicClient 是一个动态客户端,可以操作任意 Kubernetes 资源
type DynamicClient struct {
    client rest.Interface  // 通用的 REST 客户端
}

var _ Interface = &DynamicClient{}  // 实现了 Interface 接口

DynamicClient 只有一个字段:rest.Interface。所有对 APIServer 的请求都通过这个通用接口完成。创建 DynamicClient 的方式也很简单:

// 创建 DynamicClient

import "k8s.io/client-go/dynamic"

func main() {
    // 方式一:从 config 创建
    config, _ := clientcmd.BuildConfigFromFlags("", "")
    dynamicClient, _ := dynamic.NewForConfig(config)

    // 方式二:从已有的 rest.Config 创建
    dynamicClient, _ := dynamic.NewForConfigAndClient(config, httpClient)
}

三、GVR:定位任意资源的关键

typed client 通过 Clientset 直接访问特定的资源,比如 `clientset.AppsV1().Deployments()`。DynamicClient 不一样,我们需要用 GroupVersionResource(GVR)来定位资源。

import "k8s.io/apimachinery/pkg/runtime/schema"

// GVR 示例
podGVR := schema.GroupVersionResource{
    Group:    "",           // core 组,Group 为空
    Version:  "v1",        // API 版本
    Resource: "pods",      // 资源名(复数形式)
}

// 自定义资源 GVR 示例(假设有一个 Foo CRD)
fooGVR := schema.GroupVersionResource{
    Group:    "example.com",    // CRD 的 API Group
    Version:  "v1",             // CRD 的版本
    Resource: "foos",           // CRD 的复数资源名
}

有了 GVR 之后,就可以通过 DynamicClient 获取资源了:

// 获取某个 namespace 下的资源
resourceClient := dynamicClient.Resource(fooGVR).Namespace("default")

// 获取所有 namespace 下的资源
resourceClient := dynamicClient.Resource(fooGVR)  // 不调用 Namespace()

四、Unstructured:动态表示任意资源

DynamicClient 的核心是 Unstructured 对象。它是一个用 `map[string]interface{}` 表示的动态对象,可以装任意 JSON 数据。

// Unstructured 对象示例

import "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"

// 创建一个 Unstructured 对象
obj := &unstructured.Unstructured{
    Object: map[string]interface{}{
        "apiVersion": "example.com/v1",
        "kind":       "Foo",
        "metadata": map[string]interface{}{
            "name":      "my-foo",
            "namespace": "default",
        },
        "spec": map[string]interface{}{
            "replicas": 3,
            "selector": map[string]interface{}{
                "matchLabels": map[string]interface{}{
                    "app": "myapp",
                },
            },
        },
    },
}

Unstructured 提供了便捷的方法来读写嵌套字段:

// 设置嵌套字段
unstructured.SetNestedField(obj.Object, int64(3), "spec", "replicas")
unstructured.SetNestedMap(obj.Object, map[string]interface{}{
    "matchLabels": map[string]interface{}{
        "app": "myapp",
    },
}, "spec", "selector")

// 读取嵌套字段
replicas, found, err := unstructured.NestedInt64(obj.Object, "spec", "replicas")
name, found, err := unstructured.NestedString(obj.Object, "metadata", "name")

五、完整使用示例

下面是一个完整的 DynamicClient 使用示例,演示如何操作一个名为 Foo 的 CRD:

// 完整的 DynamicClient 使用示例

package main

import (
    "context"
    "fmt"

    "k8s.io/apimachinery/pkg/api/meta"
    "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
    "k8s.io/apimachinery/pkg/runtime/schema"
    "k8s.io/client-go/dynamic"
    "k8s.io/client-go/tools/clientcmd"
)

func main() {
    // 第一步:创建 DynamicClient
    config, _ := clientcmd.BuildConfigFromFlags("", "")
    dynamicClient, _ := dynamic.NewForConfig(config)

    // 第二步:定义 CRD 的 GVR
    fooGVR := schema.GroupVersionResource{
        Group:    "example.com",
        Version:  "v1",
        Resource: "foos",
    }

    // 第三步:创建资源
    foo := &unstructured.Unstructured{
        Object: map[string]interface{}{
            "apiVersion": "example.com/v1",
            "kind":       "Foo",
            "metadata": map[string]interface{}{
                "name":      "my-foo",
                "namespace": "default",
            },
            "spec": map[string]interface{}{
                "replicas": 3,
            },
        },
    }

    // 创建资源
    created, err := dynamicClient.Resource(fooGVR).Namespace("default").Create(
        context.Background(),
        foo,
        metav1.CreateOptions{},
    )
    if err != nil {
        panic(err)
    }
    fmt.Printf("Created: %s/%s\n", created.GetNamespace(), created.GetName())

    // 第四步:读取资源
    fetched, err := dynamicClient.Resource(fooGVR).Namespace("default").Get(
        context.Background(),
        "my-foo",
        metav1.GetOptions{},
    )
    if err != nil {
        panic(err)
    }
    fmt.Printf("Fetched: %+v\n", fetched.Object)

    // 第五步:修改资源(使用 Unstructured 的便捷方法)
    unstructured.SetNestedField(fetched.Object, int64(5), "spec", "replicas")
    
    updated, err := dynamicClient.Resource(fooGVR).Namespace("default").Update(
        context.Background(),
        fetched,
        metav1.UpdateOptions{},
    )
    if err != nil {
        panic(err)
    }
    fmt.Printf("Updated replicas to: %d\n", 
        updated.Object["spec"].(map[string]interface{})["replicas"])

    // 第六步:列表资源
    list, err := dynamicClient.Resource(fooGVR).Namespace("default").List(
        context.Background(),
        metav1.ListOptions{},
    )
    if err != nil {
        panic(err)
    }
    fmt.Printf("Found %d Foo resources\n", len(list.Items))

    // 第七步:删除资源
    err = dynamicClient.Resource(fooGVR).Namespace("default").Delete(
        context.Background(),
        "my-foo",
        metav1.DeleteOptions{},
    )
    if err != nil {
        panic(err)
    }
    fmt.Println("Deleted my-foo")
}

六、DynamicClient 的限制

DynamicClient 虽然灵活,但也有一些限制:

限制说明解决方案
无类型安全 map[string]interface{} 没有编译时检查,容易写错字段名 使用 Unstructured 便捷方法,设置/读取嵌套字段
无代码补全 IDE 无法提供字段补全 用 typed client 处理已知的资源类型
性能稍差 每次都需要序列化/反序列化 map[string]interface{} 对于频繁操作的资源,使用 typed client

七、DynamicInformer:监听 CRD 变化

和 typed Informer 类似,DynamicClient 也有对应的 DynamicInformer,用于监听 CRD 的变化:

import "k8s.io/client-go/dynamic/dynamicinformer"
import "k8s.io/client-go/tools/cache"

func main() {
    // 创建 DynamicInformerFactory
    factory := dynamicinformer.NewFilteredDynamicSharedInformerFactory(
        dynamicClient,
        30*time.Second,      // resync 周期
        "default",           // namespace
        nil,                 // tweakListOptions
    )

    // 为 Foo CRD 创建 Informer
    fooInformer := factory.ForResource(fooGVR)
    
    // 注册事件处理函数
    fooInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
        AddFunc: func(obj interface{}) {
            u := obj.(*unstructured.Unstructured)
            fmt.Printf("Foo Added: %s\n", u.GetName())
        },
        UpdateFunc: func(old, new interface{}) {
            u := new.(*unstructured.Unstructured)
            fmt.Printf("Foo Updated: %s\n", u.GetName())
        },
        DeleteFunc: func(obj interface{}) {
            u := obj.(*unstructured.Unstructured)
            fmt.Printf("Foo Deleted: %s\n", u.GetName())
        },
    })

    // 启动 Informer
    factory.Start(ctx.Done())
    factory.WaitForCacheSync(ctx.Done())
}

八、总结

这一节我们深入理解了 DynamicClient:

  • 为什么需要 DynamicClient:操作未知类型的 CRD,无需代码生成
  • GVR 定位资源:用 GroupVersionResource 唯一标识任意资源
  • Unstructured 对象:用 map[string]interface{} 动态表示任意 JSON 数据
  • 限制:无类型安全、无代码补全、性能稍差
  • DynamicInformer:配合 DynamicInformer 监听 CRD 变化

下一节我们将学习 工具与调试方法,了解如何排查 Controller 的问题。敬请期待!


Kubernetes 编程 / Operator 专题【左扬精讲】—— DynamicClient 操作 CRD · 来源:Kubernetes v1.36.1 client-go 源码分析