【云原生开发】如何通过client-go来操作K8S集群

✨✨ 欢迎大家来到景天科技苑✨✨

🎈🎈 养成好习惯,先赞后看哦~🎈🎈

🏆 作者简介:景天科技苑

🏆《头衔》:大厂架构师,华为云开发者社区专家博主,阿里云开发者社区专家博主,CSDN全栈领域优质创作者,掘金优秀博主,51CTO博客专家等。

🏆《博客》:Python全栈,Golang开发,云原生开发,PyQt5和Tkinter桌面开发,小程序开发,人工智能,js逆向,App逆向,网络系统安全,数据分析,Django,fastapi,flask等框架,云原生K8S,linux,shell脚本等实操经验,网站搭建,数据库等分享。

所属的专栏: 云原生开发
景天的主页: 景天科技苑

文章目录

  • client-go
    • 一、client-go介绍
      • [1. 什么是client-go?](#1. 什么是client-go?)
      • [2. client-go版本的演变](#2. client-go版本的演变)
      • [3. client-go客户端分类](#3. client-go客户端分类)
      • [4. client-go客户端工具依赖关系](#4. client-go客户端工具依赖关系)
      • [5. 安装client-go](#5. 安装client-go)
    • 二、使用client-go进行基本操作
      • [2.1 in-cluster配置](#2.1 in-cluster配置)
      • [2.2 out-of-cluster配置](#2.2 out-of-cluster配置)
      • [2.3 client-go 查询列表功能使用](#2.3 client-go 查询列表功能使用)
      • [2.4 client-go查询资源详情](#2.4 client-go查询资源详情)
      • [2.5 client-go更新资源功能](#2.5 client-go更新资源功能)
      • [2.6 client-go删除资源](#2.6 client-go删除资源)
      • [2.7 client-go创建资源](#2.7 client-go创建资源)
      • [2.8 client-go使用json串创建资源](#2.8 client-go使用json串创建资源)
    • 三、总结

client-go

一、client-go介绍

1. 什么是client-go?

client-go是Kubernetes官方提供的,用于操作kubernetes资源的Go语言客户端库,通过它,开发者可以非常方便地在Go项目中与Kubernetes集群进行交互,实现对Kubernetes资源以及自定义CRD的增删改查和事件监听等操作。

同时,可以通过client-go实现kubernetes的二次开发。自定义资源开发。

源码:

github下载地址:https://github.com/kubernetes/client-go

如果是其他语言的客户端工具,可以通过https://github.com/kubernetes-client 来查看

我们看下client-go几个比较重要的目录

2. client-go版本的演变

左边是client-go的版本。右边是k8s的版本

在client-go 1.17版本之前,client-go的版本与k8s版本保持一致,1.17之后,client-go的版本多了一个v的tag。是由于go语言的包是带v的版本

建议:client-go我们直接用最新版本

3. client-go客户端分类

markup 复制代码
restclient: 一般我们不会使用restclient,因为它需要把整个资源的yaml文件或json数据都传过去,显得比较臃肿,一般我们不用这个
discoverclient: 比如我们创建deployment时的apiversion: apps/v1    apps就是资源组Group   v1就是资源版本Version   资源信息 就是kind 。我们一般也不会用这个客户端工具
ClientSet: 只能针对K8S内置的资源进行操作,不能操作自定义的资源
DyanmicClient: 我们经常使用这个客户端,但是对于内置资源,我们还是习惯使用ClientSet,因为它更好用,自定义资源我们使用DyanmicClient。

我们可以通过命令 kubectl api-resources 查看每种资源的资源组

4. client-go客户端工具依赖关系

5. 安装client-go

client-go是一个Go模块,可以通过Go Module的方式进行安装。在你的Go项目中,执行以下命令:

bash 复制代码
go get k8s.io/client-go@latest

这将安装最新版本的client-go。此外,你还需要安装一些相关的依赖库,例如apimachinery,用于处理Kubernetes API对象。

bash 复制代码
go get k8s.io/apimachinery@latest

安装完还需要运行go mod tidy 加载依赖包

二、使用client-go进行基本操作

创建Kubernetes客户端

在使用client-go之前,首先需要创建一个Kubernetes客户端。client-go提供了两种创建客户端的方式:in-cluster配置和out-of-cluster配置。

我们根据我们之前写好的脚手架,改个名字,在此基础上开发我们的项目

并不是说在此改了就可以了,因为很多包用的还是原来的名字,

我们可以批量替换

在Goland IDE中想要替换某一段特定的字符串,可以使用Find and Replace 功能来实现。这是一种全局性的操作,将会在你的整个项目或指定的文件/文件夹中进行。

使用Ctrl + Shift + R 打开Find and Repalce对话框

我们测试client-go,要用到k8s集群,得有~/.kube/config 这个文件

里面是加载集群的配置信息

将这个文件复制到我们的项目中

2.1 in-cluster配置

在Kubernetes 集群内部运行时,可以使用in-cluster配置。这种方式不需要手动指定kubeconfig文件路径,client-go会自动使用集群中的服务账户进行身份验证。

2.2 out-of-cluster配置

测试client-go,我们使用out-of-cluster方式来测试,发布项目的时候,我们使用in-cluster方式配置

在本地电脑开发环境或其他非Kubernetes集群中运行时,可以使用out-of-cluster配置。这需要指定kubeconfig文件的路径。kubeconfig文件通常位于$HOME/.kube/config,它包含了访问Kubernetes集群所需的配置信息。

这是github上面看用法举例

go 复制代码
package main

import (
    "context"
    "fmt"
    metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    "k8s.io/client-go/kubernetes"
    "k8s.io/client-go/tools/clientcmd"
)

func main() {
    //1. 初始化config实例
    //var kubeconfig *string
    // 通过家目录找到kubeconfig文件。我们的路径是已知的,所以不用此项配置
    //if home := homedir.HomeDir(); home != "" {
    //    kubeconfig = flag.String("meta.kubeconfig", filepath.Join(home, ".kube", "config"), "(optional) absolute path to the kubeconfig file")
    //} else {
    //    kubeconfig = flag.String("kubeconfig", "", "absolute path to the kubeconfig file")
    //}
    //flag.Parse()

    // 1. 初始化config实例
    // 因为我们的路径是已知的,所以不用上面的配置。use the current context in kubeconfig
    // masterUrl就是离我们主节点的ip地址和端口号,我们在kubeconfig文件中有了,所以可以省略
    config, err := clientcmd.BuildConfigFromFlags("", "meta.kubeconfig")
    //要想正常应用我们的服务,必须能够实例化成功kubeconfig,要不然后面所有的功能都无法使用,所以这里直接报panic即可
    if err != nil {
        panic(err.Error())
    }

    // 2. 创建客户端工具 create the clientset
    clientset, err := kubernetes.NewForConfig(config)
    //这个客户端工具如果生成失败的话,后面的操作也无法完成,所以这里也报panic即可
    if err != nil {
        panic(err.Error())
    }

    //3. 操作集群
    pods, err := clientset.CoreV1().Pods("").List(context.TODO(), metav1.ListOptions{})
    //此时报错的话,不应该是panic了,但是这里官方用的还是panic。后期需要优化,我么可以返回个错误信息
    if err != nil {
        panic(err.Error())
    }

    //打印pod的数量
    fmt.Printf("There are %d pods in the cluster\n", len(pods.Items))

    // 获取指定名称空间下的pod数量,如果namespace不传值,默认查的是所有命名空间下的pod
    namespace := "h5-web"
    pods, err = clientset.CoreV1().Pods(namespace).List(context.TODO(), metav1.ListOptions{})
    if err != nil {
        panic(err.Error())
    }
    //打印pod的数量
    fmt.Printf("%s namespce has %d pods in the cluster\n", namespace, len(pods.Items))
    //看下返回的pod是什么
    fmt.Println("pods是什么:", pods)
}

2.3 client-go 查询列表功能使用

我们看下List方法的参数,包含两个,一个context,一个是ListOptions。

这个ListOptions就可以在里面做些筛选条件,比如传json串,标签等

我们看下List的返回值

所以我们要查询具体的pod里面的信息,可以在Items字段中获取到所有的pod

跟我们通过在k8s集群中通过kubectl ... -ojson的得到的信息是一样的

如果要取其中某个pod,可以通过下标来获取

如果忘记pod的层级关系,可以使用k8s命令的-ojson 查看一下

完整代码:

go 复制代码
package main

import (
    "context"
    "fmt"
    metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    "k8s.io/client-go/kubernetes"
    "k8s.io/client-go/tools/clientcmd"
)

func main() {
    // 1. 初始化config实例
    // masterUrl就是离我们主节点的ip地址和端口号,我们在kubeconfig文件中有了,所以可以省略
    config, err := clientcmd.BuildConfigFromFlags("", "meta.kubeconfig")
    //要想正常应用我们的服务,必须能够实例化成功kubeconfig,要不然后面所有的功能都无法使用,所以这里直接报panic即可
    if err != nil {
        panic(err.Error())
    }

    // 2. 创建客户端工具 create the clientset
    clientset, err := kubernetes.NewForConfig(config)
    //这个客户端工具如果生成失败的话,后面的操作也无法完成,所以这里也报panic即可
    if err != nil {
        panic(err.Error())
    }

    //3. 操作集群
    pods, err := clientset.CoreV1().Pods("").List(context.TODO(), metav1.ListOptions{})
    //此时报错的话,不应该是panic了,但是这里官方用的还是panic。后期需要优化,我么可以返回个错误信息
    if err != nil {
        panic(err.Error())
    }

    //打印pod的数量
    fmt.Printf("There are %d pods in the cluster\n", len(pods.Items))

    // 获取指定名称空间下的pod数量,如果namespace不传值,默认查的是所有命名空间下的pod
    namespace := "h5-web"
    pods, err = clientset.CoreV1().Pods(namespace).List(context.TODO(), metav1.ListOptions{})
    if err != nil {
        panic(err.Error())
    }
    //打印pod的数量
    fmt.Printf("%s namespce has %d pods in the cluster\n", namespace, len(pods.Items))
    //看下返回的pod是什么
    fmt.Println("pods是什么:", pods)

    //获取到某个pod
    fmt.Println(pods.Items[0].Spec.NodeName)
    //获取到某个容器的镜像
    fmt.Println(pods.Items[0].Spec.Containers[0].Image)

}

下面,我们探讨下,我们怎么知道我们要操作的资源是属于CoreV1()或者是其他什么组呢?

之前,我们说过,在K8S集群中,可以通过命令 kubectl api-resources查看

deployment 的apiversion是 apps/v1 对应的client-go里面的方法就是 clientset.AppsV1()

crontabs 的apiversion是batch/v1 对应的client-go里面的方法就是clientset.BatchV1()

ingresses的apiversion是networking.k8s.io/v1 对应client-go里面的方法就是clientset.NetworkingV1()

...

以此类推,由此我们就知道了个汇总资源对应的操作方法

这种V1的apiversion的资源,对应的就是clientset.CoreV1()

如果不想通过K8S命令来查,也可以在代码中查看

点进来

这里可看看到个各种方法,不过我们用的时候要把首字母大写

比如查询deployment

go 复制代码
//查询deployment列表,用法与pod类似
deploy, _ := clientset.AppsV1().Deployments("").List(context.TODO(), metav1.ListOptions{})
//查看所有名称空间下deploy的数量
fmt.Println("deployment的数量", len(deploy.Items))
//打印deploy名称,由于是多个,我们循环打印
for _, i := range deploy.Items {
    fmt.Printf("当前资源的名称空间: %s, deployment名称是: %s\n", i.Namespace, i.Name)
}

//查询没有名称空间限制的资源,比如名称空间,工作节点,clusterrole,clusterrolebinding等
//查的都是集群的资源
//比如我们查询集群有多少个名称空间
ns, _ := clientset.CoreV1().Namespaces().List(context.TODO(), metav1.ListOptions{})
fmt.Printf("There are %d namespaces in the cluster\n", len(ns.Items))

完整代码:

go 复制代码
package main

import (
    "context"
    "fmt"
    metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    "k8s.io/client-go/kubernetes"
    "k8s.io/client-go/tools/clientcmd"
)

func main() {
    // 1. 初始化config实例
    // masterUrl就是离我们主节点的ip地址和端口号,我们在kubeconfig文件中有了,所以可以省略
    config, err := clientcmd.BuildConfigFromFlags("", "meta.kubeconfig")
    //要想正常应用我们的服务,必须能够实例化成功kubeconfig,要不然后面所有的功能都无法使用,所以这里直接报panic即可
    if err != nil {
        panic(err.Error())
    }

    // 2. 创建客户端工具 create the clientset
    clientset, err := kubernetes.NewForConfig(config)
    //这个客户端工具如果生成失败的话,后面的操作也无法完成,所以这里也报panic即可
    if err != nil {
        panic(err.Error())
    }

    //3. 操作集群
    pods, err := clientset.CoreV1().Pods("").List(context.TODO(), metav1.ListOptions{})
    //此时报错的话,不应该是panic了,但是这里官方用的还是panic。后期需要优化,我么可以返回个错误信息
    if err != nil {
        panic(err.Error())
    }

    //打印pod的数量
    fmt.Printf("There are %d pods in the cluster\n", len(pods.Items))

    // 获取指定名称空间下的pod数量,如果namespace不传值,默认查的是所有命名空间下的pod
    //namespace := "h5-web"
    //pods, err = clientset.CoreV1().Pods(namespace).List(context.TODO(), metav1.ListOptions{})
    //if err != nil {
    //    panic(err.Error())
    //}
    打印pod的数量
    //fmt.Printf("%s namespce has %d pods in the cluster\n", namespace, len(pods.Items))
    看下返回的pod是什么
    //fmt.Println("pods是什么:", pods)
    //
    获取到某个pod
    //fmt.Println(pods.Items[0].Spec.NodeName)
    获取到某个容器的镜像
    //fmt.Println(pods.Items[0].Spec.Containers[0].Image)

    //查询deployment列表,用法与pod类似
    deploy, _ := clientset.AppsV1().Deployments("").List(context.TODO(), metav1.ListOptions{})
    //查看所有名称空间下deploy的数量
    fmt.Println("deployment的数量", len(deploy.Items))
    //打印deploy名称,由于是多个,我们循环打印
    for _, i := range deploy.Items {
        fmt.Printf("当前资源的名称空间: %s, deployment名称是: %s\n", i.Namespace, i.Name)
    }

    //查询没有名称空间限制的资源,比如名称空间,工作节点,clusterrole,clusterrolebinding等
    //查的都是集群的资源
    //比如我们查询集群有多少个名称空间
    ns, _ := clientset.CoreV1().Namespaces().List(context.TODO(), metav1.ListOptions{})
    fmt.Printf("There are %d namespaces in the cluster\n", len(ns.Items))
}

2.4 client-go查询资源详情

Get()方法,可以获取单个资源的详情,获取详情之后,我们可以传给前端展示,或者根据查询出来的数据进行更改

比如说,我们对K8S集群中h5-web名称空间下的 pods 查询详情

go 复制代码
// 查询资源详情 Get()方法
// Get(ctx context.Context, name string, opts metav1.GetOptions) (*v1.Pod, error)
poddetail, _ := clientset.CoreV1().Pods("h5-web").Get(context.TODO(), "web-864f4c6988-95sw4", metav1.GetOptions{})
//fmt.Println("pod详情:", poddetail)
//打印pod的镜像名称
fmt.Println("pod第一个容器的镜像名称", poddetail.Spec.Containers[0].Image)

//获取名称空间的详情
namespace, _ := clientset.CoreV1().Namespaces().Get(context.TODO(), "h5-web", metav1.GetOptions{})
fmt.Println("名称空间详情:", namespace)

完整代码

go 复制代码
package main

import (
    "context"
    "fmt"
    metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    "k8s.io/client-go/kubernetes"
    "k8s.io/client-go/tools/clientcmd"
)

func main() {
    // 1. 初始化config实例
    // masterUrl就是离我们主节点的ip地址和端口号,我们在kubeconfig文件中有了,所以可以省略
    config, err := clientcmd.BuildConfigFromFlags("", "meta.kubeconfig")
    //要想正常应用我们的服务,必须能够实例化成功kubeconfig,要不然后面所有的功能都无法使用,所以这里直接报panic即可
    if err != nil {
        panic(err.Error())
    }

    // 2. 创建客户端工具 create the clientset
    clientset, err := kubernetes.NewForConfig(config)
    //这个客户端工具如果生成失败的话,后面的操作也无法完成,所以这里也报panic即可
    if err != nil {
        panic(err.Error())
    }

    //3. 操作集群



    // 查询资源详情 Get()方法
    // Get(ctx context.Context, name string, opts metav1.GetOptions) (*v1.Pod, error)
    poddetail, _ := clientset.CoreV1().Pods("h5-web").Get(context.TODO(), "web-864f4c6988-95sw4", metav1.GetOptions{})
    //fmt.Println("pod详情:", poddetail)
    //打印pod的镜像名称
    fmt.Println("pod第一个容器的镜像名称", poddetail.Spec.Containers[0].Image)

    //获取名称空间的详情
    namespace, _ := clientset.CoreV1().Namespaces().Get(context.TODO(), "h5-web", metav1.GetOptions{})
    fmt.Println("名称空间详情:", namespace)

}

其他资源查询方式类似,感兴趣的朋友可以尝试下

2.5 client-go更新资源功能

更新的前提是该字段是可更改的

比如这种can't be updated的字段,就不能被修改

注意:如果修改的字段在资源中不存在,比如labels 。修改时会报空指针错误,此时就要初始化下才能修改

go 复制代码
//更新资源操作  Update()
//先获取资源详情,再修改
//比如,我们修改service的暴露的端口号
service, _ := clientset.CoreV1().Services("h5-web").Get(context.TODO(), "web", metav1.GetOptions{})
fmt.Printf("service对外的端口号是 %d\n", service.Spec.Ports[0].NodePort)
// 修改端口号
// service-node-port-range 默认可以设置的范围 30000-32767
service.Spec.Ports[0].NodePort = 32050
// 修改暴露的端口号
// Update(ctx context.Context, service *v1.Service, opts metav1.UpdateOptions) (*v1.Service, error)
_, err = clientset.CoreV1().Services("h5-web").Update(context.TODO(), service, metav1.UpdateOptions{})
if err != nil {
    panic(err.Error())
}
fmt.Printf("修改后service对外的端口号是 %d\n", service.Spec.Ports[0].NodePort)

//修改deploy的副本数
deploy, _ := clientset.AppsV1().Deployments("h5-web").Get(context.TODO(), "web", metav1.GetOptions{})
//查看当前deploy的副本数
fmt.Println("当前deploy的副本数是:", *deploy.Spec.Replicas)
//修改副本数
replacs := int32(3)
//注意Replicas 是int32的指针类型
deploy.Spec.Replicas = &replacs

_, err = clientset.AppsV1().Deployments("h5-web").Update(context.TODO(), deploy, metav1.UpdateOptions{})
//查看修改后deploy的副本数
fmt.Println("修改后deploy的副本数是:", *deploy.Spec.Replicas)

完整代码:

go 复制代码
package main

import (
    "context"
    "fmt"
    metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    "k8s.io/client-go/kubernetes"
    "k8s.io/client-go/tools/clientcmd"
)

func main() {
    // 1. 初始化config实例
    // masterUrl就是离我们主节点的ip地址和端口号,我们在kubeconfig文件中有了,所以可以省略
    config, err := clientcmd.BuildConfigFromFlags("", "meta.kubeconfig")
    //要想正常应用我们的服务,必须能够实例化成功kubeconfig,要不然后面所有的功能都无法使用,所以这里直接报panic即可
    if err != nil {
        panic(err.Error())
    }

    // 2. 创建客户端工具 create the clientset
    clientset, err := kubernetes.NewForConfig(config)
    //这个客户端工具如果生成失败的话,后面的操作也无法完成,所以这里也报panic即可
    if err != nil {
        panic(err.Error())
    }

    //3. 操作集群


    //更新资源操作  Update()
    //先获取资源详情,再修改
    //比如,我们修改service的暴露的端口号
    service, _ := clientset.CoreV1().Services("h5-web").Get(context.TODO(), "web", metav1.GetOptions{})
    fmt.Printf("service对外的端口号是 %d\n", service.Spec.Ports[0].NodePort)
    // 修改端口号
    // service-node-port-range 默认可以设置的范围 30000-32767
    service.Spec.Ports[0].NodePort = 32050
    // 修改暴露的端口号
    // Update(ctx context.Context, service *v1.Service, opts metav1.UpdateOptions) (*v1.Service, error)
    _, err = clientset.CoreV1().Services("h5-web").Update(context.TODO(), service, metav1.UpdateOptions{})
    if err != nil {
        panic(err.Error())
    }
    fmt.Printf("修改后service对外的端口号是 %d\n", service.Spec.Ports[0].NodePort)

    //修改deploy的副本数
    deploy, _ := clientset.AppsV1().Deployments("h5-web").Get(context.TODO(), "web", metav1.GetOptions{})
    //查看当前deploy的副本数
    fmt.Println("当前deploy的副本数是:", *deploy.Spec.Replicas)
    //修改副本数
    replacs := int32(3)
    //注意Replicas 是int32的指针类型
    deploy.Spec.Replicas = &replacs

    _, err = clientset.AppsV1().Deployments("h5-web").Update(context.TODO(), deploy, metav1.UpdateOptions{})
    //查看修改后deploy的副本数
    fmt.Println("修改后deploy的副本数是:", *deploy.Spec.Replicas)
}

2.6 client-go删除资源

删除pod,比如我们将下列的pod删除

go 复制代码
//删除资源 Delete()
//删除pod
err = clientset.CoreV1().Pods("h5-web").Delete(context.TODO(), "web-864f4c6988-r456g", metav1.DeleteOptions{})
if err != nil {
    panic(err.Error())

可见pod已被删除

2.7 client-go创建资源

创建namespace

go 复制代码
//创建名称空间
var namespace corev1.Namespace
//创建namespace只需要传个名字就可以了
namespace.Name = "test1"
// Create(ctx context.Context, namespace *v1.Namespace, opts metav1.CreateOptions) (*v1.Namespace, error)
//传的是指针
_, err = clientset.CoreV1().Namespaces().Create(context.TODO(), &namespace, metav1.CreateOptions{})
if err != nil {
    panic(err.Error())
}

注意,这个corev1的导包,我们要根据Create()这个方法里面的导包,导过来,不要导错了,因为很多v1的包

创建deployment

注意,这个deployment的v1,

导包的时候,不要导错

传参时,要把必须得参数都传进去

可以通过这个命令来查看哪些是必须传的参数

bash 复制代码
kubectl create deployment mynginx --image="nginx" --dry-run=client -oyaml

它会导出一份yaml文件

go 复制代码
package main

import (
    "context"
    "fmt"
    deployv1 "k8s.io/api/apps/v1"
    corev1 "k8s.io/api/core/v1"
    metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    "k8s.io/client-go/kubernetes"
    "k8s.io/client-go/tools/clientcmd"
)

func main() {
    // 1. 初始化config实例
    // masterUrl就是离我们主节点的ip地址和端口号,我们在kubeconfig文件中有了,所以可以省略
    config, err := clientcmd.BuildConfigFromFlags("", "meta.kubeconfig")
    //要想正常应用我们的服务,必须能够实例化成功kubeconfig,要不然后面所有的功能都无法使用,所以这里直接报panic即可
    if err != nil {
        panic(err.Error())
    }

    // 2. 创建客户端工具 create the clientset
    clientset, err := kubernetes.NewForConfig(config)
    //这个客户端工具如果生成失败的话,后面的操作也无法完成,所以这里也报panic即可
    if err != nil {
        panic(err.Error())
    }

    //3. 操作集群
    //创建deployment
    var deploy deployv1.Deployment
    //给deployment传参,要把必须传的参数都传进去,不然创建会报错
    deploy.Name = "mydeploy"
    deploy.Namespace = "test1"

    //副本数
    replicas := int32(1)
    deploy.Spec.Replicas = &replicas

    //mathlabels
    labels := make(map[string]string)
    labels["app"] = "nginx"
    //Selector 是个指针类型,需要先初始化
    //Selector *metav1.LabelSelector `json:"selector" protobuf:"bytes,2,opt,name=selector"`
    deploy.Spec.Selector = &metav1.LabelSelector{}
    deploy.Spec.Selector.MatchLabels = labels
    //deployment label   metadata会将里面的资源发布出去,metadata可以省略掉
    deploy.Labels = labels

    // 创建template 这个template模版就是pod的模板
    // Template v1.PodTemplateSpec `json:"template" protobuf:"bytes,3,opt,name=template"`
    // 此时的labels要和selector的labels一样,否则deployment就无法管理到pod
    deploy.Spec.Template.ObjectMeta.Labels = labels

    //创建容器,容器是个切片,可以创建多个容器
    var containers []corev1.Container
    var container corev1.Container
    container.Name = "nginx"
    container.Image = "nginx:1.7.9"
    containers = append(containers, container)

    container.Name = "redis"
    container.Image = "redis:6-alpine"
    containers = append(containers, container)

    deploy.Spec.Template.Spec.Containers = containers

    _, err = clientset.AppsV1().Deployments("test1").Create(context.TODO(), &deploy, metav1.CreateOptions{})

}

查看创建的deploy

查看yaml文件,两个容器都创建成功

bash 复制代码
[root@master01 svc ]#kubectl get deploy -n test1 -oyaml
apiVersion: v1
items:
- apiVersion: apps/v1
  kind: Deployment
  metadata:
    annotations:
      deployment.kubernetes.io/revision: "1"
    creationTimestamp: "2024-11-05T09:58:43Z"
    generation: 1
    labels:
      app: nginx
    name: mydeploy
    namespace: test1
    resourceVersion: "1079640"
    uid: 8df3654a-71c6-45d5-888d-6329ed81dad7
  spec:
    progressDeadlineSeconds: 600
    replicas: 1
    revisionHistoryLimit: 10
    selector:
      matchLabels:
        app: nginx
    strategy:
      rollingUpdate:
        maxSurge: 25%
        maxUnavailable: 25%
      type: RollingUpdate
    template:
      metadata:
        creationTimestamp: null
        labels:
          app: nginx
      spec:
        containers:
        - image: nginx:1.7.9
          imagePullPolicy: IfNotPresent
          name: nginx
          resources: {}
          terminationMessagePath: /dev/termination-log
          terminationMessagePolicy: File
        - image: redis:6-alpine
          imagePullPolicy: IfNotPresent
          name: redis
          resources: {}
          terminationMessagePath: /dev/termination-log
          terminationMessagePolicy: File
        dnsPolicy: ClusterFirst
        restartPolicy: Always
        schedulerName: default-scheduler
        securityContext: {}
        terminationGracePeriodSeconds: 30
  status:
    conditions:
    - lastTransitionTime: "2024-11-05T09:58:43Z"
      lastUpdateTime: "2024-11-05T09:58:43Z"
      message: Deployment does not have minimum availability.
      reason: MinimumReplicasUnavailable
      status: "False"
      type: Available
    - lastTransitionTime: "2024-11-05T09:58:43Z"
      lastUpdateTime: "2024-11-05T09:58:43Z"
      message: ReplicaSet "mydeploy-68dcc7d46d" is progressing.
      reason: ReplicaSetUpdated
      status: "True"
      type: Progressing
    observedGeneration: 1
    replicas: 1
    unavailableReplicas: 1
    updatedReplicas: 1
kind: List
metadata:
  resourceVersion: ""

2.8 client-go使用json串创建资源

上面使用手动填值的方式创建,还是比较麻烦的,稍微有不注意的地方还容易犯错,因此,在前后端分离的项目中,在web页面,一般我们不悔通过手动填值进行创建资源。

一般我们会根据json串来创建资源

首先我们通过先通过kubectl命令导出json串

--dry-run 选项只能为 "none"、"server"、"client"三者中的一个,默认是none;当不加该参数,或者为none的时候,该操作后资源会生效 ,请求会被发送到kube-apiserver并做实际更改;

当该参数为client的时候,只打印该对象并不会发送请求且并不会实际创建该对象;

当该参数为server的时候,会发送请求到服务端,但是并不会实际创建该对象。

比如我们拿到一个创建deploy的json串

bash 复制代码
kubectl create deployment myredis --image="redis" --dry-run=client -ojson

我们将json串拿出来

yaml 复制代码
{
    "kind": "Deployment",
    "apiVersion": "apps/v1",
    "metadata": {
        "name": "myredis",
        "creationTimestamp": null,
        "labels": {
            "app": "myredis"
        }
    },
    "spec": {
        "replicas": 1,
        "selector": {
            "matchLabels": {
                "app": "myredis"
            }
        },
        "template": {
            "metadata": {
                "creationTimestamp": null,
                "labels": {
                    "app": "myredis"
                }
            },
            "spec": {
                "containers": [
                    {
                        "name": "redis",
                        "image": "redis",
                        "resources": {}
                    }
                ]
            }
        },
        "strategy": {}
    },
    "status": {}
}

使用json创建deploy完整代码:

go 复制代码
package main

import (
    "context"
    "encoding/json"
    "fmt"
    deployv1 "k8s.io/api/apps/v1"
    metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    "k8s.io/client-go/kubernetes"
    "k8s.io/client-go/tools/clientcmd"
)

func main() {
    // 1. 初始化config实例
    // masterUrl就是离我们主节点的ip地址和端口号,我们在kubeconfig文件中有了,所以可以省略
    config, err := clientcmd.BuildConfigFromFlags("", "meta.kubeconfig")
    //要想正常应用我们的服务,必须能够实例化成功kubeconfig,要不然后面所有的功能都无法使用,所以这里直接报panic即可
    if err != nil {
        panic(err.Error())
    }

    // 2. 创建客户端工具 create the clientset
    clientset, err := kubernetes.NewForConfig(config)
    //这个客户端工具如果生成失败的话,后面的操作也无法完成,所以这里也报panic即可
    if err != nil {
        panic(err.Error())
    }

    //3. 操作集群

    //通过json串创建k8s资源,注意多行字符串用反引号包裹
    deployJson := `{
    "kind": "Deployment",
    "apiVersion": "apps/v1",
    "metadata": {
        "name": "myredis",
        "creationTimestamp": null,
        "labels": {
            "app": "myredis"
        }
    },
    "spec": {
        "replicas": 1,
        "selector": {
            "matchLabels": {
                "app": "myredis"
            }
        },
        "template": {
            "metadata": {
                "creationTimestamp": null,
                "labels": {
                    "app": "myredis"
                }
            },
            "spec": {
                "containers": [
                    {
                        "name": "redis",
                        "image": "redis",
                        "resources": {}
                    }
                ]
            }
        },
        "strategy": {}
    },
    "status": {}
}`

    //我们需要将json串转换成deployv1类型的资源
    var redisdeploy deployv1.Deployment
    //将json转换成struct
    // func Unmarshal(data []byte, v any) error
    err = json.Unmarshal([]byte(deployJson), &redisdeploy)
    if err != nil {
        panic(err.Error())
    }
    fmt.Println("将json转换成的struct:", redisdeploy)
    //创建deploy
    _, err = clientset.AppsV1().Deployments("default").Create(context.TODO(), &redisdeploy, metav1.CreateOptions{})
    if err != nil {
        panic(err.Error())
    }

}

在K8S集群查看下,创建成功

三、总结

client-go是Kubernetes官方提供的Go客户端库,它封装了与Kubernetes API服务器的交互,提供了便捷的方式进行各种资源的管理。通过创建客户端并使用相应的API客户端进行操作,你可以轻松地进行Pod、Deployment、Service等资源的增删改查。

本文详细介绍了client-go的安装、配置和使用方法,并通过示例代码展示了如何进行常见的Kubernetes操作。希望这些内容能帮助大家更好地理解和使用client-go,从而提高你的Kubernetes开发效率。

相关推荐
l167751685443 分钟前
天翼云服务器失联排查完整报告_事件报告
运维·服务器·云原生·云计算
江华森1 小时前
K8s集群部署实验笔记:4节点Kubernetes v1.32.13 + Calico v3.29.3
kubernetes·k8s
念何架构之路1 小时前
Go依赖管理
开发语言·后端·golang
必胜刻1 小时前
Go 调用Coze工作流实现 AI 游戏生成
开发语言·ai·golang·gin
古城小栈2 小时前
k8s 存储练习
云原生·容器·kubernetes
无级程序员2 小时前
记一次K8S增加新节点
云原生·容器·kubernetes
仙柒41510 小时前
控制平面组件和节点组件
运维·容器·kubernetes
wb18914 小时前
Kubernetes服务优化
云原生·容器·kubernetes
码点滴16 小时前
Workload 自动化进化论:从手动运维到 AI 驱动的 Kubernetes 智能管控
运维·人工智能·kubernetes·自动化·workload
Waay18 小时前
图文详解|K8s Pod内部结构
docker·云原生·kubernetes