$ operator-sdk --version
operator-sdk version 0.1.0
本指南描述如何将使用 Operator SDK v0.0.x 构建的 Operator 项目迁移到 Operator SDK v0.1.0 所需的项目结构。
Red Hat 支持的 Operator SDK CLI 工具版本(包括与 Operator 项目相关的脚手架和测试工具)已弃用,并计划在未来版本的 Red Hat OpenShift Service on AWS 中移除。Red Hat 将在当前发布生命周期内为此功能提供错误修复和支持,但此功能将不再接收增强功能,并将从未来的 Red Hat OpenShift Service on AWS 版本中移除。 不建议使用 Red Hat 支持的 Operator SDK 版本创建新的 Operator 项目。拥有现有 Operator 项目的 Operator 作者可以使用随 Red Hat OpenShift Service on AWS 发布的 Operator SDK CLI 工具版本来维护其项目并创建针对较新版本 Red Hat OpenShift Service on AWS 的 Operator 版本。 以下与 Operator 项目相关的基础镜像未被弃用。这些基础镜像的运行时功能和配置 API 仍受支持,用于修复错误和解决 CVE。
有关不受支持的社区维护的 Operator SDK 版本的信息,请参阅 Operator SDK (Operator Framework)。 |
推荐的项目迁移方法是:
初始化一个新的 v0.1.0 项目。
将您的代码复制到新项目中。
根据 v0.1.0 的说明修改新项目。
本指南使用 Operator SDK 中的示例项目 memcached-operator
来演示迁移步骤。请参阅 v0.0.7 memcached-operator 和 v0.1.0 memcached-operator 项目结构,分别了解迁移前和迁移后的示例。
重命名您的 Operator SDK v0.0.x 项目,并在其位置创建一个新的 v0.1.0 项目。
开发工作站上安装的 Operator SDK v0.1.0 CLI
以前使用早期版本的 Operator SDK 部署的 memcached-operator
项目
确保 SDK 版本为 v0.1.0
$ operator-sdk --version
operator-sdk version 0.1.0
创建一个新项目
$ mkdir -p $GOPATH/src/github.com/example-inc/
$ cd $GOPATH/src/github.com/example-inc/
$ mv memcached-operator old-memcached-operator
$ operator-sdk new memcached-operator --skip-git-init
$ ls
memcached-operator old-memcached-operator
从旧项目复制 .git
$ cp -rf old-memcached-operator/.git memcached-operator/.git
将项目的自定义类型迁移到更新的 Operator SDK v0.1.0 用法。
开发工作站上安装的 Operator SDK v0.1.0 CLI
以前使用早期版本的 Operator SDK 部署的 memcached-operator
项目
使用 Operator SDK v0.1.0 创建的新项目
为自定义类型创建脚手架 API。
使用 operator-sdk add api --api-version=<apiversion> --kind=<kind>
在新项目中创建自定义资源 (CR) 的 API。
$ cd memcached-operator
$ operator-sdk add api --api-version=cache.example.com/v1alpha1 --kind=Memcached
$ tree pkg/apis
pkg/apis/
├── addtoscheme_cache_v1alpha1.go
├── apis.go
└── cache
└── v1alpha1
├── doc.go
├── memcached_types.go
├── register.go
└── zz_generated.deepcopy.go
对旧项目中定义的每个自定义类型重复之前的命令。每个类型都将在 pkg/apis/<group>/<version>/<kind>_types.go
文件中定义。
复制类型的内容。
将旧项目 pkg/apis/<group>/<version>/types.go
文件的 Spec
和 Status
内容复制到新项目 pkg/apis/<group>/<version>/<kind>_types.go
文件。
每个 <kind>_types.go
文件都有一个 init()
函数。请确保不要删除它,因为这会将类型注册到 Manager 的方案中。
func init() {
SchemeBuilder.Register(&Memcached{}, &MemcachedList{})
将项目的协调代码迁移到更新的 Operator SDK v0.1.0 用法。
开发工作站上安装的 Operator SDK v0.1.0 CLI
以前使用早期版本的 Operator SDK 部署的 memcached-operator
项目
从 pkg/apis/
迁移的自定义类型
添加一个控制器来监视您的 CR。
在 v0.0.x 项目中,要监视的资源以前在 cmd/<operator-name>/main.go
中定义。
sdk.Watch("cache.example.com/v1alpha1", "Memcached", "default", time.Duration(5)*time.Second)
对于 v0.1.0 项目,必须定义一个 控制器 来监视资源。
使用 operator-sdk add controller --api-version=<apiversion> --kind=<kind>
添加一个控制器来监视您的 CR 类型。
$ operator-sdk add controller --api-version=cache.example.com/v1alpha1 --kind=Memcached
$ tree pkg/controller
pkg/controller/
├── add_memcached.go
├── controller.go
└── memcached
└── memcached_controller.go
检查 pkg/controller/<kind>/<kind>_controller.go
文件中的 add()
函数。
import (
cachev1alpha1 "github.com/example-inc/memcached-operator/pkg/apis/cache/v1alpha1"
...
)
func add(mgr manager.Manager, r reconcile.Reconciler) error {
c, err := controller.New("memcached-controller", mgr, controller.Options{Reconciler: r})
// Watch for changes to the primary resource Memcached
err = c.Watch(&source.Kind{Type: &cachev1alpha1.Memcached{}}, &handler.EnqueueRequestForObject{})
// Watch for changes to the secondary resource pods and enqueue reconcile requests for the owner Memcached
err = c.Watch(&source.Kind{Type: &corev1.Pod{}}, &handler.EnqueueRequestForOwner{
IsController: true,
OwnerType: &cachev1alpha1.Memcached{},
})
}
删除第二个 Watch()
或将其修改为监视由您的 CR 拥有的次要资源类型。
如果您的 Operator 正在监视多个 CR 类型,您可以根据您的应用程序执行以下操作之一:
如果 CR 由您的主 CR 拥有,请在同一个控制器中将其作为次要资源进行监视,以触发主资源的协调循环。
// Watch for changes to the primary resource Memcached
err = c.Watch(&source.Kind{Type: &cachev1alpha1.Memcached{}}, &handler.EnqueueRequestForObject{})
// Watch for changes to the secondary resource AppService and enqueue reconcile requests for the owner Memcached
err = c.Watch(&source.Kind{Type: &appv1alpha1.AppService{}}, &handler.EnqueueRequestForOwner{
IsController: true,
OwnerType: &cachev1alpha1.Memcached{},
})
添加一个新的控制器来独立于其他 CR 监视和协调 CR。
$ operator-sdk add controller --api-version=app.example.com/v1alpha1 --kind=AppService
// Watch for changes to the primary resource AppService
err = c.Watch(&source.Kind{Type: &appv1alpha1.AppService{}}, &handler.EnqueueRequestForObject{})
复制并修改 pkg/stub/handler.go
中的协调代码。
在 v0.1.0 项目中,协调代码在控制器的 Reconcile()
方法的 Reconciler 中定义。这类似于旧项目中的 Handle()
函数。请注意参数和返回值的差异。
协调
func (r *ReconcileMemcached) Reconcile(request reconcile.Request) (reconcile.Result, error)
处理
func (h *Handler) Handle(ctx context.Context, event sdk.Event) error
Reconcile()
函数不会接收 sdk.Event
(带有对象),而是接收 Request(Name
/Namespace
密钥)来查找对象。
如果 Reconcile()
函数返回错误,控制器将重新排队并重试 Request
。如果没有返回错误,则根据 Result,控制器将不会重试 Request
、立即重试或在指定持续时间后重试。
将代码从旧项目的 Handle()
函数复制到控制器 Reconcile()
函数中的现有代码。请务必保留 Reconcile()
代码中查找 Request
对象并检查其是否已删除的初始部分。
import (
apierrors "k8s.io/apimachinery/pkg/api/errors"
cachev1alpha1 "github.com/example-inc/memcached-operator/pkg/apis/cache/v1alpha1"
...
)
func (r *ReconcileMemcached) Reconcile(request reconcile.Request) (reconcile.Result, error) {
// Fetch the Memcached instance
instance := &cachev1alpha1.Memcached{}
err := r.client.Get(context.TODO()
request.NamespacedName, instance)
if err != nil {
if apierrors.IsNotFound(err) {
// Request object not found, could have been deleted after reconcile request.
// Owned objects are automatically garbage collected.
// Return and don't requeue
return reconcile.Result{}, nil
}
// Error reading the object - requeue the request.
return reconcile.Result{}, err
}
// Rest of your reconcile code goes here.
...
}
更改协调代码中的返回值
将 return err
替换为 return reconcile.Result{}, err
。
将 return nil
替换为 return reconcile.Result{}, nil
。
要定期协调控制器中的 CR,您可以设置 RequeueAfter 字段用于 reconcile.Result
。这将导致控制器重新排队 Request
并按所需持续时间触发协调。请注意,默认值 0
表示不重新排队。
reconcilePeriod := 30 * time.Second
reconcileResult := reconcile.Result{RequeueAfter: reconcilePeriod}
...
// Update the status
err := r.client.Update(context.TODO(), memcached)
if err != nil {
log.Printf("failed to update memcached status: %v", err)
return reconcileResult, err
}
return reconcileResult, nil
将对 SDK 客户端的调用 (Create、Update、Delete、Get、List) 替换为协调器的客户端。
请参阅下面的示例以及 operator-sdk
项目中的 controller-runtime
客户端 API 文档,了解更多详细信息。
// Create
dep := &appsv1.Deployment{...}
err := sdk.Create(dep)
// v0.0.1
err := r.client.Create(context.TODO(), dep)
// Update
err := sdk.Update(dep)
// v0.0.1
err := r.client.Update(context.TODO(), dep)
// Delete
err := sdk.Delete(dep)
// v0.0.1
err := r.client.Delete(context.TODO(), dep)
// List
podList := &corev1.PodList{}
labelSelector := labels.SelectorFromSet(labelsForMemcached(memcached.Name))
listOps := &metav1.ListOptions{LabelSelector: labelSelector}
err := sdk.List(memcached.Namespace, podList, sdk.WithListOptions(listOps))
// v0.1.0
listOps := &client.ListOptions{Namespace: memcached.Namespace, LabelSelector: labelSelector}
err := r.client.List(context.TODO(), listOps, podList)
// Get
dep := &appsv1.Deployment{APIVersion: "apps/v1", Kind: "Deployment", Name: name, Namespace: namespace}
err := sdk.Get(dep)
// v0.1.0
dep := &appsv1.Deployment{}
err = r.client.Get(context.TODO(), types.NamespacedName{Name: name, Namespace: namespace}, dep)
复制并初始化 Handler
结构中的任何其他字段到 Reconcile<Kind>
结构中。
// newReconciler returns a new reconcile.Reconciler
func newReconciler(mgr manager.Manager) reconcile.Reconciler {
return &ReconcileMemcached{client: mgr.GetClient(), scheme: mgr.GetScheme(), foo: "bar"}
}
// ReconcileMemcached reconciles a Memcached object
type ReconcileMemcached struct {
client client.Client
scheme *runtime.Scheme
// Other fields
foo string
}
复制 `main.go` 中的代码更改。
在 `cmd/manager/main.go` 中,v0.1.0 操作符的主函数设置了 Manager,它注册自定义资源并启动所有控制器。
不需要从旧的 `main.go` 中迁移 SDK 函数 `sdk.Watch()`、`sdk.Handle()` 和 `sdk.Run()`,因为该逻辑现在在控制器中定义。
但是,如果旧的 `main.go` 文件中定义了任何特定于操作符的标志或设置,请将其复制过来。
如果使用 SDK 的方案注册了任何第三方资源类型,请参阅 `operator-sdk` 项目中的 高级主题,了解如何在新的项目中使用 Manager 的方案注册它们。
复制用户定义的文件。
如果旧项目中存在任何用户定义的 `pkgs`、脚本或文档,请将这些文件复制到新项目中。
复制部署清单的更改。
对于旧项目中对以下清单所做的任何更新,请将更改复制到新项目中相应的文件中。注意不要直接覆盖文件,而要检查并进行必要的更改。
将 `tmp/build/Dockerfile` 复制到 `build/Dockerfile`
新项目布局中没有 tmp 目录
将 `deploy/rbac.yaml` 中的 RBAC 规则更新复制到 `deploy/role.yaml` 和 `deploy/role_binding.yaml`
将 `deploy/cr.yaml` 复制到 `deploy/crds/
将 `deploy/crd.yaml` 复制到 `deploy/crds/
复制用户定义的依赖项。
对于添加到旧项目 `Gopkg.toml` 的任何用户定义的依赖项,请将其复制并追加到新项目的 `Gopkg.toml`。运行 `dep ensure` 以更新新项目中的 vendor。
确认您的更改。
构建并运行您的操作符以验证其是否有效。