一个 API 对象在 Etcd 里的完整资源路径
- Group(API 组)
- Version(API 版本)
- Resource(API 资源类型)
API 对的结构
Kubernetes 是如何对 Resource、Group 和 Version 进行解析?
step1 首先,Kubernetes 会匹配 API 对象的组
step2 Kubernetes 会进一步匹配到 API 对象的版本号
step3 Kubernetes 会匹配 API 对象的资源类型
对象的创建过程
比如我们要创建一个 CronJob
apiVersion: batch/v2alpha1
kind: CronJob
...
第1步 请求APIServer
发起了创建 CronJob 的 POST 请求,编写的 YAML 的信息就被提交给了 APIServer。
APIServer 的第一个功能,就是过滤这个请求,并完成一些前置性的工作:
- 授权
- 超时处理
- 审计等
第2步 APIServer 进行 MUX 和 Routes 流程
MUX 和 Routes 是 APIServer 完成 URL 和 Handler 绑定的场所
APIServer 的 Handler 找到对应的 CronJob 类型定义(其实就是一个模型的概念 里面有很多的字段 属性)
第3步 APIServer 进行 Convert操作, 创建 CronJob 对象
根据这个 CronJob 类型定义,使用用户提交的 YAML 文件里的字段,创建一个 CronJob 对象。
Convert 工作
把提交的 YAML 文件,转换成一个叫作 Super Version 的对象,它正是该 API 资源类型所有版本的字段全集。
这样用户提交的不同版本的 YAML 文件,就都可以用这个 Super Version 对象来进行处理了
第4步 APIServer 进行 Admission() 和 Validation()
Admission()
使用Admission Controller
- 在对象被持久化之前拦截API服务的请求
- API服务器接收到客户端请求时对其进行验证和处理
Validation
负责验证这个对象里的各个字段是否合法
被验证过的 API 对象,保存在Registry的数据结构中
第5步 APIServer 将对象(数据结构) 保存到ETCD中
APIServer 会把验证过的 API 对象转换成用户最初提交的版本,进行序列化操作,并调用 Etcd 的 API 把它保存起来。
API插件机制-CRD(Custom Resource Definition)
允许用户在 Kubernetes 中添加一个跟 Pod、Node 类似的、新的 API 资源类型,即:自定义 API 资源。
创建Network 的 API 资源类型
需求: 一旦用户创建一个 Network 对象,那么 Kubernetes 就应该使用这个对象定义的网络参数,调用真实的网络插件,为用户创建一个真正的“网络”
example-network.yaml
apiVersion: samplecrd.k8s.io/v1
kind: Network
metadata:
name: example-network
spec:
cidr: "192.168.0.0/16"
gateway: "192.168.0.1"
step1 Network API 资源类型的定义
编写一个 CRD 的 YAML 文件 它的名字叫作 network.yaml,内容如下所示
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
name: networks.samplecrd.k8s.io
spec:
group: samplecrd.k8s.io
version: v1
names:
kind: Network
plural: networks
scope: Namespaced
复数(plural)是 networks
scope 是 Namespaced,即:我们定义的这个 Network 是一个属于 Namespace 的对象
step2 注册一个Network类型到k8s中 以便识别用户输入的参数
让 Kubernetes“认识”这种 YAML 文件里描述的“网络”部分,比如“cidr”(网段),“gateway”(网关)这些字段的含义
step1 创建工程项目
pkg/apis/samplecrd 就是 API 组的名字,v1 是版本
step2 放置后面要用到的全局变量
pkg/apis/samplecrd/register.go
package samplecrd
const (
GroupName = "samplecrd.k8s.io"
Version = "v1"
)
step3 Golang 的文档源文件
pkg/apis/samplecrd/v1/doc.go
// +k8s:deepcopy-gen=package
// +groupName=samplecrd.k8s.io
package v1
你会看到 +[=value]格式的注释,这就是 Kubernetes 进行代码生成要用的 Annotation 风格的注释。
step4 定义一个 Network 类型到底有哪些字段
比如,spec 字段里的内容
types.go 文件 定义了 Network 对象的完整描述
pkg/apis/samplecrd/v1/types.go
package v1
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
type Network struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec NetworkSpec `json:"spec"`
}
type NetworkSpec struct {
Cidr string `json:"cidr"`
Gateway string `json:"gateway"`
}
type NetworkList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata"`
Items []Network `json:"items"`
}
step5 注册Network 类型
“registry”的作用就是注册一个类型(Type)给 APIServer
pkg/apis/samplecrd/v1/register.go
package v1
...
// addKnownTypes adds our types to the API scheme by registering
// Network and NetworkList
func addKnownTypes(scheme *runtime.Scheme) error {
scheme.AddKnownTypes(
SchemeGroupVersion,
&Network{},
&NetworkList{},
)
// register the type in the scheme
metav1.AddToGroupVersion(scheme, SchemeGroupVersion)
return nil
}
step6 Network 资源类型自动生成 clientset、informer 和 lister
clientset 就是操作 Network 对象所需要使用的客户端
# 代码生成的工作目录,也就是我们的项目路径
$ ROOT_PACKAGE="github.com/resouer/k8s-controller-custom-resource"
# API Group
$ CUSTOM_RESOURCE_NAME="samplecrd"
# API Version
$ CUSTOM_RESOURCE_VERSION="v1"
# 安装k8s.io/code-generator
$ go get -u k8s.io/code-generator/...
$ cd $GOPATH/src/k8s.io/code-generator
# 执行代码自动生成,其中pkg/client是生成目标目录,pkg/apis是类型定义目录
$ ./generate-groups.sh all "$ROOT_PACKAGE/pkg/client" "$ROOT_PACKAGE/pkg/apis" "$CUSTOM_RESOURCE_NAME:$CUSTOM_RESOURCE_VERSION"
终态的项目的目录结构
pkg/apis/samplecrd/v1/zz_generated.deepcopy.go 文件,就是自动生成的 DeepCopy 代码文件
client 目录,以及下面的三个包(clientset、informers、 listers),都是 Kubernetes 为 Network 类型生成的客户端库,这些库会在后面编写自定义控制器的时候用到