client-go支持4种Client客户端对象与Kubernetes API Server交互的方式,Client交互对象如图所示。
RESTClient是最基础的客户端。RESTClient对HTTP Request进行了封装,实现了RESTful风格的API。ClientSet、DynamicClient及DiscoveryClient客户端都是基于RESTClient实现的。
ClientSet在RESTClient的基础上封装了对Resource和Version的管理方法。每一个Resource可以理解为一个客户端,而ClientSet则是多个客户端的集合,每一个Resource和Version都以函数的方式暴露给开发者。ClientSet只能够处理Kubernetes内置资源,它是通过client-gen代码生成器自动生成的。
DynamicClient与ClientSet最大的不同之处是,ClientSet仅能访问Kubernetes自带的资源(即Client集合内的资源),不能直接访问CRD自定义资源。DynamicClient能够处理Kubernetes中的所有资源对象,包括Kubernetes内置资源与CRD自定义资源。
DiscoveryClient发现客户端,用于发现kube-apiserver所支持的资源组、资源版本、资源信息(即Group、Versions、Resources)。
上面四种客户端都可以通过kubeconfig连接到集群。
一、kubeconfig
kubeconfig用于管理访问集群Kube-apiserver的配置信息。上面介绍的几个客户端都通过kubeconfig来访问集群,其步骤可分为两步:
- 第1步,加载kubeconfig配置信息;
- 第2步,合并多个kubeconfig配置信息
源码位置:staging\src\k8s.io\client-go\tools\clientcmd\loader.go
func (rules *ClientConfigLoadingRules) Load() (*clientcmdapi.Config, error) {
if err := rules.Migrate(); err != nil {
return nil, err
}
errlist := []error{}
missingList := []string{}
kubeConfigFiles := []string{}
// Make sure a file we were explicitly told to use exists
if len(rules.ExplicitPath) > 0 {
if _, err := os.Stat(rules.ExplicitPath); os.IsNotExist(err) {
return nil, err
}
kubeConfigFiles = append(kubeConfigFiles, rules.ExplicitPath)
} else {
kubeConfigFiles = append(kubeConfigFiles, rules.Precedence...)
}
kubeconfigs := []*clientcmdapi.Config{}
// read and cache the config files so that we only look at them once
for _, filename := range kubeConfigFiles {
if len(filename) == 0 {
// no work to do
continue
}
config, err := LoadFromFile(filename)
if os.IsNotExist(err) {
// skip missing files
// Add to the missing list to produce a warning
missingList = append(missingList, filename)
continue
}
if err != nil {
errlist = append(errlist, fmt.Errorf("error loading config file \"%s\": %v", filename, err))
continue
}
kubeconfigs = append(kubeconfigs, config)
}
if rules.WarnIfAllMissing && len(missingList) > 0 && len(kubeconfigs) == 0 {
klog.Warningf("Config not found: %s", strings.Join(missingList, ", "))
}
// first merge all of our maps
mapConfig := clientcmdapi.NewConfig()
for _, kubeconfig := range kubeconfigs {
mergo.MergeWithOverwrite(mapConfig, kubeconfig)
}
// merge all of the struct values in the reverse order so that priority is given correctly
// errors are not added to the list the second time
nonMapConfig := clientcmdapi.NewConfig()
for i := len(kubeconfigs) - 1; i >= 0; i-- {
kubeconfig := kubeconfigs[i]
mergo.MergeWithOverwrite(nonMapConfig, kubeconfig)
}
// since values are overwritten, but maps values are not, we can merge the non-map config on top of the map config and
// get the values we expect.
config := clientcmdapi.NewConfig()
mergo.MergeWithOverwrite(config, mapConfig)
mergo.MergeWithOverwrite(config, nonMapConfig)
if rules.ResolvePaths() {
if err := ResolveLocalPaths(config); err != nil {
errlist = append(errlist, err)
}
}
return config, utilerrors.NewAggregate(errlist)
}
获取配置文件以及合并配置文件都在同一个方法Load
中,其中kubeConfigFiles
是记录获取到的配置信息,然后会循环它通过LoadFromFile
把配置信息读取出来记录到config
对象中,然后通过kubeconfigs
将所有的config存起来,然后通过mergo.MergeWithOverwrite
将所有的config
进行合并。
二、RESTClient
RESTClient是最基础的客户端。其他的ClientSet、DynamicClient及DiscoveryClient都是基于RESTClient实现的。RESTClient对HTTP Request进行了封装,实现了RESTful风格的API。它具有很高的灵活性,数据不依赖于方法和资源,因此RESTClient能够处理多种类型的调用,返回不同的数据格式。
三、ClientSet
ClientSet在RESTClient的基础上封装了对Resource和Version的管理方法。每一个Resource可以理解为一个客户端,而ClientSet则是多个客户端的集合,每一个Resource和Version都以函数的方式暴露给开发者。
注意:ClientSet仅能访问Kubernetes自身内置的资源(即客户端集合内的资源),不能直接访问CRD自定义资源。
代码示例如下:
package main
import (
"fmt"
apiv1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/clientcmd"
)
func main() {
config, err := clientcmd.BuildConfigFromFlags("", "./config")
if err != nil {
panic(err)
}
clientset, err := kubernetes.NewForConfig(config)
if err != nil {
panic(err)
}
podClient := clientset.CoreV1().Pods(apiv1.NamespaceDefault)
podList, err := podClient.List(metav1.ListOptions{Limit: 10})
if err != nil {
panic(err)
}
for _,pod := range podList.Items{
fmt.Printf("NAMESPACE: %v \nNAME: %v \nSTATUS: %v \n",pod.Namespace,pod.Name,pod.Status)
}
}
通过clientcmd.BuildConfigFromFlags
来加载配置文件,然后通过kubernetes.NewForConfig
来创建clientset
对象。然后通过clientset对象来操作集群,比如上面的获取default命名空间下的pod。
四、DynamicClient
DynamicClient是一种动态客户端,它可以对任意Kubernetes资源进行RESTful操作,包括CRD自定义资源。DynamicClient与ClientSet操作类似,同样封装了RESTClient,同样提供了Create、Update、Delete、Get、List、Watch、Patch等方法。
DynamicClient与ClientSet最大的不同之处是,ClientSet仅能访问Kubernetes自带的资源,而DynamicClient可以。
注意:DynamicClient不是类型安全的,因此在访问CRD自定义资源时需要特别注意。例如,在操作指针不当的情况下可能会导致程序崩溃。
DynamicClient的处理过程将Resource(例如PodList)转换成Unstructured结构类型,Kubernetes的所有Resource都可以转换为该结构类型。处理完成后,再将Unstructured转换成PodList。整个过程类似于Go语言的interface{}断言转换过程。另外,Unstructured结构类型是通过map[string]interface{}转换的。
示例如下:
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"
)
func main() {
config, err := clientcmd.BuildConfigFromFlags("", "./config")
if err != nil {
panic(err)
}
dynamicClient, err := dynamic.NewForConfig(config)
if err != nil {
panic(err)
}
resource := schema.GroupVersionResource{
Version: "v1",
Resource: "pods",
}
unstructObj, err := dynamicClient.Resource(resource).Namespace(apiv1.NamespaceDefault).List(metav1.ListOptions{Limit: 10})
if err != nil {
panic(err)
}
podList := &apiv1.PodList{}
err = runtime.DefaultUnstructuredConverter.FromUnstructured(unstructObj.UnstructuredContent(), podList)
if err != nil {
panic(err)
}
for _, pod := range podList.Items {
fmt.Printf("NAMESPACE: %v \nNAME: %v \nSTATUS: %v \n", pod.Namespace, pod.Name, pod.Status)
}
}
首先加载kubeconfig配置信息,dynamic.NewForConfig通过kubeconfig配置信息实例化dynamicClient对象,该对象用于管理Kubernetes的所有Resource的客户端,例如对Resource执行Create、Update、Delete、Get、List、Watch、Patch等操作。
dynamicClient.Resource(resource)函数用于设置请求的资源组、资源版本、资源名称。Namespace函数用于设置请求的命名空间。List函数用于获取Pod列表。得到的Pod列表为unstructured.UnstructuredList指针类型,然后通过runtime.DefaultUnstructuredConverter.FromUnstructured函数将unstructured.UnstructuredList转换成PodList类型。
五、DiscoveryClient
DiscoveryClient是发现客户端,它主要用于发现Kubernetes API Server所支持的资源组、资源版本、资源信息。还可以将这些信息存储到本地,用于本地缓存(Cache),以减轻对Kubernetes API Server访问的压力。在运行Kubernetes组件的机器上,缓存信息默认存储于~/.kube/cache和~/.kube/http-cache下。
kubectl的api-versions和api-resources命令输出也是通过DiscoveryClient实现的。
示例代码:
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)
}
discoveryClient, err := discovery.NewDiscoveryClientForConfig(config)
if err != nil {
panic(err)
}
_, APIResourceLists, err := discoveryClient.ServerGroupsAndResources()
if err != nil {
panic(err)
}
for _,list := range APIResourceLists{
groupVersion, err := schema.ParseGroupVersion(list.GroupVersion)
if err != nil {
panic(err)
}
for _, resource := range list.APIResources{
fmt.Printf("name:%v,group:%v,version:%v \n",resource.Name,groupVersion.Group,groupVersion.Version)
}
}
}
运行以上代码,列出Kubernetes API Server所支持的资源组、资源版本、资源信息。首先加载kubeconfig配置信息,discovery.NewDiscoveryClientForConfig
通过kubeconfig配置信息实例化discoveryClient对象,该对象是用于发现Kubernetes API Server所支持的资源组、资源版本、资源信息的客户端。
discoveryClient.ServerGroupsAndResources
函数会返回Kubernetes APIServer所支持的资源组、资源版本、资源信息(即APIResourceList),通过遍历APIResourceList输出信息。