写点什么

K8s 二开之 client-go 初探

用户头像
雪雷
关注
发布于: 2021 年 05 月 16 日
K8s二开之 client-go 初探

一 client-go 简介

近期有需求要对 k8s 的一些数据进行自定义整合,利用 client-go 可以快速方便的实现需求,在 K8s 运维中,我们可以使用 kubectl、客户端库或者 REST 请求来访问 K8S API。而实际上,无论是 kubectl 还是客户端库,都是封装了 REST 请求的工具。client-go 作为一个客户端库,能够调用 K8S API,实现对 K8S 集群中资源对象(包括 deployment、service、ingress、replicaSet、pod、namespace、node 等)的增删改查等操作。

二 简介

2.1 结构图


从上图可以看到 client-go 中包含四种 client,ClientSetDynamicClientDiscoveryClient都是RestClient的具体实现。

2.2 目录结构

# tree client-go/ -L 1client-go/  ├── discovery  ├── dynamic  ├── informers  ├── kubernetes  ├── listers  ├── plugin  ├── rest  ├── scale  ├── tools  ├── transport  └── util
复制代码


  • discovery:提供 DiscoveryClient 发现客户端

  • dynamic:提供 DynamicClient 动态客户端

  • informers:每种 kubernetes 资源的 Informer 实现

  • kubernetes:提供 ClientSet 客户端

  • listers:为每一个 Kubernetes 资源提供 Lister 功能,该功能对 Get 和 List 请求提供只读的缓存数据

  • plugin:提供 OpenStack、GCP 和 Azure 等云服务商授权插件

  • rest:提供 RESTClient 客户端,对 Kubernetes API Server 执行 RESTful 操作

  • scale:提供 ScaleClient 客户端,用于扩容或缩容 Deployment、ReplicaSet、Relication Controller 等资源对象

  • tools:提供常用工具,例如 SharedInformer、Reflector、DealtFIFO 及 Indexers。提供 Client 查询和缓存机制,以减少向 kube-apiserver 发起的请求数等

  • transport:提供安全的 TCP 连接,支持 Http Stream,某些操作需要在客户端和容器之间传输二进制流,例如 exec、attach 等操作。该功能由内部的 spdy 包提供支持

  • util:提供常用方法,例如 WorkQueue 功能队列、Certificate 证书管理等

三 认证

这里值得一提的是 go mod 通过 go client 对接 k8s 的时候有个小坑那么就是我们需要在 go mod 文件中指定 k8s 版本 不然会默认拉去最新的 k8s 版本的包 我们也知道不同的 k8s 版本的 api 会出现差异

3.1 认证

3.1.1 token

# APISERVER=$(kubectl config view --minify -o jsonpath='{.clusters[0].cluster.server}')# TOKEN=$(kubectl get secret $(kubectl get serviceaccount default -o jsonpath='{.secrets[0].name}') -o # jsonpath='{.data.token}' | base64 --decode )# curl $APISERVER/api --header "Authorization: Bearer $TOKEN" --insecure
复制代码


四 client-go 四种类型

4.1 RestClient

RestClient 是最基础的客户端,RestClient 基于 http request 进行了封装,实现了 restful 的 api,可以直接通过 是 RESTClient 提供的 RESTful 方法如 Get(),Put(),Post(),Delete()进行交互,同时支持 Json 和 protobuf,支持所有原生资源和 CRDs,但是,一般而言,为了更为优雅的处理,需要进一步封装,通过 Clientset 封装 RESTClient,然后再对外提供接口和服务。


package main
import ( "fmt" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes/scheme" "k8s.io/client-go/rest" "k8s.io/client-go/tools/clientcmd")
var configFile = "../config"var ApiPath = "api"var nameSpace = "kube-system"var resouce = "pods"
func main() { // 生成config config, err := clientcmd.BuildConfigFromFlags("", configFile) if err != nil { panic(err) } config.APIPath = ApiPath config.GroupVersion = &corev1.SchemeGroupVersion config.NegotiatedSerializer = scheme.Codecs
// 生成restClient restClient, err := rest.RESTClientFor(config) if err != nil { panic(err) } // 声明空结构体 rest := &corev1.PodList{} if err = restClient.Get().Namespace(nameSpace).Resource("pods").VersionedParams(&metav1.ListOptions{Limit: 500}, scheme.ParameterCodec).Do().Into(rest); err != nil { panic(err) } for _, v := range rest.Items { fmt.Printf("NameSpace: %v Name: %v Status: %v \n", v.Namespace, v.Name, v.Status.Phase) }}
复制代码


4.2 ClientSet

ClientSet 在 RestClient 的基础上封装了对 Resouorce 和 Version 的管理方法一个 Resource 可以理解为一个客户端,而 ClientSet 是多个客户端的集合


其操作资源对象时需要指定 Group、指定 Version,然后根据 Resource 获取,但是 clientset 不支持自定义 crd。


package main
import ( "flag" "fmt" 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" "path/filepath")
// ~/.kube/configfunc ParseConfig(configPath string) (*kubernetes.Clientset, error) { var kubeconfigPath *string if home := homedir.HomeDir(); home != "" { kubeconfigPath = flag.String("kubeconfig", filepath.Join(home, ".kube", "config"), "(optional) absolute path to the kubeconfig file") } else { kubeconfigPath = flag.String("kubeconfig", configPath, "absolute path to the kubeconfig file") } flag.Parse() config, err := clientcmd.BuildConfigFromFlags("", *kubeconfigPath) if err != nil { return nil, err } // 生成clientSet clientSet, err := kubernetes.NewForConfig(config) if err != nil { return clientSet, err } return clientSet, nil}
func ListCm(c *kubernetes.Clientset, ns string) error { configMaps, err := c.CoreV1().ConfigMaps(ns).List(metav1.ListOptions{}) if err != nil { return err } for _, cm := range configMaps.Items { fmt.Printf("configName: %v, configData: %v \n", cm.Name, cm.Data) } return nil}
func ListNodes(c *kubernetes.Clientset) error { nodeList, err := c.CoreV1().Nodes().List(metav1.ListOptions{}) if err != nil { return err } for _, node := range nodeList.Items { fmt.Printf("nodeName: %v, status: %v", node.GetName(), node.GetCreationTimestamp()) } return nil}
func ListPods(c *kubernetes.Clientset, ns string) { pods, err := c.CoreV1().Pods(ns).List(metav1.ListOptions{}) if err != nil { panic(err) } for _, v := range pods.Items { fmt.Printf("namespace: %v podname: %v podstatus: %v \n", v.Namespace, v.Name, v.Status.Phase) }}
func ListDeployment(c *kubernetes.Clientset, ns string) error { deployments, err := c.AppsV1().Deployments(ns).List(metav1.ListOptions{}) if err != nil { return err } for _, v := range deployments.Items { fmt.Printf("deploymentname: %v, available: %v, ready: %v", v.GetName(), v.Status.AvailableReplicas, v.Status.ReadyReplicas) } return nil}
func main() { var namespace = "kube-system" configPath := "../config" config, err := ParseConfig(configPath) if err != nil { fmt.Printf("load config error: %v\n", err) } fmt.Println("list pods") ListPods(config, namespace) fmt.Println("list cm") if err = ListCm(config, namespace); err != nil { fmt.Printf("list cm error: %v", err) } fmt.Println("list nodes") if err = ListNodes(config); err != nil { fmt.Printf("list nodes error: %v", err) } fmt.Println("list deployment") if err = ListDeployment(config, namespace); err != nil { fmt.Printf("list deployment error: %v", err) }}
复制代码


4.3 DynamicClient

DynamicClient 是一种动态客户端它可以对任何资源进行 restful 操作包括 crd 自定义资源,不同于 clientset,dynamic client 返回的对象是一个 map[string]interface{},如果一个 controller 中需要控制所有的 API,可以使用 dynamic client,目前它在 garbage collector 和 namespace controller 中被使用。DynamicClient 的处理过程将 Resource 例如 podlist 转换为 unstructured 类型,k8s 的所有 resource 都可以转换为这个结构类型,处理完之后在转换为 podlist,整个转换过程类似于接口转换就是通过 interface{}的断言。


Dynamic client 是一种动态的 client,它能处理 kubernetes 所有的资源,只支持 JSON。


package main
import ( "fmt" apiv1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/client-go/dynamic" "k8s.io/client-go/tools/clientcmd")
var namespace = "kube-system"
func main() {
config, err := clientcmd.BuildConfigFromFlags("", "../config") if err != nil { panic(err) }
dynamicClient, err := dynamic.NewForConfig(config) if err != nil { panic(err) } // 定义组版本资源 gvr := schema.GroupVersionResource{Version: "v1", Resource: "pods"} unStructObj, err := dynamicClient.Resource(gvr).Namespace(namespace).List(metav1.ListOptions{}) if err != nil { panic(err) } podList := &apiv1.PodList{}
if err = runtime.DefaultUnstructuredConverter.FromUnstructured(unStructObj.UnstructuredContent(), podList); err != nil { panic(err) }
for _, v := range podList.Items { fmt.Printf("namespaces:%v name:%v status:%v \n", v.Namespace, v.Name, v.Status.Phase) }}
复制代码


4.4 DiscoveryClient

DiscoveryClient 是发现客户端,主要用于发现 api server 支持的资源组 资源版本 资源信息,k8s api server 支持很多资源组 资源版本,资源信息,此时可以通过 DiscoveryClient 来查看


kubectl 的 api-version 和 api-resource 也是通过 DiscoveryClient 来实现的,还可以将信息缓存在本地 cache,以减轻 api 的访问压力,默认在./kube/cache 和./kube/http-cache 下。


package main
import ( "fmt" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/client-go/discovery" "k8s.io/client-go/tools/clientcmd")
func main() { config, err := clientcmd.BuildConfigFromFlags("", "../config") if err != nil { panic(err) } discoverClient, err := discovery.NewDiscoveryClientForConfig(config) if err != nil { panic(err) } _, apiResourceList, err := discoverClient.ServerGroupsAndResources() for _, v := range apiResourceList { gv, err := schema.ParseGroupVersion(v.GroupVersion) if err != nil { panic(err) } for _, resource := range v.APIResources { fmt.Println("name:", resource.Name, " ", "group:", gv.Group, " ", "version:", gv.Version) } }
}
复制代码


五 其他

学习 client-go,可以非常方便的利用其对 k8s 集群资源进行操作,kubeconfig→rest.config→clientset→具体的 client(CoreV1Client)→具体的资源对象(pod)→RESTClient→http.Client→HTTP 请求的发送及响应


通过 clientset 中不同的 client 和 client 中不同资源对象的方法实现对 kubernetes 中资源对象的增删改查等操作,常用的 client 有 CoreV1Client、AppsV1beta1Client、ExtensionsV1beta1Client 等。


本篇为简单利用 client-go 实现简单的 k8s 资源操作,后期利用例如 kubebuilder 和 operator-SDK 编写 operator 也需要深入的理解和学习 client-go。后期继续继续深入学习。

参考链接

  • https://www.voidking.com/dev-k8s-client-go/

  • https://godoc.org/k8s.io/client-go/kubernetes

  • https://github.com/kubernetes/client-go

  • https://pkg.go.dev/k8s.io/client-go/kubernetes

  • https://zhuanlan.zhihu.com/p/202611841?utm_source=wechat_session

发布于: 2021 年 05 月 16 日阅读数: 41
用户头像

雪雷

关注

stay hungry stay foolish 2019.08.16 加入

Devops,python,shell,云原生,云架构,kubernetes https://github.com/redhatxl

评论

发布
暂无评论
K8s二开之 client-go 初探