CRDs code-generator 自定义资源代码生成器
参考:
Extend the Kubernetes API with CustomResourceDefinitions
Extending Kubernetes: Create Controllers for Core and Custom Resources
Background
K8S虽然提供了一些workload,但是这肯定不足以覆盖所有场景,所以需要自定义资源,让K8S更好地适配应用场景。另外,只有CRDs其实没有什么意义,根据K8S的控制器模式,必须要有与之匹配的控制器才可以,所以这里建议使用K8S官方提供的代码生成工具k8s.io/code-generator生成通用代码模板,让你的代码更符合K8S的代码规范。
但是code-generator并没有那么好用,有一些坑,这里总结下使用流程。
Define
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
name: podgroups.mind.automl
spec:
group: mind.automl
versions:
- name: v1
served: true
storage: true
scope: Namespaced
names:
plural: podgroups
singular: podgroup
kind: PodGroup
shortNames:
- pg
定义CRD,最重要的是group以及version
Init
首先在你的go项目中创建这个文件夹
mkdir -p pkg/apis/${group}/v1
然后申明CRD对应的结构体
touch pkg/apis/${group}/v1/types.go
package v1
import (
meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// +genclient
// +genclient:noStatus
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// PodGroup describes a PodGroup resource
type PodGroup struct {
// TypeMeta is the metadata for the resource, like kind and apiversion
meta_v1.TypeMeta `json:",inline"`
// ObjectMeta contains the metadata for the particular object, including
// things like...
// - name
// - namespace
// - self link
// - labels
// - ... etc ...
meta_v1.ObjectMeta `json:"metadata,omitempty"`
// Spec is the custom resource spec
Spec PodGroupSpec `json:"spec"`
}
// PodGroupSpec is the spec for a MyResource resource
type PodGroupSpec struct {
Min int `json:"min"`
}
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// PodGroupList is a list of PodGroup resources
type PodGroupList struct {
meta_v1.TypeMeta `json:",inline"`
meta_v1.ListMeta `json:"metadata"`
Items []PodGroup `json:"items"`
}
然后创建doc文件
touch pkg/apis/${group}/v1/doc.go
// +k8s:deepcopy-gen=package
// +groupName=mind.automl
package v1
最后创建schema
touch pkg/apis/${group}/v1/register.go
package v1
import (
metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
)
// GroupVersion is the identifier for the API which includes
// the name of the group and the version of the API
var SchemeGroupVersion = schema.GroupVersion{
Group: "mind.automl",
Version: "v1",
}
// create a SchemeBuilder which uses functions to add types to
// the scheme
var (
SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes)
AddToScheme = SchemeBuilder.AddToScheme
)
func Resource(resource string) schema.GroupResource {
return SchemeGroupVersion.WithResource(resource).GroupResource()
}
// addKnownTypes adds our types to the API scheme by registering
// MyResource and MyResourceList
func addKnownTypes(scheme *runtime.Scheme) error {
scheme.AddKnownTypes(
SchemeGroupVersion,
&PodGroup{},
&PodGroupList{},
)
// register the type in the scheme
metaV1.AddToGroupVersion(scheme, SchemeGroupVersion)
return nil
}
Run
执行code-generator
vendor/k8s.io/code-generator/generate-groups.sh all dl-scheduler/pkg/client dl-scheduler/pkg/apis mind.automl:v1
最终结果如下:
tree pkg
pkg
├── apis
│ └── mind.automl
│ ├── register.go
│ └── v1
│ ├── doc.go
│ ├── register.go
│ ├── types.go
│ └── zz_generated.deepcopy.go
└── client
├── clientset
│ └── versioned
│ ├── clientset.go
│ ├── doc.go
│ ├── fake
│ │ ├── clientset_generated.go
│ │ ├── doc.go
│ │ └── register.go
│ ├── scheme
│ │ ├── doc.go
│ │ └── register.go
│ └── typed
│ └── mind.automl
│ └── v1
│ ├── doc.go
│ ├── fake
│ │ ├── doc.go
│ │ ├── fake_mind.automl_client.go
│ │ └── fake_podgroup.go
│ ├── generated_expansion.go
│ ├── mind.automl_client.go
│ └── podgroup.go
├── informers
│ └── externalversions
│ ├── factory.go
│ ├── generic.go
│ ├── internalinterfaces
│ │ └── factory_interfaces.go
│ └── mind.automl
│ ├── interface.go
│ └── v1
│ ├── interface.go
│ └── podgroup.go
└── listers
└── mind.automl
└── v1
├── expansion_generated.go
└── podgroup.go
code
package main
import (
"k8s.io/client-go/rest"
"dl-scheduler/pkg/client/clientset/versioned"
"dl-scheduler/pkg/client/informers/externalversions"
"k8s.io/client-go/tools/cache"
"dl-scheduler/pkg/apis/mind.automl/v1"
"fmt"
"k8s.io/client-go/tools/clientcmd"
)
func main() {
config, err := buildConfig("https://192.168.47.49", "/etc/kubernetes/kubeconfig")
if err != nil {
fmt.Printf("%v\n", err)
return
}
client, err := versioned.NewForConfig(config)
if err != nil {
fmt.Printf("%v\n", err)
return
}
informer := externalversions.NewSharedInformerFactory(client, 0)
podGroupInformer := informer.Mind().V1().PodGroups()
podGroupInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
AddFunc: add,
})
neverStop := make(chan struct{})
informer.Start(neverStop)
<-neverStop
}
func add(obj interface{}) {
pg, ok := obj.(*v1.PodGroup)
if !ok {
fmt.Println("not pg")
return
}
fmt.Println("add pg ", pg.Name, "min ", pg.Spec.Min)
}
func buildConfig(master, kubeconfig string) (*rest.Config, error) {
if master != "" || kubeconfig != "" {
return clientcmd.BuildConfigFromFlags(master, kubeconfig)
}
return rest.InClusterConfig()
}
测试代码。这里就直接用了Informer处理ListWatch事件。