Kubernetes的client-go库介绍

转子:https://juejin.im/post/5bc9d8c75188255c9b13ebf1

github上k8s有着不同分支,注意必须和版本匹配:https://github.com/kubernetes/client-go

本介绍

Kubernetes官方从2016年8月份开始,将Kubernetes资源操作相关的核心源码抽取出来,独立出来一个项目Client-go,作为官方提供的Go client。Kubernetes的部分代码也是基于这个client实现的,所以对这个client的质量、性能等方面还是非常有信心的。

client-go是一个调用kubernetes集群资源对象API的客户端,即通过client-go实现对kubernetes集群中资源对象(包括deployment、service、ingress、replicaSet、pod、namespace、node等)的增删改查等操作。大部分对kubernetes进行前置API封装的二次开发都通过client-go这个第三方包来实现。

源码简介

 

What's included

  • The kubernetes package contains the clientset to access Kubernetes API.
  • The discovery package is used to discover APIs supported by a Kubernetes API server.
  • The dynamic package contains a dynamic client that can perform generic operations on arbitrary Kubernetes API objects.
  • The plugin/pkg/client/auth packages contain optional authentication plugins for obtaining credentials from external sources.
  • The transport package is used to set up auth and start a connection.
  • The tools/cache package is useful for writing controllers.

Client结构

  • RESTClient:RESTClient是最基础的,相当于的底层基础结构,可以直接通过 是RESTClient提供的RESTful方法如Get(),Put(),Post(),Delete()进行交互

    • 同时支持Json 和 protobuf
    • 支持所有原生资源和CRDs
    • 但是,一般而言,为了更为优雅的处理,需要进一步封装,通过Clientset封装RESTClient,然后再对外提供接口和服务
  • Clientset:Clientset是调用Kubernetes资源对象最常用的client,可以操作所有的资源对象,包含RESTClient。需要指定Group、指定Version,然后根据Resource获取

    • 优雅的姿势是利用一个controller对象,再加上Informer
  • DynamicClient:Dynamic client 是一种动态的 client,它能处理 kubernetes 所有的资源。不同于 clientset,dynamic client 返回的对象是一个 map[string]interface{},如果一个 controller 中需要控制所有的 API,可以使用dynamic client,目前它在 garbage collector 和 namespace controller中被使用。

RESTClient

RESTClient 封装了指定资源URL的通用Kubernetes API的访问姿势

Clientset

/Users/meitu/Documents/work_Meitu/goDev/Applications/src/k8s.io/client-go/kubernetes/clientset.go

Clientset 是一系列的clients的group组合,注意每个group在一个Clientset中只包含一个版本。

Clientset包含了appsV1、coreV1,这中间包含了RESTClient,因此Clientset是基于RESTClient的。

dynamic client

dynamic client针对的是所有资源,但是只支持Json;

主要源码路径在:/Users/meitu/Documents/work_Meitu/goDev/Applications/src/k8s.io/client-go/dynamic

type ResourceInterface interface {
	Create(obj *unstructured.Unstructured, options metav1.CreateOptions, subresources ...string) (*unstructured.Unstructured, error)
	Update(obj *unstructured.Unstructured, options metav1.UpdateOptions, subresources ...string) (*unstructured.Unstructured, error)
	UpdateStatus(obj *unstructured.Unstructured, options metav1.UpdateOptions) (*unstructured.Unstructured, error)
	Delete(name string, options *metav1.DeleteOptions, subresources ...string) error
	DeleteCollection(options *metav1.DeleteOptions, listOptions metav1.ListOptions) error
	Get(name string, options metav1.GetOptions, subresources ...string) (*unstructured.Unstructured, error)
	List(opts metav1.ListOptions) (*unstructured.UnstructuredList, error)
	Watch(opts metav1.ListOptions) (watch.Interface, error)
	Patch(name string, pt types.PatchType, data []byte, options metav1.UpdateOptions, subresources ...string) (*unstructured.Unstructured, error)
}

Informer

Client-go包中一个相对较为高端的设计在于Informer的设计,我们知道我们可以直接通过Kubernetes API交互,但是考虑一点就是交互的形式,Informer设计为List/Watch的方式。Informer在初始化的时先通过List去从Kubernetes API中取出资源的全部object对象,并同时缓存,然后后面通过Watch的机制去监控资源,这样的话,通过Informer及其缓存,我们就可以直接和Informer交互而不是每次都和Kubernetes API交互。——有点类似线程池

 

Informer另外一块内容在于提供了事件handler机制,并会触发回调,这样上层应用如Controller就可以基于回调处理具体业务逻辑。因为Informer通过List、Watch机制可以监控到所有资源的所有事件,因此只要给Informer添加ResourceEventHandler 实例的回调函数实例取实现OnAdd(obj interface{}) OnUpdate(oldObj, newObj interface{}) 和 OnDelete(obj interface{})这三个方法,就可以处理好资源的创建、更新和删除操作

Kubernetes中都是各种controller的实现,各种controller都会用到Informer。


对象资源的操作接口

默认的每一种资源对象都有一个interface,封装了对象的CURD方法和list/watch方法

如 Deployment(/文件路径Users/meitu/Documents/work_Meitu/goDev/Applications/src/k8s.io/client-go/kubernetes/typed/apps/v1/deployment.go):

type DeploymentInterface interface {
	Create(*v1.Deployment) (*v1.Deployment, error)
	Update(*v1.Deployment) (*v1.Deployment, error)
	UpdateStatus(*v1.Deployment) (*v1.Deployment, error)
	Delete(name string, options *metav1.DeleteOptions) error
	DeleteCollection(options *metav1.DeleteOptions, listOptions metav1.ListOptions) error
	Get(name string, options metav1.GetOptions) (*v1.Deployment, error)
	List(opts metav1.ListOptions) (*v1.DeploymentList, error)
	Watch(opts metav1.ListOptions) (watch.Interface, error)
	Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1.Deployment, err error)
	DeploymentExpansion
}

如Service(文件路径/Users/meitu/Documents/work_Meitu/goDev/Applications/src/k8s.io/client-go/kubernetes/typed/core/v1/service.go)


// ServiceInterface has methods to work with Service resources.
type ServiceInterface interface {
	Create(*v1.Service) (*v1.Service, error)
	Update(*v1.Service) (*v1.Service, error)
	UpdateStatus(*v1.Service) (*v1.Service, error)
	Delete(name string, options *metav1.DeleteOptions) error
	Get(name string, options metav1.GetOptions) (*v1.Service, error)
	List(opts metav1.ListOptions) (*v1.ServiceList, error)
	Watch(opts metav1.ListOptions) (watch.Interface, error)
	Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1.Service, err error)
	ServiceExpansion
}

这也就是说明,在Kubernetes中,所有对象资源的操作方式都是统一的,有个interface当做虚基类,包含资源的所有操作方法,然后各个子类继承然后实现它们,子类中的实现定义会针对不同的资源有不同诠释。

client-go中的设计思想

client-go 和 controller的架构设计

主要是两大块:

  • client-go组件本身
  • controller上层控制器

上图相对较为复杂,有很多细节,我自己结合源码的理解如下:

client-go组件

  • Reflector:通过Kubernetes API监控Kubernetes的资源类型

    • 采用List、Watch机制
    • 可以Watch任何资源包括CRD
    • 添加object对象到FIFO队列,然后Informer会从队列里面取数据
  • Informer:controller机制的基础

    • 循环处理object对象
      • 从Reflector取出数据,然后将数据给到Indexer去缓存
    • 提供对象事件的handler接口
  • Indexer:提供object对象的索引,是线程安全的,缓存对象信息

ontroller组件

  • Informer reference: controller需要创建合适的Informer才能通过Informer reference操作资源对象

  • Indexer reference: controller创建Indexer reference然后去利用索引做相关处理

  • Resource Event Handlers:Informer会回调这些handlers

  • Work queue: Resource Event Handlers被回调后将key写到工作队列

    • 这里的key相当于事件通知,后面根据取出事件后,做后续的处理
  • Process Item:从工作队列中取出key后进行后续处理,具体处理可以通过Indexer reference

  • controller可以直接创建上述两个引用对象去处理,也可以采用工厂模式,官方都有相关示例

Factory:

在 client-go 中提供了一个 SharedInformerFactory 来简化 informer 的构建,具体代码在:

https://github.com/kubernetes/client-go/blob/v3.0.0/informers/factory.go#L54

Lister

 


 

Lister 是用来帮助我们访问本地 cache 的一个组件。

 

Workqueue

 


 

Workqueue 是一个简单的 queue 提供了以下的特性:

  • 公平性:每个item 按顺序处理。

  • 严格性:一个 item 不会被并发地多次处理,而且一个相同的 item 被多次加入 queue 的话也只会处理一次。

  • 支持多个生产者和消费者:它允许一个正在被处理的 item 再次加入队列。

 

我们建议使用 RateLimitingQueue,它相比普通的 workqueue 多了以下的功能: 

  • 限流:可以限制一个 item 被 reenqueued 的次数。

  • 防止 hot loop:它保证了一个 item 被 reenqueued 后,不会马上被处理。

Workqueue helper:

这里有一个 workqueue 的封装,来简化 queue 的操作,代码在以下位置:

https://github.com/caicloud/loadbalancer-controller/blob/master/pkg/util/controller/helper.go

控制流总结

 


 

我们来总结一个控制器的整体工作流程。

 

1. 创建一个控制器

  • 为控制器创建 workqueue

  • 创建 informer, 为 informer 添加 callback 函数,创建 lister

 

2. 启动控制器

  • 启动 informer

  • 等待本地 cache sync 完成后, 启动 workers

 

3. 当收到变更事件后,执行 callback 

  • 等待事件触发

  • 从事件中获取变更的 Object

  • 做一些必要的检查

  • 生成 object key,一般是 namespace/name 的形式

  • 将 key 放入 workqueue 中

 

4. worker loop

  • 等待从 workqueue 中获取到 item,一般为 object key

  • 用 object key 通过 lister 从本地 cache 中获取到真正的 object 对象

  • 做一些检查

  • 执行真正的业务逻辑

  • 处理下一个 item

到这里已经讲完了一个完整的 Kubernetes 的 Controller 的构建过程。但是还想要多啰嗦几句关于 kubernetes 的设计原则和 API 习俗,它们是指导我们写出更加可靠的 Controller 的白皮书。

 

为 Kubernetes 拓展一个功能,实现一个 controller 是简单的。 

但是设计一个系统,抽象出其中的设计哲学,更加值得我们学习和深思。

 

下面这个项目可以视为 controller 的一个例子:

https://github.com/caicloud/loadbalancer-controller

func main() {
	klog.InitFlags(nil)
	flag.Parse()

	// set up signals so we handle the first shutdown signal gracefully
	stopCh := signals.SetupSignalHandler()

	cfg, err := clientcmd.BuildConfigFromFlags(masterURL, kubeconfig)
	if err != nil {
		klog.Fatalf("Error building kubeconfig: %s", err.Error())
	}

	kubeClient, err := kubernetes.NewForConfig(cfg)
	if err != nil {
		klog.Fatalf("Error building kubernetes clientset: %s", err.Error())
	}

	exampleClient, err := clientset.NewForConfig(cfg)
	if err != nil {
		klog.Fatalf("Error building example clientset: %s", err.Error())
	}

	kubeInformerFactory := kubeinformers.NewSharedInformerFactory(kubeClient, time.Second*30)
	exampleInformerFactory := informers.NewSharedInformerFactory(exampleClient, time.Second*30)

	controller := NewController(kubeClient, exampleClient,
		kubeInformerFactory.Apps().V1().Deployments(),
		exampleInformerFactory.Samplecontroller().V1alpha1().Foos())

	// notice that there is no need to run Start methods in a separate goroutine. (i.e. go kubeInformerFactory.Start(stopCh)
	// Start method is non-blocking and runs all registered informers in a dedicated goroutine.
	kubeInformerFactory.Start(stopCh)
	exampleInformerFactory.Start(stopCh)

	if err = controller.Run(2, stopCh); err != nil {
		klog.Fatalf("Error running controller: %s", err.Error())
	}
}

func init() {
	flag.StringVar(&kubeconfig, "kubeconfig", "", "Path to a kubeconfig. Only required if out-of-cluster.")
	flag.StringVar(&masterURL, "master", "", "The address of the Kubernetes API server. Overrides any value in kubeconfig. Only required if out-of-cluster.")
}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值