3.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,ClientSet
,DynamicClient
,DiscoveryClient
都是RestClient
的具体实现。
2.2 目录结构¶
# tree client-go/ -L 1
client-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/config
func 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。后期继续继续深入学习。