第 11 章 Operator SDK

11.1. Operator SDK 入门

本指南将介绍 Operator SDK 的基础知识,指导可作为集群管理员访问基于 Kubernetes 的集群(如 OpenShift Container Platform)的 Operator 作者构建简单 Go-based Memcached Operator 的示例,并管理从安装到升级的整个生命周期。

通过以下两个 Operator Framework 核心组件便可实现这一目的:Operator SDK(operator-sdk CLI 工具和 controller-runtime 库 API)以及 Operator Lifecycle Manager (OLM)。

注意

OpenShift Container Platform 4 支持 Operator SDK v0.7.0 或更高版本。

11.1.1. Operator SDK 构架

Operator Framework 是一个开源工具包,用于以有效、自动化且可扩展的方式管理 Kubernetes 原生应用程序,即 Operator。Operator 利用 Kubernetes 的可扩展性来展现云服务的自动化优势,如置备、扩展以及备份和恢复,同时能够在 Kubernetes 可运行的任何地方运行。

Operator 有助于简化对 Kubernetes 上的复杂、有状态的应用程序的管理。然而,现在编写 Operator 并不容易,会面临一些挑战,如使用低级别 API、编写样板文件以及缺乏模块化功能(这会导致重复工作)。

Operator SDK 是一个框架,通过提供以下内容来降低 Operator 的编写难度:

  • 高级 API 和抽象,用于更直观地编写操作逻辑
  • 支架和代码生成工具,用于快速引导新项目
  • 扩展项,覆盖常见的 Operator 用例

11.1.1.1. 工作流

Operator SDK 提供以下工作流来开发新的 Operator:

  1. 使用 Operator SDK 命令行界面 (CLI) 新建一个 Operator 项目。
  2. 通过添加自定义资源定义 (CRD) 来定义新的资源 API。
  3. 使用 Operator SDK API 来指定要监视的资源。
  4. 在指定的处理程序中定义 Operator 协调逻辑,并使用 Operator SDK API 与资源交互。
  5. 使用 Operator SDK CLI 来构建和生成 Operator 部署清单。

图 11.1. Operator SDK 工作流

osdk workflow

在高级别上,使用了 Operator SDK 的 Operator 会在 Operator 作者定义的处理程序中处理与被监视资源相关的事件,并采取措施协调应用程序的状态。

11.1.1.2. 管理器文件

Operator 的主要程序为 cmd/manager/main.go 中的管理器文件。管理器会自动注册 pkg/apis/ 下定义的所有自定义资源 (CR) 的方案,并运行 pkg/controller/ 下的所有控制器。

管理器可限制所有控制器监视资源的命名空间:

mgr, err := manager.New(cfg, manager.Options{Namespace: namespace})

默认情况下,这是 Operator 运行时所处的命名空间。要监视所有命名空间,把命名空间选项设为空:

mgr, err := manager.New(cfg, manager.Options{Namespace: ""})

11.1.1.3. Prometheus Operator 支持

Prometheus 是一个开源系统监视和警报工具包。Prometheus Operator 会创建、配置和管理在基于 Kubernetes 的集群(如 OpenShift Container Platform)中运行的 Prometheus 集群。

默认情况下,Operator SDK 中包括帮助函数,用于在任何生成的 Go-based Operator 中自动设置指标,以便在部署了 Prometheus Operator 的集群上使用。

11.1.2. 安装 Operator SDK CLI

Operator SDK 配有一个 CLI 工具,可协助开发人员创建、构建和部署新的 Operator 项目。您可在工作站上安装 SDK CLI,为编写您自己的 Operator 做准备。

注意

本指南中,minikube v0.25.0+ 用作本地 Kubernetes 集群,Quay.io 用于公共 registry。

11.1.2.1. 从 GitHub 版本安装

您可从 GitHub 上的项目下载并安装 SDK CLI 的预构建发行版文件。

先决条件

  • docker v17.03+
  • 已安装 OpenShift CLI (oc) v4.1+
  • 访问基于 Kubernetes v1.11.3+ 的集群
  • 访问容器 registry

流程

  1. 设置发行版本变量:

    RELEASE_VERSION=v0.8.0
  2. 下载发行版二进制文件。

    • Linux:

      $ curl -OJL https://github.com/operator-framework/operator-sdk/releases/download/${RELEASE_VERSION}/operator-sdk-${RELEASE_VERSION}-x86_64-linux-gnu
    • macOS:

      $ curl -OJL https://github.com/operator-framework/operator-sdk/releases/download/${RELEASE_VERSION}/operator-sdk-${RELEASE_VERSION}-x86_64-apple-darwin
  3. 验证所下载的发行版本二进制文件。

    1. 下载所提供的 ASC 文件。

      • Linux:

        $ curl -OJL https://github.com/operator-framework/operator-sdk/releases/download/${RELEASE_VERSION}/operator-sdk-${RELEASE_VERSION}-x86_64-linux-gnu.asc
      • macOS:

        $ curl -OJL https://github.com/operator-framework/operator-sdk/releases/download/${RELEASE_VERSION}/operator-sdk-${RELEASE_VERSION}-x86_64-apple-darwin.asc
    2. 将二进制文件与对应的 ASC 文件放在同一个目录中,并运行以下命令来验证该二进制文件:

      • Linux:

        $ gpg --verify operator-sdk-${RELEASE_VERSION}-x86_64-linux-gnu.asc
      • macOS:

        $ gpg --verify operator-sdk-${RELEASE_VERSION}-x86_64-apple-darwin.asc

      如果您的工作站上没有维护人员公钥,则会出现以下错误:

      $ gpg --verify operator-sdk-${RELEASE_VERSION}-x86_64-apple-darwin.asc
      $ gpg: assuming signed data in 'operator-sdk-${RELEASE_VERSION}-x86_64-apple-darwin'
      $ gpg: Signature made Fri Apr  5 20:03:22 2019 CEST
      $ gpg:                using RSA key <key_id> 1
      $ gpg: Can't check signature: No public key
      1
      RSA 密钥字符串。

      要下载密钥,请运行以下命令,用上一条命令输出的 RSA 密钥字符串来替换 <key_id>

      $ gpg [--keyserver keys.gnupg.net] --recv-key "<key_id>" 1
      1
      如果您尚未配置密钥服务器,请使用 --keyserver 选项指定一个密钥服务器。
  4. 在您的 PATH 中安装发行版本二进制文件:

    • Linux:

      $ chmod +x operator-sdk-${RELEASE_VERSION}-x86_64-linux-gnu
      $ sudo cp operator-sdk-${RELEASE_VERSION}-x86_64-linux-gnu /usr/local/bin/operator-sdk
      $ rm operator-sdk-${RELEASE_VERSION}-x86_64-linux-gnu
    • macOS:

      $ chmod +x operator-sdk-${RELEASE_VERSION}-x86_64-apple-darwin
      $ sudo cp operator-sdk-${RELEASE_VERSION}-x86_64-apple-darwin /usr/local/bin/operator-sdk
      $ rm operator-sdk-${RELEASE_VERSION}-x86_64-apple-darwin
  5. 验证是否已正确安装 CLI 工具:

    $ operator-sdk version

11.1.2.2. 通过 Homebrew 安装

可使用 Homebrew 来安装 SDK CLI。

先决条件

  • Homebrew
  • docker v17.03+
  • 已安装 OpenShift CLI (oc) v4.1+
  • 访问基于 Kubernetes v1.11.3+ 的集群
  • 访问容器 registry

流程

  1. 使用 brew 命令安装 SDK CLI:

    $ brew install operator-sdk
  2. 验证是否已正确安装 CLI 工具:

    $ operator-sdk version

11.1.2.3. 通过源代码编译并安装

可获取 Operator SDK 源代码来编译和安装 SDK CLI。

先决条件

  • dep v0.5.0+
  • Git
  • Go v1.10+
  • docker v17.03+
  • 已安装 OpenShift CLI (oc) v4.1+
  • 访问基于 Kubernetes v1.11.3+ 的集群
  • 访问容器 registry

流程

  1. 克隆 operator-sdk 存储库:

    $ mkdir -p $GOPATH/src/github.com/operator-framework
    $ cd $GOPATH/src/github.com/operator-framework
    $ git clone https://github.com/operator-framework/operator-sdk
    $ cd operator-sdk
  2. 检查所需的发行版本分支:

    $ git checkout master
  3. 编译并安装 SDK CLI:

    $ make dep
    $ make install

    该操作会在 $GOPATH/bin 中安装 CLI 二进制 operator-sdk

  4. 验证是否已正确安装 CLI 工具:

    $ operator-sdk version

11.1.3. 使用 Operator SDK 来构建 Go-based Memcached Operator

Operator SDK 可简化 Kubernetes 原生应用程序的构建,构建该应用程序原本需要深入掌握特定于应用程序的操作知识。SDK 不仅降低了这一障碍,而且有助于减少许多常见管理功能(如计量或监控)所需的样板代码量。

本流程介绍了一个使用 SDK 提供的工具和库构建简单 Memcached Operator 的示例。

先决条件

  • 开发工作站已安装 Operator SDK CLI
  • 基于 Kubernetes 的集群(v1.8 或更高版本,支持 apps/v1beta2 API 组,如 OpenShift Container Platform 4.2)上已安装 Operator Lifecycle Manager (OLM)
  • 使用具有 cluster-admin 权限的账户访问该集群
  • 已安装 OpenShift CLI (oc) v4.1+

流程

  1. 创建一个新项目。

    使用 CLI 来新建 memcached-operator 项目:

    $ mkdir -p $GOPATH/src/github.com/example-inc/
    $ cd $GOPATH/src/github.com/example-inc/
    $ operator-sdk new memcached-operator --dep-manager dep
    $ cd memcached-operator
  2. 添加新的自定义资源定义 (CRD)。

    1. 使用 CLI 来添加名为 Memcached 的新 CRD API,将 APIVersion 设置为 cache.example.com/v1apha1,将 Kind 设置为 Memcached

      $ operator-sdk add api \
          --api-version=cache.example.com/v1alpha1 \
          --kind=Memcached

      这样会将 Memcached 资源 API 构建至 pkg/apis/cache/v1alpha1/ 下。

    2. 修改 pkg/apis/cache/v1alpha1/memcached_types.go 文件中 Memcached 自定义资源 (CR) 的 spec 和状态:

      type MemcachedSpec struct {
      	// Size is the size of the memcached deployment
      	Size int32 `json:"size"`
      }
      type MemcachedStatus struct {
      	// Nodes are the names of the memcached pods
      	Nodes []string `json:"nodes"`
      }
    3. 修改好 *_types.go 文件后,务必要运行以下命令来更新该资源类型的生成代码:

      $ operator-sdk generate k8s
  3. 添加新控制器。

    1. 在项目中添加新控制器以观察和协调 Memcached 资源:

      $ operator-sdk add controller \
          --api-version=cache.example.com/v1alpha1 \
          --kind=Memcached

      这样会在 pkg/controller/memcached/ 下构建新控制器实现。

    2. 在本示例中,将所生成的控制器文件 pkg/controller/memcached/memcached_controller.go 替换成示例实现

      示例控制器会对每个 Memcached CR 执行以下协调逻辑:

      • 如果尚无 Memcached Deployment,请创建一个。
      • 确保 Deployment 大小与 Memcached CR spec 中指定的大小相同。
      • 使用 Memcached Pod 的名称来更新 Memcached CR 状态。

      接下来的两个子步骤用于检查控制器如何监视资源以及协调循环会如何被触发。您可跳过这些步骤,直接构建和运行 Operator。

    3. 检查 pkg/controller/memcached/memcached_controller.go 文件中的控制器实现,了解控制器如何监视资源。

      首先要监视的是作为主要资源的 Memcached 类型。对于每个添加、更新或删除事件,协调循环均会针对该 Memcached 对象发送一个协调 Request<namespace>:<name> 键):

      err := c.Watch(
        &source.Kind{Type: &cachev1alpha1.Memcached{}}, &handler.EnqueueRequestForObject{})

      接下来监视 Deployment,但事件处理程序会将每个事件映射到 Deployment 所有者的协调 Request 中。即本例中为其创建 Deployment 的 Memcached 对象。这样可让控制器将 Deployment 视为辅助资源:

      err := c.Watch(&source.Kind{Type: &appsv1.Deployment{}}, &handler.EnqueueRequestForOwner{
      		IsController: true,
      		OwnerType:    &cachev1alpha1.Memcached{},
      	})
    4. 每个控制器均有一个协调器对象,且该对象带有实现协调循环的 Reconcile() 方法。系统会将 Request 参数传递到该协调循环,该参数是一个用于从缓存中查找主资源对象 Memcached 的 <namespace>:<name> 键:

      func (r *ReconcileMemcached) Reconcile(request reconcile.Request) (reconcile.Result, error) {
        // Lookup the Memcached instance for this reconcile request
        memcached := &cachev1alpha1.Memcached{}
        err := r.client.Get(context.TODO(), request.NamespacedName, memcached)
        ...
      }

      根据 Reconcile() 的返回值,协调 Request 可能会重新排队,且可能再次触发循环:

      // Reconcile successful - don't requeue
      return reconcile.Result{}, nil
      // Reconcile failed due to error - requeue
      return reconcile.Result{}, err
      // Requeue for any reason other than error
      return reconcile.Result{Requeue: true}, nil
  4. 构建并运行 Operator。

    1. 在运行 Operator 之前,必须使用 Kubernetes API 服务器来注册 CRD:

      $ oc create \
          -f deploy/crds/cache_v1alpha1_memcached_crd.yaml
    2. 注册 CRD 之后,运行 Operator 时有两个选项可供选择:

      • 作为 Kubernetes 集群内的一个 Deployment 来运行
      • 作为集群外的 Go 程序运行

      选择以下任一方法。

      1. 选项 A:作为集群内的一个 Deployment 来运行。

        1. 构建 memcached-operator 镜像并将其推送至 registry:

          $ operator-sdk build quay.io/example/memcached-operator:v0.0.1
        2. Deployment 清单在 deploy/operator.yaml 中生成。按如下方式更新 Deployment 镜像,因为默认值只是一个占位符:

          $ sed -i 's|REPLACE_IMAGE|quay.io/example/memcached-operator:v0.0.1|g' deploy/operator.yaml
        3. 确保 Quay.io 上有一个可供下一步使用的账户,或者替换您的首选容器 registry。在 registry 中,创建一个名为 memcached-operator 的新公共镜像存储库。
        4. 将镜像推送至 registry:

          $ docker push quay.io/example/memcached-operator:v0.0.1
        5. 设置 RBAC 并部署 memcached-operator

          $ oc create -f deploy/role.yaml
          $ oc create -f deploy/role_binding.yaml
          $ oc create -f deploy/service_account.yaml
          $ oc create -f deploy/operator.yaml
        6. 验证 memcached-operator 是否正在运行:

          $ oc get deployment
          NAME                     DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
          memcached-operator       1         1         1            1           1m
      2. 选项 B:在集群外本地运行。

        这是开发循环中的首选方法,可加快部署和测试的速度。

        使用 $HOME/.kube/config 中的默认 Kubernetes 配置文件在本地运行 Operator:

        $ operator-sdk up local --namespace=default

        您可借助标记 --kubeconfig=<path/to/kubeconfig> 来使用特定的 kubeconfig

  5. 通过创建 Memcached CR 来验证该 Operator 可否部署 Memcached 应用程序

    1. 创建 deploy/crds/cache_v1alpha1_memcached_cr.yaml 中生成的 Memcached CR 示例:

      $ cat deploy/crds/cache_v1alpha1_memcached_cr.yaml
      apiVersion: "cache.example.com/v1alpha1"
      kind: "Memcached"
      metadata:
        name: "example-memcached"
      spec:
        size: 3
      
      $ oc apply -f deploy/crds/cache_v1alpha1_memcached_cr.yaml
    2. 确保 memcached-operator 为 CR 创建 Deployment:

      $ oc get deployment
      NAME                     DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
      memcached-operator       1         1         1            1           2m
      example-memcached        3         3         3            3           1m
    3. 检查 Pod 和 CR 状态,以确认其状态中是否更新了 memcached Pod 名称:

      $ oc get pods
      NAME                                  READY     STATUS    RESTARTS   AGE
      example-memcached-6fd7c98d8-7dqdr     1/1       Running   0          1m
      example-memcached-6fd7c98d8-g5k7v     1/1       Running   0          1m
      example-memcached-6fd7c98d8-m7vn7     1/1       Running   0          1m
      memcached-operator-7cc7cfdf86-vvjqk   1/1       Running   0          2m
      
      $ oc get memcached/example-memcached -o yaml
      apiVersion: cache.example.com/v1alpha1
      kind: Memcached
      metadata:
        clusterName: ""
        creationTimestamp: 2018-03-31T22:51:08Z
        generation: 0
        name: example-memcached
        namespace: default
        resourceVersion: "245453"
        selfLink: /apis/cache.example.com/v1alpha1/namespaces/default/memcacheds/example-memcached
        uid: 0026cc97-3536-11e8-bd83-0800274106a1
      spec:
        size: 3
      status:
        nodes:
        - example-memcached-6fd7c98d8-7dqdr
        - example-memcached-6fd7c98d8-g5k7v
        - example-memcached-6fd7c98d8-m7vn7
  6. 通过更新部署的大小来验证 Operator 可否管理所部署的 Memcached 应用程序

    1. memcached CR 中的 spec.size 字段从 3 改为 4

      $ cat deploy/crds/cache_v1alpha1_memcached_cr.yaml
      apiVersion: "cache.example.com/v1alpha1"
      kind: "Memcached"
      metadata:
        name: "example-memcached"
      spec:
        size: 4
    2. 应用更改:

      $ oc apply -f deploy/crds/cache_v1alpha1_memcached_cr.yaml
    3. 确认 Operator 已更改 Deployment 大小:

      $ oc get deployment
      NAME                 DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
      example-memcached    4         4         4            4           5m
  7. 清理资源:

    $ oc delete -f deploy/crds/cache_v1alpha1_memcached_cr.yaml
    $ oc delete -f deploy/crds/cache_v1alpha1_memcached_crd.yaml
    $ oc delete -f deploy/operator.yaml
    $ oc delete -f deploy/role.yaml
    $ oc delete -f deploy/role_binding.yaml
    $ oc delete -f deploy/service_account.yaml

11.1.4. 使用 Operator Lifecycle Manager 来管理 Memcached Operator

上一节介绍了如何手动运行 Operator。下面我们将探索如何使用 Operator Lifecycle Manager (OLM),OLM 将为生产环境中运行的 Operator 启用更强大的部署模型。

OLM 可帮助您在 Kubernetes 集群中安装、更新所有 Operator(及其相关服务)并对其整个生命周期实施一般性管理。OLM 将作为 Kubernetes 的扩展程序运行,支持您使用 oc 实现所有生命周期管理功能,而无需额外工具。

先决条件

  • 基于 Kubernetes 的集群(v1.8 或更高版本,支持 apps/v1beta2 API 组,如已启用预览 OLM 的 OpenShift Container Platform 4.2)上已安装 OLM
  • 已构建 Memcached Operator

流程

  1. 生成 Operator 清单。

    Operator 清单描述了如何整体显示、创建和管理应用程序,即本例中的 Memcached。该清单由 ClusterServiceVersion (CSV) 对象定义,是运行 OLM 的必要条件。

    您可使用以下命令来生成 CSV 清单:

    $ operator-sdk olm-catalog gen-csv --csv-version 0.0.1
    注意

    该命令通过您在构建 Memcached Operator 时创建的 memcached-operator/ 目录运行。

    在本指南中,我们将继续使用该预定义清单文件来执行后续步骤。您可更改清单中的镜像字段,以反映通过前面的步骤构建的镜像,但这不是硬性要求。

    注意

    更多有关手动定义清单文件的信息,请参阅构建用于 Operator Framework 的 CSV

  2. 部署 Operator。

    1. 创建一个 OperatorGroup,指定 Operator 的目标命名空间。在要创建 CSV 的命名空间中创建以下 OperatorGroup。本例中使用了 default 命名空间:

      apiVersion: operators.coreos.com/v1
      kind: OperatorGroup
      metadata:
        name: memcached-operator-group
        namespace: default
      spec:
        targetNamespaces:
        - default
    2. 将 Operator 的 CSV 清单应用于集群中指定的命名空间:

      $ curl -Lo memcachedoperator.0.0.1.csv.yaml https://raw.githubusercontent.com/operator-framework/getting-started/master/memcachedoperator.0.0.1.csv.yaml
      $ oc apply -f memcachedoperator.0.0.1.csv.yaml
      $ oc get csv memcachedoperator.v0.0.1 -n default -o json | jq '.status'

      应用该清单时,集群不会立即更新,因为尚未满足清单中指定的要求。

    3. 创建角色、角色绑定和服务账户以便向 Operator 授予资源权限,创建自定义资源定义 (CRD) 以便创建受 Operator 管理的 Memcached 类型:

      $ oc create -f deploy/crds/cache_v1alpha1_memcached_crd.yaml
      $ oc create -f deploy/service_account.yaml
      $ oc create -f deploy/role.yaml
      $ oc create -f deploy/role_binding.yaml
      注意

      这些文件在您构建 Memcached Operator 时由 Operator SDK 生成并存储至 deploy/ 目录中。

      因为在应用清单时,OLM 会在特定命名空间中创建 Operator,所以管理员可利用原生 Kubernetes RBAC 权限模型来限制哪些用户可以安装 Operator。

  3. 创建应用程序实例。

    Memcached Operator 正在 default 命名空间中运行。用户通过 CustomResource 实例与 Operator 交互;这种情况下,资源拥有类型 Memcached。原生 Kubernetes RBAC 也适用于 CustomResource,让管理员能够控制与每个 Operator 的交互。

    在该命名空间中创建 Memcached 实例将触发 Memcached Operator,以针对运行由 Operator 管理的 Memcached 服务器的 Pod 进行实例化。您创建的 CustomResource 越多,在此命名空间中运行的 Memcached Operator 管理的 Memcached 实例就越独特。

    $ cat <<EOF | oc apply -f -
    apiVersion: "cache.example.com/v1alpha1"
    kind: "Memcached"
    metadata:
      name: "memcached-for-wordpress"
    spec:
      size: 1
    EOF
    
    $ cat <<EOF | oc apply -f -
    apiVersion: "cache.example.com/v1alpha1"
    kind: "Memcached"
    metadata:
      name: "memcached-for-drupal"
    spec:
      size: 1
    EOF
    
    $ oc get Memcached
    NAME                      AGE
    memcached-for-drupal      22s
    memcached-for-wordpress   27s
    
    $ oc get pods
    NAME                                       READY     STATUS    RESTARTS   AGE
    memcached-app-operator-66b5777b79-pnsfj    1/1       Running   0          14m
    memcached-for-drupal-5476487c46-qbd66      1/1       Running   0          3s
    memcached-for-wordpress-65b75fd8c9-7b9x7   1/1       Running   0          8s
  4. 更新应用程序。

    通过引用旧 Operator 清单的 replaces 字段来创建新 Operator 清单,以手动更新 Operator。OLM 可确保由旧 Operator 管理的所有资源的所有权均转移到至新 Operator,而不用担心任何程序停止执行。升级资源以便在新版 Operator 下运行所需的所有数据迁移均将由 Operator 直接执行。

    以下命令演示了如何使用新版 Operator 来应用新的 Operator 清单文件,并显示了 Pod 仍在执行:

    $ curl -Lo memcachedoperator.0.0.2.csv.yaml https://raw.githubusercontent.com/operator-framework/getting-started/master/memcachedoperator.0.0.2.csv.yaml
    $ oc apply -f memcachedoperator.0.0.2.csv.yaml
    $ oc get pods
    NAME                                       READY     STATUS    RESTARTS   AGE
    memcached-app-operator-66b5777b79-pnsfj    1/1       Running   0          3s
    memcached-for-drupal-5476487c46-qbd66      1/1       Running   0          14m
    memcached-for-wordpress-65b75fd8c9-7b9x7   1/1       Running   0          14m

11.1.5. 其他资源