client-go就是字面意思,一个go的client库,用于和k8s的各种资源对象进行交互,但是它比传统的api客户端库多了很多高级的交互机制,比如informer,dynamic机制,这些也和k8s机制有一定的关系。

client-go

client-go的核心就是客户端,我们直接看其源码目录结构如下:

  • rest: 提供 RESTClient 客户端,对 K8S API Server 执行 RESTful http操作,是最基础也是我们最常见的httpclient,所有客户端都是在这个基础上进行封装。
  • kubernetes: 提供 ClientSet 客户端,ClientSet在 RESTClient 的基础上封装了对资源和版本的管理方法。
  • discovery: 提供 DiscoveryClient 发现客户端。
  • dynamic: 提供 DynamicClient 动态客户端。
  • informers: 每种 K8S 资源的 Informer 实现机制。
  • tools: 提供常用工具,例如 SharedInformer, Reflector, DeltaFIFO 及 Indexers。 提供 Client 查询和缓存机制,以减少向 kube-apiserver 发起的请求数等。informer的核心逻辑就是在子目录/tools/cache中实现的,cache也是tools的核心。
  • listers: 为每一个 K8S 资源提供 Lister 功能,该功能对 Get 和 List 请求提供只读的缓存数据。
  • transport: 提供安全的 TCP 连接,支持 HTTP Stream,某些操作需要在客户端和容器之间传输二进制流,例如 exec,attach 等操作。该功能由内部的 SPDY 包提供支持。
  • util: 提供常用方法。例如 WorkQueue 工作队列,Certificate 证书管理等。
  • scale: 提供 ScaleClient 客户端,用于扩容或缩容 Deployment, Replicaset, Replication Controller 等资源对象。
  • plugin: 提供 OpenStack,GCP 和 Azure 等云服务商授权插件。

RESTClient 客户端

RESTful Client 是最基础的客户端,它主要是对 HTTP 请求进行了封装,并且支持 JSON 和 Protobuf 格式数据,直接和k8s通过restful http进行交互。

ClientSet 客户端

ClientSet 客户端在 RESTClient 的基础上封装了对资源和版本的管理方法。每个资源可以理解为一个客户端,而 ClientSet 则是多个客户端的集合,每一个资源和版本都以函数的方式暴露给开发者。

具体使用方法,可参考官方示例:create-update-delete-deployment

类型化 ClientSets 使得使用预先生成的本地 API 对象与 API 服务器通信变得简单,交互和 RPC 类似。同时,ClientSets使得程序被迫与所使用的版本和类型紧密耦合。

DynamicClient 客户端

DynamicClient 是一种动态客户端,它可以动态的指定资源的组,版本和资源。因此它可以对任意 K8S 资源进行 RESTful 操作,包括 CRD 自定义资源。它封装了 RESTClient。所以同样提供 RESTClient 的各种方法。

具体使用方法,可参考官方示例:dynamic-create-update-delete-deployment

NOTE: 该官方示例是基于集群外的环境,如果你需要在集群内部使用(例如你需要在 container 中访问),你将需要调用 rest.InClusterConfig() 生成一个 configuration。具体的示例请参考 in-cluster-client-configuration

DynamicClient 则使用 unstructured.Unstructured 表示来自 API Server 的所有对象值。Unstructured 类型是一个嵌套的 map[string]inferface{} 值的集合来创建一个内部结构。通过这个结构,DynamicClient 将所有数据绑定推迟到运行时,松耦合意味着当客户端 API 发生变化时,使用 DynamicClient 的程序不需要重新编译。客户端程序在处理 API 表面更新时具有更大的灵活性,而无需提前知道这些更改是什么。

当然 DynamicClient 也有一定的弊端,不能类似rpc编程,某些需要强数据类型检查和验证的应用程也可能是一个问题,所以和clientset各有利弊,合理使用。

DiscoveryClient 客户端

DiscoveryClient 是一个发现客户端,它主要用于发现 K8S API Server 支持的资源组,资源版本和资源信息。所以开发者可以通过使用 DiscoveryClient 客户端查看所支持的资源组,资源版本和资源信息。

下面对各种客户端的基本使用和原理做一个详细的讲解。

客户端

RESTClient

基本使用和原理分析

RESTful Client 是最基础的客户端,其实很好理解,就是restful http client,所有的客户端都是基于这个客户端之上封装的,我们先重一个实例看起。

package main

import (
    "context"
    "flag"
    "fmt"
    "k8s.io/client-go/kubernetes/scheme"
    "k8s.io/client-go/rest"
    "k8s.io/client-go/tools/clientcmd"
    "k8s.io/client-go/util/homedir"
    corev1 "k8s.io/api/core/v1"
    metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    "path/filepath"
)

func main() {
    var kubeconfig *string

    // home是家目录,如果能取得家目录的值,就可以用来做默认值
    if home:=homedir.HomeDir(); home != "" {
        // 如果输入了kubeconfig参数,该参数的值就是kubeconfig文件的绝对路径,
        // 如果没有输入kubeconfig参数,就用默认路径~/.kube/config
        kubeconfig = flag.String("kubeconfig", filepath.Join(home, ".kube", "config"), "(optional) absolute path to the kubeconfig file")
    } else {
        // 如果取不到当前用户的家目录,就没办法设置kubeconfig的默认目录了,只能从入参中取
        kubeconfig = flag.String("kubeconfig", "", "absolute path to the kubeconfig file")
    }

    flag.Parse()

    // 从本机加载kubeconfig配置文件,因此第一个参数为空字符串
    config, err := clientcmd.BuildConfigFromFlags("", *kubeconfig)

    // kubeconfig加载失败就直接退出了
    if err != nil {
        panic(err.Error())
    }

    // 参考path : /api/v1/namespaces/{namespace}/pods
    config.APIPath = "api"
    // pod的group是空字符串
    config.GroupVersion = &corev1.SchemeGroupVersion
    // 指定序列化工具
    config.NegotiatedSerializer = scheme.Codecs

    // 根据配置信息构建restClient实例
    restClient, err := rest.RESTClientFor(config)

    if err!=nil {
        panic(err.Error())
    }

    // 保存pod结果的数据结构实例
    result := &corev1.PodList{}

    //  指定namespace
    namespace := "kube-system"
    // 设置请求参数,然后发起请求
    // GET请求
    err = restClient.Get().
        //  指定namespace,参考path : /api/v1/namespaces/{namespace}/pods
        Namespace(namespace).
        // 查找多个pod,参考path : /api/v1/namespaces/{namespace}/pods
        Resource("pods").
        // 指定大小限制和序列化工具
        VersionedParams(&metav1.ListOptions{Limit:100}, scheme.ParameterCodec).
        // 请求
        Do(context.TODO()).
        // 结果存入result
        Into(result)

    if err != nil {
        panic(err.Error())
    }

    // 表头
    fmt.Printf("namespace\t status\t\t name\n")

    // 每个pod都打印namespace、status.Phase、name三个字段
    for _, d := range result.Items {
        fmt.Printf("%v\t %v\t %v\n",
            d.Namespace,
            d.Status.Phase,
            d.Name)
    }
}

运行结果

$ go run main.go
namespace    status      name
kube-system  Running     coredns-546565776c-p754f
kube-system  Running     coredns-546565776c-ssxlk
kube-system  Running     etcd-minikube
kube-system  Running     kube-apiserver-minikube
kube-system  Running     kube-controller-manager-minikube
kube-system  Running     kube-proxy-z4vhb
kube-system  Running     kube-scheduler-minikube
kube-system  Running     storage-provisioner

说明

1、获取kubeconfig

获取kubernetes配置文件kubeconfig的绝对路径。一般路径为$HOME/.kube/config。该文件主要用来配置本地连接的kubernetes集群。

kubeconfig = flag.String("kubeconfig", filepath.Join(home,".kube","config"),"(optional) absolute path to the kubeconfig file")

2、通过参数(master的url或者kubeconfig路径)和BuildConfigFromFlags方法来获取restclient.Config对象

config, err := clientcmd.BuildConfigFromFlags("",*kubeconfig)

我们来看看restclient.Config结构:

type Config struct {
    // Host must be a host string, a host:port pair, or a URL to the base of the apiserver.
    // If a URL is given then the (optional) Path of that URL represents a prefix that must
    // be appended to all request URIs used to access the apiserver. This allows a frontend
    // proxy to easily relocate all of the apiserver endpoints.
    Host string
    // APIPath is a sub-path that points to an API root.
    APIPath string

    // ContentConfig contains settings that affect how objects are transformed when
    // sent to the server.
    ContentConfig

    // Server requires Basic authentication
    Username string
    Password string `datapolicy:"password"`

    // Server requires Bearer authentication. This client will not attempt to use
    // refresh tokens for an OAuth2 flow.
    // TODO: demonstrate an OAuth2 compatible client.
    BearerToken string `datapolicy:"token"`

    // Path to a file containing a BearerToken.
    // If set, the contents are periodically read.
    // The last successfully read value takes precedence over BearerToken.
    BearerTokenFile string

    // Impersonate is the configuration that RESTClient will use for impersonation.
    Impersonate ImpersonationConfig

    // Server requires plugin-specified authentication.
    AuthProvider *clientcmdapi.AuthProviderConfig

    // Callback to persist config for AuthProvider.
    AuthConfigPersister AuthProviderConfigPersister

    // Exec-based authentication provider.
    ExecProvider *clientcmdapi.ExecConfig

    // TLSClientConfig contains settings to enable transport layer security
    TLSClientConfig

    // UserAgent is an optional field that specifies the caller of this request.
    UserAgent string

    // DisableCompression bypasses automatic GZip compression requests to the
    // server.
    DisableCompression bool

    // Transport may be used for custom HTTP behavior. This attribute may not
    // be specified with the TLS client certificate options. Use WrapTransport
    // to provide additional per-server middleware behavior.
    Transport http.RoundTripper
    // WrapTransport will be invoked for custom HTTP behavior after the underlying
    // transport is initialized (either the transport created from TLSClientConfig,
    // Transport, or http.DefaultTransport). The config may layer other RoundTrippers
    // on top of the returned RoundTripper.
    //
    // A future release will change this field to an array. Use config.Wrap()
    // instead of setting this value directly.
    WrapTransport transport.WrapperFunc

    // QPS indicates the maximum QPS to the master from this client.
    // If it's zero, the created RESTClient will use DefaultQPS: 5
    QPS float32

    // Maximum burst for throttle.
    // If it's zero, the created RESTClient will use DefaultBurst: 10.
    Burst int

    // Rate limiter for limiting connections to the master from this client. If present overwrites QPS/Burst
    RateLimiter flowcontrol.RateLimiter

    // WarningHandler handles warnings in server responses.
    // If not set, the default warning handler is used.
    // See documentation for SetDefaultWarningHandler() for details.
    WarningHandler WarningHandler

    // The maximum length of time to wait before giving up on a server request. A value of zero means no timeout.
    Timeout time.Duration

    // Dial specifies the dial function for creating unencrypted TCP connections.
    Dial func(ctx context.Context, network, address string) (net.Conn, error)

    // Proxy is the proxy func to be used for all requests made by this
    // transport. If Proxy is nil, http.ProxyFromEnvironment is used. If Proxy
    // returns a nil *URL, no proxy is used.
    //
    // socks5 proxying does not currently support spdy streaming endpoints.
    Proxy func(*http.Request) (*url.URL, error)

    // Version forces a specific version to be used (if registered)
    // Do we need this?
    // Version string
}

可以看出来,这个config主要是对k8s的api的各种参数配置进行的封装。

3、对接config进行关键配置

对config的核心字段进行配置,比如path,group,序列化工具等。

4、通过config构建rest客户端

先看restclient结构体

type RESTClient struct {
    // base is the root URL for all invocations of the client
    base *url.URL
    // versionedAPIPath is a path segment connecting the base URL to the resource root
    versionedAPIPath string

    // content describes how a RESTClient encodes and decodes responses.
    content ClientContentConfig

    // creates BackoffManager that is passed to requests.
    createBackoffMgr func() BackoffManager

    // rateLimiter is shared among all requests created by this client unless specifically
    // overridden.
    rateLimiter flowcontrol.RateLimiter

    // warningHandler is shared among all requests created by this client.
    // If not set, defaultWarningHandler is used.
    warningHandler WarningHandler

    // Set specific behavior of the client.  If not set http.DefaultClient will be used.
    Client *http.Client
}

可以看出来就是对http的封装,我们再来看看创建函数

func RESTClientFor(config *Config) (*RESTClient, error) {
    if config.GroupVersion == nil {
        return nil, fmt.Errorf("GroupVersion is required when initializing a RESTClient")
    }
    if config.NegotiatedSerializer == nil {
        return nil, fmt.Errorf("NegotiatedSerializer is required when initializing a RESTClient")
    }

    //重config中解析出url相关的参数
    baseURL, versionedAPIPath, err := defaultServerUrlFor(config)
    if err != nil {
        return nil, err
    }

    //通过config构建transport
    transport, err := TransportFor(config)
    if err != nil {
        return nil, err
    }

    //通过transport构建httpclient
    var httpClient *http.Client
    if transport != http.DefaultTransport {
        httpClient = &http.Client{Transport: transport}
        if config.Timeout > 0 {
            httpClient.Timeout = config.Timeout
        }
    }

    rateLimiter := config.RateLimiter
    if rateLimiter == nil {
        qps := config.QPS
        if config.QPS == 0.0 {
            qps = DefaultQPS
        }
        burst := config.Burst
        if config.Burst == 0 {
            burst = DefaultBurst
        }
        if qps > 0 {
            rateLimiter = flowcontrol.NewTokenBucketRateLimiter(qps, burst)
        }
    }

    var gv schema.GroupVersion
    if config.GroupVersion != nil {
        gv = *config.GroupVersion
    }
    clientContent := ClientContentConfig{
        AcceptContentTypes: config.AcceptContentTypes,
        ContentType:        config.ContentType,
        GroupVersion:       gv,
        Negotiator:         runtime.NewClientNegotiator(config.NegotiatedSerializer, gv),
    }

    //通过上面的参数构建restClient
    restClient, err := NewRESTClient(baseURL, versionedAPIPath, clientContent, rateLimiter, httpClient)
    if err == nil && config.WarningHandler != nil {
        restClient.warningHandler = config.WarningHandler
    }
    return restClient, err
}

这个函数其实很简单,就是通过config构建restClient所需要的各种参数,然后构建restClient结构体。

5、下面就是调用restClient的成员函数获取对应的集群数据,比如实例中的GET。

// Get begins a GET request. Short for c.Verb("GET").
func (c *RESTClient) Get() *Request {
    return c.Verb("GET")
}

func (c *RESTClient) Verb(verb string) *Request {
    return NewRequest(c).Verb(verb)
}

先是调用get函数获取一个request,然后调用request的Namespace,Resource,VersionedParams函数,给request赋值。

// Namespace applies the namespace scope to a request (<resource>/[ns/<namespace>/]<name>)
func (r *Request) Namespace(namespace string) *Request {
    if r.err != nil {
        return r
    }
    if r.namespaceSet {
        r.err = fmt.Errorf("namespace already set to %q, cannot change to %q", r.namespace, namespace)
        return r
    }
    if msgs := IsValidPathSegmentName(namespace); len(msgs) != 0 {
        r.err = fmt.Errorf("invalid namespace %q: %v", namespace, msgs)
        return r
    }
    r.namespaceSet = true
    r.namespace = namespace
    return r
}


// Resource sets the resource to access (<resource>/[ns/<namespace>/]<name>)
func (r *Request) Resource(resource string) *Request {
    if r.err != nil {
        return r
    }
    if len(r.resource) != 0 {
        r.err = fmt.Errorf("resource already set to %q, cannot change to %q", r.resource, resource)
        return r
    }
    if msgs := IsValidPathSegmentName(resource); len(msgs) != 0 {
        r.err = fmt.Errorf("invalid resource %q: %v", resource, msgs)
        return r
    }
    r.resource = resource
    return r
}

func (r *Request) VersionedParams(obj runtime.Object, codec runtime.ParameterCodec) *Request {
    return r.SpecificallyVersionedParams(obj, codec, r.c.content.GroupVersion)
}

func (r *Request) SpecificallyVersionedParams(obj runtime.Object, codec runtime.ParameterCodec, version schema.GroupVersion) *Request {
    if r.err != nil {
        return r
    }
    params, err := codec.EncodeParameters(obj, version)
    if err != nil {
        r.err = err
        return r
    }
    for k, v := range params {
        if r.params == nil {
            r.params = make(url.Values)
        }
        r.params[k] = append(r.params[k], v...)
    }
    return r
}

继续调用request的DO函数发生请求。

func (r *Request) Do(ctx context.Context) Result {
    var result Result
    err := r.request(ctx, func(req *http.Request, resp *http.Response) {
        result = r.transformResponse(resp, req)
    })
    if err != nil {
        return Result{err: err}
    }
    return result
}

其实就是调用http的Do函数进行请求,最后对Response进行解析,打印。

总体来说,restClient其实就是一个httpclient与k8s进行交互,处理相关信息。

ClientSet

基本使用和原理分析

client-go中除了基本的rest http客户端,最先使用的就是ClientSet,这个客户端和我们常见的客户端使用差不多,我们先看一个实例。

package main

import(
	"flag"
	"fmt"
	"os"
	"path/filepath"
	"time"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/tools/clientcmd"
)

func main(){
	//get kubeconfig
	var kubeconfig *string
	if home := homeDir(); home !=""{
		kubeconfig = flag.String("kubeconfig", filepath.Join(home,".kube","config"),"(optional) absolute path to the kubeconfig file")
	}else{
		kubeconfig = flag.String("kubeconfig","","absolute path to the kubeconfig file")
	}
	flag.Parse()

	// uses the current context in kubeconfig
	config, err := clientcmd.BuildConfigFromFlags("",*kubeconfig)
	if err !=nil{
		panic(err.Error())
	}

	// creates the clientset
	clientset, err := kubernetes.NewForConfig(config)
	if err !=nil{
		panic(err.Error())
	}
	for{
		//option
		pods, err := clientset.CoreV1().Pods("").List(metav1.ListOptions{})
		if err !=nil{
			panic(err.Error())
		}
		fmt.Printf("There are %d pods in the cluster\n", len(pods.Items))
		time.Sleep(10* time.Second)
	}
}
func homeDir()string{
	if h := os.Getenv("HOME"); h !=""{
		return h
	}
	return os.Getenv("USERPROFILE")// windows
}

运行结果

There are 8 pods in the cluster
There are 10 pods in the cluster
There are 10 pods in the cluster
There are 7 pods in the cluster
There are 7 pods in the cluster

分析

构建config在restclient中已经说过了,我们直接来看客户端的构建。

1、通过restclient.Config参数和NewForConfig方法来获取clientset对象,clientset是多个client的集合,每个client可能包含不同版本的方法调用。

clientset, err := kubernetes.NewForConfig(config)

我们看一下clientset这个结构体

// Clientset contains the clients for groups. Each group has exactly one
// version included in a Clientset.
type Clientset struct {
	*discovery.DiscoveryClient
	admissionregistrationV1      *admissionregistrationv1.AdmissionregistrationV1Client
	admissionregistrationV1beta1 *admissionregistrationv1beta1.AdmissionregistrationV1beta1Client
	internalV1alpha1             *internalv1alpha1.InternalV1alpha1Client
	appsV1                       *appsv1.AppsV1Client
	appsV1beta1                  *appsv1beta1.AppsV1beta1Client
	appsV1beta2                  *appsv1beta2.AppsV1beta2Client
	authenticationV1             *authenticationv1.AuthenticationV1Client
	authenticationV1beta1        *authenticationv1beta1.AuthenticationV1beta1Client
	authorizationV1              *authorizationv1.AuthorizationV1Client
	authorizationV1beta1         *authorizationv1beta1.AuthorizationV1beta1Client
	autoscalingV1                *autoscalingv1.AutoscalingV1Client
	autoscalingV2beta1           *autoscalingv2beta1.AutoscalingV2beta1Client
	autoscalingV2beta2           *autoscalingv2beta2.AutoscalingV2beta2Client
	batchV1                      *batchv1.BatchV1Client
	batchV1beta1                 *batchv1beta1.BatchV1beta1Client
	certificatesV1               *certificatesv1.CertificatesV1Client
	certificatesV1beta1          *certificatesv1beta1.CertificatesV1beta1Client
	coordinationV1beta1          *coordinationv1beta1.CoordinationV1beta1Client
	coordinationV1               *coordinationv1.CoordinationV1Client
	coreV1                       *corev1.CoreV1Client
	discoveryV1                  *discoveryv1.DiscoveryV1Client
	discoveryV1beta1             *discoveryv1beta1.DiscoveryV1beta1Client
	eventsV1                     *eventsv1.EventsV1Client
	eventsV1beta1                *eventsv1beta1.EventsV1beta1Client
	extensionsV1beta1            *extensionsv1beta1.ExtensionsV1beta1Client
	flowcontrolV1alpha1          *flowcontrolv1alpha1.FlowcontrolV1alpha1Client
	flowcontrolV1beta1           *flowcontrolv1beta1.FlowcontrolV1beta1Client
	networkingV1                 *networkingv1.NetworkingV1Client
	networkingV1beta1            *networkingv1beta1.NetworkingV1beta1Client
	nodeV1                       *nodev1.NodeV1Client
	nodeV1alpha1                 *nodev1alpha1.NodeV1alpha1Client
	nodeV1beta1                  *nodev1beta1.NodeV1beta1Client
	policyV1                     *policyv1.PolicyV1Client
	policyV1beta1                *policyv1beta1.PolicyV1beta1Client
	rbacV1                       *rbacv1.RbacV1Client
	rbacV1beta1                  *rbacv1beta1.RbacV1beta1Client
	rbacV1alpha1                 *rbacv1alpha1.RbacV1alpha1Client
	schedulingV1alpha1           *schedulingv1alpha1.SchedulingV1alpha1Client
	schedulingV1beta1            *schedulingv1beta1.SchedulingV1beta1Client
	schedulingV1                 *schedulingv1.SchedulingV1Client
	storageV1beta1               *storagev1beta1.StorageV1beta1Client
	storageV1                    *storagev1.StorageV1Client
	storageV1alpha1              *storagev1alpha1.StorageV1alpha1Client
}

再来看一下NewForConfig函数

// NewForConfig creates a new Clientset for the given config.
// If config's RateLimiter is not set and QPS and Burst are acceptable,
// NewForConfig will generate a rate-limiter in configShallowCopy.
func NewForConfig(c *rest.Config) (*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
	cs.admissionregistrationV1, err = admissionregistrationv1.NewForConfig(&configShallowCopy)
	if err != nil {
		return nil, err
	}
	cs.admissionregistrationV1beta1, err = admissionregistrationv1beta1.NewForConfig(&configShallowCopy)
	if err != nil {
		return nil, err
	}
	cs.internalV1alpha1, err = internalv1alpha1.NewForConfig(&configShallowCopy)
	if err != nil {
		return nil, err
	}
	cs.appsV1, err = appsv1.NewForConfig(&configShallowCopy)
	if err != nil {
		return nil, err
	}
	cs.appsV1beta1, err = appsv1beta1.NewForConfig(&configShallowCopy)
	if err != nil {
		return nil, err
	}
	cs.appsV1beta2, err = appsv1beta2.NewForConfig(&configShallowCopy)
	if err != nil {
		return nil, err
	}
	cs.authenticationV1, err = authenticationv1.NewForConfig(&configShallowCopy)
	if err != nil {
		return nil, err
	}
	cs.authenticationV1beta1, err = authenticationv1beta1.NewForConfig(&configShallowCopy)
	if err != nil {
		return nil, err
	}
	cs.authorizationV1, err = authorizationv1.NewForConfig(&configShallowCopy)
	if err != nil {
		return nil, err
	}
	cs.authorizationV1beta1, err = authorizationv1beta1.NewForConfig(&configShallowCopy)
	if err != nil {
		return nil, err
	}
	cs.autoscalingV1, err = autoscalingv1.NewForConfig(&configShallowCopy)
	if err != nil {
		return nil, err
	}
	cs.autoscalingV2beta1, err = autoscalingv2beta1.NewForConfig(&configShallowCopy)
	if err != nil {
		return nil, err
	}
	cs.autoscalingV2beta2, err = autoscalingv2beta2.NewForConfig(&configShallowCopy)
	if err != nil {
		return nil, err
	}
	cs.batchV1, err = batchv1.NewForConfig(&configShallowCopy)
	if err != nil {
		return nil, err
	}
	cs.batchV1beta1, err = batchv1beta1.NewForConfig(&configShallowCopy)
	if err != nil {
		return nil, err
	}
	cs.certificatesV1, err = certificatesv1.NewForConfig(&configShallowCopy)
	if err != nil {
		return nil, err
	}
	cs.certificatesV1beta1, err = certificatesv1beta1.NewForConfig(&configShallowCopy)
	if err != nil {
		return nil, err
	}
	cs.coordinationV1beta1, err = coordinationv1beta1.NewForConfig(&configShallowCopy)
	if err != nil {
		return nil, err
	}
	cs.coordinationV1, err = coordinationv1.NewForConfig(&configShallowCopy)
	if err != nil {
		return nil, err
	}
	cs.coreV1, err = corev1.NewForConfig(&configShallowCopy)
	if err != nil {
		return nil, err
	}
	cs.discoveryV1, err = discoveryv1.NewForConfig(&configShallowCopy)
	if err != nil {
		return nil, err
	}
	cs.discoveryV1beta1, err = discoveryv1beta1.NewForConfig(&configShallowCopy)
	if err != nil {
		return nil, err
	}
	cs.eventsV1, err = eventsv1.NewForConfig(&configShallowCopy)
	if err != nil {
		return nil, err
	}
	cs.eventsV1beta1, err = eventsv1beta1.NewForConfig(&configShallowCopy)
	if err != nil {
		return nil, err
	}
	cs.extensionsV1beta1, err = extensionsv1beta1.NewForConfig(&configShallowCopy)
	if err != nil {
		return nil, err
	}
	cs.flowcontrolV1alpha1, err = flowcontrolv1alpha1.NewForConfig(&configShallowCopy)
	if err != nil {
		return nil, err
	}
	cs.flowcontrolV1beta1, err = flowcontrolv1beta1.NewForConfig(&configShallowCopy)
	if err != nil {
		return nil, err
	}
	cs.networkingV1, err = networkingv1.NewForConfig(&configShallowCopy)
	if err != nil {
		return nil, err
	}
	cs.networkingV1beta1, err = networkingv1beta1.NewForConfig(&configShallowCopy)
	if err != nil {
		return nil, err
	}
	cs.nodeV1, err = nodev1.NewForConfig(&configShallowCopy)
	if err != nil {
		return nil, err
	}
	cs.nodeV1alpha1, err = nodev1alpha1.NewForConfig(&configShallowCopy)
	if err != nil {
		return nil, err
	}
	cs.nodeV1beta1, err = nodev1beta1.NewForConfig(&configShallowCopy)
	if err != nil {
		return nil, err
	}
	cs.policyV1, err = policyv1.NewForConfig(&configShallowCopy)
	if err != nil {
		return nil, err
	}
	cs.policyV1beta1, err = policyv1beta1.NewForConfig(&configShallowCopy)
	if err != nil {
		return nil, err
	}
	cs.rbacV1, err = rbacv1.NewForConfig(&configShallowCopy)
	if err != nil {
		return nil, err
	}
	cs.rbacV1beta1, err = rbacv1beta1.NewForConfig(&configShallowCopy)
	if err != nil {
		return nil, err
	}
	cs.rbacV1alpha1, err = rbacv1alpha1.NewForConfig(&configShallowCopy)
	if err != nil {
		return nil, err
	}
	cs.schedulingV1alpha1, err = schedulingv1alpha1.NewForConfig(&configShallowCopy)
	if err != nil {
		return nil, err
	}
	cs.schedulingV1beta1, err = schedulingv1beta1.NewForConfig(&configShallowCopy)
	if err != nil {
		return nil, err
	}
	cs.schedulingV1, err = schedulingv1.NewForConfig(&configShallowCopy)
	if err != nil {
		return nil, err
	}
	cs.storageV1beta1, err = storagev1beta1.NewForConfig(&configShallowCopy)
	if err != nil {
		return nil, err
	}
	cs.storageV1, err = storagev1.NewForConfig(&configShallowCopy)
	if err != nil {
		return nil, err
	}
	cs.storageV1alpha1, err = storagev1alpha1.NewForConfig(&configShallowCopy)
	if err != nil {
		return nil, err
	}

	cs.DiscoveryClient, err = discovery.NewDiscoveryClientForConfig(&configShallowCopy)
	if err != nil {
		return nil, err
	}
	return &cs, nil
}

可见通过config创建了结构体中所有的client,我们常用的client有CoreV1ClientAppsV1beta1ClientExtensionsV1beta1Client等,我们就以实例中的CoreV1Client来继续分析

2、CoreV1Client

corev1.NewForConfig方法本质是调用了rest.RESTClientFor(&config)方法创建RESTClient对象,即CoreV1Client的本质就是一个RESTClient对象,赋值到对应的CoreV1Client结构体。

cs.coreV1, err = corev1.NewForConfig(&configShallowCopy)

// NewForConfig creates a new CoreV1Client for the given config.
func NewForConfig(c *rest.Config) (*CoreV1Client, error) {
	config := *c
	if err := setConfigDefaults(&config); err != nil {
		return nil, err
	}
	client, err := rest.RESTClientFor(&config)
	if err != nil {
		return nil, err
	}
	return &CoreV1Client{client}, nil
}

3、CoreV1Interface

CoreV1Client实现了CoreV1Interface的接口,CoreV1Interface中包含了各种kubernetes对象的调用接口,对kubernetes的资源对象进行增删改查的操作,例如PodsGetter是对kubernetes中pod对象增删改查操作的接口。ServicesGetter是对service对象的操作的接口。

type CoreV1Interface interface {
	RESTClient() rest.Interface
	ComponentStatusesGetter
	ConfigMapsGetter
	EndpointsGetter
	EventsGetter
	LimitRangesGetter
	NamespacesGetter
	NodesGetter
	PersistentVolumesGetter
	PersistentVolumeClaimsGetter
	PodsGetter
	PodTemplatesGetter
	ReplicationControllersGetter
	ResourceQuotasGetter
	SecretsGetter
	ServicesGetter
	ServiceAccountsGetter
}

对应的结构体CoreV1Client的实现

// CoreV1Client is used to interact with features provided by the  group.
type CoreV1Client struct {
	restClient rest.Interface
}

func (c *CoreV1Client) ComponentStatuses() ComponentStatusInterface {
	return newComponentStatuses(c)
}

func (c *CoreV1Client) ConfigMaps(namespace string) ConfigMapInterface {
	return newConfigMaps(c, namespace)
}

func (c *CoreV1Client) Endpoints(namespace string) EndpointsInterface {
	return newEndpoints(c, namespace)
}

func (c *CoreV1Client) Events(namespace string) EventInterface {
	return newEvents(c, namespace)
}

func (c *CoreV1Client) LimitRanges(namespace string) LimitRangeInterface {
	return newLimitRanges(c, namespace)
}

func (c *CoreV1Client) Namespaces() NamespaceInterface {
	return newNamespaces(c)
}

func (c *CoreV1Client) Nodes() NodeInterface {
	return newNodes(c)
}

func (c *CoreV1Client) PersistentVolumes() PersistentVolumeInterface {
	return newPersistentVolumes(c)
}

func (c *CoreV1Client) PersistentVolumeClaims(namespace string) PersistentVolumeClaimInterface {
	return newPersistentVolumeClaims(c, namespace)
}

func (c *CoreV1Client) Pods(namespace string) PodInterface {
	return newPods(c, namespace)
}

func (c *CoreV1Client) PodTemplates(namespace string) PodTemplateInterface {
	return newPodTemplates(c, namespace)
}

func (c *CoreV1Client) ReplicationControllers(namespace string) ReplicationControllerInterface {
	return newReplicationControllers(c, namespace)
}

func (c *CoreV1Client) ResourceQuotas(namespace string) ResourceQuotaInterface {
	return newResourceQuotas(c, namespace)
}

func (c *CoreV1Client) Secrets(namespace string) SecretInterface {
	return newSecrets(c, namespace)
}

func (c *CoreV1Client) Services(namespace string) ServiceInterface {
	return newServices(c, namespace)
}

func (c *CoreV1Client) ServiceAccounts(namespace string) ServiceAccountInterface {
	return newServiceAccounts(c, namespace)
}

4、PodsGetter

我们以实例中的操作为例

pods, err := clientset.CoreV1().Pods("").List(metav1.ListOptions{})

可以看到CoreV1Client的Pods函数就是调用newPods函数,创建了一个pods对象。

5、pods对象

// pods implements PodInterface
type pods struct{
    client rest.Interface
    ns     string
}

pods对象实现了PodInterface接口。

6、PodInterface

// PodInterface has methods to work with Pod resources.
type PodInterface interface {
	Create(ctx context.Context, pod *v1.Pod, opts metav1.CreateOptions) (*v1.Pod, error)
	Update(ctx context.Context, pod *v1.Pod, opts metav1.UpdateOptions) (*v1.Pod, error)
	UpdateStatus(ctx context.Context, pod *v1.Pod, opts metav1.UpdateOptions) (*v1.Pod, error)
	Delete(ctx context.Context, name string, opts metav1.DeleteOptions) error
	DeleteCollection(ctx context.Context, opts metav1.DeleteOptions, listOpts metav1.ListOptions) error
	Get(ctx context.Context, name string, opts metav1.GetOptions) (*v1.Pod, error)
	List(ctx context.Context, opts metav1.ListOptions) (*v1.PodList, error)
	Watch(ctx context.Context, opts metav1.ListOptions) (watch.Interface, error)
	Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts metav1.PatchOptions, subresources ...string) (result *v1.Pod, err error)
	Apply(ctx context.Context, pod *corev1.PodApplyConfiguration, opts metav1.ApplyOptions) (result *v1.Pod, err error)
	ApplyStatus(ctx context.Context, pod *corev1.PodApplyConfiguration, opts metav1.ApplyOptions) (result *v1.Pod, err error)
	GetEphemeralContainers(ctx context.Context, podName string, options metav1.GetOptions) (*v1.EphemeralContainers, error)
	UpdateEphemeralContainers(ctx context.Context, podName string, ephemeralContainers *v1.EphemeralContainers, opts metav1.UpdateOptions) (*v1.EphemeralContainers, error)

	PodExpansion
}

对应的pods对象的实现

// pods implements PodInterface
type pods struct {
	client rest.Interface
	ns     string
}

// newPods returns a Pods
func newPods(c *CoreV1Client, namespace string) *pods {
	return &pods{
		client: c.RESTClient(),
		ns:     namespace,
	}
}

// Get takes name of the pod, and returns the corresponding pod object, and an error if there is any.
func (c *pods) Get(ctx context.Context, name string, options metav1.GetOptions) (result *v1.Pod, err error) {
	result = &v1.Pod{}
	err = c.client.Get().
		Namespace(c.ns).
		Resource("pods").
		Name(name).
		VersionedParams(&options, scheme.ParameterCodec).
		Do(ctx).
		Into(result)
	return
}

// List takes label and field selectors, and returns the list of Pods that match those selectors.
func (c *pods) List(ctx context.Context, opts metav1.ListOptions) (result *v1.PodList, err error) {
	var timeout time.Duration
	if opts.TimeoutSeconds != nil {
		timeout = time.Duration(*opts.TimeoutSeconds) * time.Second
	}
	result = &v1.PodList{}
	err = c.client.Get().
		Namespace(c.ns).
		Resource("pods").
		VersionedParams(&opts, scheme.ParameterCodec).
		Timeout(timeout).
		Do(ctx).
		Into(result)
	return
}

// Watch returns a watch.Interface that watches the requested pods.
func (c *pods) Watch(ctx context.Context, opts metav1.ListOptions) (watch.Interface, error) {
	var timeout time.Duration
	if opts.TimeoutSeconds != nil {
		timeout = time.Duration(*opts.TimeoutSeconds) * time.Second
	}
	opts.Watch = true
	return c.client.Get().
		Namespace(c.ns).
		Resource("pods").
		VersionedParams(&opts, scheme.ParameterCodec).
		Timeout(timeout).
		Watch(ctx)
}

// Create takes the representation of a pod and creates it.  Returns the server's representation of the pod, and an error, if there is any.
func (c *pods) Create(ctx context.Context, pod *v1.Pod, opts metav1.CreateOptions) (result *v1.Pod, err error) {
	result = &v1.Pod{}
	err = c.client.Post().
		Namespace(c.ns).
		Resource("pods").
		VersionedParams(&opts, scheme.ParameterCodec).
		Body(pod).
		Do(ctx).
		Into(result)
	return
}

// Update takes the representation of a pod and updates it. Returns the server's representation of the pod, and an error, if there is any.
func (c *pods) Update(ctx context.Context, pod *v1.Pod, opts metav1.UpdateOptions) (result *v1.Pod, err error) {
	result = &v1.Pod{}
	err = c.client.Put().
		Namespace(c.ns).
		Resource("pods").
		Name(pod.Name).
		VersionedParams(&opts, scheme.ParameterCodec).
		Body(pod).
		Do(ctx).
		Into(result)
	return
}

// UpdateStatus was generated because the type contains a Status member.
// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus().
func (c *pods) UpdateStatus(ctx context.Context, pod *v1.Pod, opts metav1.UpdateOptions) (result *v1.Pod, err error) {
	result = &v1.Pod{}
	err = c.client.Put().
		Namespace(c.ns).
		Resource("pods").
		Name(pod.Name).
		SubResource("status").
		VersionedParams(&opts, scheme.ParameterCodec).
		Body(pod).
		Do(ctx).
		Into(result)
	return
}

// Delete takes name of the pod and deletes it. Returns an error if one occurs.
func (c *pods) Delete(ctx context.Context, name string, opts metav1.DeleteOptions) error {
	return c.client.Delete().
		Namespace(c.ns).
		Resource("pods").
		Name(name).
		Body(&opts).
		Do(ctx).
		Error()
}

// DeleteCollection deletes a collection of objects.
func (c *pods) DeleteCollection(ctx context.Context, opts metav1.DeleteOptions, listOpts metav1.ListOptions) error {
	var timeout time.Duration
	if listOpts.TimeoutSeconds != nil {
		timeout = time.Duration(*listOpts.TimeoutSeconds) * time.Second
	}
	return c.client.Delete().
		Namespace(c.ns).
		Resource("pods").
		VersionedParams(&listOpts, scheme.ParameterCodec).
		Timeout(timeout).
		Body(&opts).
		Do(ctx).
		Error()
}

// Patch applies the patch and returns the patched pod.
func (c *pods) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts metav1.PatchOptions, subresources ...string) (result *v1.Pod, err error) {
	result = &v1.Pod{}
	err = c.client.Patch(pt).
		Namespace(c.ns).
		Resource("pods").
		Name(name).
		SubResource(subresources...).
		VersionedParams(&opts, scheme.ParameterCodec).
		Body(data).
		Do(ctx).
		Into(result)
	return
}

// Apply takes the given apply declarative configuration, applies it and returns the applied pod.
func (c *pods) Apply(ctx context.Context, pod *corev1.PodApplyConfiguration, opts metav1.ApplyOptions) (result *v1.Pod, err error) {
	if pod == nil {
		return nil, fmt.Errorf("pod provided to Apply must not be nil")
	}
	patchOpts := opts.ToPatchOptions()
	data, err := json.Marshal(pod)
	if err != nil {
		return nil, err
	}
	name := pod.Name
	if name == nil {
		return nil, fmt.Errorf("pod.Name must be provided to Apply")
	}
	result = &v1.Pod{}
	err = c.client.Patch(types.ApplyPatchType).
		Namespace(c.ns).
		Resource("pods").
		Name(*name).
		VersionedParams(&patchOpts, scheme.ParameterCodec).
		Body(data).
		Do(ctx).
		Into(result)
	return
}

// ApplyStatus was generated because the type contains a Status member.
// Add a +genclient:noStatus comment above the type to avoid generating ApplyStatus().
func (c *pods) ApplyStatus(ctx context.Context, pod *corev1.PodApplyConfiguration, opts metav1.ApplyOptions) (result *v1.Pod, err error) {
	if pod == nil {
		return nil, fmt.Errorf("pod provided to Apply must not be nil")
	}
	patchOpts := opts.ToPatchOptions()
	data, err := json.Marshal(pod)
	if err != nil {
		return nil, err
	}

	name := pod.Name
	if name == nil {
		return nil, fmt.Errorf("pod.Name must be provided to Apply")
	}

	result = &v1.Pod{}
	err = c.client.Patch(types.ApplyPatchType).
		Namespace(c.ns).
		Resource("pods").
		Name(*name).
		SubResource("status").
		VersionedParams(&patchOpts, scheme.ParameterCodec).
		Body(data).
		Do(ctx).
		Into(result)
	return
}

// GetEphemeralContainers takes name of the pod, and returns the corresponding v1.EphemeralContainers object, and an error if there is any.
func (c *pods) GetEphemeralContainers(ctx context.Context, podName string, options metav1.GetOptions) (result *v1.EphemeralContainers, err error) {
	result = &v1.EphemeralContainers{}
	err = c.client.Get().
		Namespace(c.ns).
		Resource("pods").
		Name(podName).
		SubResource("ephemeralcontainers").
		VersionedParams(&options, scheme.ParameterCodec).
		Do(ctx).
		Into(result)
	return
}

// UpdateEphemeralContainers takes the top resource name and the representation of a ephemeralContainers and updates it. Returns the server's representation of the ephemeralContainers, and an error, if there is any.
func (c *pods) UpdateEphemeralContainers(ctx context.Context, podName string, ephemeralContainers *v1.EphemeralContainers, opts metav1.UpdateOptions) (result *v1.EphemeralContainers, err error) {
	result = &v1.EphemeralContainers{}
	err = c.client.Put().
		Namespace(c.ns).
		Resource("pods").
		Name(podName).
		SubResource("ephemeralcontainers").
		VersionedParams(&opts, scheme.ParameterCodec).
		Body(ephemeralContainers).
		Do(ctx).
		Into(result)
	return
}

可以看出最后对pods对象进行的操作都是通过corev1client调用来操作的,corev1client本质是创建了RESTClient,即最终的实现本质是RESTClient的HTTP调用。

7、list

pods.List()方法通过RESTClient的HTTP调用来实现对kubernetes的pod资源的获取。

// List takes label and field selectors, and returns the list of Pods that match those selectors.
func (c *pods) List(ctx context.Context, opts metav1.ListOptions) (result *v1.PodList, err error) {
	var timeout time.Duration
	if opts.TimeoutSeconds != nil {
		timeout = time.Duration(*opts.TimeoutSeconds) * time.Second
	}
	result = &v1.PodList{}
	err = c.client.Get().
		Namespace(c.ns).
		Resource("pods").
		VersionedParams(&opts, scheme.ParameterCodec).
		Timeout(timeout).
		Do(ctx).
		Into(result)
	return
}

8、RESTClient

在来看RESTClient,restclient是在corev1client创建的函数中创建的,RESTClient对象的创建同样是依赖传入的config信息。

client, err := rest.RESTClientFor(&config)

我们在RESTClientFor函数中看到了对应的http.Client的创建,在RESTClient结构体中包含了http.Client,即本质上RESTClient就是一个http.Client的封装实现,这里的实现就不一一看了,很简单可以想到的http client的封装。

// RESTClient imposes common Kubernetes API conventions on a set of resource paths.
// The baseURL is expected to point to an HTTP or HTTPS path that is the parent
// of one or more resources.  The server should return a decodable API resource
// object, or an api.Status object which contains information about the reason for
// any failure.
//
// Most consumers should use client.New() to get a Kubernetes API client.
type RESTClientstruct{
// base is the root URL for all invocations of the client
base*url.URL
// versionedAPIPath is a path segment connecting the base URL to the resource root
    versionedAPIPath string
// contentConfig is the information used to communicate with the server.
    contentConfig ContentConfig
// serializers contain all serializers for underlying content type.
    serializers Serializers
// creates BackoffManager that is passed to requests.
    createBackoffMgr func()BackoffManager
// TODO extract this into a wrapper interface via the RESTClient interface in kubectl.
Throttle flowcontrol.RateLimiter
// Set specific behavior of the client.  If not set http.DefaultClient will be used.
Client*http.Client
}

通过http client实现了对应的get,post等请求,最后都是通过http Do函数来实现一个http request,所以我们可以看到restclient的rest接口interface和我们想的差不多。

// Interface captures the set of operations for generically interacting with Kubernetes REST apis.
type Interface interface {
	GetRateLimiter() flowcontrol.RateLimiter
	Verb(verb string) *Request
	Post() *Request
	Put() *Request
	Patch(pt types.PatchType) *Request
	Get() *Request
	Delete() *Request
	APIVersion() schema.GroupVersion
}

总结

整个调用的过程如下:

kubeconfig→rest.config→clientset→具体的client(CoreV1Client)→具体的资源对象(pod)→RESTClient→http.Client→HTTP请求的发送及响应

总结成图(todo)

常用操作实现

clientset

创建clientset,原生所有的资源操作都要先创建clientset。

//获取kubeconfig
kubeconfig = flag.String("kubeconfig", filepath.Join(home,".kube","config"),"(optional) absolute path to the kubeconfig file")
//创建config
config, err := clientcmd.BuildConfigFromFlags("",*kubeconfig)
//创建clientset
clientset, err := kubernetes.NewForConfig(config)
//具体的资源调用见以下例子

deployment

//声明deployment对象
var deployment *v1beta1.Deployment
//构造deployment对象
//创建deployment
deployment, err := clientset.AppsV1beta1().Deployments(<namespace>).Create(<deployment>)
//更新deployment
deployment, err := clientset.AppsV1beta1().Deployments(<namespace>).Update(<deployment>)
//删除deployment
err := clientset.AppsV1beta1().Deployments(<namespace>).Delete(<deployment.Name>, &meta_v1.DeleteOptions{})
//查询deployment
deployment, err := clientset.AppsV1beta1().Deployments(<namespace>).Get(<deployment.Name>, meta_v1.GetOptions{})
//列出deployment
deploymentList, err := clientset.AppsV1beta1().Deployments(<namespace>).List(&meta_v1.ListOptions{})
//watch deployment
watchInterface, err := clientset.AppsV1beta1().Deployments(<namespace>).Watch(&meta_v1.ListOptions{})

service

//声明service对象
var service *v1.Service
//构造service对象
//创建service
service, err := clientset.CoreV1().Services(<namespace>).Create(<service>)
//更新service
service, err := clientset.CoreV1().Services(<namespace>).Update(<service>)
//删除service
err := clientset.CoreV1().Services(<namespace>).Delete(<service.Name>, &meta_v1.DeleteOptions{})
//查询service
service, err := clientset.CoreV1().Services(<namespace>).Get(<service.Name>, meta_v1.GetOptions{})
//列出service
serviceList, err := clientset.CoreV1().Services(<namespace>).List(&meta_v1.ListOptions{})
//watch service
watchInterface, err := clientset.CoreV1().Services(<namespace>).Watch(&meta_v1.ListOptions{})

ingress

//声明ingress对象
var ingress *v1beta1.Ingress
//构造ingress对象
//创建ingress
ingress, err := clientset.ExtensionsV1beta1().Ingresses(<namespace>).Create(<ingress>)
//更新ingress
ingress, err := clientset.ExtensionsV1beta1().Ingresses(<namespace>).Update(<ingress>)
//删除ingress
err := clientset.ExtensionsV1beta1().Ingresses(<namespace>).Delete(<ingress.Name>, &meta_v1.DeleteOptions{})
//查询ingress
ingress, err := clientset.ExtensionsV1beta1().Ingresses(<namespace>).Get(<ingress.Name>, meta_v1.GetOptions{})
//列出ingress
ingressList, err := clientset.ExtensionsV1beta1().Ingresses(<namespace>).List(&meta_v1.ListOptions{})
//watch ingress
watchInterface, err := clientset.ExtensionsV1beta1().Ingresses(<namespace>).Watch(&meta_v1.ListOptions{})

replicaSet

//声明replicaSet对象
var replicaSet *v1beta1.ReplicaSet
//构造replicaSet对象
//创建replicaSet
replicaSet, err := clientset.ExtensionsV1beta1().ReplicaSets(<namespace>).Create(<replicaSet>)
//更新replicaSet
replicaSet, err := clientset.ExtensionsV1beta1().ReplicaSets(<namespace>).Update(<replicaSet>)
//删除replicaSet
err := clientset.ExtensionsV1beta1().ReplicaSets(<namespace>).Delete(<replicaSet.Name>, &meta_v1.DeleteOptions{})
//查询replicaSet
replicaSet, err := clientset.ExtensionsV1beta1().ReplicaSets(<namespace>).Get(<replicaSet.Name>, meta_v1.GetOptions{})
//列出replicaSet
replicaSetList, err := clientset.ExtensionsV1beta1().ReplicaSets(<namespace>).List(&meta_v1.ListOptions{})
//watch replicaSet
watchInterface, err := clientset.ExtensionsV1beta1().ReplicaSets(<namespace>).Watch(&meta_v1.ListOptions{})

新版的kubernetes中一般通过deployment来创建replicaSet,再通过replicaSet来控制pod。

pod

//声明pod对象
var pod *v1.Pod
//创建pod
pod, err := clientset.CoreV1().Pods(<namespace>).Create(<pod>)
//更新pod
pod, err := clientset.CoreV1().Pods(<namespace>).Update(<pod>)
//删除pod
err := clientset.CoreV1().Pods(<namespace>).Delete(<pod.Name>, &meta_v1.DeleteOptions{})
//查询pod
pod, err := clientset.CoreV1().Pods(<namespace>).Get(<pod.Name>, meta_v1.GetOptions{})
//列出pod
podList, err := clientset.CoreV1().Pods(<namespace>).List(&meta_v1.ListOptions{})
//watch pod
watchInterface, err := clientset.CoreV1().Pods(<namespace>).Watch(&meta_v1.ListOptions{})

statefulset

//声明statefulset对象
var statefulset *v1.StatefulSet
//创建statefulset
statefulset, err := clientset.AppsV1().StatefulSets(<namespace>).Create(<statefulset>)
//更新statefulset
statefulset, err := clientset.AppsV1().StatefulSets(<namespace>).Update(<statefulset>)
//删除statefulset
err := clientset.AppsV1().StatefulSets(<namespace>).Delete(<statefulset.Name>, &meta_v1.DeleteOptions{})
//查询statefulset
statefulset, err := clientset.AppsV1().StatefulSets(<namespace>).Get(<statefulset.Name>, meta_v1.GetOptions{})
//列出statefulset
statefulsetList, err := clientset.AppsV1().StatefulSets(<namespace>).List(&meta_v1.ListOptions{})
//watch statefulset
watchInterface, err := clientset.AppsV1().StatefulSets(<namespace>).Watch(&meta_v1.ListOptions{})

通过以上对kubernetes的资源对象的操作函数可以看出,每个资源对象都有增删改查等方法,基本调用逻辑类似。一般二次开发只需要创建deployment、service、ingress三个资源对象即可,pod对象由deployment包含的replicaSet来控制创建和删除。函数调用的入参一般只有NAMESPACEkubernetesObject两个参数,部分操作有Options的参数。在创建前,需要对资源对象构造数据,可以理解为编辑一个资源对象的yaml文件,然后通过kubectl create -f xxx.yaml来创建对象。

dynamicClient

基本使用和原理分析

discoveryClient

基本使用和原理分析