Kubernetes Clientset
- Kubernetes Clientset
- 資源類型 Scheme
- types.go 檔案
- zz_generated.deepcopy.go 檔案
- register.go 檔案
- 注冊所有内置資源類型到 Scheme 對象
- 建立和使用 Kubernetes Clientset
- 建立支援所有資源類型的全局 Clientset
- 各資源類型的 Clientset
- 各資源類型的 RestFul 方法
- 使用資源類型的 Clientset 建立 Informer 和 Lister
- 使用 codegen 工具生成資源類型的 Clientset、Informer 和 Lister
- 參考
- 資源類型 Scheme
資源類型 Scheme
Clienset 和 apiserver 通信時,需要根據資源對象的類型生成 Resource URL、對 Wire-data 進行編解碼(序列化/反序列化)。
資源類型的 Group、Version、Kind、go struct 定義、編解碼(序列化/反序列化) 等内容構成了它的
Scheme
。
K8S 内置資源類型的 Scheme 位于
k8s.io/api/<group>/<version>
目錄下,以
Deployment
為例:
$ pwd
/Users/zhangjun/go/src/gitlab.4pd.io/pht3/aol/vendor/k8s.io/api/extensions/v1beta1
$ ls -l
total 1048
-rw-r--r-- 1 zhangjun staff 642 Jan 22 15:16 doc.go
-rw-r--r-- 1 zhangjun staff 308747 Jan 22 15:16 generated.pb.go
-rw-r--r-- 1 zhangjun staff 49734 Jan 22 15:16 generated.proto
-rw-r--r-- 1 zhangjun staff 2042 Jan 22 15:16 register.go
-rw-r--r-- 1 zhangjun staff 69022 Jan 23 22:30 types.go
-rw-r--r-- 1 zhangjun staff 47996 Jan 22 15:16 types_swagger_doc_generated.go
-rw-r--r-- 1 zhangjun staff 41555 Jan 22 15:16 zz_generated.deepcopy.go
可以暫時忽略無關的檔案,我們主要分析
types.go
、
zz_generated.deepcopy.go
和
register.go
三個檔案。
- types.go:定義本
下所有的資源類型和 codegen 注釋;<group>/<version>
- zz_generated.deepcopy.go:
工具建立的、定義各資源類型deepcopy-gen
方法的檔案;DeepCopyObject()
- register.go:定義了
函數,用于将本AddToScheme()
下的各資源類型注冊到 Clientset 使用的 Scheme 對象中(k8s.io/client-go/kubernetes/scheme/);<group>/<version>
- 對于自定義資源類型,需要在 doc.go 中添加注釋 // +groupName=<group_name>,否則後續自動生成的 fake clientset 的 Group 不對;
types.go 檔案
該檔案包含資源類型的 go struct 定義及 codegen 指令行工具使用的注釋:
// 來源于:k8s.io/api/extensions/v1beta1/types.go
// +genclient
// +genclient:method=GetScale,verb=get,subresource=scale,result=Scale
// +genclient:method=UpdateScale,verb=update,subresource=scale,input=Scale,result=Scale
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// DEPRECATED - This group version of Deployment is deprecated by apps/v1beta2/Deployment. See the release notes for
// more information.
// Deployment enables declarative updates for Pods and ReplicaSets.
type Deployment struct {
metav1.TypeMeta `json:",inline"`
// Standard object metadata.
// +optional
metav1.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`
// Specification of the desired behavior of the Deployment.
// +optional
Spec DeploymentSpec `json:"spec,omitempty" protobuf:"bytes,2,opt,name=spec"`
// Most recently observed status of the Deployment.
// +optional
Status DeploymentStatus `json:"status,omitempty" protobuf:"bytes,3,opt,name=status"`
}
// DeploymentSpec is the specification of the desired behavior of the Deployment.
type DeploymentSpec struct {
Replicas *int32 `json:"replicas,omitempty" protobuf:"varint,1,opt,name=replicas"`
Selector *metav1.LabelSelector `json:"selector,omitempty" protobuf:"bytes,2,opt,name=selector"`
Template v1.PodTemplateSpec `json:"template" protobuf:"bytes,3,opt,name=template"`
Strategy DeploymentStrategy `json:"strategy,omitempty" patchStrategy:"retainKeys" protobuf:"bytes,4,opt,name=strategy"`
MinReadySeconds int32 `json:"minReadySeconds,omitempty" protobuf:"varint,5,opt,name=minReadySeconds"`
RevisionHistoryLimit *int32 `json:"revisionHistoryLimit,omitempty" protobuf:"varint,6,opt,name=revisionHistoryLimit"`
Paused bool `json:"paused,omitempty" protobuf:"varint,7,opt,name=paused"`
RollbackTo *RollbackConfig `json:"rollbackTo,omitempty" protobuf:"bytes,8,opt,name=rollbackTo"`
ProgressDeadlineSeconds *int32 `json:"progressDeadlineSeconds,omitempty" protobuf:"varint,9,opt,name=progressDeadlineSeconds"`
}
zz_generated.deepcopy.go 檔案
所有注冊到 Scheme 的資源類型都要實作
runtime.Object
接口:
// 來源于:k8s.io/apimachinery/pkg/runtime/interfaces.go
type Object interface {
GetObjectKind() schema.ObjectKind
DeepCopyObject() Object
}
K8S 各資源類型的 go struct 定義都嵌入了
metav1.TypeMeta
類型,而該類型實作了
GetObjectKind()
方法,故各資源類型隻需要實作
DeepCopyObject()
方法。
我們不需要手動為各資源類型定義
DeepCopyObject()
方法,而是使用
deepcopy-gen
工具指令統一、自動地生成該方法。
deepcopy-gen 工具讀取
types.go
檔案中的
+k8s:deepcopy-gen
注釋,如:
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
然後将生成的結果儲存到
zz_generated.deepcopy.go
檔案。
以
Deployment
類型為例:
// 來源于:k8s.io/api/extensions/v1beta1/zz_generated.deepcopy.go
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Deployment.
func (in *Deployment) DeepCopy() *Deployment {
if in == nil {
return nil
}
out := new(Deployment)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *Deployment) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
register.go 檔案
使用
deepcopy-gen
工具指令統一、自動地為各資源類型生成
DeepCopyObject()
方法後,各資源類型就滿足了
runtime.Object
接口,進而可以注冊到 Scheme 中。
各 API Group/Version 目錄下都有一個
register.go
檔案,該檔案對外提供的
AddToScheme()
方法用于将本 Group/Version 下的各資源類型注冊到傳入的 Scheme 對象中(k8s.io/client-go/kubernetes/scheme/register.go 中建立該 Scheme 對象),然後 Clientset 就可以使用它進行 Wired-data 和對象之間的轉換了。
// 來源于:k8s.io/api/extensions/v1beta1/register.go
// 本 package 的 Group 名稱
const GroupName = "extensions"
// 注冊時提供的 Group/Version 資訊
// 一個 Group 目錄下,有可能有多個 Version 的子目錄
var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1beta1"}
// Resource 實際就是資源類型的完整路徑 <Group>/<Version>/<Plural>,如 extensions/v1beta1/deployments
// Plural 是資源類型的複數形式
func Resource(resource string) schema.GroupResource {
return SchemeGroupVersion.WithResource(resource).GroupResource()
}
var (
SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes)
localSchemeBuilder = &SchemeBuilder
// 對外暴露的 AddToScheme() 方法用于注冊該 Group/Verion 下的所有資源類型
AddToScheme = localSchemeBuilder.AddToScheme
)
// 将本 Group/Version 下的所有資源類型注冊到傳入的 scheme
func addKnownTypes(scheme *runtime.Scheme) error {
scheme.AddKnownTypes(SchemeGroupVersion,
&Deployment{},
&DeploymentList{},
...
)
// Add the watch version that applies
metav1.AddToGroupVersion(scheme, SchemeGroupVersion)
return nil
}
注冊所有内置資源類型到 Scheme 對象
需要将
k8s.io/api/<group>/<version>
目錄下的各資源類型注冊到全局 Scheme 對象,這樣 Clienset 才能識别和使用它們。
client-go 的
scheme package
(k8s.io/client-go/kubernetes/scheme/)定義了這個全局
Scheme
對象,并将各
k8s.io/api/<Group>/<Version>
目錄下的資源類型注冊到它上面。
Scheme 對象還被用于建立另外兩個外部對象:
- 對資源類型對象進行編解碼(序列化/反序列化)的工廠對象
,後續使用它配置Codecs
;rest.Config.NegotiatedSerializer
- 參數編解碼對象
,後續調用 RestFul 的方法時使用,如ParameterCodec
。VersionedParams(&options, scheme.ParameterCodec)
// 來源于 k8s.io/client-go/kubernetes/scheme/register.go
// 建立一個 Scheme,後續所有 K8S 類型均添加到該 Scheme;
var Scheme = runtime.NewScheme()
// 為 Scheme 中的所有類型建立一個編解碼工廠;
var Codecs = serializer.NewCodecFactory(Scheme)
// 為 Scheme 中的所有類型建立一個參數編解碼工廠
var ParameterCodec = runtime.NewParameterCodec(Scheme)
// 将各 `k8s.io/api/<Group>/<Version>` 目錄下的資源類型的 AddToScheme() 方法注冊到 SchemeBuilder 中
var localSchemeBuilder = runtime.SchemeBuilder{
...
extensionsv1beta1.AddToScheme,
...
}
var AddToScheme = localSchemeBuilder.AddToScheme
func init() {
v1.AddToGroupVersion(Scheme, schema.GroupVersion{Version: "v1"})
// 調用 SchemeBuilder 中各資源對象的 AddToScheme() 方法,将它們注冊到到 Scheme 對象。
utilruntime.Must(AddToScheme(Scheme))
}
建立和使用 Kubernetes Clientset
經過前面的鋪墊分析後,我們開始分析 Kubernetes Clientset 的建立過程。
先從使用者的角度看看如何建立和使用 Kubernetes Clientset:
var err error
var config *rest.Config
// 使用 ServiceAccount 建立叢集配置
if config, err = rest.InClusterConfig(); err != nil {
// 使用 kubeConfig 指向的配置檔案建立叢集配置
if config, err = clientcmd.BuildConfigFromFlags("", *kubeConfig); err != nil {
panic(err.Error())
}
}
// 建立 k8s clientset
clientset, err = kubernetes.NewForConfig(config)
if err != nil {
panic(err.Error())
}
// 使用 clienset 建立一個 Deploy
deploy, err := c.kubeclientset.ExtensionsV1beta1().Deployments(aolDeploy.ObjectMeta.Namespace).Create(myDeploy)
- 使用 Kubeconfig 檔案或 ServiceAccount 建立 Kubernetes 的 RestFul 配置參數;
- 使用 Kubernetes 的 RestFul 配置參數,建立 Clientset;
- 調用 Clientset 的方法對資源對象進行 CRUD;
建立支援所有資源類型的全局 Clientset
k8s.io/client-go/kubernetes/clientset.go
檔案中建立的 Clientset 實際上是對各資源類型的 Clientset 做了一次封裝:
- 調用各資源類型的 NewForConfig() 函數建立對應的 Clientset;
- 後續可以使用 Clientset.(),如 Clientset.ExtensionsV1beta1() 來調用具體資源類型的 Clientset;
// 來源于 k8s.io/client-go/kubernetes/clientset.go
// 傳入的 rest.Config 包含 apiserver 伺服器和認證資訊
func NewForConfig(c *rest.Config) (*Clientset, error) {
configShallowCopy := *c
...
// 透傳 rest.Config,調用具體分組和版本的資源類型的 ClientSet 構造函數
cs.extensionsV1beta1, err = extensionsv1beta1.NewForConfig(&configShallowCopy)
if err != nil {
return nil, err
}
...
cs.DiscoveryClient, err = discovery.NewDiscoveryClientForConfig(&configShallowCopy)
if err != nil {
return nil, err
}
return &cs, nil
}
// ExtensionsV1beta1 retrieves the ExtensionsV1beta1Client
func (c *Clientset) ExtensionsV1beta1() extensionsv1beta1.ExtensionsV1beta1Interface {
return c.extensionsV1beta1
}
各資源類型的 Clientset
各資源類型的 Clientset 定義位于
k8s.io/client-go/kubernetes/typed/<group>/<version>/<plug>_client.go
檔案中,如
k8s.io/client-go/kubernetes/typed/extensions/v1beta1/extensions_client.go
。
比較關鍵的是
setConfigDefaults()
函數,它負責為 Clientset 配置參數:
- 資源對象的 GroupVersion;
- 資源對象的 root path;
- 對 wired data 進行編解碼(序列化/反序列化)的
,使用的NegotiatedSerializer
為前面介紹過的scheme.Codecs
;scheme package
RESTClient 根據配置的 root path 和 GroupVersion,構造 Resource 位址(格式為
/apis/<group>/<version>/<name>
)。
// 來源于 k8s.io/client-go/kubernetes/typed/extensions/v1beta1/extensions_client.go
// 傳入的 rest.Config 包含 apiserver 伺服器和認證資訊
func NewForConfig(c *rest.Config) (*ExtensionsV1beta1Client, error) {
config := *c
// 為 rest.Config 設定資源對象相關的參數
if err := setConfigDefaults(&config); err != nil {
return nil, err
}
// 建立 ExtensionsV1beta1 的 RestClient
client, err := rest.RESTClientFor(&config)
if err != nil {
return nil, err
}
return &ExtensionsV1beta1Client{client}, nil
}
func setConfigDefaults(config *rest.Config) error {
// 資源對象的 GroupVersion
gv := v1beta1.SchemeGroupVersion
config.GroupVersion = &gv
// 資源對象的 root path
config.APIPath = "/apis"
// 使用注冊的資源類型 Schema 對請求和響應進行編解碼
// scheme 為前面分析過的 k8s.io/client-go/kubernetes/scheme package
config.NegotiatedSerializer = serializer.DirectCodecFactory{CodecFactory: scheme.Codecs}
if config.UserAgent == "" {
config.UserAgent = rest.DefaultKubernetesUserAgent()
}
return nil
}
func (c *ExtensionsV1beta1Client) Deployments(namespace string) DeploymentInterface {
return newDeployments(c, namespace)
}
各資源類型的 RestFul 方法
使用各資源類型的 Clientset 建立特定資源類型的 RestFul 方法,參數的編解碼工廠
scheme.ParameterCodec
來源于前面介紹的
scheme package
中。
// 來源于 k8s.io/client-go/kubernetes/typed/extensions/v1beta1/deployment.go
// newDeployments returns a Deployments
func newDeployments(c *ExtensionsV1beta1Client, namespace string) *deployments {
return &deployments{
client: c.RESTClient(),
ns: namespace,
}
}
// Get takes name of the deployment, and returns the corresponding deployment object, and an error if there is any.
func (c *deployments) Get(name string, options v1.GetOptions) (result *v1beta1.Deployment, err error) {
result = &v1beta1.Deployment{}
// 發起實際的 RestFul 請求;
err = c.client.Get().
Namespace(c.ns).
Resource("deployments").
Name(name).
VersionedParams(&options, scheme.ParameterCodec).
Do().
Into(result)
return
}
使用資源類型的 Clientset 建立 Informer 和 Lister
使用 codegen 工具生成資源類型的 Clientset、Informer 和 Lister
目錄結構:
zhangjun:core zhangjun$ pwd
/Users/zhangjun/go/src/gitlab.4pd.io/pht3/aol/pkg/apis/core/v1alpha1
zhangjun:v1alpha1 zhangjun$ ls -l
total 976
-rw-r--r-- 1 zhangjun staff 643 Jan 28 21:14 doc.go
-rw-r--r-- 1 zhangjun staff 1200 Jan 28 21:37 objectreference.go
-rw-r--r-- 1 zhangjun staff 2741 Jan 28 21:39 register.go
-rw-r--r-- 1 zhangjun staff 273720 Jan 28 21:40 types.go
-rw-r--r-- 1 zhangjun staff 154116 Jan 28 21:41 zz_generated.deepcopy.go
在
doc.go
檔案裡添加
// +k8s:deepcopy-gen=package
,否則後續不會為
types.go
中的類型生成
DeepCopy()
方法:
// 來源于 doc.go
// +k8s:deepcopy-gen=package
// +k8s:openapi-gen=true
package v1alpha1
參考
Kubernetes Deep Dive: Code Generation for CustomResources