使用client-go 创建应用

  1. 1. About client-go
    1. 1.1. 创建Deployment
    2. 1.2. 创建Service
  2. 2. 添加更新逻辑
  3. 3. 创建对应的clientset
  4. 4. 创建kubetoken

About client-go

client-go是调用kubernetes集群资源对象API的客户端,kubernetes官方抽取出部分核心代码作为提供的client。利用client-go可以对kubernetes集群中的各种对象进行增删改查等操作。

具体用法参考client-go

创建Deployment

随便举个例子,过程相对简单,直接使用appsv1.Deployment结构体,填入我们需要的部分参数。在我们的需求中,deployment的一切属性都可以在结构体中找到,按需求将对应的属性填好。然后直接调用client创建即可。

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
import (
appsv1 "k8s.io/api/apps/v1"
apiv1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"context"
)

func createDeployment(clientset *kubernetes.Clientset) error {

labels := map[string]string{
"app.kubernetes.io/env": "dev",
"app.kubernetes.io/name": "nginx",
"app.kubernetes.io/version": "base",
},

replicas := 1

var revisionhistorylimit int32 = 1

imagepullsecret := []apiv1.LocalObjectReference{
apiv1.LocalObjectReference{Name: "acr-registry-token"},
}


deployment = &appsv1.Deployment{
ObjectMeta: metav1.ObjectMeta{
Name: "test-nginx",
Namespace: "dev",
Labels:
},
Spec: appsv1.DeploymentSpec{
Replicas: &replicas,
Selector: &metav1.LabelSelector{
MatchLabels: labels,
},
RevisionHistoryLimit: &revisionhistorylimit,
Template: apiv1.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Labels: labels,
},
Spec: apiv1.PodSpec{
Volumes: []apiv1.Volume{
apiv1.Volume{
Name: "timezone",
VolumeSource: apiv1.VolumeSource{
ConfigMap: &apiv1.ConfigMapVolumeSource{
LocalObjectReference: apiv1.LocalObjectReference{
Name: "timezone",
},
},
},
},
},
Containers: []apiv1.Container{
apiv1.Container{
Name: "web-service",
Image: fmt.Sprintf("%s:%s", image, tag),
Ports: []apiv1.ContainerPort{
apiv1.ContainerPort{
Name: "web",
ContainerPort: 80,
Protocol: "TCP",
},
},
Resources: apiv1.ResourceRequirements{
Requests: apiv1.ResourceList{
apiv1.ResourceCPU: resource.MustParse("100m"),
apiv1.ResourceMemory: resource.MustParse("256Mi"),
},
Limits: apiv1.ResourceList{
apiv1.ResourceMemory: resource.MustParse("1024Mi"),
},
},
VolumeMounts: []apiv1.VolumeMount{
apiv1.VolumeMount{
Name: "timezone",
MountPath: "/etc/localtime",
SubPath: "Shanghai",
ReadOnly: true,
},
},
LivenessProbe: &apiv1.Probe{
ProbeHandler: apiv1.ProbeHandler{
TCPSocket: &apiv1.TCPSocketAction{
Port: intstr.FromInt(80),
},
},
InitialDelaySeconds: 60,
PeriodSeconds: 15,
},
ImagePullPolicy: "Always",
},
},
ImagePullSecrets: imagepullsecret,
},
},
},
}

client := clientset.AppsV1().Deployments("dev")

// 创建应用
result, err := clientset.AppsV1().Deployments(env).Create(context.TODO(), newappdeploy, metav1.CreateOptions{})
if err != nil {
return err
}
fmt.Printf("Created deployment %q. \n", result.GetObjectMeta().GetName())
}

创建Service

deployment同理,service字段较少,直接创建即可。

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
func createServiceIfNotExists(clientset *kubernetes.Clientset) (err error) {
labels := map[string]string{
"app.kubernetes.io/env": "dev",
"app.kubernetes.io/name": "nginx",
"app.kubernetes.io/version": "base",
}

containerport = intstr.FromInt(80)

service = &apiv1.Service{
ObjectMeta: metav1.ObjectMeta{
Name: "test-svc",
Namespace: "dev",
Labels: labels,
},
Spec: apiv1.ServiceSpec{
Selector: labels,
Ports: []apiv1.ServicePort{
apiv1.ServicePort{
Name: "web",
Protocol: "TCP",
Port: 80,
TargetPort: containerport,
},
},
},
}

result, err := clientset.CoreV1().Services(env).Create(context.TODO(), service, metav1.CreateOptions{})
if err != nil {
return err
}
fmt.Printf("Created service %q. \n", result.GetObjectMeta().GetName())
}

添加更新逻辑

在添加完创建资源部分,就可以通过client-go进行对应资源的新建了,但是如果在目标namespace下已存在同名名称的资源,使用Create则会报错,所以我们在创建资源前先获取对应命名空间的资源进行判断,以Deployment为例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
_, err := clientset.AppsV1().Deployments("dev").Get(context.TODO(), "test-nginx", metav1.GetOptions{})
// err为nil时,对应的Deployment已存在,使用更新逻辑Update,只需要改变函数名称即可,其他的基本不变
if err == nil {
fmt.Println("deployment exists,update deployments")
result, err := deploycli.Update(context.TODO(), newappdeploy, metav1.UpdateOptions{})
if err != nil {
return err
}
fmt.Printf("Updated deployment %q. \n", result.GetObjectMeta().GetName())
return nil
}
// 出现其他非预期错误处理
if !errors.IsNotFound(err) {
return err
}
// 不存在deployment时创建deployment
fmt.Println("deploymnent not exists,start a new deployment.....")
result, err := clientset.AppsV1().Deployments("dev").Create(context.TODO(), "test-nginx", metav1.CreateOptions{})
if err != nil {
return err
}
fmt.Printf("Created deployment %q. \n", result.GetObjectMeta().GetName())

创建对应的clientset

通过以上的操作就可以成功创建/更新应用了,不过在此之前还需要创建clientset,通过clientset来进行操作,创建clientset很简单,通过token创建流程如下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import (
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
)


host := "http://xxxx:6443"
kubetoken := "eyxxxxxxxxxx"
kubeconfig := &rest.Config{
TLSClientConfig: rest.TLSClientConfig{
Insecure: true,
},
Host: host,
BearerToken: kubetoken,
}
clientSet, err := kubernetes.NewForConfig(kubeconfig)
if err != nil {
return error
}

也可通过kubeconfig创建

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var kubeconfig *string
if home := homedir.HomeDir(); home != "" {
kubeconfig = flag.String("kubeconfig", filepath.Join(home, ".kube", "config"), "(optional) absolute path to the kubeconfig file")
} else {
kubeconfig = flag.String("kubeconfig", "", "absolute path to the kubeconfig file")
}
flag.Parse()

config, err := clientcmd.BuildConfigFromFlags("", *kubeconfig)
if err != nil {
panic(err)
}
clientset, err := kubernetes.NewForConfig(config)
if err != nil {
panic(err)
}

创建kubetoken

在kubernetes1.24版本后,secret不再保存token,为获取对应的token,需手动进行创建

首先创建serviceAccount

1
2
3
4
5
6
apiVersion: v1
kind: ServiceAccount
metadata:
name: deployment-controller
namespace: dev

创建clusterRoleBinding

1
2
3
4
5
6
7
8
9
10
11
12
13
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: deployment-controller
subjects:`
- kind: ServiceAccount
name: deployment-controller
namespace: dev
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: admin

创建secret

1
2
3
4
5
6
7
8
apiVersion: v1
kind: Secret
metadata:
name: deployment-controller
namespace: dev
annotations:
kubernetes.io/service-account.name: deployment-controller
type: kubernetes.io/service-account-token

获取token

1
2
3
4
5
6
7
8
9
10
11
12
13
14
 kubectl  describe -n  dev secret/deployment-controller-token-9dqrl
Name: deployment-controller-token-9dqrl
Namespace: dev
Labels: <none>
Annotations: kubernetes.io/service-account.name: deployment-controller
kubernetes.io/service-account.uid: 900916ef-c332-49e7-bed3-22b998a08e2b

Type: kubernetes.io/service-account-token

Data
====
token: eyJUiOiGVz......nIvPg
ca.crt: 1099 bytes
namespace: 4 bytes