kubernetes client-go源代码阅读之动态客户端
邓胖
共 4673字,需浏览 10分钟
· 2023-07-15
与client-go静态客户端相对应的自然就是动态客户端了,动态客户端的价值在于灵活,不用重新生成客户端代码就能访问k8s集群中的所有资源,即使是非内置资源。
快速入门
下面是官方的一个例子,完整版可参考: https://github.com/kubernetes/client-go/blob/v0.20.2/examples/dynamic-create-update-delete-deployment/main.go
func main() {
var kubeconfig *string
config, err := clientcmd.BuildConfigFromFlags("", *kubeconfig)
client, err := dynamic.NewForConfig(config)
deploymentRes := schema.GroupVersionResource{Group: "apps", Version: "v1", Resource: "deployments"}
deployment := &unstructured.Unstructured{/*具体代码被省略了*/}
// Create Deployment
fmt.Println("Creating deployment...")
result, err := client.Resource(deploymentRes).Namespace(namespace).Create(context.TODO(), deployment, metav1.CreateOptions{})
fmt.Printf("Created deployment %q.\n", result.GetName())
fmt.Println("Updating deployment...")
retryErr := retry.RetryOnConflict(retry.DefaultRetry, func() error {
// Retrieve the latest version of Deployment before attempting update
// RetryOnConflict uses exponential backoff to avoid exhausting the apiserver
result, getErr := client.Resource(deploymentRes).Namespace(namespace).Get(context.TODO(), "demo-deployment", metav1.GetOptions{})
// update replicas to 1
if err := unstructured.SetNestedField(result.Object, int64(1), "spec", "replicas"); err != nil {
panic(fmt.Errorf("failed to set replica value: %v", err))
}
// extract spec containers
containers, found, err := unstructured.NestedSlice(result.Object, "spec", "template", "spec", "containers")
if err != nil || !found || containers == nil {
panic(fmt.Errorf("deployment containers not found or error in spec: %v", err))
}
// update container[0] image
if err := unstructured.SetNestedField(containers[0].(map[string]interface{}), "nginx:1.13", "image"); err != nil {
panic(err)
}
if err := unstructured.SetNestedField(result.Object, containers, "spec", "template", "spec", "containers"); err != nil {
panic(err)
}
_, updateErr := client.Resource(deploymentRes).Namespace(namespace).Update(context.TODO(), result, metav1.UpdateOptions{})
return updateErr
})
fmt.Println("Updated deployment...")
fmt.Printf("Listing deployments in namespace %q:\n", apiv1.NamespaceDefault)
list, err := client.Resource(deploymentRes).Namespace(namespace).List(context.TODO(), metav1.ListOptions{})
for _, d := range list.Items {
replicas, found, err := unstructured.NestedInt64(d.Object, "spec", "replicas")
if err != nil || !found {
fmt.Printf("Replicas not found for deployment %s: error=%s", d.GetName(), err)
continue
}
fmt.Printf(" * %s (%d replicas)\n", d.GetName(), replicas)
}
fmt.Println("Deleting deployment...")
deletePolicy := metav1.DeletePropagationForeground
deleteOptions := metav1.DeleteOptions{
PropagationPolicy: &deletePolicy,
}
if err := client.Resource(deploymentRes).Namespace(namespace).Delete(context.TODO(), "demo-deployment", deleteOptions); err != nil {
panic(err)
}
fmt.Println("Deleted deployment.")
}
动态客户端的与静态客户端的区别主要在于是否手动传GVR。
客户端构造
具体定位到deployment客户端,可以这么写
client, err := dynamic.NewForConfig(config)
deploymentRes := schema.GroupVersionResource{Group: "apps", Version: "v1", Resource: "deployments"}
deploymentsClient := client.Resource(deploymentRes).Namespace(namespace)
为了减少文章篇幅,动态客户端就只分析Create的代码了, 其他接口代码可阅读client-go/dynamic/simple.go
,大致逻辑都是差不多的,不同点主要在于验证逻辑和最终的构造方法。
Create 代码解析
代码如下
func (c *dynamicResourceClient) Create(ctx context.Context, obj *unstructured.Unstructured, opts metav1.CreateOptions, subresources ...string) (*unstructured.Unstructured, error) {
// 1.
outBytes, err := runtime.Encode(unstructured.UnstructuredJSONScheme, obj)
// 2.
name := ""
if len(subresources) > 0 {
accessor, err := meta.Accessor(obj)
name = accessor.GetName()
if len(name) == 0 {
return nil, fmt.Errorf("name is required")
}
}
// 3.
result := c.client.client.
Post().
AbsPath(append(c.makeURLSegments(name), subresources...)...).
Body(outBytes).
SpecificallyVersionedParams(&opts, dynamicParameterCodec, versionV1).
Do(ctx)
// 4.
retBytes, err := result.Raw()
uncastObj, err := runtime.Decode(unstructured.UnstructuredJSONScheme, retBytes)
return uncastObj.(*unstructured.Unstructured), nil
}
代码主要分为四个部分。
-
将对象序列化成
[]byte
对象,用于后续上传 - 如果是子资源,就进一步获取子资源的名字,比如status, scale等
- 构造请求,具体就是使用Post方法,构造请求路径,设置请求体参数, 指定编解码参数等,最后请求
- 将结果解码并返回
k8s里面的编解码是个很复杂的过程,这个以后再说,可以单独写一篇或者两篇文章呢。
总结
在不使用反射的情况下,静态客户端的接口调用比较机械化,不够灵活,所以存在动态客户端,GVR的指定可以动态的指定,相较于静态客户端要灵活很多,两者各有优缺点。
评论
手动部署jar包,太low!动态上传热部署真爽!
本文来源:https://blog.csdn.net/zhangzhiqiang_0912近期开发系统过程中遇到的一个需求,系统给定一个接口,用户可以自定义开发该接口的实现,并将实现打成jar包,上传到系统中。系统完成热部署,并切换该接口的实现。定义简单的接口这里以一个简单的计算器功能为例,接口定义
Java高效学习
10
带你玩转Linux系统之lscpu命令
链接:https://bbs.huaweicloud.com/blogs/422603一、lscpu命令介绍lscpu 是一个 Linux 命令,用于显示CPU架构的详细信息。它可以用来查看 CPU 的型号、主频、架构、虚拟化支持等。二、lscpu命令的使用帮助2.1 命令格式lscpu [选项]2
良许Linux
0
C语言基础之动态内存操作汇总
来源:机器之心1、堆区空间申请#include <stdlib.h> //头文件void *malloc(size_t size);//函数size表示申请的空间字节数函数的返回值:成功:返回值空间起始地址失败:NULL特点:分配指定大小的内存空间;分配的内存空间是连续的;需要手动释放
良许Linux
0
大模型并行训练指南:通俗理解Megatron-DeepSpeed之模型并行与数据并行(下)
文末《大模型项目开发线上营》秒杀倒计时↓↓↓接前文:(上)篇>>>大模型并行训练指南:通俗理解Megatron-DeepSpeed之模型并行与数据并行(上)(中)篇>>>大模型并行训练指南:通俗理解Megatron-DeepSpeed之模型并行与数据并行(中)06
七月在线实验室
10
实战 | 基于YOLOv9+SAM实现动态目标检测和分割(步骤 + 代码)
点击上方“小白学视觉”,选择加"星标"或“置顶”重磅干货,第一时间送达导 读 本文主要介绍基于YOLOv9+SAM实现动态目标检测和分割,并给出详细步骤和代码。 背景介绍 &nb
小白学视觉
10
以环境之“优”谋发展之“势”
好的营商环境是生产力、竞争力,更是吸引力。近年来,我县始终坚持“项目为王、环境是金”工作导向,践行101%服务理念,大力优化营商环境,厚植高质量发展沃土,为县域经济发展提供强大支撑。 紧盯重大项目,推动企业投产增效 江苏伟复能源有限公司主要生产铅酸动力蓄电池,产品直供
盱眙老妹
0
Kubernetes 策略管理引擎 - Kyverno
Kyverno 是来自 Nirmata 的开源项目,后来捐赠给了 CNCF。Kyverno 是一个具有验证和变异能力的 Kubernetes 策略引擎,但是它还有生成资源的功能,还加入了 API 对象查询的能力。Kyverno 原本就是为 Kubernetes 编写的,除了对象生成功能之外,无需专用
k8s技术圈
8
炫技Groovy!SpringBoot中的动态编程实战
来源:juejin.cn/post/7139877924676567048👉 欢迎加入小哈的星球 ,你将获得: 专属的项目实战 / Java 学习路线 / 一对一提问 / 学习打卡 / 赠书福利全栈前后端分离博客项目 2.0 版本完结啦, 演示链接
小哈学Java
10