在实现的开发过程中,开发人员需要借助 Operator 开发框架来扩展 Kubernetes API。operator framework 可以提供 webhook 和 controller 的框架,它的主要意义在于帮助开发者屏蔽了一些通用的底层细节,不需要开发者再去实现消息通知触发、失败重新入队等,只需关注被管理应用的运维逻辑实现即可。主流的 operator framework 主要有两个:kubebuilder 和 operator-sdk。
主要步骤是首先定义好 Group、version 以及 kind,然后使用 operator framework 命令即可生成所需的 operator 框架,CRD结构等,具体用法这里不详细叙述。
自定义controller实现// Define some nested types to simplify the codebase
type Controller interface {
Run(int, <-chan struct{})
}
type InformerFactory interface {
Start(stopCh <-chan struct{})
WaitForCacheSync(stopCh <-chan struct{}) map[reflect.Type]bool
}
// 初始化两种 CRD 的 controller
// Initialize all controllers
controllers := []Controller{
// add example1 controller
example.NewController(deps),
}
informer初始化
// Start informer factories after all controllers are initialized.
informerFactories := []InformerFactory{
informerFactory,
}
for _, f := range informerFactories {
f.Start(ctx.Done())
for v, synced := range f.WaitForCacheSync(wait.NeverStop) {
if !synced {
klog.Fatalf("error syncing informer for %v", v)
}
}
}
klog.Info("cache of informer factories sync successfully")
// Start syncLoop for all controllers
for _, controller := range controllers {
c := controller
go wait.Forever(func() { c.Run(cliCfg.Workers, ctx.Done()) }, cliCfg.WaitDuration)
}
只需要实现 Run 方法
// Controller controls backup.
type Controller struct {
// control returns an interface capable of syncing a backup.
// Abstracted out for testing.
control ControlInterface
// backups that need to be synced.
queue workqueue.RateLimitingInterface
}
// NewController creates a backup controller.
func NewController(control ControlInterface) *Controller {
c := &Controller{
control: control,
queue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "backup"),
}
informer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
AddFunc: c.update,
UpdateFunc: func(old, cur interface{}) {
c.update(cur)
},
DeleteFunc: c.update,
})
return c
}
// Run runs the controller.
func (c *Controller) Run(workers int, stopCh <-chan struct{}) {
defer utilruntime.HandleCrash()
defer c.queue.ShutDown()
klog.Info("Starting controller")
defer klog.Info("Shutting down controller")
for i := 0; i < workers; i++ {
go wait.Until(c.worker, time.Second, stopCh)
}
<-stopCh
}
// worker runs a worker goroutine that invokes processNextWorkItem until the the controller's queue is closed
func (c *Controller) worker() {
for c.processNextWorkItem() {
}
}
// processNextWorkItem dequeues items, processes them, and marks them done. It enforces that the syncHandler is never
// invoked concurrently with the same key.
func (c *Controller) processNextWorkItem() bool {
key, quit := c.queue.Get()
if quit {
return false
}
defer c.queue.Done(key)
if err := c.sync(key.(string)); err != nil {
if perrors.Find(err, util.IsRequeueError) != nil {
c.queue.AddRateLimited(key)
} else if perrors.Find(err, util.IsIgnoreError) != nil {
klog.V(4).Infof("CR: %v, ignore err: %v", key.(string), err)
} else {
utilruntime.HandleError(fmt.Errorf("CR: %v, sync failed, err: %v, requeuing", key.(string), err))
c.queue.AddRateLimited(key)
}
} else {
c.queue.Forget(key)
}
return true
}
// enqueueBackup enqueues the given backup in the work queue.
func (c *Controller) update(obj interface{}) {
// TODO: Determine whether to enter the queue
}
client 生成工具
生成完 operator 框架之后,目录结构一般如下(不同工具生成的文件目录可能有细微区别):
pis/
`-- bcrds
|-- group.go
`-- v1alpha1
|-- doc.go
|-- register.go
|-- example_types.go
`-- zz_generated.deepcopy.go
其中 types.go 为具体的 CRD struct 结构。
对 CRD 的增删改查需要通过 client 方式来实现,这里用 github.com/openshift-evangelists/crd-code-generation/vendor/k8s.io/code-generator 工具生成 client。
# `--output-base $SCRIPT_ROOT` will output generated code to current dir
bash "${CODEGEN_PKG}"/generate-groups.sh "deepcopy,client,informer,lister"
operator/pkg/client
operator/pkg/apis
bcrds:v1alpha1
--output-base $SCRIPT_ROOT
--go-header-file ./hack/boilerplate/boilerplate.generatego.txt
boilerplate.generatego.txt 为 client 模板文件,生成的文件均会在 go 文件前加上 TXT 中的模板内容。
目录结构
pkg
|--pis
`-- bcrds
|-- group.go
`-- v1alpha1
|-- doc.go
|-- register.go
|-- example_types.go
`-- zz_generated.deepcopy.go
|--client
|--clientset
|--informers
|--listers



