Go语言中使用K8s API及一些常用API整理

 


1|0Go Client

在进入代码之前,理解k8s的go client项目是对我们又帮助的。它是k8s client中最古老的一个,因此具有很多特性。 Client-go 没有使用Swagger生成器,就像前面我们介绍的openAPI一样。它使用的是源于k8s项目中的源代码生成工具,这个工具的目的是要生成k8s风格的对象和序列化程序。
该项目是一组包的集合,该包能够满足从REST风格的原语到复杂client的不同的编程需求。

1

RESTClient是一个基础包,它使用api-machinery库中的类型作为一组REST原语提供对API的访问。作为对RESTClient之上的抽象,_clientset_将是你创建k8s client工具的起点。它暴露了公开化的API资源及其对应的序列化。
注意: 在 client-go中还包含了如discovery, dynamic, 和 scale这样的包,虽然本次不介绍这些包,但是了解它们的能力还是很重要的。

2|0一个简单的k8s client工具

让我们再次回顾我们将要构建的工具,来说明go client的用法。pvcwatch是一个简单的命令行工具,它可以监听集群中声明的PVC容量。当总数到达一个阈值的时候,他会采取一个action(在这个例子中是在屏幕上通知显示)

2

这个例子是为了展示k8s的go client的以下几个方面: - 如何去连接 - 资源列表的检索和遍历 - 对象监听

3|0连接API Server

我们Go client的第一步就是建立一个于API Server的连接。为了做到这一点,我们要使用实体包中的clientcmd,如下代码所示:

import ( ... "k8s.io/client-go/tools/clientcmd" ) func main() { kubeconfig := filepath.Join( os.Getenv("HOME"), ".kube", "config", ) config, err := clientcmd.BuildConfigFromFlags("", kubeconfig) if err != nil { log.Fatal(err) } ... }

_Client-go_通过提供实体功能来从不同的上下文中获取你的配置,从而使之成为一个不重要的任务。

3|1从config文件

正如上面的例子所做的那样,你能从kubeconfig文件启动配置来连接API server。当你的代码运行在集群之外的时候这是一个理想的方案。 clientcmd.BuildConfigFromFlags("", configFile)

3|2从集群

当你的代码运行在这个集群中的时候,你可以用上面的函数并且不使用任何参数,这个函数就会通过集群的信息去连接api server。
clientcmd.BuildConfigFromFlags("", "")
或者我们可以通过rest包来创建一个使用集群中的信息去配置启动的(译者注:k8s里所有的Pod都会以Volume的方式自动挂载k8s里面默认的ServiceAccount,所以会实用默认的ServiceAccount的授权信息),如下:

import "k8s.io/client-go/rest" ... rest.InClusterConfig()

3|3创建一个clientset

我们需要创建一个序列化的client为了让我们获取API对象。在kubernetes包中的Clientset类型定义,提供了去访问公开的API对象的序列化client,如下:

type Clientset struct { *authenticationv1beta1.AuthenticationV1beta1Client *authorizationv1.AuthorizationV1Client ... *corev1.CoreV1Client }

一旦我们有正确的配置连接,我们就能使用这个配置去初始化一个clientset,如下:

func main() { config, err := clientcmd.BuildConfigFromFlags("", kubeconfig) ... clientset, err := kubernetes.NewForConfig(config) if err != nil { log.Fatal(err) } }

对于我们的例子,我们使用的是v1的API对象。下一步,我们要使用clientset通过CoreV1()去访问核心api资源,如下:

func main() { ... clientset, err := kubernetes.NewForConfig(config) if err != nil { log.Fatal(err) } api := clientset.CoreV1() }

4|0获取集群的PVC列表

我们对clientset执行的最基本操作之一获取存储的API对象的列表。在我们的例子中,我们将要拿到一个namespace下面的pvc列表,如下:

import ( ... metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) func main() { var ns, label, field string flag.StringVar(&ns, "namespace", "", "namespace") flag.StringVar(&label, "l", "", "Label selector") flag.StringVar(&field, "f", "", "Field selector") ... api := clientset.CoreV1() // setup list options listOptions := metav1.ListOptions{ LabelSelector: label, FieldSelector: field, } pvcs, err := api.PersistentVolumeClaims(ns).List(listOptions) if err != nil { log.Fatal(err) } printPVCs(pvcs) ... }

在上面的代码中,我们使用ListOptions指定 label 和 field selectors (还有namespace)来缩小pvc列表的范围,这个结果的返回类型是v1.PeristentVolumeClaimList。下面的这个代码展示了我们如何去遍历和打印从api server中获取的pvc列表。

func printPVCs(pvcs *v1.PersistentVolumeClaimList) { template := "%-32s%-8s%-8s\n" fmt.Printf(template, "NAME", "STATUS", "CAPACITY") for _, pvc := range pvcs.Items { quant := pvc.Spec.Resources.Requests[v1.ResourceStorage] fmt.Printf( template, pvc.Name, string(pvc.Status.Phase), quant.String()) } }

4|1监听集群中pvc

k8s的Go client框架支持为指定的API对象在其生命周期事件中监听集群的能力,包括创建,更新,删除一个指定对象时候触发的CREATED,MODIFIED,DELETED事件。对于我们的命令行工具,我们将要监听在集群中已经声明的PVC的总量。
对于某一个namespace,当pvc的容量到达了某一个阈值(比如说200Gi),我们将会采取某个动作。为了简单起见,我们将要在屏幕上打印个通知。但是在更复杂的实现中,可以使用相同的办法触发一个自动操作。

4|2启动监听功能

现在让我们为PersistentVolumeClaim这个资源通过Watch去创建一个监听器。然后这个监听器通过ResultChan从go的channel中访问事件通知。

func main() { ... api := clientset.CoreV1() listOptions := metav1.ListOptions{ LabelSelector: label, FieldSelector: field, } watcher, err :=api.PersistentVolumeClaims(ns). Watch(listOptions) if err != nil { log.Fatal(err) } ch := watcher.ResultChan() ... }

4|3循环事件

接下来我们将要处理资源事件。但是在我们处理事件之前,我们先声明resource.Quantity类型的的两个变量为maxClaimsQuant和totalClaimQuant来分别表示我们的申请资源阈值(译者注:代表某个ns下集群中运行的PVC申请的上限)和运行总数。

import( "k8s.io/apimachinery/pkg/api/resource" ... ) func main() { var maxClaims string flag.StringVar(&maxClaims, "max-claims", "200Gi", "Maximum total claims to watch") var totalClaimedQuant resource.Quantity maxClaimedQuant := resource.MustParse(maxClaims) ... ch := watcher.ResultChan() for event := range ch { pvc, ok := event.Object.(*v1.PersistentVolumeClaim) if !ok { log.Fatal("unexpected type") } ... } }

在上面的for-range循环中,watcher的channel用于处理来自服务器传入的通知。每个事件赋值给变量event,并且event.Object的类型被声明为PersistentVolumeClaim类型,所以我们能从中提取出来。

4|4处理ADDED事件

当一个新的PVC创建的时候,event.Type的值被设置为watch.Added。然后我们用下面的代码去获取新增的声明的容量(quant),将其添加到正在运行的总容量中(totalClaimedQuant)。最后我们去检查是否当前的容量总值大于当初设定的最大值(maxClaimedQuant),如果大于的话我们就触发一个事件。

import( "k8s.io/apimachinery/pkg/watch" ... ) func main() { ... for event := range ch { pvc, ok := event.Object.(*v1.PersistentVolumeClaim) if !ok { log.Fatal("unexpected type") } quant := pvc.Spec.Resources.Requests[v1.ResourceStorage] switch event.Type { case watch.Added: totalClaimedQuant.Add(quant) log.Printf("PVC %s added, claim size %s\n", pvc.Name, quant.String()) if totalClaimedQuant.Cmp(maxClaimedQuant) == 1 { log.Printf( "\nClaim overage reached: max %s at %s", maxClaimedQuant.String(), totalClaimedQuant.String()) // trigger action log.Println("*** Taking action ***") } } ... } } }

4|5处理DELETED事件

代码也会在PVC被删除的时候做出反应,它执行相反的逻辑以及把被删除的这个PVC申请的容量在正在运行的容量的总值里面减去。

func main() { ... for event := range ch { ... switch event.Type { case watch.Deleted: quant := pvc.Spec.Resources.Requests[v1.ResourceStorage] totalClaimedQuant.Sub(quant) log.Printf("PVC %s removed, size %s\n", pvc.Name, quant.String()) if totalClaimedQuant.Cmp(maxClaimedQuant) <= 0 { log.Printf("Claim usage normal: max %s at %s", maxClaimedQuant.String(), totalClaimedQuant.String(), ) // trigger action log.Println("*** Taking action ***") } } ... } }

5|0运行程序

当程序在一个运行中的集群被执行的时候,首先会列出PVC的列表。然后开始监听集群中新的PersistentVolumeClaim事件。

$> ./pvcwatch Using kubeconfig: /Users/vladimir/.kube/config --- PVCs ---- NAME STATUS CAPACITY my-redis-redis Bound 50Gi my-redis2-redis Bound 100Gi ----------------------------- Total capacity claimed: 150Gi ----------------------------- --- PVC Watch (max claims 200Gi) ---- 2018/02/13 21:55:03 PVC my-redis2-redis added, claim size 100Gi 2018/02/13 21:55:03 At 50.0% claim capcity (100Gi/200Gi) 2018/02/13 21:55:03 PVC my-redis-redis added, claim size 50Gi 2018/02/13 21:55:03 At 75.0% claim capcity (150Gi/200Gi)

下面让我们部署一个应用到集群中,这个应用会申请75Gi容量的存储。(例如,让我们通过helm去部署一个实例influxdb)。

helm install --name my-influx \ --set persistence.enabled=true,persistence.size=75Gi stable/influxdb

正如下面你看到的,我们的工具立刻反应出来有个新的声明以及一个警告因为当前的运行的声明总量已经大于我们设定的阈值。

--- PVC Watch (max claims 200Gi) ---- ... 2018/02/13 21:55:03 At 75.0% claim capcity (150Gi/200Gi) 2018/02/13 22:01:29 PVC my-influx-influxdb added, claim size 75Gi 2018/02/13 22:01:29 Claim overage reached: max 200Gi at 225Gi 2018/02/13 22:01:29 *** Taking action *** 2018/02/13 22:01:29 At 112.5% claim capcity (225Gi/200Gi)

相反,从集群中删除一个PVC的时候,该工具会相应展示提示信息。

... At 112.5% claim capcity (225Gi/200Gi) 2018/02/14 11:30:36 PVC my-redis2-redis removed, size 100Gi 2018/02/14 11:30:36 Claim usage normal: max 200Gi at 125Gi 2018/02/14 11:30:36 *** Taking action ***

6|0client-go常用api

Example1

import ( "k8s.io/client-go/tools/clientcmd" "k8s.io/client-go/kubernetes" appsv1beta1 "k8s.io/api/apps/v1beta1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" apiv1 "k8s.io/api/core/v1" "k8s.io/client-go/kubernetes/typed/apps/v1beta1" "flag" "fmt" "encoding/json" ) func main() { //kubelet.kubeconfig 是文件对应地址 kubeconfig := flag.String("kubeconfig", "kubelet.kubeconfig", "(optional) absolute path to the kubeconfig file") flag.Parse() // 解析到config config, err := clientcmd.BuildConfigFromFlags("", *kubeconfig) if err != nil { panic(err.Error()) } // 创建连接 clientset, err := kubernetes.NewForConfig(config) if err != nil { panic(err.Error()) } deploymentsClient := clientset.AppsV1beta1().Deployments(apiv1.NamespaceDefault) //创建deployment go createDeployment(deploymentsClient) //监听deployment startWatchDeployment(deploymentsClient) } //监听Deployment变化 func startWatchDeployment(deploymentsClient v1beta1.DeploymentInterface) { w, _ := deploymentsClient.Watch(metav1.ListOptions{}) for { select { case e, _ := <-w.ResultChan(): fmt.Println(e.Type, e.Object) } } } //创建deployemnt,需要谨慎按照部署的k8s版本来使用api接口 func createDeployment(deploymentsClient v1beta1.DeploymentInterface) { var r apiv1.ResourceRequirements //资源分配会遇到无法设置值的问题,故采用json反解析 j := `{"limits": {"cpu":"2000m", "memory": "1Gi"}, "requests": {"cpu":"2000m", "memory": "1Gi"}}` json.Unmarshal([]byte(j), &r) deployment := &appsv1beta1.Deployment{ ObjectMeta: metav1.ObjectMeta{ Name: "engine", Labels: map[string]string{ "app": "engine", }, }, Spec: appsv1beta1.DeploymentSpec{ Replicas: int32Ptr2(1), Template: apiv1.PodTemplateSpec{ ObjectMeta: metav1.ObjectMeta{ Labels: map[string]string{ "app": "engine", }, }, Spec: apiv1.PodSpec{ Containers: []apiv1.Container{ { Name: "engine", Image: "my.com/engine:v2", Resources: r, }, }, }, }, }, } fmt.Println("Creating deployment...") result, err := deploymentsClient.Create(deployment) if err != nil { panic(err) } fmt.Printf("Created deployment %q.\n", result.GetObjectMeta().GetName()) } func int32Ptr2(i int32) *int32 { return &i }

Example2

package main import ( "flag" "fmt" "k8s.io/client-go/1.4/kubernetes" "k8s.io/client-go/1.4/pkg/api" "k8s.io/client-go/1.4/pkg/api/unversioned" "k8s.io/client-go/1.4/pkg/api/v1" "k8s.io/client-go/1.4/tools/clientcmd" "log" ) var ( kubeconfig = flag.String("kubeconfig", "./config", "absolute path to the kubeconfig file") ) func main() { flag.Parse() // uses the current context in kubeconfig config, err := clientcmd.BuildConfigFromFlags("116.213.205.180:8080", *kubeconfig) if err != nil { panic(err.Error()) } // creates the clientset clientset, err := kubernetes.NewForConfig(config) if err != nil { panic(err.Error()) } // 创建pod pod := new(v1.Pod) pod.TypeMeta = unversioned.TypeMeta{Kind: "Pod", APIVersion: "v1"} pod.ObjectMeta = v1.ObjectMeta{Name: "testapi", Namespace: "default", Labels: map[string]string{"name": "testapi"}} pod.Spec = v1.PodSpec{ RestartPolicy: v1.RestartPolicyAlways, Containers: []v1.Container{ v1.Container{ Name: "testapi", Image: "nginx", Ports: []v1.ContainerPort{ v1.ContainerPort{ ContainerPort: 80, Protocol: v1.ProtocolTCP, }, }, }, }, } _, err = clientset.Core().Pods("default").Create(pod) if err != nil { panic(err.Error()) } // 获取现有的pod数量 pods, err := clientset.Core().Pods("").List(api.ListOptions{}) if err != nil { panic(err.Error()) } fmt.Printf("There are %d pods in the cluster\n", len(pods.Items)) // 创建namespace nc := new(v1.Namespace) nc.TypeMeta = unversioned.TypeMeta{Kind: "NameSpace", APIVersion: "v1"} nc.ObjectMeta = v1.ObjectMeta{ Name: "k8s-test", } nc.Spec = v1.NamespaceSpec{} _, err = clientset.Core().Namespaces().Create(nc) if err != nil { log.Println(err) } // 获取namespace namespaces, err := clientset.Core().Namespaces().List(api.ListOptions{}) if err != nil { panic(err.Error()) } fmt.Printf("There are %d namespaces in the cluster\n", len(namespaces.Items)) }

6|1deployment

// 列出 deploymentList deploymentList, err := clientset.AppsV1().Deployments("default").List(metav1.ListOptions{}) // 查询 deploymentGet deploymentGet, err := clientset.AppsV1().Deployments("default").Get("nginx-web-v2", metav1.GetOptions{}) // 创建 deploymentCreate deploymentCreate, err := clientset.AppsV1().Deployments("default").Create(deploymentName) // 更新 deploymentUpdate deploymentUpdate, err := clientset.AppsV1().Deployments("default").Update(deploymentName) // 删除deployment err = clientset.AppsV1().Deployments("default").Delete("deploymentName", &metav1.DeleteOptions{})

6|2Pod

不写命令空间 即列出所有pod //列出pod podList, err := clientset.CoreV1().Pods("default").List(&meta_v1.ListOptions{}) //查询pod pod, err := clientset.CoreV1().Pods("default").Get(<podName>, meta_v1.GetOptions{}) //创建pod pod, err := clientset.CoreV1().Pods("default").Create(web) //更新pod pod, err := clientset.CoreV1().Pods("default").Update(web) //删除pod err := clientset.CoreV1().Pods("default").Delete(<podName>, &meta_v1.DeleteOptions{})

6|3statefulset

// 列出 statefulList statefulList, err := clientset.AppsV1().StatefulSets("default").List(metav1.ListOptions{}) // 查询 statefulGet statefulGet, err := clientset.AppsV1().StatefulSets("default").Get("web", metav1.GetOptions{}) // 创建 statefulCreate statefulCreate, err := clientset.AppsV1().StatefulSets("default").Create(statefulName) // 更新 statefulUpdate statefulUpdate, err := clientset.AppsV1().StatefulSets("default").Update(statefulName) // 删除 stateful err = clientset.AppsV1().StatefulSets("default").Delete("statefulName", &metav1.DeleteOptions{})

6|4service

// 列出 serviceList serviceList, err := clientset.CoreV1().Services("default").List(metav1.ListOptions{}) // 查询 serviceGet serviceGet, err := clientset.CoreV1().Services("default").Get("web", metav1.GetOptions{}) // 创建 serviceCreate serviceCreate, err := clientset.CoreV1().Services("default").Create(web) // 更新 serviceUpdate serviceUpdate, err := clientset.CoreV1().Services("default").Update(web) // 删除 service err = clientset.CoreV1().Services.("default").Delete("serviceName", &metav1.DeleteOptions{})

6|5Ingress

// 列出 ingressList ingressList, err := clientset.ExtensionsV1beta1().Ingresses("default").List(metav1.ListOptions{}) // 查询 ingressGet ingressGet, err := clientset.ExtensionsV1beta1().Ingresses("default").Get("web", metav1.GetOptions{}) // 创建 ingressCreate ingressCreate, err := clientset.ExtensionsV1beta1().Ingresses("default").Create(web) // 更新 ingressUpdate ingressUpdate, err := clientset.ExtensionsV1beta1().Ingresses("default").Update(web) // 删除 ingress err = clientset.ExtensionsV1beta1().Ingresses("default").Delete("web", &metav1.DeleteOptions{})

参考:
[https://zhuanlan.zhihu.com/p/76706821](


__EOF__

本文作者一入IT深似海,从此妹子是路人
本文链接https://www.cnblogs.com/you-men/p/14551630.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   常见-youmen  阅读(6723)  评论(1编辑  收藏  举报
编辑推荐:
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· AI与.NET技术实操系列(六):基于图像分类模型对图像进行分类
点击右上角即可分享
微信分享提示