原文连接:https://blog.csdn.net/u012986012/article/details/119710511

kubebuilder是一个官方提供快速实现Operator的工具包,可快速生成k8s的CRD、Controller、Webhook,用户只需要实现业务逻辑。

类似工具还有operader-sdk,目前正在与Kubebuilder融合

kubebuilder封装了controller-runtimecontroller-tools,通过controller-gen来生产代码,简化了用户创建Operator的步骤。

一般创建Operator流程如下:

  1. 创建工作目录,初始化项目
  2. 创建API,填充字段
  3. 创建Controller,编写核心协调逻辑(Reconcile)
  4. 创建Webhook,实现接口,可选
  5. 验证测试
  6. 发布到集群中

示例

我们准备创建一个2048的游戏,对外可以提供服务,也能方便地扩缩容。

准备环境

首先你需要有Kubernetes、Docker、Golang相关环境。
Linux下安装kubebuilder

curl -L -o kubebuilder https://go.kubebuilder.io/dl/latest/$(go env GOOS)/$(go env GOARCH)
chmod +x kubebuilder && mv kubebuilder /usr/local/bin/

Create Resource [y/n]
y #生成CR
Create Controller [y/n]
y #生成Controller

目录结构如下:

├── api
│   └── v1 # CRD定义
├── bin
│   └── controller-gen
├── config
│   ├── crd # crd配置
│   ├── default
│   ├── manager # operator部署文件
│   ├── prometheus
│   ├── rbac
│   └── samples # cr示例
├── controllers
│   ├── game_controller.go # controller逻辑
│   └── suite_test.go
├── Dockerfile
├── go.mod
├── go.sum
├── hack
│   └── boilerplate.go.txt # 头文件模板
├── main.go # 项目主函数
├── Makefile
└── PROJECT #项目元数据

编写API

mygame/api/v1/game_types.go定义我们需要的字段

Spec配置如下

type GameSpec struct {// Number of desired pods. This is a pointer to distinguish between explicit// zero and not specified. Defaults to 1.// +optional//+kubebuilder:default:=1 //+kubebuilder:validation:Minimum:=1Replicas *int32 `json:"replicas,omitempty" protobuf:"varint,1,opt,name=replicas"`// Docker image name// +optionalImage string `json:"image,omitempty"`// Ingress Host nameHost string `json:"host,omitempty"`
}

kubebuilder:default可以设置默认值

Status定义如下

const (Running  = "Running"Pending  = "Pending"NotReady = "NotReady"Failed   = "Failed"
)
type GameStatus struct {// Phase is the phase of guestbookPhase string `json:"phase,omitempty"`// replicas is the number of Pods created by the StatefulSet controller.Replicas int32 `json:"replicas"`// readyReplicas is the number of Pods created by the StatefulSet controller that have a Ready Condition.ReadyReplicas int32 `json:"readyReplicas"`// LabelSelector is label selectors for query over pods that should match the replica count used by HPA.LabelSelector string `json:"labelSelector,omitempty"`
}

另外需要添加scale接口

//+kubebuilder:subresource:scale:specpath=.spec.replicas,statuspath=.status.replicas,selectorpath=.status.labelSelector

编写Controller逻辑

Controller的核心逻辑在Reconcile中,我们只需要填充自己的业务逻辑

func (r *GameReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {logger := log.FromContext(ctx)logger.Info("revice reconcile event", "name", req.String())// 获取game对象game := &myappv1.Game{}if err := r.Get(ctx, req.NamespacedName, game); err != nil {return ctrl.Result{}, client.IgnoreNotFound(err)}// 如果处在删除中直接跳过if game.DeletionTimestamp != nil {logger.Info("game in deleting", "name", req.String())return ctrl.Result{}, nil}// 同步资源,如果资源不存在创建deployment、ingress、service,并更新statusif err := r.syncGame(ctx, game); err != nil {logger.Error(err, "failed to sync game", "name", req.String())return ctrl.Result{}, nil}return ctrl.Result{}, nil
}

添加rbac配置

//+kubebuilder:rbac:groups=apps,resources=deployments,verbs=get;list;watch;create;update;patch;delete
//+kubebuilder:rbac:groups=apps,resources=deployments/status,verbs=get;update;patch
//+kubebuilder:rbac:groups=core,resources=services,verbs=get;list;watch;create;update;patch;delete
//+kubebuilder:rbac:groups=networking,resources=ingresses,verbs=get;list;watch;create;update;patch;delete

具体syncGame逻辑如下

func (r *GameReconciler) syncGame(ctx context.Context, obj *myappv1.Game) error {logger := log.FromContext(ctx)game := obj.DeepCopy()name := types.NamespacedName{Namespace: game.Namespace,Name:      game.Name,}// 构造ownerowner := []metav1.OwnerReference{{APIVersion:         game.APIVersion,Kind:               game.Kind,Name:               game.Name,Controller:         pointer.BoolPtr(true),BlockOwnerDeletion: pointer.BoolPtr(true),UID:                game.UID,},}labels := game.Labelslabels[gameLabelName] = game.Namemeta := metav1.ObjectMeta{Name:            game.Name,Namespace:       game.Namespace,Labels:          labels,OwnerReferences: owner,}// 获取对应deployment, 如不存在则创建deploy := &appsv1.Deployment{}if err := r.Get(ctx, name, deploy); err != nil {if !errors.IsNotFound(err) {return err}deploy = &appsv1.Deployment{ObjectMeta: meta,Spec:       getDeploymentSpec(game, labels),}if err := r.Create(ctx, deploy); err != nil {return err}logger.Info("create deployment success", "name", name.String())} else {// 如果存在对比和game生成的deployment是否一致,不一致则更新want := getDeploymentSpec(game, labels)get := getSpecFromDeployment(deploy)if !reflect.DeepEqual(want, get) {deploy = &appsv1.Deployment{ObjectMeta: meta,Spec:       want,}if err := r.Update(ctx, deploy); err != nil {return err}logger.Info("update deployment success", "name", name.String())}}//service创建svc := &corev1.Service{}if err := r.Get(ctx, name, svc); err != nil {...}// ingress创建ing := &networkingv1.Ingress{}if err := r.Get(ctx, name, ing); err != nil {...}newStatus := myappv1.GameStatus{Replicas:      *game.Spec.Replicas,ReadyReplicas: deploy.Status.ReadyReplicas,}if newStatus.Replicas == newStatus.ReadyReplicas {newStatus.Phase = myappv1.Running} else {newStatus.Phase = myappv1.NotReady}// 更新状态if !reflect.DeepEqual(game.Status, newStatus) {game.Status = newStatuslogger.Info("update game status", "name", name.String())return r.Client.Status().Update(ctx, game)}return nil
}

默认情况下生成的controller只监听自定义资源,在示例中我们也需要监听game的子资源,如监听deployment是否符合预期

// SetupWithManager sets up the controller with the Manager.
func (r *GameReconciler) SetupWithManager(mgr ctrl.Manager) error {// 创建controllerc, err := controller.New("game-controller", mgr, controller.Options{Reconciler:              r,MaxConcurrentReconciles: 3, //controller运行的worker数})if err != nil {return err}//监听自定义资源if err := c.Watch(&source.Kind{Type: &myappv1.Game{}}, &handler.EnqueueRequestForObject{}); err != nil {return err}//监听deployment,将owner信息即game namespace/name添加到队列if err := c.Watch(&source.Kind{Type: &appsv1.Deployment{}}, &handler.EnqueueRequestForOwner{OwnerType:    &myappv1.Game{},IsController: true,}); err != nil {return err}return nil
}

部署验证

安装CRD

make install

# 查看deploy
kubectl get deploy game-sample
NAME READY UP-TO-DATE AVAILABLE AGE
game-sample 1/1 1 1 6m

# 查看ingress
kubectl get ing game-sample
NAME CLASS HOSTS ADDRESS PORTS AGE
game-sample <none> mygame.io 192.168.49.2 80 7m

验证应用,在/etc/host中添加<Ingress ADDRESS Ip> mygame.io,访问浏览器如下图所示

验证扩容

kubectl scale games.myapp.qingwave.github.io game-sample --replicas 2
game.myapp.qingwave.github.io/game-sample scaled

# 扩容后
kubectl get games.myapp.qingwave.github.io
NAME PHASE HOST DESIRED CURRENT READY AGE
game-sample Running mygame.io 2 2 2 7m

同样的通过ValidateCreateValidateUpdate可实现validating webhook

func (r *Game) ValidateCreate() error {gamelog.Info("validate create", "name", r.Name)// Host不能包括通配符if strings.Contains(r.Spec.Host, "*") {return errors.New("host should not contain *")}return nil
}

本地验证webhook需要配置证书,在集群中测试更方便点,可参考官方文档。

总结

至此,我们已经实现了一个功能完全的game-operator,可以管理game资源的生命周期,创建/更新game时会自动创建deployment、service、ingress等资源,当deployment被误删或者误修改时也可以自动回复到期望状态,也实现了scale接口。

通过kubebuiler大大简化了开发operator的成本,我们只需要关心业务逻辑即可,不需要再手动去创建client/controller等,但同时kubebuilder生成的代码中屏蔽了很多细节,比如controller的最大worker数、同步时间、队列类型等参数设置,只有了解operator的原理才更好应用于生产。

引用

[1] https://book.kubebuilder.io/

使用kuberbuilder创建工程示例相关推荐

  1. QT应用编程: windows下使用UDT传输协议_创建工程示例(高速数据传输)

    一.环境介绍 操作系统: win10 64位 QT版本:  QT5.12.6 编译器:  MinGW 32 二.UDT传输协议介绍 UDT是基于UDP的数据传输协议(UDP-based Data Tr ...

  2. 【SeeMusic】创建 SeeMusic 工程并编辑相关内容 ( 创建工程 | 导入 MIDI 文件 | 导入音频 | 导入视频 )

    SeeMusic 系列文章目录 [SeeMusic]下载安装并注册 SeeMusic 软件 [SeeMusic]创建 SeeMusic 工程并编辑相关内容 ( 创建工程 | 导入 MIDI 文件 | ...

  3. 第1章 Qt概述和下载安装及创建工程

    目录 1.1 什么是Qt 1.2 Qt的下载及安装 1.3 开发界面及创建工程介绍 1.1 什么是Qt 学习一项技能,首先要了解一下这项技能能做什么,那Qt到底是什么,能用它做什么呢? Qt是什么?简 ...

  4. 如何创建工程流程图?

    如何创建工程流程图?亿图图示(Edraw Max) for mac是一款适用于MAC平台专业强大而优秀的图形图表绘制工具,它包含丰富的实例和模版,帮助您轻松创建流程图.网络拓扑图.组织结构图.商业图表 ...

  5. 创建Django项目和模型(创建工程、子应用、设置pycharm环境、使用Django进行数据库开发的步骤)

    1.创建Django项目 文档:Writing your first Django app, part 1 | Django documentation | Django 步骤 创建Django项目 ...

  6. 在VC6.0中创建工程并输入源代码

    为了把程序代码输入而交给计算机,需要使用VC 6.0的编辑器来完成.如前所述,首先要创建工程以及工程工作区,而后才能输入具体程序完成所谓的"编辑"工作(注意,该步工作在四步骤中最繁 ...

  7. Xamarin iOS编写第一个应用程序创建工程

    Xamarin iOS编写第一个应用程序创建工程 在Xcode以及Xamarin安装好后,就可以在Xamarin Studio中编写程序了.本节将主要讲解在Xamarin Studio中如何进行工程的 ...

  8. php javabean对象,Struts2 bean标签:创建并示例化一个JavaBean对象

    bean 标签也是一个十分常用的标签,它与 JSP 中的 动作类似,主要用于创建并示例化一个 JavaBean 对象,对于 JavaBean 中的属性可以通过 param 标签对其赋值. 语法: // ...

  9. 【开发环境】Ubuntu 中使用 VSCode 开发 C/C++ ③ ( 创建工程目录 | 添加 C++ 源代码 | 代码自动提示 )

    文章目录 一.创建工程目录 二.添加 C++ 源代码 三.代码自动提示 可以参考官方提供的文档 : https://code.visualstudio.com/docs/cpp/config-linu ...

最新文章

  1. Java对象XML序列化框架-Simple2.0
  2. 利用Advanced Installer将asp.netMVC连同IIS服务和mysql数据库一块打包成exe安装包
  3. Android中的dispatchTouchEvent()、onInterceptTouchEvent()和onTouchEvent()
  4. javaweb上传文件_javaWeb中,如何通过CommonsFileUpload组件上传文件
  5. mmap函数_分析由 mmap 导致的内存泄漏
  6. 中信国健临床通讯2011年1月第1期目录
  7. java 并发编程实例_java并发编程应用实例
  8. 【白皮书分享】2020年度薪酬白皮书.pdf(附下载链接)
  9. Git 使用文档( git pull/fetch )
  10. 定制通达信多周期同列版面
  11. Java性能优化面试题汇总
  12. Git学习8:Git分支操作
  13. 图片还原去遮挡_怎么去马赛克 还原图片去掉遮挡软件
  14. MySql表的基本增删改查详解
  15. iOS-事件响应链、单例模式、工厂模式、观察者模式
  16. ssd存储的SLC、MLC、TLC闪存芯片颗粒有什么区别?
  17. 微信小程序中图片高度被压扁的解决办法
  18. 怎么连接云服务器共享文件夹,如何设置局域网共享文件夹
  19. 操作系统基础教程——第六章课后答案
  20. Excel如何一键改色

热门文章

  1. linux文件目录和属性知识,Linux文件和目录属性
  2. 数据中心建设提速 2021年呈四大发展趋势
  3. 某银行省级数据中心IT运维服务体系建设完整思路
  4. 再次携号转网_“携号转网”日期再次确定!这三个开头的号码,可以优先办理转网...
  5. 成功解决_catboost.CatBoostError: Invalid cat_features[4] = 8 value: index must be < 8.
  6. 成功解决NameError: name ‘norm‘ is not defined
  7. 成功解决AttributeError: 'list' object has no attribute 'ndim'
  8. Matlab:成功解决The option is not valid. The options must be'double','native','default','omitnan', or'inc
  9. 成功解决AttributeError: module 'string' has no attribute 'find'
  10. BlockChain:《区块链世界简明生存指南(一块听听)》2017-06-06 李笑来—听课笔记分享(2)