5.17. 迁移到 Operator SDK v0.1.0
本指南论述了如何将使用 Operator SDK v0.0.x 构建的 Operator 项目迁移到 Operator SDK v0.1.0 所需的项目结构。
红帽支持的 Operator SDK CLI 工具版本,包括 Operator 项目的相关构建和测试工具已被弃用,计划在以后的 OpenShift Dedicated 发行版本中删除。红帽将在当前发行生命周期中提供对这个功能的程序错误修复和支持,但此功能将不再获得改进,并将在以后的 OpenShift Dedicated 版本中删除。
对于创建新 Operator 项目,不建议使用红帽支持的 Operator SDK 版本。现有 Operator 项目的 Operator 作者可使用 OpenShift Dedicated 4 发布的 Operator SDK CLI 工具版本来维护其项目,并创建针对较新版本的 OpenShift Dedicated 的 Operator 发行版本。
以下与 Operator 项目相关的基础镜像 没有被弃用。这些基础镜像的运行时功能和配置 API 仍然会有程序错误修复和并提供对相关 CVE 的解决方案。
- 基于 Ansible 的 Operator 项目的基础镜像
- 基于 Helm 的 Operator 项目的基础镜像
有关 Operator SDK 不支持的、社区维护版本的信息,请参阅 Operator SDK (Operator Framework)。
迁移项目的建议方法是:
- 初始化新的 v0.1.0 项目。
- 将您的代码复制到新项目。
- 修改新项目,如 v0.1.0 所述。
本指南使用 memcached-operator
(来自 Operator SDK 的示例项目)来说明迁移步骤。有关 pre- 和 post-migration 示例,请参阅 v0.0.7 memcached-operator 和 v0.1.0 memcached-operator 项目结构。
5.17.1. 创建新的 Operator SDK v0.1.0 项目
重命名 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
5.17.2. 从 pkg/apis 中迁移自定义类型
将项目的自定义类型迁移到更新的 Operator SDK v0.1.0 用法。
先决条件
- 开发工作站上安装 operator SDK v0.1.0 CLI
-
之前使用较早版本的 Operator SDK 部署
memcached-operator
项目 - 使用 Operator SDK v0.1.0 创建新项目
流程
为自定义类型创建 scaffold 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{})
-
将来自旧项目的
5.17.3. 迁移协调代码
将项目的自定义类型迁移到更新的 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 项目,您必须定义一个 Controller 监视资源:
添加控制器以使用
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 拥有的二级资源类型。通过监控多个资源,您可以针对与应用程序相关的多个资源触发协调循环。如需了解更多详细信息,请参阅 监视和事件处理 文档和 Kubernetes 控制器惯例 文档。
如果 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 项目中,协调代码在控制器的 Reconciler 的
Reconcile()
方法中定义。这与较旧的项目中的Handle()
函数类似。请注意参数和返回值之间的区别:Reconcile:
func (r *ReconcileMemcached) Reconcile(request reconcile.Request) (reconcile.Result, error)
Handle:
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,您可以为
reconcile.Result
设置 RequeueAfter 字段。这会导致控制器重新排队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 }
Copy changes from
main.go
.cmd/manager/main.go
中 v0.1.0 Operator 的主要功能设置 Manager,它将注册自定义资源并启动所有控制器。因为逻辑已在控制器中定义,所以现在不需要从旧的
main.go
中迁移 SDK 函数sdk.Watch()
,sdk.Handle()
, 和sdk.Run()
。但是,如果在旧的
main.go
文件中定义了任何特定于 Operator 的标记或设置,则需要复制它们。如果您使用 SDK 的方案注册了任何第三方资源类型,请参阅
operator-sdk
项目中的 高级主题,了解如何使用新项目中的 Manager 方案注册。复制用户定义的文件。
如果较旧的项目中有任何用户定义的
pkgs
、脚本或文档,请将这些文件复制到新项目中。将更改复制到部署清单。
对于对旧项目中以下清单所做的任何更新,请将更改复制到新项目中的对应文件。请注意,不要直接覆盖文件,而是检查并进行必要的更改:
tmp/build/Dockerfile
到build/Dockerfile
- 在新项目的布局中没有 tmp 目录
-
RBAC 规则从
deploy/rbac.yaml
更新至deploy/role.yaml
和deploy/role_binding.yaml
-
deploy/cr.yaml
到deploy/crds/<group>_<version>_<kind>_cr.yaml
-
deploy/crd.yaml
到deploy/crds/<group>_<version>_<kind>_crd.yaml
复制用户定义的依赖项。
对于添加到旧项目的
Gopkg.toml
的任何用户定义的依赖项,请复制它们并将其附加到新项目的Gopkg.toml
中。运行dep ensure
更新新项目中的供应商。确认您的更改。
构建并运行 Operator 以验证其是否正常工作。