在业务团队中,当前开发模式用的主要是命令式。但是 Go 项目开发中还有一种声明式的编程范式,在某些场景下非常适合。业务团队可以尝试将声明式引入到业务开发中,通过声明式 API 的开发模式,来提高整个团队的开发效率、提高业务的稳定性和体验。
声明式 API 编程模式是整个云原生的基石,在云原生领域,声明式 API 被大量使用,已经是非常成熟的技术。但是云原生中的声明式 API 开发模式,依赖于 Kubernetes 集群,尤其是 kube-apiserver。在业务开发中,如果因为引入声明式 API 的开发模式,而给业务团队部署一套 Kubernetes 集群,显然,这是不适合的。
为此,参考 Kubernetes 的实现,借助 Kubernetes 中的 Go 包,OneX 项目设计和实现了一套不依赖于 Kubernetes 集群的声明式 API 架构和套件。该套件,可被业务团队,拿来实现某些适合声明式编程的业务场景。
命令式 VS 声明式 API
常见的编程方式如下:
- 命令式编程: 命令式编程是一种编程范式,它的主要思想是将程序的逻辑描述为一系列的命令,这些命令指定了程序的执行顺序和每一步的细节。在命令式编程中,开发者需要描述如何做,而不仅仅是要做什么。例如,如果有人想让计算机画一张猫脸,他们可能会给出如下指令:"在这里画一个圆圈,在那里画两个较小的圆圈,在上面画两个三角形"等等;
- 声明式编程: 声明式编程是一种编程范式,它的主要思想是将程序的逻辑描述为一系列的声明,而非一系列的命令。在声明式编程中,开发者只需要描述要做什么,而不需要描述如何做。程序的执行过程由编译器或运行时系统来处理,开发者只需要关注程序的逻辑。例如,如果他们想让计算机画一张猫脸,他们可能会给出如下指令:"画一个脸,画两只眼睛、两只耳朵和一个嘴巴"。
声明式编程范式和命令式编程范式各自尤其使用的场景。总的来说,声明式编程是命令式编程的一种编程升华,除了能够满足命令式编程的场景外,还具有声明式的特性,也即后端程序会始终确保资源处在声明的状态,这就保证了最终一致性,使得业务更加稳定。不管参数如何改变,后端系统如何不稳定,后端环境各种异常等,程序最终会一致试图维持在业务期望的状态。
二者场景区别如下:
- 命令式编程: 适用于对状态属性要求不高的场景。例如:一些数据库资源的 CURD,仅涉及资源属性,不涉及资源属性变化及资源属性计算;
- 声明式编程: 适用于一些是要维持状态的任务场景。例如:根据资源属性,计算资源状态的场景。具体例如:模型对比、模型训练。
基于 OneX 项目构建声明式 API 基架
OneX 项目地址:github.com/onexstack/o...
上述架构中,存以下 3 层:
- 客户端: 声明式 API 架构体系支持所有的请求方式:
- 前端:前端可通过 HTTP、RPC 通信协议请求后端组件;
- SDK:可生成不同编程语言的 SDK,提高开发者调用效率
- client-go:本套声明式 API 编程架构,可根据接入的资源,自动生成更加清晰、易用的 Go SDK;
- kubectl:声明式 API 架构完全兼容 Kubernetes 的 kubectl 工具,命令行工具,可以提高运维效率,并可通过嵌入 Shell 脚本,再一次提高运维效率;
- 接入层:基于命令式编程范式实现的业务网关,采用简洁架构。该层用来屏蔽上层的环境差异,例如:API 接口调用方式、参数、公司级的各种系统等,通过接入层的处理,向下对接到标准的声明式基架中。接入层中,我们还可以通过 CRD + Controller 这种 Operator 开发模式,来使用底层的声明式 API 基架,实现声明式 API 编程。
- 声明式基架 :声明式 API 基架,以标准化的形式对接各种资源,并通过声明式的方式确保资源状态的最终一致性。同时提供易用的 SDK、命令行工具,提高开发者的开发效率、运维者的运维效率。此外,声明式 API 架构层,也可以实现命令式编程场景,但会带来以下优缺点:
- 优点:可以借助 onex-apiserver 的标准 REST 路由构建方法,构建出标准的 REST 资源(参数、路径、使用方式等都是标准化的)
- 缺点:onex-apiserver 有业务自己封装的 CURD 逻辑,会是 onex-apiserver 逐渐丧失通用性。
- 建议:声明式基架只通过 CRD + Controller 的方式接入业务。从而直接复用基架的声明式 API 能力、并提供标准化的资源管理。插件化接入方式,也可以降低业务开发技术门槛;
资源请求流程:
- 开发者通过 SDK、前端、kubectl 工具等请求接入层组件,例如:talk-gateway 等,talk-gateway 会进行一些非标准化资源参数解析、非标流程处理,最终构建标准化的资源请求,请求 onex-apiserver;
- onex-apiserver 收到资源的创建/更新/删除操作后,会将资源更新在 ETCD 中,同时产生一套创建/更新/删除资源事件;
- onex-controller-manager 通过 List&Watch 的方式,实时监控到资源变更,根据资源的变更类型、变更数据等,进行资源调和,确保资源维持在其声明的状态。onex-controller-manager 内置一些通用的资源管理能力,例如:资源垃圾回收、资源同步、多租户管理、资源对账等。
业务接入流程
onex-apiserver 完全兼容 Kubernetes CRD 机制。所以可以使用 kubebuilder
工具生成自定义资源及 Controller。
提示:示例魔改自:Kubebuilder Quick Start。魔改点:
- 修改了
kubebuilder init
的--repo
参数; - 修改了
internal/controller/guestbook_controller.go
文件中的 reconcile 逻辑。
具体步骤如下。
- 编译
v0.2.0-alpha.0
版本,并重新部署 onex-apiserver。
编译命令如下:
bash
$ git clone -b v0.2.0-alpha.0 --depth=1 https://github.com/superproj/onex
$ make build BINS=onex-apiserver
- 使用 kubebuilder 创建一个测试项目。
bash
$ make tools.install.kubebuilder # 安装最新版本的 kubebuilder(4.0.0)
$ mkdir -p $GOPATH/src/github.com/superproj/kubebuilder-for-onex
$ cd $GOPATH/src/github.com/superproj/kubebuilder-for-onex
$ go mod init
$ kubebuilder init --domain my.domain --repo github.com/superproj/kubebuilder-for-onex
$ go work init .
$ go work use .
- 创建 API。
在常见 API 时,选择生成 Resource 和 Controller。具体命令如下:
bash
$ kubebuilder create api --group webapp --version v1 --kind Guestbook
INFO Create Resource [y/n] # 会生成 api/v1/guestbook_types.go 文件
y
INFO Create Controller [y/n] # 会生成 internal/controllers/guestbook_controller.go 文件
y
可以根据需要修改:api/v1/guestbook_types.go
文件中的GuestbookSpec
定义。
- 安装 CRD
执行以下命令安装 CRD:
bash
$ export KUBECONFIG=/opt/onex/etc/config # 注意:一定要执行这一步,config 是安装 onex 时生成的 config 文件
$ make install
$ kubectl get crd
NAME CREATED AT
evaluates.apps.ies.bytedance.com 2024-06-20T23:14:32Z
- 修改 contorller 代码,打印 Annotation 列表
go
import (
...
"fmt"
...
)
func (r *GuestbookReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
_ = log.FromContext(ctx)
// TODO(user): your logic here
book := &webappv1.Guestbook{}
if err := r.Get(ctx, req.NamespacedName, book); err != nil {
return ctrl.Result{}, client.IgnoreNotFound(err)
}
for key, value := range book.GetAnnotations() {
fmt.Printf("%s=%s\n", key, value)
}
return ctrl.Result{}, nil
}
- 运行 controller
bash
$ make run
- 创建 CRD 资源
可根据你的资源定义修改 config/samples/webapp_v1_guestbook.yaml
文件。之后就可以在 onex-apiserver 中创建该资源。命令如下:
bash
$ kubectl apply -k config/samples/
- 修改 CRD
bash
$ kubectl annotate guestbook guestbook-sample my.domain/cr-used-by=onex
guestbook.webapp.my.domain/guestbook-sample annotated
在执行 make run
的 Linux 终端中,查看日志:
bash
$ make run
/home/colin/workspace/golang/src/github.com/superproj/kubebuilder-for-onex/bin/controller-gen-v0.15.0 rbac:roleName=manager-role crd webhook paths="./..." output:crd:artifacts:config=config/crd/bases
/home/colin/workspace/golang/src/github.com/superproj/kubebuilder-for-onex/bin/controller-gen-v0.15.0 object:headerFile="hack/boilerplate.go.txt" paths="./..."
go fmt ./...
go vet ./...
go run ./cmd/main.go
2024-06-21T05:45:22+08:00 INFO setup starting manager
...
my.domain/cr-used-by=onex
可以看到成功打印出了新增的 annotation:my.domain/cr-used-by: onex
。
完整示例见:github.com/superproj/k...。
魔改业务
之后,我们便可以修改 API 定义和 Controller 逻辑来适配自己的业务。具体可参考: Designing an API and What's in a Controller。
总结
可以看到,我们没有安装 Kubernetes 集群,但是却可以使用 Operator 开发模式(CRD + Controller)来开发我们的业务代码。也就是说,我们将云原生中最核心、用的最多的声明式 API 引入到了业务开发中,用云原生中的技术、开发理念赋能我们的业务开发。