×

本指南描述如何将使用 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。

  • 基于 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 项目,必须定义一个 控制器 来监视资源。

    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() 函数。请注意参数和返回值的差异。

    • 协调

          func (r *ReconcileMemcached) Reconcile(request reconcile.Request) (reconcile.Result, error)
    • 处理

          func (h *Handler) Handle(ctx context.Context, event sdk.Event) error

    Reconcile() 函数不会接收 sdk.Event(带有对象),而是接收 RequestName/Namespace 密钥)来查找对象。

    如果 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,您可以设置 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
    4. 将对 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)
    5. 复制并初始化 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
      }
  3. 复制 `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 的方案注册它们。

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

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

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

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

    • 将 `tmp/build/Dockerfile` 复制到 `build/Dockerfile`

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

    • 将 `deploy/rbac.yaml` 中的 RBAC 规则更新复制到 `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. 确认您的更改。

    构建并运行您的操作符以验证其是否有效。