文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

使用Go构建Kubernetes应用

2024-12-03 11:34

关注

kubernetes 的Go Client项目(client-go)

go client是k8s client中最古老的一个,具有很多特性。Client-go 没有使用Swagger生成器,它使用的是源于k8s项目中的源代码生成工具,这个工具的目的是要生成k8s风格的对象和序列化程序。

该项目是一组包的集合,该包能够满足从REST风格的原语到复杂client的不同的编程需求。

RESTClient是一个基础包,它使用api-machinery库中的类型作为一组REST原语提供对API的访问。作为对RESTClient之上的抽象,_clientset_将是你创建k8s client工具的起点。它暴露了公开化的API资源及其对应的序列化。

注意:在 client-go中还包含了如discovery, dynamic, 和 scale这样的包,虽然本次不介绍这些包,但是了解它们的能力还是很重要的。

一个简单的k8s client 工具

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

你能在github上找到完整的例子

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

Setup

client-go支持Godep和dep作为vendor的管理程序,我觉得dep便于使用所以继续使用dep。例如,以下是client-go v6.0和k8s API v1.9所需最低限度的Gopkg.toml。 

  1. [[constraint]]  
  2.   name = "k8s.io/api"  
  3.   version = "kubernetes-1.9.0"  
  4. [[constraint]]  
  5.   name = "k8s.io/apimachinery"  
  6.   version = "kubernetes-1.9.0"  
  7. [[constraint]]  
  8.   name = "k8s.io/client-go"  
  9.   version = "6.0.0" 

运行dep ensure确保剩下的工作。

连接 API Server

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

  1. import (  
  2. ...  
  3.     "k8s.io/client-go/tools/clientcmd"  
  4.  
  5. func main() {  
  6.     kubeconfig :filepath.Join(  
  7.          os.Getenv("HOME"), ".kube", "config",  
  8.     )  
  9.     config, err :clientcmd.BuildConfigFromFlags("", kubeconfig)  
  10.     if err != nil {  
  11.         log.Fatal(err)  
  12.     }  
  13. ...  

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

从config文件

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

从集群

当你的代码运行在这个集群中的时候,你可以用上面的函数并且不使用任何参数,这个函数就会通过集群的信息去连接api server。

clientcmd.BuildConfigFromFlags("", "")

或者我们可以通过rest包来创建一个使用集群中的信息去配置启动的(译者注:k8s里所有的Pod都会以Volume的方式自动挂载k8s里面默认的ServiceAccount,所以会用默认的ServiceAccount的授权信息),如下: 

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

创建一个clientset

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

  1. type Clientset struct {  
  2.     *authenticationv1beta1.AuthenticationV1beta1Client  
  3.     *authorizationv1.AuthorizationV1Client  
  4. ...  
  5.     *corev1.CoreV1Client  

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

  1. func main() {  
  2.     config, err :clientcmd.BuildConfigFromFlags("", kubeconfig)  
  3.     ...  
  4.     clientset, err :kubernetes.NewForConfig(config)  
  5.     if err != nil {  
  6.         log.Fatal(err)  
  7.     }  

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

  1. func main() {  
  2.     ...  
  3.     clientset, err :kubernetes.NewForConfig(config)  
  4.     if err != nil {  
  5.         log.Fatal(err)  
  6.     }  
  7.     api :clientset.CoreV1()  

你能在这里看到可以获得clientsets。

获取集群的PVC列表

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

  1. import (  
  2. ...  
  3.     metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"  
  4.  
  5. func main() {  
  6.     var ns, label, field string  
  7.     flag.StringVar(&ns, "namespace", "", "namespace")  
  8.     flag.StringVar(&label, "l", "", "Label selector")  
  9.     flag.StringVar(&field, "f", "", "Field selector")  
  10. ...  
  11.     api :clientset.CoreV1()  
  12.     // setup list options  
  13.     listOptions :metav1.ListOptions{  
  14.         LabelSelector: label,   
  15.         FieldSelector: field,  
  16.     }  
  17.     pvcs, err :api.PersistentVolumeClaims(ns).List(listOptions)  
  18.     if err != nil {  
  19.         log.Fatal(err)  
  20.     }  
  21.     printPVCs(pvcs)  
  22. ...  

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

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

监听集群中pvc

k8s的Go client框架支持为指定的API对象在其生命周期事件中监听集群的能力,包括创建,更新,删除一个指定对象时候触发的CREATED,MODIFIED,DELETED事件。对于我们的命令行工具,我们将要监听在集群中已经声明的PVC的总量。

对于某一个namespace,当pvc的容量到达了某一个阈值(比如说200Gi),我们将会采取某个动作。为了简单起见,我们将要在屏幕上打印个通知。但是在更复杂的实现中,可以使用相同的办法触发一个自动操作。

启动监听功能

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

  1. func main() {  
  2. ...  
  3.     api :clientset.CoreV1()  
  4.     listOptions :metav1.ListOptions{  
  5.         LabelSelector: label,   
  6.         FieldSelector: field,  
  7.     }  
  8.     watcher, err :=api.PersistentVolumeClaims(ns). 
  9.        Watch(listOptions)  
  10.     if err != nil {  
  11.       log.Fatal(err)  
  12.     }  
  13.     ch :watcher.ResultChan()  
  14. ...  

循环事件

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

  1. import(  
  2.     "k8s.io/apimachinery/pkg/api/resource"  
  3.     ...  
  4.  
  5. func main() {  
  6.     var maxClaims string  
  7.     flag.StringVar(&maxClaims, "max-claims", "200Gi",   
  8.         "Maximum total claims to watch")  
  9.     var totalClaimedQuant resource.Quantity  
  10.     maxClaimedQuant :resource.MustParse(maxClaims)  
  11. ...  
  12.     ch :watcher.ResultChan()  
  13.     for event :range ch {  
  14.         pvc, ok :event.Object.(*v1.PersistentVolumeClaim)  
  15.         if !ok {  
  16.             log.Fatal("unexpected type")  
  17.         }  
  18.         ...  
  19.     }  

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

处理ADDED事件

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

  1. import(  
  2.     "k8s.io/apimachinery/pkg/watch"  
  3.     ...  
  4.  
  5. func main() {  
  6. ...  
  7.     for event :range ch {  
  8.         pvc, ok :event.Object.(*v1.PersistentVolumeClaim)  
  9.         if !ok {  
  10.             log.Fatal("unexpected type")  
  11.         }  
  12.         quant :pvc.Spec.Resources.Requests[v1.ResourceStorage]  
  13.         switch event.Type {  
  14.             case watch.Added:  
  15.                 totalClaimedQuant.Add(quant)  
  16.                 log.Printf("PVC %s added, claim size %s\n",  
  17.                     pvc.Name, quant.String())  
  18.                 if totalClaimedQuant.Cmp(maxClaimedQuant) == 1 {  
  19.                     log.Printf(  
  20.                         "\nClaim overage reached: max %s at %s",  
  21.                         maxClaimedQuant.String(),  
  22.                         totalClaimedQuant.String())  
  23.                     // trigger action  
  24.                     log.Println("*** Taking action ***")  
  25.                 }  
  26.             }  
  27.         ...  
  28.         }  
  29.     }  

处理DELETED事件

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

  1. func main() {  
  2. ...  
  3.     for event :range ch {  
  4.         ...  
  5.         switch event.Type {  
  6.         case watch.Deleted:  
  7.             quant :pvc.Spec.Resources.Requests[v1.ResourceStorage]  
  8.             totalClaimedQuant.Sub(quant)  
  9.             log.Printf("PVC %s removed, size %s\n",   
  10.                pvc.Name, quant.String())  
  11.             if totalClaimedQuant.Cmp(maxClaimedQuant) <= 0 { 
  12.                 log.Printf("Claim usage normal: max %s at %s",  
  13.                     maxClaimedQuant.String(),  
  14.                     totalClaimedQuant.String(),  
  15.                 )  
  16.                 // trigger action  
  17.                 log.Println("*** Taking action ***")  
  18.             }  
  19.         }  
  20.         ...  
  21.     }  

运行程序

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

  1. $> ./pvcwatch  
  2. Using kubeconfig:  /Users/vladimir/.kube/config  
  3. --- PVCs ----  
  4. NAME                            STATUS  CAPACITY  
  5. my-redis-redis                  Bound   50Gi  
  6. my-redis2-redis                 Bound   100Gi  
  7. -----------------------------  
  8. Total capacity claimed: 150Gi  
  9. -----------------------------  
  10. --- PVC Watch (max claims 200Gi) ----  
  11. 2018/02/13 21:55:03 PVC my-redis2-redis added, claim size 100Gi  
  12. 2018/02/13 21:55:03  
  13. At 50.0% claim capcity (100Gi/200Gi)  
  14. 2018/02/13 21:55:03 PVC my-redis-redis added, claim size 50Gi  
  15. 2018/02/13 21:55:03  
  16. At 75.0% claim capcity (150Gi/200Gi) 

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

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

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

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

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

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

总结

这篇文章是进行的系列的一部分,使用Go语言的官方k8s客户端与API server进行交互。和以前一样,这个代码会逐步的去实现一个命令行工具去监听指定namespace下面的PVC的大小。这个代码实现了一个简单的监听列表去触发从服务器返回的资源事件。 

 

来源:马哥Linux运维内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯