主要参考这两篇文章:代码部分基本参考k8s自定义生成controller三部曲 & 中间细节部分参考code-generator使用
然后踩了非常多的坑,最后在师兄亲自指导下才成功x
完整操作流程个人总结一下
初始化项目
这里有一个大坑
一开始在 $ GOPATH/src/目录下创建文件夹k8s_customize_controller:
走到后面发现,自动生成代码那里由于路径设计等原因生成的代码要么没有informer和lister要么就自己在项目root下重新新建一个k8s_customize_controller 然后在下面生成代码,就非常奇怪。最后发现,$GOPATH/src下必须要要两个目录层级,比如 $GOPATH/src/work/k8s_customize_contrller
才能正常生成代码
进入k8s_customize_contrller目录,执行
#创建目录
mkdir -p pkg/apis/bolingcavalry
#初始化项目 把环境拉到vendor中
go mod init vendor
# 获取依赖:此处可能会出现拉不到code-generator的情况,需要手动进入vendor目录 git clone k8s.io/code-generator
go get k8s.io/apimachinery@v0.0.0-20190425132440-17f84483f500
go get k8s.io/client-go@v0.0.0-20190425172711-65184652c889
go get k8s.io/code-generator@v0.0.0-20190419212335-ff26e7842f9d
创建CRD(Custom Resource Definition)
- 在能够执行kubectl的机器上创建student.yaml文件,内容如下:
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
# metadata.name的内容是由"复数名.分组名"构成,如下,students是复数名,bolingcavalry.k8s.io是分组名
name: students.bolingcavalry.k8s.io
spec:
# 分组名,在REST API中也会用到的,格式是: /apis/分组名/CRD版本
group: bolingcavalry.k8s.io
# list of versions supported by this CustomResourceDefinition
versions:
- name: v1
# 是否有效的开关.
served: true
# 只有一个版本能被标注为storage
storage: true
# 范围是属于namespace的
scope: Namespaced
names:
# 复数名
plural: students
# 单数名
singular: student
# 类型名
kind: Student
# 简称,就像service的简称是svc
shortNames:
- stu
2.在student.yaml所在目录执行命令
kubectl apply -f student.yaml
即可在k8s环境创建Student的定义,今后如果发起对类型为Student的对象的处理,k8s的api server就能识别到该对象类型,
kubectl get crd 查看已有的crd
kubectl get crd
NAME CREATED AT
students.bolingcavalry.k8s.io 2021-04-29T08:16:08Z
kubectl describe crd stu 命令查看更多细节,stu是在student.yaml中定义的简称
kubectl get crd
NAME CREATED AT
students.bolingcavalry.k8s.io 2021-04-29T08:16:08Z
ydzhang@ydzhangdeMacBook-Pro ~ % kubectl describe crd stu
Name: students.bolingcavalry.k8s.io
Namespace:
Labels: <none>
Annotations: <none>
API Version: apiextensions.k8s.io/v1
Kind: CustomResourceDefinition
Metadata:
Creation Timestamp: 2021-04-29T08:16:08Z
Generation: 1
Managed Fields:
API Version: apiextensions.k8s.io/v1
Fields Type: FieldsV1
fieldsV1:
f:status:
f:acceptedNames:
f:kind:
f:listKind:
f:plural:
f:shortNames:
f:singular:
f:conditions:
Manager: kube-apiserver
Operation: Update
Time: 2021-04-29T08:16:08Z
API Version: apiextensions.k8s.io/v1beta1
Fields Type: FieldsV1
fieldsV1:
f:metadata:
f:annotations:
.:
f:kubectl.kubernetes.io/last-applied-configuration:
f:spec:
f:conversion:
.:
f:strategy:
f:group:
f:names:
f:kind:
f:listKind:
f:plural:
f:shortNames:
f:singular:
f:preserveUnknownFields:
f:scope:
f:version:
f:versions:
f:status:
f:storedVersions:
Manager: kubectl-client-side-apply
Operation: Update
Time: 2021-04-29T08:16:08Z
Resource Version: 76925
Self Link: /apis/apiextensions.k8s.io/v1/customresourcedefinitions/students.bolingcavalry.k8s.io
UID: 78f13ee1-7c2b-400e-9073-77e311653e23
Spec:
Conversion:
Strategy: None
Group: bolingcavalry.k8s.io
Names:
Kind: Student
List Kind: StudentList
Plural: students
Short Names:
stu
Singular: student
Preserve Unknown Fields: true
Scope: Namespaced
Versions:
Name: v1
Served: true
Storage: true
Status:
Accepted Names:
Kind: Student
List Kind: StudentList
Plural: students
Short Names:
stu
Singular: student
Conditions:
Last Transition Time: 2021-04-29T08:16:08Z
Message: protected groups must have approval annotation "api-approved.kubernetes.io", see https://github.com/kubernetes/enhancements/pull/1111
Reason: MissingAnnotation
Status: False
Type: KubernetesAPIApprovalPolicyConformant
Last Transition Time: 2021-04-29T08:16:08Z
Message: no conflicts found
Reason: NoConflicts
Status: True
Type: NamesAccepted
Last Transition Time: 2021-04-29T08:16:08Z
Message: the initial names have been accepted
Reason: InitialNamesAccepted
Status: True
Type: Established
Stored Versions:
v1
Events: <none>
3.使用etcd查看保存的crd,(这一步没有做,本地没有装etcd)
4.前面的步骤使得k8s能识别Student类型,接下来创建个Student对象:创建object-student.yaml
apiVersion: bolingcavalry.k8s.io/v1
kind: Student
metadata:
name: object-student
spec:
name: "张三"
school: "深圳中学"
执行kubectl apply -f object-student.yaml
会看到
kubectl apply -f object-student.yaml
student.bolingcavalry.k8s.io/object-student created
至此,自定义API对象(也就是CRD)就创建成功了,此刻我们只是让k8s能识别到Student这个对象的身份,但是当我们创建Student对象的时候,还没有触发任何业务(相当于创建Pod对象的时候,会触发kubelet在node节点创建docker容器),接下来要通过go代码实现创建Student对象时的触发各种业务操作(监听等等)
自动代码生成
- 进入项目目录 k8s_customize_controller/pkg/apis/bolingcavalry 下创建register.go
package bolingcavalry
const (
GroupName = "bolingcavalry.k8s.io"
Version = "v1"
)
- 在bolingcavalry下创建目录v1,在v1下创建doc.go,代码中的两行注释,都是代码生成工具会用到的,一个是声明为整个v1包下的类型定义生成DeepCopy方法,另一个声明了这个包对应的API的组名,和CRD中的组名一致;
// +k8s:deepcopy-gen=package
// +groupName=bolingcavalry.k8s.io
package v1
- 在v1下创建types.go,里面定义了Student对象的具体内容:
package v1
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// +genclient
// +genclient:noStatus
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
type Student struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec StudentSpec `json:"spec"`
}
type StudentSpec struct {
name string `json:"name"`
school string `json:"school"`
}
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// StudentList is a list of Student resources
type StudentList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata"`
Items []Student `json:"items"`
}
从上述源码可见,Student对象的内容已经被设定好,主要有name和school这两个字段,表示学生的名字和所在学校,因此创建Student对象的时候内容就要和这里匹配了;
4. 在v1目录下创建register.go文件,此文件的作用是通过addKnownTypes方法使得client可以知道Student类型的API对象:
package v1
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s_customize_controller/pkg/apis/bolingcavalry"
)
var SchemeGroupVersion = schema.GroupVersion{
Group: bolingcavalry.GroupName,
Version: bolingcavalry.Version,
}
var (
SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes)
AddToScheme = SchemeBuilder.AddToScheme
)
func Resource(resource string) schema.GroupResource {
return SchemeGroupVersion.WithResource(resource).GroupResource()
}
func Kind(kind string) schema.GroupKind {
return SchemeGroupVersion.WithKind(kind).GroupKind()
}
func addKnownTypes(scheme *runtime.Scheme) error {
scheme.AddKnownTypes(
SchemeGroupVersion,
&Student{
},
&StudentList{
},
)
// register the type in the scheme
metav1.AddToGroupVersion(scheme, SchemeGroupVersion)
return nil
}
目前为止的代码结构如下:
tree
.
└── k8s_customize_controller
└── pkg
└── apis
└── bolingcavalry
├── register.go
└── v1
├── doc.go
├── register.go
└── types.go
5 directories, 4 files
5.本来使用如下方法即可生成代码,但是由于我的code-generator拉不下来,加上go环境有些问题,以及原文章省略了一些东西,所以使用创建脚本,自动生成代码的方法最后成功生成。
cd $GOPATH/src \
&& go get -