Go调用K8S

前言

使用Go创建k8s的客户端和k8s API server进行交互是非常简便的,本文将进行简要的讲解

拉取依赖

1
2
go get k8s.io/client-go@v0.18.11-rc.0
go get k8s.io/api@v0.18.11-rc.0

创建客户端

1
2
3
4
5
6
7
const (
server = "https://127.0.0.1:8080"
token = "xxxx"
)
var(
Client *kubernetes.Clientset
)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
func InitK8sClient() *kubernetes.Clientset {
tokenBytes, err := base64.StdEncoding.DecodeString(token) // 解析token
if err != nil {
panic("token 解析失败")
}
config := &rest.Config{
BearerToken: string(tokenBytes), // k8s的token
Host: server, // k8s的host
TLSClientConfig: rest.TLSClientConfig{
Insecure: true,
},
}
// 根据指定的 config 创建一个新的 clientSet
clientSet, err := kubernetes.NewForConfig(config)
if err != nil {
panic(err.Error())
}
return clientSet
}

获取deployment

使用API获取deployment

1
2
3
4
5
6
7
8
9
// 获取deployment
// 指定namespace为test_namespace
deployments, err := Client.AppsV1().Deployments("test_namespace").List(context.TODO(), meta_v1.ListOptions{})
if err != nil {
panic(err)
}
for _, v := range deployments.Items {
fmt.Println(v.Name)
}

使用informer获取deployment

informer

为什么使用informer?

使用 Informer 实例的 Lister() 方法, List/Get Kubernetes 中的 Object 时,Informer 不会去请求 Kubernetes API,而是直接查找缓存在本地内存中的数据(这份数据由 Informer 自己维护)。通过这种方式,Informer 既可以更快地返回结果,又能减少对 Kubernetes API 的直接调用

informer的机制

Informer 只会调用 Kubernetes List 和 Watch 两种类型的 API。Informer 在初始化的时,先调用 Kubernetes List API 获得某种 resource 的全部 Object,缓存在内存中; 然后,调用 Watch API 去 watch 这种 resource,去维护这份缓存; 最后,Informer 就不再调用 Kubernetes 的任何 API。

可监听并触发回调函数

Informer 通过 Kubernetes Watch API 监听某种 resource 下的所有事件。而且,Informer 可以添加自定义的回调函数,这个回调函数实例(即 ResourceEventHandler 实例)只需实现 OnAdd(obj interface{})OnUpdate(oldObj, newObj interface{})OnDelete(obj interface{}) 三个方法,这三个方法分别对应 informer 监听到创建、更新和删除这三种事件类型。

二级缓存

二级缓存属于 Informer 的底层缓存机制,这两级缓存分别是 DeltaFIFOLocalStore

Delta代表变化, FIFO则是先入先出的队列。DeltaFIFO将接受来的资源event,转化为特定的变化类型,存储在队列中,周期性的POP出去,分发到事件处理器,并更新Indexer中的本地缓存

这两级缓存的用途各不相同。DeltaFIFO 用来存储 Watch API 返回的各种事件 ,LocalStore 只会被 Lister 的 List/Get 方法访问 。

虽然 Informer 和 Kubernetes 之间没有 resync 机制,但 Informer 内部的这两级缓存之间存在 resync 机制。

详细参考资料

informer详解1

informer详解2

goalng中使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
var DeploymentInformer cache.SharedIndexInformer

stop := make(chan struct{})
//创建factory,第一个参数是与k8s-api-server交互的客户端,第二个参数用于设置多久进行一次resync(周期性的List操作)
factory := informers.NewSharedInformerFactoryWithOptions(Client, time.Second*10)
DeploymentInformer = factory.Apps().V1().Deployments().Informer() // 创建deployment的informer

//通过informer.Run(stop)运行该informer,它是一个持久化的goroutine,通过client对象与k8s-api-server交互。
//chan_stop用于在程序进程退出前通知Informer退出
go func() {
DeploymentInformer.Run(stop)
}()

//需要先运行informer监听后才能获取列表
list := DeploymentInformer.GetStore().List()
for _, v := range list {
deployment := v.(*apps_v1.Deployment)
fmt.Println(deployment.Name)
}
select {}

创建deployment

说明

label

Label是Kubernetes系列中另外一个核心概念。是一组绑定到K8s资源对象上的key/value对。同一个对象的labels属性的key必须唯一。label可以附加到各种资源对象上,如Node,Pod,Service,RC等。

通过给指定的资源对象捆绑一个或多个不用的label来实现多维度的资源分组管理功能,以便于灵活,方便地进行资源分配,调度,配置,部署等管理工作

示例如下: 

  • 版本标签:”release” : “stable” , “release” : “canary”…
  • 环境标签:”environment” : “dev” , “environment” : “production”
  • 架构标签:”tier” : “frontend” , “tier” : “backend” , “tier” : “middleware”
  • 分区标签:”partition” : “customerA” , “partition” : “customerB”…
  • 质量管控标签:”track” : “daily” , “track” : “weekly”

namespace

​ namespaces是kubernetes集群中的虚拟化集群。在一个Kubernetes集群中可以拥有多个命名空间,它们在逻辑上彼此隔离。 他们可以为个人和团队提供组织,安全甚至性能方面的帮助。

​ 命名空间适用于存在很多跨多个团队或项目的用户的场景。对于只有几到几十个用户的集群,根本不需要创建或考虑命名空间。当需要名称空间提供的功能时,请开始使用它们。
命名空间为名称提供了一个范围。资源的名称需要在命名空间内是唯一的,但不能跨命名空间。命名空间不能相互嵌套,每个 Kubernetes 资源只能在一个命名空间中
​ 命名空间是在多个用户之间划分集群资源的一种方法(通过资源配额)。
​ 在 Kubernetes 未来版本中,相同命名空间中的对象默认将具有相同的访问控制策略。
​ 不需要使用多个命名空间来分隔轻微不同的资源,例如同一软件的不同版本:使用 labels来区分同一命名空间中的不同资源

selector

K8S中的Service是一个抽象概念,它定义了一个服务的多个pod逻辑合集和访问pod的策略,一般把service称为微服务

举个例子:一个a服务运行3个pod,b服务怎么访问a服务的pod,pod的ip都不是持久化的重启之后就会有变化
这时候b服务可以访问跟a服务绑定的service,service信息是固定的提前告诉b就行了,service通过Label Selector跟a服务的pod绑定,无论a的pod如何变化对b来说都是透明的。

ImagePullPolicy

  • Allow:总是拉取pull
  • ifNotPresent:本地有镜像则优先使用本地
  • Never:只是用本地镜像,从不拉取

代码内书写yaml进行创建

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63


func CreateDeployment() error {

deployment := &apps_v1.Deployment{
ObjectMeta: meta_v1.ObjectMeta{
Name: "xxx-name",
},
Spec: apps_v1.DeploymentSpec{
Replicas: int32Ptr(2),
Selector: &meta_v1.LabelSelector{
MatchLabels: map[string]string{
"app": "xxx-name",
},
},
Template: core_v1.PodTemplateSpec{
ObjectMeta: meta_v1.ObjectMeta{
Labels: map[string]string{
"app": "xxx-name",
},
},
Spec: core_v1.PodSpec{
Containers: []core_v1.Container{
{
Name: "test-a",
Image: "image_a:1.1.0",
ImagePullPolicy: "IfNotPresent",
Ports: []core_v1.ContainerPort{
{
Name: "http",
Protocol: core_v1.ProtocolTCP,
ContainerPort: 8080,
},
},
},
{
Name: "test-b",
Image: "image_b:1.1.0",
ImagePullPolicy: "IfNotPresent",
Ports: []core_v1.ContainerPort{
{
Name: "http",
Protocol: core_v1.ProtocolTCP,
ContainerPort: 8081,
},
},
},
},
},
},
},
}
// 创建deployment
// 指定namespace为test_namespace
if _, err := Client.AppsV1().Deployments("test_namespace").Create(context.TODO(), deployment, meta_v1.CreateOptions{}); err != nil {
return err
}
return nil
}

func int32Ptr(i int32) *int32 {
return &i
}

获取configmap

ConfigMap是一种API对象,用来将非加密数据保存到键值对中。可以用作环境变量、命令行参数或者存储卷中的配置文件。

ConfigMap可以将环境变量配置信息和容器镜像解耦,便于应用配置的修改。如果需要存储加密信息时可以使用Secret对象

k8s中configmap的增删查改

golang中使用

1
2
3
4
5
6
7
8
data, err := Client.CoreV1().ConfigMaps("namespace_name").Get(context.TODO(), "configmap_name", meta_v1.GetOptions{})
if err != nil {
fmt.Println("err:", err)
return
}
for key,value:=range data.Data{
fmt.Println(key,":",value)
}

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!