0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

什么是Operator?Operator是如何工作的?如何构建Operator?

马哥Linux运维 来源:马哥Linux运维 2023-09-01 15:35 次阅读

你也许能够将应用熟练的部署到 Kubernetes 上,但你知道什么是 Operator 吗?Operator 是如何工作的?如何构建 Operator?这是一个复杂的课题,但幸运的是,自 2016 年发明以来,已经开发了许多相关工具,可以简化工程师的生活。

这些工具允许我们将自定义逻辑加入 Kubernetes,从而自动化大量任务,而这已经超出了软件本身功能的范围。

闲话少说,让我们深入了解更多关于 Operator 的知识吧!

什么是 Operator?

等一下,你知道 Kubernetes(或 k8s)吗?简单介绍一下,这是由谷歌云开发的“可以在任何地方部署、扩展和管理容器应用程序的开源系统”。

大多数人使用 Kubernetes 的方式是使用原生资源(如 Pod、Deployment、Service 等)部署应用程序。但是,也可以扩展 Kubernetes 的功能,从而添加满足特定需求的新业务逻辑,这就是 Operator 的作用。Operator 的主要目标是将工程师的逻辑转换为代码,以便实现原生 Kubernetes 无法完成的某些任务的自动化。

负责开发应用程序或服务的工程师对系统应该如何运行、如何部署以及如何在出现问题时做出反应有很深的了解。将这些技术知识封装在代码中并自动化操作的能力意味着在可以花费更少的时间处理重复任务,而在重要问题上可以投入更多时间。

例如,可以想象 Operator 在 Kubernetes 中部署和维护 MySQL、Elasticsearch 或 Gitlab runner 等工具,Operator 可以配置这些工具,根据事件调整系统状态,并对故障做出反应。

听起来很有趣不是吗?让我们动手干吧。

构建 Operator

可以使用 Kubernetes 开发的 controller-runtime 项目从头构建 Operator,也可以使用最流行的框架之一加速开发周期并降低复杂性(Kubebuilder 或 OperatorSDK)。因为 Kubebuilder 框架非常容易使用,文档也很容易阅读,而且久经考验,因此我选择基于 Kubebuilder 构建。

不管怎样,这两个项目目前正在合并为单独的项目。

「设置开发环境」

开发 Operator 需要以下必备工具:

Go v1.17.9+

Docker 17.03+

kubectl v1.11.3+

访问 Kubernetes v1.11.3+集群(强烈建议使用 kind 设置自己的本地集群,它非常容易使用!)

然后安装 kubebuilder:

$curl-L-okubebuilderhttps://go.kubebuilder.io/dl/latest/$(goenvGOOS)/$(goenvGOARCH)&&chmod+xkubebuilder&&mvkubebuilder/usr/local/bin/

如果一切正常,应该会看到类似输出(版本可能会随时间发生变化):

$kubebuilderversion
Version:main.version{KubeBuilderVersion:"3.4.1",KubernetesVendor:"1.23.5",GitCommit:"d59d7882ce95ce5de10238e135ddff31d8ede026",BuildDate:"2022-05-06T1356Z",GoOs:"darwin",GoArch:"amd64"}

太棒了,现在可以开始了!

「构建简单的 Operator」

接下来做个小练习,构建一个简单的 foo operator,除了演示 Operator 的功能之外,没有实际用处。运行以下命令初始化新项目,该命令将下载 controller-runtime 二进制文件,并为我们准备好项目。

$kubebuilderinit--domainmy.domain--repomy.domain/tutorial
Writingkustomizemanifestsforyoutoedit...
Writingscaffoldforyoutoedit...
Getcontrollerruntime:
$gogetsigs.k8s.io/controller-runtime@v0.11.2
go:downloadingsigs.k8s.io/controller-runtimev0.11.2
...
Updatedependencies:
$gomodtidy
go:downloadinggithub.com/onsi/gomegav1.17.0
...

下面是项目结构(注意这是一个 Go 项目):

$ls-a
-rw-------1leovctstaff129Jun3016:08.dockerignore
-rw-------1leovctstaff367Jun3016:08.gitignore
-rw-------1leovctstaff776Jun3016:08Dockerfile
-rw-------1leovctstaff5029Jun3016:08Makefile
-rw-------1leovctstaff104Jun3016:08PROJECT
-rw-------1leovctstaff2718Jun3016:08README.md
drwx------6leovctstaff192Jun3016:08config
-rw-------1leovctstaff3218Jun3016:08go.mod
-rw-r--r--1leovctstaff94801Jun3016:08go.sum
drwx------3leovctstaff96Jun3016:08hack
-rw-------1leovctstaff2780Jun3016:08main.go

我们来看看这个 Operator 最重要的组成部分:

main.go 是项目入口,负责设置并运行管理器。

config/包含在 Kubernetes 中部署 Operator 的 manifest。

Dockerfile 是用于构建管理器镜像的容器文件。

等等,这个管理器组件是什么玩意儿?

这涉及到部分理论知识,我们稍后再说!

Operator 由两个组件组成,自定义资源定义(CRD,Custom Resource Definition)和控制器(Controller)。

CRD 是“Kubernetes 自定义类型”或资源蓝图,用于描述其规范和状态。我们可以定义 CRD 的实例,称为自定义资源(CR,Custom Resource)。

控制器(也称为控制循环)持续监视集群状态,并根据事件做出变更,目标是将资源的当前状态变为用户在自定义资源规范中定义的期望状态。一般来说,控制器是特定于某种类型的资源的,但也可以对一组不同的资源执行 CRUD(创建、读取、更新和删除)操作。

在 Kubernetes 的文档中举了一个控制器的例子:恒温器。当我们设置温度时,告诉恒温器所需的状态,房间的实际温度就是当前的实际状态,恒温器通过打开或关闭空调,使实际状态更接近预期状态。

那管理器(manager)呢?该组件的目标是启动所有控制器,并使控制循环共存。假设项目中有两个 CRD,同时有两个控制器,每个 CRD 对应一个控制器,管理器将启动这两个控制器并使它们共存。

现在我们知道了 Operator 是如何工作的,可以开始使用 Kubebuilder 框架创建一个 Operator,我们从创建新的 API(组/版本)和新的 Kind(CRD)开始,当提示创建 CRD 和控制器时,按 yes。

$kubebuildercreateapi--grouptutorial--versionv1--kindFoo
CreateResource[y/n]y
CreateController[y/n]y
Writingkustomizemanifestsforyoutoedit...
Writingscaffoldforyoutoedit...
api/v1/foo_types.go
controllers/foo_controller.go
Updatedependencies:
$gomodtidy
Runningmake:
$makegenerate
mkdir-p/Users/leovct/Documents/tutorial/bin
GOBIN=/Users/leovct/Documents/tutorial/bingoinstallsigs.k8s.io/controller-tools/cmd/controller-gen@v0.8.0
/Users/leovct/Documents/tutorial/bin/controller-genobject:headerFile="hack/boilerplate.go.txt"paths="./..."

接下来是最有意思的部分!我们将定制 CRD 和控制器来满足需求,注意看已经创建了两个新文件夹:

api/v1 包含 Foo CRD

controllers 包含 Foo 控制器

「自定义 CRD 和 Controller」

接下来定制我们可爱的 Foo CRD。正如前面所说,这个 CRD 没有任何目的,只是简单展示如何使用 Operator 在 Kubernetes 中执行简单的任务。

Foo CRD 在其定义中有 name 字段,该字段指的是 Foo 正在寻找的朋友的名称。如果 Foo 找到了一个朋友(一个和朋友同名的 Pod),happy 状态将被设置为 true。

packagev1

import(
metav1"k8s.io/apimachinery/pkg/apis/meta/v1"
)

//FooSpecdefinesthedesiredstateofFoo
typeFooSpecstruct{
//NameofthefriendFooislookingfor
Namestring`json:"name"`
}

//FooStatusdefinestheobservedstateofFoo
typeFooStatusstruct{
//HappywillbesettotrueifFoofoundafriend
Happybool`json:"happy,omitempty"`
}

//+kubebuilderroot=true
//+kubebuilderstatus

//FooistheSchemaforthefoosAPI
typeFoostruct{
metav1.TypeMeta`json:",inline"`
metav1.ObjectMeta`json:"metadata,omitempty"`

SpecFooSpec`json:"spec,omitempty"`
StatusFooStatus`json:"status,omitempty"`
}

//+kubebuilderroot=true

//FooListcontainsalistofFoo
typeFooListstruct{
metav1.TypeMeta`json:",inline"`
metav1.ListMeta`json:"metadata,omitempty"`
Items[]Foo`json:"items"`
}

funcinit(){
SchemeBuilder.Register(&Foo{},&FooList{})
}

接下来实现控制器逻辑。没什么复杂的,通过触发 reconciliation 请求获取 Foo 资源,从而得到 Foo 的朋友的名称。然后,列出所有和 Foo 的朋友同名的 Pod。如果找到一个或多个,将 Foo 的 happy 状态更新为 true,否则设置为 false。

注意,控制器也会对 Pod 事件做出反应。实际上,如果创建了一个新的 Pod,我们希望 Foo 资源能够相应更新其状态。这个方法将在每次发生 Pod 事件时被触发(创建、更新或删除)。然后,只有当 Pod 名称是集群中部署的某个 Foo 自定义资源的“朋友”时,才触发 Foo 控制器的 reconciliation 循环。

packagecontrollers

import(
"context"

corev1"k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
ctrl"sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/handler"
"sigs.k8s.io/controller-runtime/pkg/log"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
"sigs.k8s.io/controller-runtime/pkg/source"

tutorialv1"my.domain/tutorial/api/v1"
)

//FooReconcilerreconcilesaFooobject
typeFooReconcilerstruct{
client.Client
Scheme*runtime.Scheme
}

//RBACpermissionstomonitorfoocustomresources
//+kubebuildergroups=tutorial.my.domain,resources=foos,verbs=get;list;watch;create;update;patch;delete
//+kubebuildergroups=tutorial.my.domain,resources=foos/status,verbs=get;update;patch
//+kubebuildergroups=tutorial.my.domain,resources=foos/finalizers,verbs=update

//RBACpermissionstomonitorpods
//+kubebuildergroups="",resources=pods,verbs=get;list;watch

//Reconcileispartofthemainkubernetesreconciliationloopwhichaimsto
//movethecurrentstateoftheclusterclosertothedesiredstate.
func(r*FooReconciler)Reconcile(ctxcontext.Context,reqctrl.Request)(ctrl.Result,error){
log:=log.FromContext(ctx)
log.Info("reconcilingfoocustomresource")

//GettheFooresourcethattriggeredthereconciliationrequest
varfootutorialv1.Foo
iferr:=r.Get(ctx,req.NamespacedName,&foo);err!=nil{
log.Error(err,"unabletofetchFoo")
returnctrl.Result{},client.IgnoreNotFound(err)
}

//GetpodswiththesamenameasFoo'sfriend
varpodListcorev1.PodList
varfriendFoundbool
iferr:=r.List(ctx,&podList);err!=nil{
log.Error(err,"unabletolistpods")
}else{
for_,item:=rangepodList.Items{
ifitem.GetName()==foo.Spec.Name{
log.Info("podlinkedtoafoocustomresourcefound","name",item.GetName())
friendFound=true
}
}
}

//UpdateFoo'happystatus
foo.Status.Happy=friendFound
iferr:=r.Status().Update(ctx,&foo);err!=nil{
log.Error(err,"unabletoupdatefoo'shappystatus","status",friendFound)
returnctrl.Result{},err
}
log.Info("foo'shappystatusupdated","status",friendFound)

log.Info("foocustomresourcereconciled")
returnctrl.Result{},nil
}

//SetupWithManagersetsupthecontrollerwiththeManager.
func(r*FooReconciler)SetupWithManager(mgrctrl.Manager)error{
returnctrl.NewControllerManagedBy(mgr).
For(&tutorialv1.Foo{}).
Watches(
&source.Kind{Type:&corev1.Pod{}},
handler.EnqueueRequestsFromMapFunc(r.mapPodsReqToFooReq),
).
Complete(r)
}

func(r*FooReconciler)mapPodsReqToFooReq(objclient.Object)[]reconcile.Request{
ctx:=context.Background()
log:=log.FromContext(ctx)

//ListalltheFoocustomresource
req:=[]reconcile.Request{}
varlisttutorialv1.FooList
iferr:=r.Client.List(context.TODO(),&list);err!=nil{
log.Error(err,"unabletolistfoocustomresources")
}else{
//OnlykeepFoocustomresourcesrelatedtothePodthattriggeredthereconciliationrequest
for_,item:=rangelist.Items{
ifitem.Spec.Name==obj.GetName(){
req=append(req,reconcile.Request{
NamespacedName:types.NamespacedName{Name:item.Name,Namespace:item.Namespace},
})
log.Info("podlinkedtoafoocustomresourceissuedanevent","name",obj.GetName())
}
}
}
returnreq
}

我们已经完成了对 API 定义和控制器的编辑,可以运行以下命令来更新 Operator manifest。

$makemanifests
/Users/leovct/Documents/tutorial/bin/controller-genrbac:roleName=manager-rolecrdwebhookpaths="./..."outputartifacts:config=config/crd/bases

「运行 Controller」

我们使用 Kind 设置本地 Kubernetes 集群,它很容易使用。

首先将 CRD 安装到集群中。

$makeinstall
/Users/leovct/Documents/tutorial/bin/controller-genrbac:roleName=manager-rolecrdwebhookpaths="./..."outputartifacts:config=config/crd/bases
kubectlapply-kconfig/crd
customresourcedefinition.apiextensions.k8s.io/foos.tutorial.my.domaincreated

可以看到 Foo CRD 已经创建好了。

$kubectlgetcrds
NAMECREATEDAT
foos.tutorial.my.domain2022-06-30T1745Z

然后终端中运行控制器。请记住,也可以将其部署为 Kubernetes 集群中的 deployment。

$makerun
/Users/leovct/Documents/tutorial/bin/controller-genrbac:roleName=manager-rolecrdwebhookpaths="./..."outputartifacts:config=config/crd/bases
/Users/leovct/Documents/tutorial/bin/controller-genobject:headerFile="hack/boilerplate.go.txt"paths="./..."
gofmt./...
govet./...
gorun./main.go
INFOcontroller-runtime.metricsMetricsserverisstartingtolisten{"addr":":8080"}
INFOsetupstartingmanager
INFOStartingserver{"path":"/metrics","kind":"metrics","addr":"[::]:8080"}
INFOStartingserver{"kind":"healthprobe","addr":"[::]:8081"}
INFOcontroller.fooStartingEventSource{"reconcilergroup":"tutorial.my.domain","reconcilerkind":"Foo","source":"kindsource:*v1.Foo"}
INFOcontroller.fooStartingEventSource{"reconcilergroup":"tutorial.my.domain","reconcilerkind":"Foo","source":"kindsource:*v1.Pod"}
INFOcontroller.fooStartingController{"reconcilergroup":"tutorial.my.domain","reconcilerkind":"Foo"}
INFOcontroller.fooStartingworkers{"reconcilergroup":"tutorial.my.domain","reconcilerkind":"Foo","workercount":1}

如你所见,管理器启动了,然后 Foo 控制器也启动了,控制器现在正在运行并监听事件!

「测试控制器」

为了测试是否一切工作正常,我们创建两个 Foo 自定义资源以及一些 pod,观察控制器的行为。

首先,在 config/samples 中创建 Foo 自定义资源清单,运行以下命令在本地 Kubernetes 集群中创建资源。

apiVersion:tutorial.my.domain/v1
kind:Foo
metadata:
name:foo-01
spec:
name:jack

---
apiVersion:tutorial.my.domain/v1
kind:Foo
metadata:
name:foo-02
spec:
name:joe
$kubectlapply-fconfig/samples
foo.tutorial.my.domain/foo-1created
foo.tutorial.my.domain/foo-2created

可以看到控制器为每个 Foo 自定义资源创建事件触发了 reconciliation 循环。

INFO controller.foo reconciling foo custom resource {"reconciler group": "tutorial.my.domain", "reconciler kind": "Foo", "name": "foo-1", "namespace": "default"}
INFO controller.foo foo's happy status updated {"reconciler group": "tutorial.my.domain", "reconciler kind": "Foo", "name": "foo-1", "namespace": "default", "status": "false"}
INFO controller.foo foo custom resource reconciled {"reconciler group": "tutorial.my.domain", "reconciler kind": "Foo", "name": "foo-1", "namespace": "default"}
INFO controller.foo reconciling foo custom resource {"reconciler group": "tutorial.my.domain", "reconciler kind": "Foo", "name": "foo-2", "namespace": "default"}
INFO controller.foo foo's happy status updated {"reconciler group": "tutorial.my.domain", "reconciler kind": "Foo", "name": "foo-2", "namespace": "default", "status": "false"}
INFO controller.foo foo custom resource reconciled {"reconciler group": "tutorial.my.domain", "reconciler kind": "Foo", "name": "foo-2", "namespace": "default"}

如果检查 Foo 自定义资源状态,可以看到状态为空,这正是所期望的,目前为止一切正常!

$kubectldescribefoos
Name:foo-1
Namespace:default
APIVersion:tutorial.my.domain/v1
Kind:Foo
Metadata:...
Spec:
Name:jack
Status:
Name:foo-2
Namespace:default
APIVersion:tutorial.my.domain/v1
Kind:Foo
Metadata:...
Spec:
Name:joe
Status:

接下来我们部署一个叫 jack 的 Pod 来观察系统的反应。

apiVersion:v1
kind:Pod
metadata:
name:jack
spec:
containers:
-name:ubuntu
image:ubuntu:latest
#Justsleepforever
command:["sleep"]
args:["infinity"]

Pod 部署完成后,应该可以看到控制器对 Pod 创建事件作出响应,然后按照预期更新第一个 Foo 自定义资源状态,可以通过 describe Foo 自定义资源来验证。

INFO pod linked to a foo custom resource issued an event {"name": "jack"}
INFO controller.foo reconciling foo custom resource {"reconciler group": "tutorial.my.domain", "reconciler kind": "Foo", "name": "foo-1", "namespace": "default"}
INFO controller.foo pod linked to a foo custom resource found {"reconciler group": "tutorial.my.domain", "reconciler kind": "Foo", "name": "foo-1", "namespace": "default", "name": "jack"}
INFO controller.foo foo's happy status updated {"reconciler group": "tutorial.my.domain", "reconciler kind": "Foo", "name": "foo-1", "namespace": "default", "status": true}
INFO controller.foo foo custom resource reconciled {"reconciler group": "tutorial.my.domain", "reconciler kind": "Foo", "name": "foo-1", "namespace": "default"}

我们更新第二个 Foo 自定义资源规范,将其 name 字段的值从 joe 更改为 jack,控制器应该捕获更新事件并触发 reconciliation 循环。

INFO controller.foo pod linked to a foo custom resource found {"reconciler group": "tutorial.my.domain", "reconciler kind": "Foo", "name": "foo-2", "namespace": "default", "name": "jack"}
INFO controller.foo foo's happy status updated {"reconciler group": "tutorial.my.domain", "reconciler kind": "Foo", "name": "foo-2", "namespace": "default", "status": true}
INFO controller.foo foo custom resource reconciled {"reconciler group": "tutorial.my.domain", "reconciler kind": "Foo", "name": "foo-2", "namespace": "default"}

Yeah,成功了!我们已经做了足够多的实验,你应该明白这是怎么回事了!如果删除名为 jack 的 pod,自定义资源的 happy 状态将被设置为 false。

我们可以确认 Operator 是正常工作的!最好再编写一些单元测试和端到端测试,但本文不会覆盖相关内容。

为自己感到骄傲吧,你已经设计、部署并测试了第一个 Operator!恭喜!!

如果需要浏览完整代码,请访问GitHub[1]:

更多工作

我们已经看到如何创建非常基本的 Kubernetes operator,但远非完美,还有很多地方需要改善,下面是可以探索的主题列表:

优化事件过滤(有时,事件会被提交两次……)。

完善 RBAC 权限。

改进日志记录系统。

当 operator 更新资源时,触发 Kubernetes 事件。

获取 Foo 自定义资源时添加自定义字段(也许显示 happy 状态?)

编写单元测试和端到端测试。

通过这个列表,可以深入挖掘这一主题。

审核编辑:汤梓红

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 代码
    +关注

    关注

    30

    文章

    4556

    浏览量

    66784
  • MySQL
    +关注

    关注

    1

    文章

    775

    浏览量

    26006
  • 开发环境
    +关注

    关注

    1

    文章

    203

    浏览量

    16461
  • kubernetes
    +关注

    关注

    0

    文章

    219

    浏览量

    8568

原文标题:通过例子介绍如何从零开发 Kubernetes Operator

文章出处:【微信号:magedu-Linux,微信公众号:马哥Linux运维】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    QSG_DP2K_Operator_CN_快速参考手册_110711

    QSG_DP2K_Operator_CN_快速参考手册_110711
    发表于 08-05 20:35

    With mismatch in relational operator

    library ieee;use ieee.std_logic_1164.all;use ieee.std_logic_arith.all;use ieee.std_logic_unsigned.all; entity fp isgeneric (data_width : integer := 8 );port(input : in std_logic_vector(data_width-1 downto0);clk_in : instd_logic;clk_out :out std_logic);end entity fp; architecture div of fp issignal clk_outQ : std_logic ;signal coutQ : std_logic_vector (data_width - 1downto 0);signal cout1,cout2 : std_logic_vector(data_width - 1downto 0);signal clk1,clk2 : std_logic;signal a : std_logic;begin-------------------------------------------------process(clk_in) begin ifclk_in'event and clk_in = '1' thenif coutQ< (conv_integer(input) - 1) thencoutQ
    发表于 07-31 10:36

    C ++错误:字段类型不完整

    ;CIM2Operator2ConsoleControls.pb.h"#include "CI2CPort.h"class CCIM2Operator2ConsoleControls{public
    发表于 05-13 14:53

    Analog/.Discharge的神秘工作原理

    , while protecting the operator, the board under test, and the 3070 itself from harm.
    发表于 05-13 12:13

    ESP32-DU1906编译失败报Error: missing binary operator before token "("怎么解决?

    /airkiss_config/airkiss_config.c:44:44: error: missing binary operator before token "("
    发表于 03-10 09:00

    SAMSUNG FACSIMILE Operator’s G

    Thank You for Choosing Samsung!Your SF 150 facsimile machine represents the very latest in technology.  It is a full-featured, high-performance device, built to Samsung’s strict quality standards- the highest in the industry
    发表于 08-10 12:10 12次下载

    placement new详解

    placement new是重载operator new的一个标准、全局的版本,它不能被自定义的版本代替(不像普通的operator new和operator delete能够被替换成用户自定义的版本)。
    发表于 03-02 14:24 0次下载

    "Eclipse + AVR插件编译类时,报undefined reference to `operator delete(void*, unsigned int)‘"

    使用Eclipse + AVR插件做Arduino开发,新建一个类,编写头文件和cpp文件,编译后,报错undefined reference to `operator delete(void
    发表于 11-15 14:36 5次下载
    "Eclipse + AVR插件编译类时,报undefined reference to `<b class='flag-5'>operator</b> delete(void*, unsigned int)‘"

    使用GPU Operator 1.8简化GPU对部署场景的管理

      在本文中,我们将介绍 GPU Operator release 1.8 中添加的新功能,进一步简化 GPU 对各种部署场景的管理,包括:
    的头像 发表于 04-11 16:06 1387次阅读

    Postgres Operator在K8s上构建高可用PostgreSQL集群

    postgres-operator.zip
    发表于 04-28 09:58 0次下载
    Postgres <b class='flag-5'>Operator</b>在K8s上<b class='flag-5'>构建</b>高可用PostgreSQL集群

    TiDB Operator自动化部署运维工具

    tidb-operator.zip
    发表于 04-28 09:15 0次下载
    TiDB <b class='flag-5'>Operator</b>自动化部署运维工具

    emqx-operator管理EMQ X的应用控制器

    ./oschina_soft/emqx-operator.zip
    发表于 05-13 09:57 2次下载
    emqx-<b class='flag-5'>operator</b>管理EMQ X的应用控制器

    为什么要设计模式

    Operator 条件更新上应用 Go 风格的构建器模式的实际示例
    的头像 发表于 06-14 11:08 753次阅读

    如何从零开发Kubernetes Operator

    大多数人使用Kubernetes的方式是使用原生资源(如Pod、Deployment、Service等)部署应用程序。但是,也可以扩展Kubernetes的功能,从而添加满足特定需求的新业务逻辑,这就是Operator的作用。
    的头像 发表于 01-05 11:27 972次阅读

    Kubernetes Operator最佳实践介绍

    kubernetes operator是通过连接主API并watch时间的一组进程,一般会watch有限的资源类型。
    的头像 发表于 04-19 09:16 650次阅读