最近的工作中呢一直和Kubernetes打交道,目前结合着业务场景一直在开发自定义资源。本来觉得没什么,奈何k8s内容太过庞大有时还是让人不知所措的。所以呢就想结合着自己的开发过程来提供一些值得参考的内容。本次的篇幅也许会很长不会一次性更完,不过我会尽自己所能把开发流程讲清楚。本次为了方便初学者理解,将实现一个很简单的功能,围绕着创建项目、编写operator、打包运行、webhook、多API管理来尽可能的做到有始有终。
Kubernetes(K8s)的自定义资源(Custom Resource Definitions,简称CRD)是一种扩展Kubernetes API的方式。Kubernetes本身已经有很多内建的API资源,如Pod、Service、Volume等。但在一些特定场景下,这些内建资源可能无法满足所有需求。此时,可以使用自定义资源来定义新的资源类型。
自定义资源是API的扩展,可以将其视为数据库的模式,它说明了一种新的对象类型的名称、结构、行为。CRD允许用户在不修改Kubernetes源代码的情况下增加他们自己的存储类,并通过Kubernetes风格的API进行访问。
以CRD定义的新的资源类型在使用上和Kubernetes的内建资源类型一样,可以通过kubectl等工具进行管理。自定义资源也可以配合自定义控制器使用,形成一个运行在Kubernetes上的自定义应用或服务,这就是Kubernetes的Operator模式。
如何开发呢?我肯定不会从头用手搓啊,毕竟也不是大神,还是要借助一些框架的。目前最合适的框架就是Kubebuilder和operator,本次将使用operator进行开发CRD。其实operator底层用的也是kubebuilder,只不过封装了一些其他可用的内容。
git clone https://github.com/operator-framework/operator-sdk.git
cd operator-sdk
make install
mkdir atom_operator
cd atom_operator
operator-sdk init --domain atom.com --repo github.com/xiaowei6688/atom-operator
这时就准备好了一个Operator项目骨架,项目名默认是用的当前目录名,也就是application-operator这个目录名。项目名也可以通过–proiect-name参数来自定义,但是一般没有这个需求因为很少会有目录名和项目名保持不一致的场景。这里需要知道的是项目名体现在哪些地方,假如中途想修改项目名,直接找到这些配置项,手动调整即可
operator为我们创建很多文件,先来看看都有哪些内容
go.mod
:该文件包含着项目的基础依赖
Makefile
:该文件中存放的是和开发过程中构建、部署、测试等相关的一系列命令
PROJECT
:存放的是项目用到的元数据
main.go
: 程序入口文件
operator-sdk create api --group apps --version v1 --kind Atom --resource --controller
创建API之后我们看这里都生成了一些什么文件
api/
config/crd/
config/rbac/application_editor_role.yaml
config/rbac/application_viewer_role.yaml
config/samples/controllers/
变更的文件是:
PROJECT
go.mod
main.go
看看PROJECT多了哪些内容
resources:
- api:
crdVersion: v1
namespaced: true
controller: true
domain: atom.com
group: apps
kind: Atom
path: github.com/xiaowei6688/atom-operator/api/v1
version: v1
main.go多出的代码
mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{
Scheme: scheme,
MetricsBindAddress: metricsAddr,
Port: 9443,
HealthProbeBindAddress: probeAddr,
LeaderElection: enableLeaderElection,
LeaderElectionID: "f10bfacf.atom.com",
})
if err != nil {
setupLog.Error(err, "unable to start manager")
os.Exit(1)
}
if err = (&controllers.AtomReconciler{
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
}).SetupWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "Atom")
os.Exit(1)
}
if err = (&appsv1.Atom{}).SetupWebhookWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create webhook", "webhook", "Atom")
os.Exit(1)
}
先有印象,暂时不细研究代码
go.mod
: 其中多出来的是BDD测试相关的ginkgo和gomega依赖
接下来看一下新增的几个文件/目录:
api/
: 这个目录中包含刚才添加的API,后面会经常编辑这里的application types.go文件
config/crd
: 存放的是crd部署相关的kustomize文件。
config/rbac
: 其中多了两个文件"atom_editor_role.yaml: 定义了一个有atom资源编辑权限的ClusterRole。
atom_viewer_role.yaml: 定义了一个有applications资源查询权限的ClusterRole。
samples/apps_v1_atom.yaml
: 这是一个CR示例文件,从这个文件的骨架很容易看到通过填充内容即可用来创建一个自定义资源Atom类型的实例
controllers/
: 这里包含控制器的代码逻辑入口。我们看一下Reconcile函数,“调谐”(Reconcile) 这个词会贯穿整个教程
// 重点!
func (r *AtomReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
<-time.NewTicker(100 * time.Millisecond).C
logs := log.FromContext(ctx)
// TODO(user): your logic here
return ctrl.Result{}, nil
}
CRD 的代码主要定义在 api/v1/atom_types.go文件中,先打开看一下现有代码是怎样的。主要看Spec结构,也就是AtomSpec这个结构体的定义:
// 后期项目的类型定义都需要写在这里面
type AtomSpec struct {
// INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
// Important: Run "make" to regenerate code after modifying this file
// Foo is an example field of Atom. Edit atom_types.go to remove/update
Foo string `json:"foo,omitempty"`
}
目前operator项目大致的架构已经了解了,接下来就可以整理我们的需求进行CRD以及controller的编写了