Go Client

在进入代码之前,理解k8s的go client项目是对我们又帮助的。它是k8s client中最古老的一个,因此具有很多特性。 Client-go 没有使用Swagger生成器,就像前面我们介绍的openAPI一样。它使用的是源于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(在这个例子中是在屏幕上通知显示)

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

连接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_通过提供实体功能来从不同的上下文中获取你的配置,从而使之成为一个不重要的任务。

从config文件

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

从集群

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

clientcmd.BuildConfigFromFlags("", "")

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

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

创建一个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()
}

获取集群的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())
}
}

监听集群中pvc

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

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

启动监听功能

现在让我们为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()
...
}

循环事件

接下来我们将要处理资源事件。但是在我们处理事件之前,我们先声明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类型,所以我们能从中提取出来。

处理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 ***")
}
}
...
}
}
}

处理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 ***")
}
}
...
}
}

运行程序

当程序在一个运行中的集群被执行的时候,首先会列出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 ***

client-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))
}

deployment

// 列出 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{})

Pod

不写命令空间  即列出所有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{})

statefulset

// 列出 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{})

service

// 列出 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{})

Ingress

// 列出 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](

Go语言中使用K8s API及一些常用API整理的更多相关文章

  1. C++ 中超类化和子类化常用API

    在windows平台上,使用C++实现子类化和超类化常用的API并不多,由于这些API函数的详解和使用方法,网上一大把.本文仅作为笔记,简单的记录一下. 子类化:SetWindowLong,GetWi ...

  2. C++ - C语言中数组的另一种常用写法(数组大小可变!!!)

    在 C 和 C++ 中,数组在声明过程中,数组名称为 const 指针,不许修改.且数组的大小在声明时被写死,非常不方便. C语言中常用下面代码替代指针. #include <stdio.h&g ...

  3. go语言中通过http访问需要认证的api

    func main() { //生成client 参数为默认 client := &http.Client{} //生成要访问的url url := "https://api.XXX ...

  4. Coursera-Getting and Cleaning Data-week4-R语言中的正则表达式以及文本处理

    博客总目录:http://www.cnblogs.com/weibaar/p/4507801.html Thursday, January 29, 2015 补上第四周笔记,以及本次课程总结. 第四周 ...

  5. PHP语言中使用JSON

    原文地址:http://www.ruanyifeng.com/blog/2011/01/json_in_php.html 在PHP语言中使用JSON 目前,JSON已经成为最流行的数据交换格式之一,各 ...

  6. .Net语言中关于AOP 的实现详解

    来源: IT人家  发布时间: 2011-03-22 20:28  阅读: 3546 次  推荐: 2   原文链接   [收藏] 摘要:该文章主要和大家讲解开发应用系统时在.Net语言中关于AOP ...

  7. Python Cookbook(第3版)中文版:15.19 从C语言中读取类文件对象

    15.19 从C语言中读取类文件对象¶ 问题¶ 你要写C扩展来读取来自任何Python类文件对象中的数据(比如普通文件.StringIO对象等). 解决方案¶ 要读取一个类文件对象的数据,你需要重复调 ...

  8. 世界上最好的语言搭建短链接及统计功能(附API代码)

    前言 在这个营销的时代,短链接和二维码是企业进行营销中非常重要的工具,不仅仅是缩短了链接,而且还可以通过扩展获得更多的数据,诸如点击数.下载量.来源以及时间等等. 网上搜寻了一下比较有名有U.NU和0 ...

  9. 『Python CoolBook』C扩展库_其六_从C语言中调用Python代码

    点击进入项目 一.C语言运行pyfun的PyObject对象 思路是在C语言中提供实参,传给python函数: 获取py函数对象(PyObject),函数参数(C类型) 获取GIL(PyGILStat ...

随机推荐

  1. Gym 101170I Iron and Coal(BFS + 思维)题解

    题意:有一个有向图,有些点是煤,有些点是铁,但不会同时有铁和煤.现在我要从1出发,占领可以到达的点.问最少占领几个点能同时拥有一个煤和一个铁(1不用占领). 思路:思路很秀啊.我们从1往外bfs,得到 ...

  2. sqli-libs(4) 双引号报错

    经测试,发现单引号不报错,而双引号却报错了 通过查看源码,发现下图中红色的箭头,如果不知道是什么意思,我们可以复制出来看看是什么含义: <?php$id=1;$id='"' .$id. ...

  3. Java中的Lambda匿名函数后续

    函数式编程(函数式接口):一个接口只包含一个方法实现 public interface Lambda{ void method(); } // 调用 Lambda lambda = new Lambd ...

  4. H5 Funny Games All In One

    H5 Funny Games All In One H5 游戏 盖楼 游戏 https://iamkun.github.io/tower_game/ https://github.com/iamkun ...

  5. vue template

    vue template <template> <div class="custom-class"> ... </div> </templ ...

  6. API 授权 All In One

    API 授权 All In One 身份验证 授权类型 身份验证类型 继承认证 没有认证 API密钥 不记名令牌 基本认证 摘要授权 OAuth 1.0 OAuth 2.0 授权码 隐含的 密码凭证 ...

  7. React + GraphQL 2020 速成课程

    React + GraphQL 2020 速成课程 technologies React (to build our user interface) GraphQL (to get and chang ...

  8. Gatsby Themes

    Gatsby Themes React & SSR gatsby-config.js refs https://www.gatsbyjs.com/docs/themes/ https://ww ...

  9. JavaScript & Automatic Semicolon Insertion

    JavaScript & Automatic Semicolon Insertion ECMA 262 真香警告️ https://www.ecma-international.org/ecm ...

  10. Dart All In One

    Dart All In One dart & flutter https://github.com/dart-lang https://github.com/dart-lang/sdk win ...