Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 4efd64f

Browse files
author
Zhang Jun
committed
add codegen
1 parent 55eb288 commit 4efd64f

File tree

1 file changed

+359
-0
lines changed

1 file changed

+359
-0
lines changed
Lines changed: 359 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,359 @@
1+
# Kubernetes Clientset
2+
3+
<!-- TOC -->
4+
5+
- [Kubernetes Clientset](#kubernetes-clientset)
6+
- [资源类型 Scheme](#资源类型-scheme)
7+
- [types.go 文件](#typesgo-文件)
8+
- [zz_generated.deepcopy.go 文件](#zz_generateddeepcopygo-文件)
9+
- [register.go 文件](#registergo-文件)
10+
- [注册所有内置资源类型到 Scheme 对象](#注册所有内置资源类型到-scheme-对象)
11+
- [创建和使用 Kubernetes Clientset](#创建和使用-kubernetes-clientset)
12+
- [创建支持所有资源类型的全局 Clientset](#创建支持所有资源类型的全局-clientset)
13+
- [各资源类型的 Clientset](#各资源类型的-clientset)
14+
- [各资源类型的 RestFul 方法](#各资源类型的-restful-方法)
15+
- [使用资源类型的 Clientset 创建 Informer 和 Lister](#使用资源类型的-clientset-创建-informer-和-lister)
16+
- [使用 client-gen 工具生成资源类型的 Clientset](#使用-client-gen-工具生成资源类型的-clientset)
17+
- [使用 informer-gen 工具生成资源类型的 Informer](#使用-informer-gen-工具生成资源类型的-informer)
18+
- [使用 lister-gen 工具生成资源类型的 Lister](#使用-lister-gen-工具生成资源类型的-lister)
19+
20+
<!-- /TOC -->
21+
22+
## 资源类型 Scheme
23+
24+
Clienset 和 apiserver 通信时,需要根据资源对象的类型生成 Resource URL、对 Wire-data 进行**编解码(序列化/反序列化)**
25+
26+
资源类型的 Group、Version、Kind、go struct 定义、编解码(序列化/反序列化) 等内容构成了它的 `Scheme`
27+
28+
K8S 内置资源类型的 Scheme 位于 `k8s.io/api/<group>/<version>` 目录下,以 `Deployment` 为例:
29+
30+
``` bash
31+
$ pwd
32+
/Users/zhangjun/go/src/gitlab.4pd.io/pht3/aol/vendor/k8s.io/api/extensions/v1beta1
33+
34+
$ ls -l
35+
total 1048
36+
-rw-r--r-- 1 zhangjun staff 642 Jan 22 15:16 doc.go
37+
-rw-r--r-- 1 zhangjun staff 308747 Jan 22 15:16 generated.pb.go
38+
-rw-r--r-- 1 zhangjun staff 49734 Jan 22 15:16 generated.proto
39+
-rw-r--r-- 1 zhangjun staff 2042 Jan 22 15:16 register.go
40+
-rw-r--r-- 1 zhangjun staff 69022 Jan 23 22:30 types.go
41+
-rw-r--r-- 1 zhangjun staff 47996 Jan 22 15:16 types_swagger_doc_generated.go
42+
-rw-r--r-- 1 zhangjun staff 41555 Jan 22 15:16 zz_generated.deepcopy.go
43+
```
44+
45+
可以暂时忽略无关的文件,我们主要分析 `types.go``zz_generated.deepcopy.go``register.go` 三个文件。
46+
47+
1. types.go:定义本 `<group>/<version>` 下所有的资源类型,即 codegen 注释;
48+
2. zz_generated.deepcopy.go:`deepcopy-gen` 工具自动创建的、包含各资源类型的 `DeepCopyObject()` 方法的文件;
49+
3. register.go:定义了 `AddToScheme()` 函数,用于将本 `<group>/<version>` 下的各资源类型注册到 Clientset 使用的 Scheme 对象中(k8s.io/client-go/kubernetes/scheme/);
50+
51+
### types.go 文件
52+
53+
该文件定义了资源类型相关的 go struct 及 codegen 命令行工具使用的注释:
54+
55+
``` go
56+
// 来源于:k8s.io/api/extensions/v1beta1/types.go
57+
58+
// +genclient
59+
// +genclient:method=GetScale,verb=get,subresource=scale,result=Scale
60+
// +genclient:method=UpdateScale,verb=update,subresource=scale,input=Scale,result=Scale
61+
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
62+
63+
// DEPRECATED - This group version of Deployment is deprecated by apps/v1beta2/Deployment. See the release notes for
64+
// more information.
65+
// Deployment enables declarative updates for Pods and ReplicaSets.
66+
type Deployment struct {
67+
metav1.TypeMeta `json:",inline"`
68+
// Standard object metadata.
69+
// +optional
70+
metav1.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`
71+
72+
// Specification of the desired behavior of the Deployment.
73+
// +optional
74+
Spec DeploymentSpec `json:"spec,omitempty" protobuf:"bytes,2,opt,name=spec"`
75+
76+
// Most recently observed status of the Deployment.
77+
// +optional
78+
Status DeploymentStatus `json:"status,omitempty" protobuf:"bytes,3,opt,name=status"`
79+
}
80+
81+
// DeploymentSpec is the specification of the desired behavior of the Deployment.
82+
type DeploymentSpec struct {
83+
Replicas *int32 `json:"replicas,omitempty" protobuf:"varint,1,opt,name=replicas"`
84+
Selector *metav1.LabelSelector `json:"selector,omitempty" protobuf:"bytes,2,opt,name=selector"`
85+
Template v1.PodTemplateSpec `json:"template" protobuf:"bytes,3,opt,name=template"`
86+
Strategy DeploymentStrategy `json:"strategy,omitempty" patchStrategy:"retainKeys" protobuf:"bytes,4,opt,name=strategy"`
87+
MinReadySeconds int32 `json:"minReadySeconds,omitempty" protobuf:"varint,5,opt,name=minReadySeconds"`
88+
RevisionHistoryLimit *int32 `json:"revisionHistoryLimit,omitempty" protobuf:"varint,6,opt,name=revisionHistoryLimit"`
89+
Paused bool `json:"paused,omitempty" protobuf:"varint,7,opt,name=paused"`
90+
RollbackTo *RollbackConfig `json:"rollbackTo,omitempty" protobuf:"bytes,8,opt,name=rollbackTo"`
91+
ProgressDeadlineSeconds *int32 `json:"progressDeadlineSeconds,omitempty" protobuf:"varint,9,opt,name=progressDeadlineSeconds"`
92+
}
93+
```
94+
95+
### zz_generated.deepcopy.go 文件
96+
97+
所有注册到 Scheme 的资源类型都要实现 `runtime.Object` 接口:
98+
99+
``` go
100+
// 来源于:k8s.io/apimachinery/pkg/runtime/interfaces.go
101+
type Object interface {
102+
GetObjectKind() schema.ObjectKind
103+
DeepCopyObject() Object
104+
}
105+
```
106+
107+
K8S 各资源类型的 go struct 定义都嵌入了 `metav1.TypeMeta` 类型,而该类型实现了 `GetObjectKind()` 方法,故各资源类型只需要实现 `DeepCopyObject()` 方法。
108+
109+
我们不需要手动为各资源类型定义 `DeepCopyObject()` 方法,而是使用 `deepcopy-gen` 工具命令统一、自动地为各资源类型生成该方法。
110+
111+
deepcopy-gen 工具读取 `types.go` 文件中的 `+k8s:deepcopy-gen` 注释,如:
112+
113+
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
114+
115+
然后将生成的结果保存到 `zz_generated.deepcopy.go` 文件,以 `Deployment` 类型为例:
116+
117+
``` go
118+
// 来源于:k8s.io/api/extensions/v1beta1/zz_generated.deepcopy.go
119+
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Deployment.
120+
func (in *Deployment) DeepCopy() *Deployment {
121+
if in == nil {
122+
return nil
123+
}
124+
out := new(Deployment)
125+
in.DeepCopyInto(out)
126+
return out
127+
}
128+
129+
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
130+
func (in *Deployment) DeepCopyObject() runtime.Object {
131+
if c := in.DeepCopy(); c != nil {
132+
return c
133+
}
134+
return nil
135+
}
136+
```
137+
138+
### register.go 文件
139+
140+
使用 `deepcopy-gen` 工具命令统一、自动地为各资源类型生成`DeepCopyObject()` 方法后,各资源类型就满足了 `runtime.Object` 接口,进而可以注册到 Scheme 中。
141+
142+
各 API Group 的各 Version 目录下,都有一个 `register.go` 文件,该文件对外提供的 `AddToScheme()` 方法用于将本 Group/Version 下的各资源类型注册到**传入**的 Scheme 对象中(k8s.io/client-go/kubernetes/scheme/register.go 中创建该 Scheme 对象),然后 Clientset 就可以使用它对各类型对象进行编解码了。
143+
144+
``` go
145+
// 来源于:k8s.io/api/extensions/v1beta1/register.go
146+
// 本 package 的 Group 名称
147+
const GroupName = "extensions"
148+
149+
// 注册时提供的 Group/Version 信息
150+
// 一个 Group 目录下,有可能有多个 Version 的子目录
151+
var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1beta1"}
152+
153+
// Resource 实际就是资源类型的完整路径 <Group>/<Version>/<Plural>,如 extensions/v1beta1/deployments
154+
// Plural 是资源类型的复数形式
155+
func Resource(resource string) schema.GroupResource {
156+
return SchemeGroupVersion.WithResource(resource).GroupResource()
157+
}
158+
159+
var (
160+
SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes)
161+
localSchemeBuilder = &SchemeBuilder
162+
// 对外暴露的 AddToScheme() 方法用于注册该 Group/Verion 下的所有资源类型
163+
AddToScheme = localSchemeBuilder.AddToScheme
164+
)
165+
166+
// 将本 Group/Version 下的所有资源类型注册到传入的 scheme
167+
func addKnownTypes(scheme *runtime.Scheme) error {
168+
scheme.AddKnownTypes(SchemeGroupVersion,
169+
&Deployment{},
170+
&DeploymentList{},
171+
...
172+
)
173+
// Add the watch version that applies
174+
metav1.AddToGroupVersion(scheme, SchemeGroupVersion)
175+
return nil
176+
}
177+
```
178+
179+
## 注册所有内置资源类型到 Scheme 对象
180+
181+
需要将 `k8s.io/api/<group>/<version>` 目录下的各资源类型注册到**全局 Scheme 对象**,这样 Clienset 才能识别和使用它们的对象。
182+
183+
client-go 的 `scheme package`(k8s.io/client-go/kubernetes/scheme/)定义了这个全局 `Scheme` 对象,并将各 `k8s.io/api/<Group>/<Version>` 目录下的资源类型注册到它上面。
184+
185+
Scheme 对象还被用于创建另外两个外部对象:
186+
1. 对资源类型对象进行编解码(序列化/反序列化)的工厂对象 `Codecs`,后续使用它配置 `rest.Config.NegotiatedSerializer`;
187+
2. 参数编解码对象 `ParameterCodec`,后续调用 RestFul 的方法时使用,如 `VersionedParams(&options, scheme.ParameterCodec)`
188+
189+
``` go
190+
// 来源于 k8s.io/client-go/kubernetes/scheme/register.go
191+
// 新建一个 Scheme,后续所有 K8S 类型均添加到该 Scheme;
192+
var Scheme = runtime.NewScheme()
193+
// 为 Scheme 中的所有类型创建一个编解码工厂;
194+
var Codecs = serializer.NewCodecFactory(Scheme)
195+
// 为 Scheme 中的所有类型创建一个参数编解码工厂
196+
var ParameterCodec = runtime.NewParameterCodec(Scheme)
197+
198+
// 将各 `k8s.io/api/<Group>/<Version>` 目录下的资源类型的 AddToScheme() 方法注册到 SchemeBuilder 中
199+
var localSchemeBuilder = runtime.SchemeBuilder{
200+
...
201+
extensionsv1beta1.AddToScheme,
202+
...
203+
}
204+
var AddToScheme = localSchemeBuilder.AddToScheme
205+
206+
func init() {
207+
v1.AddToGroupVersion(Scheme, schema.GroupVersion{Version: "v1"})
208+
// 调用 SchemeBuilder 中各资源对象的 AddToScheme() 方法,将它们注册到到 Scheme 对象。
209+
utilruntime.Must(AddToScheme(Scheme))
210+
}
211+
```
212+
213+
## 创建和使用 Kubernetes Clientset
214+
215+
经过前面的铺垫分析后,我们开始分析 Kubernetes Clientset 的创建过程。
216+
217+
先从使用者的角度看看如何创建和使用 Kubernetes Clientset:
218+
219+
``` go
220+
var err error
221+
var config *rest.Config
222+
// 使用 ServiceAccount 创建集群配置
223+
if config, err = rest.InClusterConfig(); err != nil {
224+
// 使用 kubeConfig 指向的配置文件创建集群配置
225+
if config, err = clientcmd.BuildConfigFromFlags("", *kubeConfig); err != nil {
226+
panic(err.Error())
227+
}
228+
}
229+
230+
// 创建 clientset
231+
clientset, err = kubernetes.NewForConfig(config)
232+
if err != nil {
233+
panic(err.Error())
234+
}
235+
236+
// 使用 clienset 创建一个 Deploy
237+
deploy, err := c.kubeclientset.ExtensionsV1beta1().Deployments(aolDeploy.ObjectMeta.Namespace).Create(myDeploy)
238+
```
239+
240+
1. 使用 Kubeconfig 文件或 ServiceAccount 创建 Kubernetes 的 RestFul 配置参数;
241+
2. 使用 Kubernetes 的 RestFul 配置参数,创建 Clientset;
242+
3. 调用 Clientset 的方法对资源对象进行 CRUD;
243+
244+
## 创建支持所有资源类型的全局 Clientset
245+
246+
`k8s.io/client-go/kubernetes/clientset.go` 文件中创建的 Clientset 实际上是对各资源类型的 Clientset 做了一次封装:
247+
248+
1. 调用各资源类型的 NewForConfig() 函数创建对应的 Clientset;
249+
2. 后续可以使用 Clientset.<Group><Version>(),如 Clientset.ExtensionsV1beta1() 来调用具体资源类型的 Clientset;
250+
251+
``` go
252+
// 来源于 k8s.io/client-go/kubernetes/clientset.go
253+
// 传入的 rest.Config 包含 apiserver 服务器和认证信息
254+
func NewForConfig(c *rest.Config) (*Clientset, error) {
255+
configShallowCopy := *c
256+
...
257+
// 透传 rest.COnfig,调用具体分组和版本的资源类型的 ClientSet 构造函数
258+
cs.extensionsV1beta1, err = extensionsv1beta1.NewForConfig(&configShallowCopy)
259+
if err != nil {
260+
return nil, err
261+
}
262+
...
263+
cs.DiscoveryClient, err = discovery.NewDiscoveryClientForConfig(&configShallowCopy)
264+
if err != nil {
265+
return nil, err
266+
}
267+
return &cs, nil
268+
}
269+
270+
// ExtensionsV1beta1 retrieves the ExtensionsV1beta1Client
271+
func (c *Clientset) ExtensionsV1beta1() extensionsv1beta1.ExtensionsV1beta1Interface {
272+
return c.extensionsV1beta1
273+
}
274+
```
275+
276+
## 各资源类型的 Clientset
277+
278+
各资源类型的 Clientset 定义位于 `k8s.io/client-go/kubernetes/typed/<group>/<version>/<plug>_client.go` 文件中,如
279+
`k8s.io/client-go/kubernetes/typed/extensions/v1beta1/extensions_client.go`
280+
281+
比较关键的是 `setConfigDefaults()` 函数,它负责为 Clientset 配置参数:
282+
1. 资源对象的 GroupVersion;
283+
2. 资源对象的 root path;
284+
3. 对 wired data 进行编解码(序列化/反序列化)的 `NegotiatedSerializer`,使用的 `scheme.Codecs` 为前面介绍过的 `scheme package`;
285+
286+
RESTClient 根据配置的 root path 和 GroupVersion,构造 Resource 地址(格式为 `/apis/<group>/<version>/<name>`)。
287+
288+
``` go
289+
// 来源于 k8s.io/client-go/kubernetes/typed/extensions/v1beta1/extensions_client.go
290+
// 传入的 rest.Config 包含 apiserver 服务器和认证信息
291+
func NewForConfig(c *rest.Config) (*ExtensionsV1beta1Client, error) {
292+
config := *c
293+
// 为 rest.Config 设置资源对象相关的参数
294+
if err := setConfigDefaults(&config); err != nil {
295+
return nil, err
296+
}
297+
// 创建 ExtensionsV1beta1 的 RestClient
298+
client, err := rest.RESTClientFor(&config)
299+
if err != nil {
300+
return nil, err
301+
}
302+
return &ExtensionsV1beta1Client{client}, nil
303+
}
304+
305+
func setConfigDefaults(config *rest.Config) error {
306+
// 资源对象的 GroupVersion
307+
gv := v1beta1.SchemeGroupVersion
308+
config.GroupVersion = &gv
309+
// 资源对象的 root path
310+
config.APIPath = "/apis"
311+
// 使用注册的资源类型 Schema 对请求和响应进行编解码
312+
// scheme 为前面分析过的 k8s.io/client-go/kubernetes/scheme package
313+
config.NegotiatedSerializer = serializer.DirectCodecFactory{CodecFactory: scheme.Codecs}
314+
315+
if config.UserAgent == "" {
316+
config.UserAgent = rest.DefaultKubernetesUserAgent()
317+
}
318+
319+
return nil
320+
}
321+
322+
func (c *ExtensionsV1beta1Client) Deployments(namespace string) DeploymentInterface {
323+
return newDeployments(c, namespace)
324+
}
325+
```
326+
327+
## 各资源类型的 RestFul 方法
328+
329+
使用各资源类型的 Clientset 创建资源类型的 RestFul 方法,参数的编解码工厂 `scheme.ParameterCodec` 来源于前面介绍的 `scheme package` 中。
330+
331+
``` go
332+
// 来源于 k8s.io/client-go/kubernetes/typed/extensions/v1beta1/deployment.go
333+
// newDeployments returns a Deployments
334+
func newDeployments(c *ExtensionsV1beta1Client, namespace string) *deployments {
335+
return &deployments{
336+
client: c.RESTClient(),
337+
ns: namespace,
338+
}
339+
}
340+
341+
// Get takes name of the deployment, and returns the corresponding deployment object, and an error if there is any.
342+
func (c *deployments) Get(name string, options v1.GetOptions) (result *v1beta1.Deployment, err error) {
343+
result = &v1beta1.Deployment{}
344+
// 发起实际的 RestFul 请求;
345+
err = c.client.Get().
346+
Namespace(c.ns).
347+
Resource("deployments").
348+
Name(name).
349+
VersionedParams(&options, scheme.ParameterCodec).
350+
Do().
351+
Into(result)
352+
return
353+
}
354+
```
355+
356+
## 使用资源类型的 Clientset 创建 Informer 和 Lister
357+
## 使用 client-gen 工具生成资源类型的 Clientset
358+
## 使用 informer-gen 工具生成资源类型的 Informer
359+
## 使用 lister-gen 工具生成资源类型的 Lister

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /