client-go之ClientSet
Clientset是什么
结论:Clientset 是 一系列 RESTClient 的 集合。
使用 RESTClient 操作kubernetes资源,太麻烦了
- 要操作 pods,需要指定config,给config设置 APIPath 为 “/api”、设置序列化器、设置 GroupVersion,最后还要调用 rest.RESTClientFor(config) 得到一个 用于操作pods的Clientset
- 而如果我要操作 deployment,这个过程又需要写一遍,然后又得到一个 用于操作deployment的Clientset
- 代码冗余,不优雅,而且到处创建Clientset,耗时又浪费资源
因此,就有了事先创建 各种资源的RESTClient,存起来备用的需求。Clientset就是这样封装起来的一个set集合。
Clientset的结构体
位于 /kubernetes/clientset.go 中,Clientset结构体:
1 | type Clientset struct { |
- 以 appsv1 的类型 *appsv1.AppsV1Client 举例:可以看到,内部包含了一个 restClient。这也进一步认证,Clientset 就是一系列 RESTClient 的集合。
1
2
3type AppsV1Client struct {
restClient rest.Interface
}
Clientset的常用方法
1、NewForConfig()方法
- 位于 /kubernetes/clientset.go 中,所以可以直接使用 kubernetes.NewForConfig() 使用
- 用于创建一个Clientset,传入一个rest.Config配置对象
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
44func NewForConfig(c *rest.Config) (*Clientset, error) {
configShallowCopy := *c
if configShallowCopy.UserAgent == "" {
configShallowCopy.UserAgent = rest.DefaultKubernetesUserAgent()
}
// share the transport between all clients
httpClient, err := rest.HTTPClientFor(&configShallowCopy)
if err != nil {
return nil, err
}
// 这个方法,就完成了所有 RESTClient 的创建
return NewForConfigAndClient(&configShallowCopy, httpClient)
}
func NewForConfigAndClient(c *rest.Config, httpClient *http.Client) (*Clientset, error) {
configShallowCopy := *c
if configShallowCopy.RateLimiter == nil && configShallowCopy.QPS > 0 {
if configShallowCopy.Burst <= 0 {
return nil, fmt.Errorf("burst is required to be greater than 0 when RateLimiter is not set and QPS is set to greater than 0")
}
configShallowCopy.RateLimiter = flowcontrol.NewTokenBucketRateLimiter(configShallowCopy.QPS, configShallowCopy.Burst)
}
var cs Clientset
var err error
// 下面就是创建各种 RESTClient 了,创建结果,被保存到 cs 中
cs.admissionregistrationV1, err = admissionregistrationv1.NewForConfigAndClient(&configShallowCopy, httpClient)
if err != nil {
return nil, err
}
cs.admissionregistrationV1alpha1, err = admissionregistrationv1alpha1.NewForConfigAndClient(&configShallowCopy, httpClient)
if err != nil {
return nil, err
}
cs.admissionregistrationV1beta1, err = admissionregistrationv1beta1.NewForConfigAndClient(&configShallowCopy, httpClient)
if err != nil {
return nil, err
}
......
return &cs, nil
}
2、Clientset的实例方法
Clientset 实现了 /kubernetes/clientset.go 下的 Interface接口,将自己内部的 私有属性 供外部使用
Interface 接口源码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24type Interface interface {
......
AppsV1() appsv1.AppsV1Interface
AppsV1beta1() appsv1beta1.AppsV1beta1Interface
AppsV1beta2() appsv1beta2.AppsV1beta2Interface
AuthenticationV1() authenticationv1.AuthenticationV1Interface
AuthenticationV1alpha1() authenticationv1alpha1.AuthenticationV1alpha1Interface
AuthenticationV1beta1() authenticationv1beta1.AuthenticationV1beta1Interface
AuthorizationV1() authorizationv1.AuthorizationV1Interface
AuthorizationV1beta1() authorizationv1beta1.AuthorizationV1beta1Interface
AutoscalingV1() autoscalingv1.AutoscalingV1Interface
AutoscalingV2() autoscalingv2.AutoscalingV2Interface
AutoscalingV2beta1() autoscalingv2beta1.AutoscalingV2beta1Interface
AutoscalingV2beta2() autoscalingv2beta2.AutoscalingV2beta2Interface
BatchV1() batchv1.BatchV1Interface
BatchV1beta1() batchv1beta1.BatchV1beta1Interface
CertificatesV1() certificatesv1.CertificatesV1Interface
CertificatesV1beta1() certificatesv1beta1.CertificatesV1beta1Interface
CertificatesV1alpha1() certificatesv1alpha1.CertificatesV1alpha1Interface
CoordinationV1beta1() coordinationv1beta1.CoordinationV1beta1Interface
CoordinationV1() coordinationv1.CoordinationV1Interface
CoreV1() corev1.CoreV1Interface
......
}以 AppsV1() 方法为例,返回值是接口 appsv1.AppsV1Interface 的实现类 appsv1.AppsV1Client 的对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22// 接口
type AppsV1Interface interface {
RESTClient() rest.Interface
ControllerRevisionsGetter
DaemonSetsGetter
DeploymentsGetter
ReplicaSetsGetter
StatefulSetsGetter
}
// 实现类
type AppsV1Client struct {
restClient rest.Interface
}
// AppsV1Client 实现 AppsV1Interface 接口的方法
func (c *AppsV1Client) RESTClient() rest.Interface {
if c == nil {
return nil
}
return c.restClient
}以 appsv1.AppsV1Client.Deployments() 方法举例
- Deployments() 方法源码
1
2
3
4
5
6
7
8
9
10
11
12
13// 返回值是DeploymentInterface
func (c *AppsV1Client) Deployments(namespace string) DeploymentInterface {
// 实际上,返回值是 DeploymentInterface 的实现类 deployments 的对象
return newDeployments(c, namespace)
}
// 构造一个 deployments 的对象
func newDeployments(c *AppsV1Client, namespace string) *deployments {
return &deployments{
client: c.RESTClient(),
ns: namespace,
}
}
- Deployments() 方法源码
返回值:DeploymentInterface 接口源码,可以看到包含操作Deployment的各种方法
1 | type DeploymentInterface interface { |
挑选 DeploymentInterface.Create 方法,查看 实现类 deployments 的 Create实现
- 可以看出,Create方法的内容,就跟我们4.2.4中使用 RESTClient 的方式差不多
- 这更加印证了,Clientset 就是 对各种 GroupVersion 的 RESTClient 的封装
1
2
3
4
5
6
7
8
9
10
11func (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
}
实际上,这些方法都不是人工写的,都是 code-generator 自动生成的
- code-generator 提供了很多工具用于为k8s中的资源生成相关代码,其中包括一个 client-gen
- client-gen:可以为资源生成标准的操作方法(get;list;watch;create;update;patch;delete)
- 比如,在kuberentes源码 staging/src/k8s.io/api/core/v1/types.go 中,可以看到 type Pod struct 注释上,就使用了 genclient 的标记
1
2
3
4
5
6// +genclient
// +genclient:method=UpdateEphemeralContainers,verb=update,subresource=ephemeralcontainers
// ......
type Pod struct {
......
}
client-gen 常用标记
1
2
3
4
5// +genclient - 生成默认的客户端动作函数(create, update, delete, get, list, update, patch, watch以及 是否生成updateStatus取决于.Status字段是否存在)。
// +genclient:nonNamespaced - 所有动作函数都是在没有名称空间的情况下生成
// +genclient:onlyVerbs=create,get - 指定的动作函数被生成.
// +genclient:skipVerbs=watch - 生成watch以外所有的动作函数.
// +genclient:noStatus - 即使.Status字段存在也不生成updateStatus动作函数
Clientset使用示例
- 需求:获取default命名空间下的pod列表,并获取kube-system命名空间下的deploy列表
- 从下面代码来看,创建了一个 clientset,就可以操作不同 GroupVersion 下的 不同资源,也无需再去手动指定 APIPath 等值了
- 代码编写
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
44package main
import (
"context"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/clientcmd"
)
func main() {
// 同样是先 创建一个客户端配置config
config, err := clientcmd.BuildConfigFromFlags("", clientcmd.RecommendedHomeFile)
if err != nil {
panic(err)
}
// 使用 kubernetes.NewForConfig(),创建一个ClientSet对象
clientSet, err := kubernetes.NewForConfig(config)
if err != nil {
panic(err)
}
// 1、从 clientSet 中调用操作pod的 RESTClient,获取default命名空间下的pod列表
pods, err := clientSet.CoreV1().Pods(v1.NamespaceDefault).List(context.TODO(), v1.ListOptions{})
if err != nil {
panic(err)
}
// 打印pod名称
for _, pod := range pods.Items {
println(pod.Name)
}
println("------")
// 2、从 clientSet 中调用操作 deploy 的 RESTClient,获取kube-system命名空间下的deploy列表
deploys, err := clientSet.AppsV1().Deployments("kube-system").List(context.TODO(), v1.ListOptions{})
if err != nil {
panic(err)
}
// 打印 deploy 名称
for _, deploy := range deploys.Items {
println(deploy.Name)
}
}
输出结果
1 | cassandra-5hbf7 |
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Macolm's Blog!