×

本指南介绍如何将使用 Operator SDK v0.0.x 构建的 Operator 项目迁移到 Operator SDK v0.1.0 所需项目结构。

Red Hat 支持的 Operator SDK CLI 工具版本(包括与 Operator 项目相关的脚手架和测试工具)已弃用,并计划在未来版本的 OpenShift Dedicated 中移除。Red Hat 将在当前版本生命周期内为此功能提供错误修复和支持,但此功能将不再接收增强功能,并将从未来的 OpenShift Dedicated 版本中移除。

不建议使用 Red Hat 支持的 Operator SDK 版本创建新的 Operator 项目。拥有现有 Operator 项目的 Operator 作者可以使用 OpenShift Dedicated 发布的 Operator SDK CLI 工具版本来维护其项目并创建针对较新版本的 OpenShift Dedicated 的 Operator 版本。

以下与 Operator 项目相关的基础镜像 *未* 被弃用。这些基础镜像的运行时功能和配置 API 仍然支持错误修复和解决 CVE。

  • 基于 Ansible 的 Operator 项目的基础镜像

  • 基于 Helm 的 Operator 项目的基础镜像

有关不受支持的社区维护的 Operator SDK 版本的信息,请参见 Operator SDK (Operator Framework)

推荐的项目迁移方法是:

  1. 初始化一个新的 v0.1.0 项目。

  2. 将您的代码复制到新项目。

  3. 根据 v0.1.0 的说明修改新项目。

本指南使用 Operator SDK 中的示例项目 memcached-operator 来演示迁移步骤。请参阅 v0.0.7 memcached-operatorv0.1.0 memcached-operator 项目结构,分别查看迁移前和迁移后的示例。

创建新的 Operator SDK v0.1.0 项目

重命名您的 Operator SDK v0.0.x 项目,并在其位置创建一个新的 v0.1.0 项目。

前提条件
  • 在开发工作站上安装 Operator SDK v0.1.0 CLI

  • 使用早期版本的 Operator SDK 部署的 memcached-operator 项目

步骤
  1. 确保 SDK 版本为 v0.1.0

    $ operator-sdk --version
    operator-sdk version 0.1.0
  2. 创建一个新项目

    $ 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
  3. 从旧项目复制 .git

    $ cp -rf old-memcached-operator/.git memcached-operator/.git

从 pkg/apis 迁移自定义类型

将项目的自定义类型迁移到更新的 Operator SDK v0.1.0 用法。

前提条件
  • 在开发工作站上安装 Operator SDK v0.1.0 CLI

  • 使用早期版本的 Operator SDK 部署的 memcached-operator 项目

  • 使用 Operator SDK v0.1.0 创建的新项目

步骤
  1. 为自定义类型创建脚手架 API。

    1. 使用 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
    2. 对旧项目中定义的每个自定义类型重复之前的命令。每个类型都将定义在文件 pkg/apis/<group>/<version>/<kind>_types.go 中。

  2. 复制类型的内容。

    1. 将旧项目 pkg/apis/<group>/<version>/types.go 文件的 SpecStatus 内容复制到新项目 pkg/apis/<group>/<version>/<kind>_types.go 文件。

    2. 每个 <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/ 迁移的自定义类型

步骤
  1. 添加一个控制器来监视您的 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 来监视资源。

    1. 使用 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
    2. 检查 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{})
  2. 复制并修改 `pkg/stub/handler.go` 中的协调代码。

    在 v0.1.0 项目中,协调代码定义在控制器的`Reconcile()`方法中,该方法位于Reconciler。这类似于旧项目中的`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()`函数接收一个Request ( `Name`/`Namespace`键) 来查找对象,而不是接收一个`sdk.Event`(包含对象)。

    如果`Reconcile()`函数返回错误,控制器将重新入队并重试`Request`。如果没有返回错误,则根据Result,控制器将要么不重试`Request`,要么立即重试,要么在指定持续时间后重试。

    1. 将旧项目`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.
          ...
      }
    2. 更改协调代码中的返回值。

      1. 将`return err`替换为`return reconcile.Result{}, err`。

      2. 将`return nil`替换为`return reconcile.Result{}, nil`。

    3. 要定期协调控制器中的 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
    4. 将对 SDK 客户端的调用(创建、更新、删除、获取、列出)替换为协调器的客户端。

      请参阅下面的示例以及`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)
    5. 将`Handler`结构中的任何其他字段复制并初始化到`Reconcile`结构中。

      // 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
      }
  3. 复制`main.go`中的更改。

    v0.1.0 Operator 的`cmd/manager/main.go`中的主函数设置了Manager,它注册自定义资源并启动所有控制器。

    不需要从旧`main.go`中迁移 SDK 函数`sdk.Watch()`、`sdk.Handle()`和`sdk.Run()`,因为该逻辑现在已在控制器中定义。

    但是,如果旧`main.go`文件中定义了任何特定于 Operator 的标志或设置,请将它们复制过来。

    如果您有任何使用 SDK 的 scheme 注册的第三方资源类型,请参阅`operator-sdk`项目中的高级主题,了解如何在新的项目中使用 Manager 的 scheme 注册它们。

  4. 复制用户定义的文件。

    如果旧项目中存在任何用户定义的`pkgs`、脚本或文档,请将这些文件复制到新项目中。

  5. 复制部署清单的更改。

    对于旧项目中对以下清单所做的任何更新,请将更改复制到新项目中相应的文件中。注意不要直接覆盖文件,而是检查并进行必要的更改。

    • `tmp/build/Dockerfile` 到 `build/Dockerfile`

      • 新项目布局中没有 tmp 目录。

    • RBAC 规则更新,从`deploy/rbac.yaml`到`deploy/role.yaml`和`deploy/role_binding.yaml`。

    • `deploy/cr.yaml` 到 `deploy/crds/___cr.yaml`

    • `deploy/crd.yaml` 到 `deploy/crds/___crd.yaml`

  6. 复制用户定义的依赖项。

    对于添加到旧项目`Gopkg.toml`的任何用户定义的依赖项,请将其复制并追加到新项目`Gopkg.toml`中。运行`dep ensure`以更新新项目中的 vendor。

  7. 确认您的更改。

    构建并运行您的 Operator 以验证其是否正常工作。