VB.net 2010 视频教程 VB.net 2010 视频教程 python基础视频教程
SQL Server 2008 视频教程 c#入门经典教程 Visual Basic从门到精通视频教程
当前位置:
首页 > 编程开发 > Java教程 >
  • client-go实战之三:Clientset

关于Clientset
本篇是《client-go实战》系列的第三篇,前文学习了最基础的客户端Restclient,尽管咱们实战的需求很简单(获取指定namespace下所有pod的信息),但还是写了不少代码,如下图,各种设置太麻烦,例如api的path、Group、Version、返回的数据结构、编解码工具等:
 
 
如果业务代码中,需要操作kubernetes资源的代码都写成上图的样子,相信您是难以忍受的,应该会做一些封装以简化代码,不过client-go已经给出了简化版客户端,就省去了咱们自己动手的麻烦,也就是本文的主题:Clientset
 
本篇概览
本篇目标是学习如何使用Clientset,因此毫无难度,咱们先来个源码速读,快速搞清楚Clientset到底是啥,然后确认需求,最后快速编码和验证就完事儿了;
源码速度
之所以是速读而非精度,是因为Clientset内容简单容易理解,快速掌握其原理即可用于实战;
Clientset源码阅读的切入点就是其名字中的set,这是个集合,里面有很多东西,看一下Clientset数据结构的源码(限于篇幅只展示了一部分):
type Clientset struct {
*discovery.DiscoveryClient
admissionregistrationV1      *admissionregistrationv1.AdmissionregistrationV1Client
admissionregistrationV1beta1 *admissionregistrationv1beta1.AdmissionregistrationV1beta1Client
internalV1alpha1             *internalv1alpha1.InternalV1alpha1Client
appsV1                       *appsv1.AppsV1Client
appsV1beta1                  *appsv1beta1.AppsV1beta1Client
appsV1beta2                  *appsv1beta2.AppsV1beta2Client
authenticationV1             *authenticationv1.AuthenticationV1Client
    ...
1
2
3
4
5
6
7
8
9
10
如果您还没有理解上述源码的含义,再请看下图,左边是kubernetes的Group、Version信息,右边依旧是Clientset数据结构的源码,通过箭头可见:kubernetes的Group和Version的每个组合,都对应Clientset数据结构的一个字段:
 
Clientset果然名副其实,是所有Group和Version组合对象的集合,不过Group和Version组合对象到底是啥呢?以appsV1字段为例,去看看其类型appsv1.AppsV1Client,如下图,AppsV1Client只有一字段,就是咱们熟悉的restClient,所以RESTClient是Clientset的基础,这话没毛病,另外注意红框2中的Deployments方法,返回的是DeploymentInterface接口实现:
 
顺藤摸瓜去看DeploymentInterface,打开deployment.go文件后真相大白,接口定义的和实现一目了然:
 
挑一个接口实现的代码看看,就选新建deployment的方法吧,如下,和我们使用RESTClient编码差不多:
func (c *deployments) Create(ctx context.Context, deployment *v1.Deployment, opts metav1.CreateOptions) (result *v1.Deployment, err error) {
result = &v1.Deployment{}
err = c.client.Post().
Namespace(c.ns).
Resource("deployments").
VersionedParams(&opts, scheme.ParameterCodec).
Body(deployment).
Do(ctx).
Into(result)
return
}
1
2
3
4
5
6
7
8
9
10
11
至此,您对Clientset应该心里有数了:其实就是把我们使用RESTClient操作资源的代码按照Group和Version分类再封装而已,这不像技术活,更像体力活—所以,这种体力活是谁做的呢?如下图红框所示,源码中已经注明这些代码是工具client-gen自动生成的:
 
至此,Clientset的源码速度就算完成了,咱们已经知道了Clientset的内幕,接下来开始尝试使用它;
需求确认
本次编码实战的需求如下:
写一段代码,检查用户输入的operate参数,该参数默认是create,也可以接受clean;
如果operate参数等于create,就执行以下操作:
新建名为test-clientset的namespace
新建一个deployment,namespace为test-clientset,镜像用tomcat,副本数为2
新建一个service,namespace为test-clientset,类型是NodePort
如果operate参数等于clean,就删除create操作中创建的service、deployment、namespace等资源:
以上需求使用Clientset客户端实现,完成后咱们用浏览器访问来验证tomcat是否正常;
源码下载
本篇实战中的源码可在GitHub下载到,地址和链接信息如下表所示(https://github.com/zq2599/blog_demos):
名称 链接 备注
项目主页 https://github.com/zq2599/blog_demos 该项目在GitHub上的主页
git仓库地址(https) https://github.com/zq2599/blog_demos.git 该项目源码的仓库地址,https协议
git仓库地址(ssh) git@github.com:zq2599/blog_demos.git 该项目源码的仓库地址,ssh协议
这个git项目中有多个文件夹,client-go相关的应用在client-go-tutorials文件夹下,如下图红框所示:
 
client-go-tutorials文件夹下有多个子文件夹,本篇对应的源码在clientsetdemo目录下,如下图红框所示:
 
编码
新建文件夹restclientdemo,在里面执行以下命令,新建module:
go mod init clientsetdemo
1
添加k8s.io/api和k8s.io/client-go这两个依赖,注意版本要匹配kubernetes环境:
go get k8s.io/api@v0.20.0
go get k8s.io/client-go@v0.20.0
1
2
新建main.go,内容如下,已经都添加了详细的注释,就不赘述了:
package main
 
import (
"context"
"flag"
"fmt"
appsv1 "k8s.io/api/apps/v1"
apiv1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/clientcmd"
"k8s.io/client-go/util/homedir"
"k8s.io/utils/pointer"
"path/filepath"
)
 
const (
NAMESPACE = "test-clientset"
DEPLOYMENT_NAME = "client-test-deployment"
SERVICE_NAME = "client-test-service"
)
 
func main() {
 
var kubeconfig *string
 
// home是家目录,如果能取得家目录的值,就可以用来做默认值
if home:=homedir.HomeDir(); home != "" {
// 如果输入了kubeconfig参数,该参数的值就是kubeconfig文件的绝对路径,
// 如果没有输入kubeconfig参数,就用默认路径~/.kube/config
kubeconfig = flag.String("kubeconfig", filepath.Join(home, ".kube", "config"), "(optional) absolute path to the kubeconfig file")
} else {
// 如果取不到当前用户的家目录,就没办法设置kubeconfig的默认目录了,只能从入参中取
kubeconfig = flag.String("kubeconfig", "", "absolute path to the kubeconfig file")
}
 
// 获取用户输入的操作类型,默认是create,还可以输入clean,用于清理所有资源
operate := flag.String("operate", "create", "operate type : create or clean")
 
flag.Parse()
 
// 从本机加载kubeconfig配置文件,因此第一个参数为空字符串
config, err := clientcmd.BuildConfigFromFlags("", *kubeconfig)
 
// kubeconfig加载失败就直接退出了
if err != nil {
panic(err.Error())
}
 
// 实例化clientset对象
clientset, err := kubernetes.NewForConfig(config)
 
if err!= nil {
panic(err.Error())
}
 
fmt.Printf("operation is %v\n", *operate)
 
// 如果要执行清理操作
if "clean"==*operate {
clean(clientset)
} else {
// 创建namespace
createNamespace(clientset)
 
// 创建deployment
createDeployment(clientset)
 
// 创建service
createService(clientset)
}
}
 
// 清理本次实战创建的所有资源
func clean(clientset *kubernetes.Clientset) {
emptyDeleteOptions := metav1.DeleteOptions{}
 
// 删除service
if err := clientset.CoreV1().Services(NAMESPACE).Delete(context.TODO(), SERVICE_NAME, emptyDeleteOptions) ; err != nil {
panic(err.Error())
}
 
// 删除deployment
if err := clientset.AppsV1().Deployments(NAMESPACE).Delete(context.TODO(), DEPLOYMENT_NAME, emptyDeleteOptions) ; err != nil {
panic(err.Error())
}
 
// 删除namespace
if err := clientset.CoreV1().Namespaces().Delete(context.TODO(), NAMESPACE, emptyDeleteOptions) ; err != nil {
panic(err.Error())
}
}
 
// 新建namespace
func createNamespace(clientset *kubernetes.Clientset) {
namespaceClient := clientset.CoreV1().Namespaces()
 
namespace := &apiv1.Namespace{
ObjectMeta: metav1.ObjectMeta{
Name: NAMESPACE,
},
}
 
result, err := namespaceClient.Create(context.TODO(), namespace, metav1.CreateOptions{})
 
if err!=nil {
panic(err.Error())
}
 
fmt.Printf("Create namespace %s \n", result.GetName())
}
 
// 新建service
func createService(clientset *kubernetes.Clientset) {
// 得到service的客户端
serviceClient := clientset.CoreV1().Services(NAMESPACE)
 
// 实例化一个数据结构
service := &apiv1.Service{
ObjectMeta: metav1.ObjectMeta{
Name: SERVICE_NAME,
},
Spec: apiv1.ServiceSpec{
Ports: []apiv1.ServicePort{{
Name: "http",
Port: 8080,
NodePort: 30080,
},
},
Selector: map[string]string{
"app" : "tomcat",
},
Type: apiv1.ServiceTypeNodePort,
},
}
 
result, err := serviceClient.Create(context.TODO(), service, metav1.CreateOptions{})
 
if err!=nil {
panic(err.Error())
}
 
fmt.Printf("Create service %s \n", result.GetName())
}
 
// 新建deployment
func createDeployment(clientset *kubernetes.Clientset) {
// 得到deployment的客户端
deploymentClient := clientset.
AppsV1().
Deployments(NAMESPACE)
 
// 实例化一个数据结构
deployment := &appsv1.Deployment{
ObjectMeta: metav1.ObjectMeta{
Name: DEPLOYMENT_NAME,
},
Spec: appsv1.DeploymentSpec{
Replicas: pointer.Int32Ptr(2),
Selector: &metav1.LabelSelector{
MatchLabels: map[string]string{
"app" : "tomcat",
},
},
 
Template: apiv1.PodTemplateSpec{
ObjectMeta:metav1.ObjectMeta{
Labels: map[string]string{
"app" : "tomcat",
},
},
Spec: apiv1.PodSpec{
Containers: []apiv1.Container{
{
Name: "tomcat",
Image: "tomcat:8.0.18-jre8",
ImagePullPolicy: "IfNotPresent",
Ports: []apiv1.ContainerPort{
{
Name: "http",
Protocol: apiv1.ProtocolSCTP,
ContainerPort: 8080,
},
},
},
},
},
},
},
}
 
result, err := deploymentClient.Create(context.TODO(), deployment, metav1.CreateOptions{})
 
if err!=nil {
panic(err.Error())
}
 
fmt.Printf("Create deployment %s \n", result.GetName())
}
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
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
数据结构初始化的烦恼
看过或者上述代码后您可能在烦恼:创建资源时,数据结构的字段太多太复杂根本记不住,对应的代码不好写,这里分享一个我的做法,如下图,我在开发的时候一共有两个窗口,左侧是官方的yaml示例,右侧用了GoLand的分屏功能,分屏的左侧是我写代码的窗口,右侧是数据结构定义,此时内容不会搞错,数据结构也能对应上,写起来就舒服多了:
 
验证
代码写完后,执行go run main.go,即可创建namespace、deployment、service等资源;
查看kubernetes上资源是否成功创建:
[root@hedy ~]# kubectl get pods -n test-clientset
NAME                                      READY   STATUS    RESTARTS   AGE
client-test-deployment-7677cc9669-kd7l7   1/1     Running   0          178m
client-test-deployment-7677cc9669-kt5rv   1/1     Running   0          178m
[root@hedy ~]# kubectl get service -n test-clientset
NAME                  TYPE       CLUSTER-IP       EXTERNAL-IP   PORT(S)          AGE
client-test-service   NodePort   10.109.189.151   <none>        8080:30080/TCP   178m
1
2
3
4
5
6
7
浏览器访问kubernetes服务器的30080端口,可见熟悉的tomcat首页:
 
 
执行命令go run main.go -operate clean即可删除刚才创建的所有资源;
 
至此Clientset的学习和实战就结束了,总得来说这是个大部分都是自动生成代码的客户端,逻辑简单容易理解,多用几次熟练后,就能随心所欲的操控kubernetes的资源了;
————————————————
版权声明:本文为CSDN博主「程序员欣宸」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/boling_cavalry/article/details/113788269出处:https://www.cnblogs.com/bolingcavalry/p/15232143.html

相关教程