12.2. 创建基于 Ansible 的 Operator

本指南概述了 Operator SDK 中的 Ansible 支持,通过一些示例指导 Operator 作者通过使用 Ansible playbook 和模块的 operator-sdk CLI 工具来构建和运行基于 Ansible 的 Operator。

12.2.1. Operator SDK 中的 Ansible 支持

Operator Framework 是一个开源工具包,用于以有效、自动化且可扩展的方式管理 Kubernetes 原生应用程序,即 Operator。该框架中包含 Operator SDK,可协助开发人员利用自己的专业知识来引导和构建 Operator,而无需了解 Kubernetes API 复杂性。

通过 Operator SDK 生成 Operator 项目的其中一种方案是利用现有 Ansible playbook 和模块来部署 Kubernetes 资源作为统一应用程序,而无需编写任何 Go 代码。

12.2.1.1. 自定义资源文件

Operator 会使用 Kubernetes 的扩展机制,即自定义资源定义 (CRD),这样您的自定义资源 (CR) 的外观和行为均类似于内置的原生 Kubernetes 对象。

CR 文件格式是一个 Kubernetes 资源文件。该对象具有必填和选填字段:

表 12.1. 自定义资源字段

字段描述

apiVersion

要创建 CR 的版本。

kind

要创建 CR 的类型。

metadata

要创建的 Kubernetes 特定元数据。

spec(选填)

传输至 Ansible 的变量键值列表。本字段默认为空。

status

总结对象的当前状态。对于基于 Ansible 的 Operator,status 子资源默认为 CRD 启用,由 k8s_status Ansible 模块管理,其中包含 CR statuscondition 信息。

annotations

要附于 CR 的 Kubernetes 特定注解。

以下 CR 注解列表会修改 Operator 的行为:

表 12.2. 基于 Ansible 的 Operator 注解

注解描述

ansible.operator-sdk/reconcile-period

为 CR 指定协调间隔。该值将通过标准 Golang 软件包 time 来解析。具体来说,使用 ParseDuration,默认后缀 s,给出的数值以秒为单位。

基于 Ansible 的 Operator 注解示例

apiVersion: "foo.example.com/v1alpha1"
kind: "Foo"
metadata:
  name: "example"
annotations:
  ansible.operator-sdk/reconcile-period: "30s"

12.2.1.2. Watches 文件

Watches 文件中包含从自定义资源 (CR)(通过 GroupVersionKind 标识)到 Ansible 角色或 playbook 的映射列表。Operator 期望该映射文件位于预定义位置,/opt/ansible/watches.yaml

表 12.3. Watches 文件映射

字段描述

group

要监视的 CR 组。

version

要监视的 CR 版本。

kind

要监视的 CR 类型

role(默认)

添加至容器中的 Ansible 角色的路径。例如:如果您的 roles 目录位于 /opt/ansible/roles/ 中,角色名为 busybox,则该值应为 /opt/ansible/roles/busybox。该字段与 playbook 字段相互排斥。

playbook

添加至容器中的 Ansible playbook 的路径。该 playbook 需要以简单方式调用角色。该字段与 role 字段相互排斥。

reconcilePeriod(选填)

给定 CR 的协调间隔,角色或 playbook 运行的频率。

manageStatus(选填)

如果设置为 true(默认),则 CR 的状态通常由 Operator 来管理。如果设置为 false,则 CR 的状态则会由指定角色或 playbook 在别处管理,或在单独控制器中管理。

Watches 文件示例

- version: v1alpha1 1
  group: foo.example.com
  kind: Foo
  role: /opt/ansible/roles/Foo

- version: v1alpha1 2
  group: bar.example.com
  kind: Bar
  playbook: /opt/ansible/playbook.yml

- version: v1alpha1 3
  group: baz.example.com
  kind: Baz
  playbook: /opt/ansible/baz.yml
  reconcilePeriod: 0
  manageStatus: false

1
Foo 映射至 Foo 角色的简单示例。
2
Bar 映射至 playbook 的简单示例。
3
针对 Baz 类型的更复杂示例。在 playbook 中禁止对 CR 状态重新排队和管理。
12.2.1.2.1. 高级选项

高级功能可通过添加至每个 GVK(group、version 和 kind)的 Watches 文件中进行启用。它们可放在 groupversionkindplaybookrole 字段下方。

可使用自定义资源 (CR) 上的注解覆盖每个资源的某些功能。可覆盖的选项会指定以下注解。

表 12.4. 高级 Watches 文件选项

功能YAML 密钥描述覆盖注解默认值

协调周期

reconcilePeriod

特定 CR 的协调运行间隔时间。

ansbile.operator-sdk/reconcile-period

1m

管理状态

manageStatus

支持 Operator 管理每个 CR status 部分中的 conditions 部分。

 

true

监视依赖资源

watchDependentResources

支持 Operator 动态监视由 Ansible 创建的资源。

 

true

监控集群范围内的资源

watchClusterScopedResources

支持 Operator 监视由 Ansible 创建的集群范围的资源。

 

false

最大运行程序工件

maxRunnerArtifacts

管理 Ansible Runner 在 Operator 容器中为每个单独资源保存的构件目录的数量。

ansible.operator-sdk/max-runner-artifacts

20

带有高级选项的 Watches 文件示例

- version: v1alpha1
  group: app.example.com
  kind: AppService
  playbook: /opt/ansible/playbook.yml
  maxRunnerArtifacts: 30
  reconcilePeriod: 5s
  manageStatus: False
  watchDependentResources: False

12.2.1.3. 发送至 Ansible 的额外变量

额外变量可发送至 Ansible,然后由 Operator 管理。自定义资源 (CR) 的 spec 部分作为额外变量按照键值对传递。等同于传递给 ansible-playbook 命令的额外变量。

Operator 还会在 meta 字段下传递额外变量,用于 CR 的名称和 CR 的命名空间。

对于以下 CR 示例:

apiVersion: "app.example.com/v1alpha1"
kind: "Database"
metadata:
  name: "example"
spec:
  message:"Hello world 2"
  newParameter: "newParam"

作为额外变量传递至 Ansible 的结构为:

{ "meta": {
        "name": "<cr_name>",
        "namespace": "<cr_namespace>",
  },
  "message": "Hello world 2",
  "new_parameter": "newParam",
  "_app_example_com_database": {
     <full_crd>
   },
}

messagenewParameter 字段在顶层被设置为额外变量,meta 则为 Operator 中定义的 CR 提供相关元数据。meta 字段可使用 Ansible 中的点符号来访问,如:

- debug:
    msg: "name: {{ meta.name }}, {{ meta.namespace }}"

12.2.1.4. Ansible Runner 目录

Ansible Runner 会将与 Ansible 运行相关的信息保存至容器中。具体位于:/tmp/ansible-operator/runner/<group>/<version>/<kind>/<namespace>/<name>

其他资源

12.2.2. 安装 Operator SDK CLI

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

注意

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

12.2.2.1. 从 GitHub 版本安装

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

先决条件

  • Go v1.13+
  • docker v17.03+、podman v1.2.0+ 或 buildah v1.7+
  • 已安装 OpenShift CLI (oc) v4.3+
  • 访问基于 Kubernetes v1.12.0+ 的集群
  • 访问容器 registry

流程

  1. 设置发行版本变量:

    RELEASE_VERSION=v0.12.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

12.2.2.2. 通过 Homebrew 安装

可使用 Homebrew 来安装 SDK CLI。

先决条件

  • Homebrew
  • docker v17.03+、podman v1.2.0+ 或 buildah v1.7+
  • 已安装 OpenShift CLI (oc) v4.3+
  • 访问基于 Kubernetes v1.12.0+ 的集群
  • 访问容器 registry

流程

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

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

    $ operator-sdk version

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

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

先决条件

  • Git
  • Go v1.13+
  • docker v17.03+、podman v1.2.0+ 或 buildah v1.7+
  • 已安装 OpenShift CLI (oc) v4.3+
  • 访问基于 Kubernetes v1.12.0+ 的集群
  • 访问容器 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

12.2.3. 使用 Operator SDK 来构建基于 Ansible 的 Operator

本流程介绍了使用 Operator SDK 中的工具和库来构建由 Ansible playbook 和模块提供技术支持的简单 Memcached Operator 示例。

先决条件

  • 开发工作站已安装 Operator SDK CLI
  • 使用具有 cluster-admin 权限的账户访问基于 Kubernetes 的集群 v1.11.3+(如 OpenShift Container Platform 4.3)
  • 已安装 OpenShift CLI (oc) v4.1+
  • ansible v2.6.0+
  • ansible-runner v1.1.0+
  • ansible-runner-http v1.0.0+

流程

  1. 创建新 Operator 项目。命名空间范围的 Operator 会监视和管理单一命名空间中的资源。命名空间范围的 Operator 因具有高灵活性而常被视为首选。这类 Operator 支持解耦升级、针对故障和监控隔离命名空间以及区别化 API 定义。

    要创建基于 Ansible 的、全命名空间范围的新 memcached-operator 项目并改为其目录,请使用以下命令:

    $ operator-sdk new memcached-operator \
        --api-version=cache.example.com/v1alpha1 \
        --kind=Memcached \
        --type=ansible
    $ cd memcached-operator

    这样会创建一个 memcached-operator 项目,专门用于监视 APIVersion 为 example.com/v1apha1、Kind 为 Memcached 的 Memcached 资源。

  2. 自定义 Operator 逻辑。

    在本例中,memcached-operator 会针对每个 Memcached 自定义资源 (CR) 执行以下协调逻辑:

    • 如果尚无 memcached Deployment,请创建一个。
    • 确保 Deployment 大小与 Memcached CR 指定的大小相同。

    默认情况下,memcached-operator 会监视 Memcached 资源事件,如 watches.yaml 文件中所示,并执行 Ansible 角色 Memcached

    - version: v1alpha1
      group: cache.example.com
      kind: Memcached

    您可选择在 watches.yaml 文件中自定义以下逻辑:

    1. 指定 role 选项会将 Operator 配置为在通过 Ansible 角色启动 ansible-runner 时使用该指定路径。默认情况下,新命令会填写一个绝对路径,指向您的角色应前往的位置:

      - version: v1alpha1
        group: cache.example.com
        kind: Memcached
        role: /opt/ansible/roles/memcached
    2. 指定 watches.yaml 文件中的 playbook 选项会将 Operator 配置为在通过 Ansible playbook 启动 ansible-runner 时使用该指定路径。

      - version: v1alpha1
        group: cache.example.com
        kind: Memcached
        playbook: /opt/ansible/playbook.yaml
  3. 构建 Memcached Ansible 角色。

    修改 roles/memcached/ 目录下生成的 Ansible 角色。该 Ansible 角色会控制修改资源时所执行的逻辑。

    1. 定义 Memcached spec。

      可完全在 Ansible 中定义基于 Ansible 的 Operator spec。Ansible Operator 会将 CR spec 字段中列出的所有键值对作为变量传递给 Ansible。在运行 Ansible 之前,Operator 会将 spec 字段中所有变量的名称转换为 snake 格式(小写并附带下划线)。例如,spec 中的 serviceAccount 在 Ansible 中会变成 service_account

      提示

      您应在 Ansible 中对变量执行一些类型验证,以确保应用程序收到所需输入。

      如果用户未设置 spec 字段,则请修改 roles/memcached/defaults/main.yml 文件设置默认值:

      size: 1
    2. 定义 Memcached Deployment。

      定义了 Memcached spec 后,即可定义资源发生更改时实际应执行的 Ansible。因为这是一个 Ansible 角色,所以默认行为是执行 roles/memcached/tasks/main.yml 文件中的任务。

      目的是在不存在 Deployment 的情况下让 Ansible 创建 Deployment,该 Deployment 将运行 memcached:1.4.36-alpine 镜像。Ansible 2.7+ 支持 k8s Ansible 模块,本示例利用该模块来控制 Deployment 定义。

      修改 roles/memcached/tasks/main.yml 以匹配以下内容:

      - name: start memcached
        k8s:
          definition:
            kind: Deployment
            apiVersion: apps/v1
            metadata:
              name: '{{ meta.name }}-memcached'
              namespace: '{{ meta.namespace }}'
            spec:
              replicas: "{{size}}"
              selector:
                matchLabels:
                  app: memcached
              template:
                metadata:
                  labels:
                    app: memcached
                spec:
                  containers:
                  - name: memcached
                    command:
                    - memcached
                    - -m=64
                    - -o
                    - modern
                    - -v
                    image: "docker.io/memcached:1.4.36-alpine"
                    ports:
                      - containerPort: 11211
      注意

      本示例使用 size 变量来控制 Memcached Deployment 的副本数。本示例将默认值设定为 1,但任何用户都可以创建一个 CR 覆盖该默认值。

  4. 部署 CRD。

    在运行 Operator 之前,Kubernetes 需要了解 Operator 将会监视的新自定义资源定义 (CRD)。部署 Memcached CRD:

    $ oc create -f deploy/crds/cache.example.com_memcacheds_crd.yaml
  5. 构建并运行 Operator。

    有两种方式来构建和运行 Operator:

    • 作为 Kubernetes 集群内的一个 Pod。
    • 作为集群外的 Go 程序,使用 operator-sdk up 命令。

    选择以下任一方法:

    1. 作为 Kubernetes 集群内的一个 Pod 来运行。这是生产环境的首先方法。

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

        $ operator-sdk build quay.io/example/memcached-operator:v0.0.1
        $ podman push quay.io/example/memcached-operator:v0.0.1
      2. deploy/operator.yaml 文件中会生成 Deployment 清单。本文件中的部署镜像需要从占位符 REPLACE_IMAGE 修改为之前构建的镜像。为此,请运行:

        $ sed -i 's|REPLACE_IMAGE|quay.io/example/memcached-operator:v0.0.1|g' deploy/operator.yaml
      3. 部署 memcached-operator

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

        $ oc get deployment
        NAME                     DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
        memcached-operator       1         1         1            1           1m
    2. 在集群外运行。这是开发阶段的首选方法,可加快部署和测试的速度。

      请确保已安装 Ansible Runner 和 Ansible Runner HTTP Plug-in,否则在创建 CR 时,系统会显示 Ansible Runner 出现意外错误。

      此外,还务必要确保您的计算机上存在 watches.yaml 文件中引用的角色路径。因为通常会在磁盘上角色所处的位置使用容器,所以必须手动将角色复制到已配置的 Ansible 角色路径中(如 /etc/ansible/roles)。

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

        $ operator-sdk up local

        要使用所提供的 Kubernetes 配置文件在本地运行 Operator:

        $ operator-sdk up local --kubeconfig=config
  6. 创建一个 Memcached CR。

    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,确认已创建三个副本:

      $ 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
  7. 更新大小。

    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
      
      $ oc apply -f deploy/crds/cache_v1alpha1_memcached_cr.yaml
    2. 确认 Operator 已更改 Deployment 大小:

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

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

12.2.4. 使用 k8s Ansible 模块来管理应用程序生命周期

要使用 Ansible 管理 Kubernetes 上的应用程序生命周期,您可使用 k8s Ansible 模块。该 Ansible 模块支持开发人员利用其现有 Kubernetes 资源文件(用 YAML 编写),或用原生 Ansible 来表达生命周期管理。

将 Ansible 与现有 Kubernetes 资源文件相结合的一个最大好处在于可使用 Jinja 模板,这样您只需借助 Ansible 中的几个变量即可轻松自定义资源。

本部分将详细介绍 k8s Ansible 模块的用途。开始之前,请先在本地工作站安装该模块,然后使用 playbook 进行测试,最后移至 Operator 中继续使用。

12.2.4.1. 安装 k8s Ansible 模块

要在本地工作站中安装 k8s Ansible 模块:

流程

  1. 安装 Ansible 2.6+:

    $ sudo yum install ansible
  2. 使用 pip 来安装 OpenShift python 客户端软件包:

    $ pip install openshift

12.2.4.2. 在本地测试 k8s Ansible 模块

有时,开发人员最好在其本地机器上运行 Ansible 代码,而不必每次都运行和重构 Operator。

流程

  1. 初始化基于 Ansible 的新 Operator 项目:

    $ operator-sdk new --type ansible --kind Foo --api-version foo.example.com/v1alpha1 foo-operator
    Create foo-operator/tmp/init/galaxy-init.sh
    Create foo-operator/tmp/build/Dockerfile
    Create foo-operator/tmp/build/test-framework/Dockerfile
    Create foo-operator/tmp/build/go-test.sh
    Rendering Ansible Galaxy role [foo-operator/roles/Foo]...
    Cleaning up foo-operator/tmp/init
    Create foo-operator/watches.yaml
    Create foo-operator/deploy/rbac.yaml
    Create foo-operator/deploy/crd.yaml
    Create foo-operator/deploy/cr.yaml
    Create foo-operator/deploy/operator.yaml
    Run git init ...
    Initialized empty Git repository in /home/dymurray/go/src/github.com/dymurray/opsdk/foo-operator/.git/
    Run git init done
    $ cd foo-operator
  2. 使用所需 Ansible 逻辑来修改 roles/foo/tasks/main.yml 文件。本示例通过变量切换来创建和删除命名空间。

    - name: set test namespace to {{ state }}
      k8s:
        api_version: v1
        kind: Namespace
        state: "{{ state }}"
        name: test
      ignore_errors: true 1
    1
    设置 ignore_errors: true 可确保删除不存在的项目不会失败。
  3. 修改 roles/foo/defaults/main.yml 文件,将默认 state 设置为 present

    state: present
  4. 在顶层目录中创建一个 Ansible playbook playbook.yml 文件,其中包含 Foo 角色:

    - hosts: localhost
      roles:
        - Foo
  5. 运行 playbook:

    $ ansible-playbook playbook.yml
     [WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match 'all'
    
    PLAY [localhost] ***************************************************************************
    
    PROCEDURE [Gathering Facts] *********************************************************************
    ok: [localhost]
    
    Task [Foo : set test namespace to present]
    changed: [localhost]
    
    PLAY RECAP *********************************************************************************
    localhost                  : ok=2    changed=1    unreachable=0    failed=0
  6. 检查是否已创建命名空间:

    $ oc get namespace
    NAME          STATUS    AGE
    default       Active    28d
    kube-public   Active    28d
    kube-system   Active    28d
    test          Active    3s
  7. 重新运行 playbook,设置 stateabsent

    $ ansible-playbook playbook.yml --extra-vars state=absent
     [WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match 'all'
    
    PLAY [localhost] ***************************************************************************
    
    PROCEDURE [Gathering Facts] *********************************************************************
    ok: [localhost]
    
    Task [Foo : set test namespace to absent]
    changed: [localhost]
    
    PLAY RECAP *********************************************************************************
    localhost                  : ok=2    changed=1    unreachable=0    failed=0
  8. 检查是否已删除命名空间:

    $ oc get namespace
    NAME          STATUS    AGE
    default       Active    28d
    kube-public   Active    28d
    kube-system   Active    28d

12.2.4.3. 在 Operator 内测试 k8s Ansible 模块

熟悉在本地使用 k8s Ansible 模块后,您可在 Operator 内触发自定义资源 (CR) 发生变化时的相同 Ansible 逻辑。本示例将 Ansible 角色映射到 Operator 所监视的特定 Kubernetes 资源。该映射在 Watches 文件中完成。

12.2.4.3.1. 本地测试基于 Ansible 的 Operator

熟悉在本地测试 Ansible 工作流后,您可以在本地运行的基于 Ansible 的 Operator 内测试逻辑。

为此,请使用 Operator 项目顶层目录中的 operator-sdk up local 命令。该命令读取 ./watches.yaml 文件,并使用 ~/.kube/config 文件与 Kubernetes 集群通信,就像 k8s Ansible 一样。

流程

  1. 因为 up local 命令是从 ./watches.yaml 文件中读取的,所以有一些选项可供 Operator 作者使用。如果 role 独立存在(默认为 /opt/ansible/roles/<name>),则必须直接从 Operator 中复制该角色至 /opt/ansible/roles/ 目录。

    这很麻烦,因为更改不会反映在当前目录中。相反,可更改 role 字段以指向当前目录并注释掉现有行:

    - version: v1alpha1
      group: foo.example.com
      kind: Foo
      #  role: /opt/ansible/roles/Foo
      role: /home/user/foo-operator/Foo
  2. 为自定义资源 (CR) Foo 创建自定义资源定义 (CRD) 和适当的基于角色的访问控制 (RBAC) 定义。operator-sdk 命令会在 deploy/ 目录中自动生成这些文件:

    $ oc create -f deploy/crds/foo_v1alpha1_foo_crd.yaml
    $ oc create -f deploy/service_account.yaml
    $ oc create -f deploy/role.yaml
    $ oc create -f deploy/role_binding.yaml
  3. 运行 up local 命令:

    $ operator-sdk up local
    [...]
    INFO[0000] Starting to serve on 127.0.0.1:8888
    INFO[0000] Watching foo.example.com/v1alpha1, Foo, default
  4. 现在 Operator 正在监视资源 Foo 中的事件,创建 CR 会触发您的 Ansible 角色运行。查看 deploy/cr.yaml 文件:

    apiVersion: "foo.example.com/v1alpha1"
    kind: "Foo"
    metadata:
      name: "example"

    因为未设置 spec 字段,所以调用 Ansible 时无额外变量。下一部分将介绍额外变量如何从 CR 传递至 Ansible。这也是务必要为 Operator 设置合理默认值的原因。

  5. 创建 Foo 的 CR 实例,并将默认变量 state 设置为 present

    $ oc create -f deploy/cr.yaml
  6. 检查是否已创建命名空间 test

    $ oc get namespace
    NAME          STATUS    AGE
    default       Active    28d
    kube-public   Active    28d
    kube-system   Active    28d
    test          Active    3s
  7. 修改 deploy/cr.yaml 文件,将 state 字段设置为 absent

    apiVersion: "foo.example.com/v1alpha1"
    kind: "Foo"
    metadata:
      name: "example"
    spec:
      state: "absent"
  8. 应用这些更改,并确认已删除命名空间:

    $ oc apply -f deploy/cr.yaml
    
    $ oc get namespace
    NAME          STATUS    AGE
    default       Active    28d
    kube-public   Active    28d
    kube-system   Active    28d
12.2.4.3.2. 在集群上测试基于 Ansible 的 Operator

熟悉了在基于 Ansible 的 Operator 内部本地运行 Ansible 逻辑之后,您可在 Kubernetes 集群(如 OpenShift Container Platform)上的 Pod 内部测试 Operator。作为 Pod 在集群中运行是生产环境的首选方法。

流程

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

    $ operator-sdk build quay.io/example/foo-operator:v0.0.1
    $ podman push quay.io/example/foo-operator:v0.0.1
  2. deploy/operator.yaml 文件中会生成 Deployment 清单。本文件中的 Deployment 镜像必须从占位符 REPLACE_IMAGE 修改为之前构建的镜像。为此,请运行以下命令:

    $ sed -i 's|REPLACE_IMAGE|quay.io/example/foo-operator:v0.0.1|g' deploy/operator.yaml

    如果要在 OSX 中执行这些步骤,使用以下命令:

    $ sed -i "" 's|REPLACE_IMAGE|quay.io/example/foo-operator:v0.0.1|g' deploy/operator.yaml
  3. 部署 foo-operator

    $ oc create -f deploy/crds/foo_v1alpha1_foo_crd.yaml # if CRD doesn't exist already
    $ oc create -f deploy/service_account.yaml
    $ oc create -f deploy/role.yaml
    $ oc create -f deploy/role_binding.yaml
    $ oc create -f deploy/operator.yaml
  4. 验证 foo-operator 是否正在运行:

    $ oc get deployment
    NAME                     DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
    foo-operator       1         1         1            1           1m

12.2.5. 使用 k8s_status Ansible 模块来管理自定义资源状态

基于 Ansible 的 Operator 会自动将上一次 Ansible 运行的一般信息更新到自定义资源 (CR) status 子资源中。其中包括成功和失败任务的数量以及相关的错误消息,如下所示:

status:
  conditions:
    - ansibleResult:
      changed: 3
      completion: 2018-12-03T13:45:57.13329
      failures: 1
      ok: 6
      skipped: 0
    lastTransitionTime: 2018-12-03T13:45:57Z
    message: 'Status code was -1 and not [200]: Request failed: <urlopen error [Errno
      113] No route to host>'
    reason: Failed
    status: "True"
    type: Failure
  - lastTransitionTime: 2018-12-03T13:46:13Z
    message: Running reconciliation
    reason: Running
    status: "True"
    type: Running

基于 Ansible 的 Operator 还支持 Operator 作者通过 k8s_status Ansible 模块提供自定义状态值。作者可以根据需要使用任意键值对从 Ansible 内部更新 status

基于 Ansible 的 Operator 默认始终包含如上所示的通用 Ansible 运行输出。如果希望您的应用程序使用 Ansible 输出来更新状态,您可以通过应用程序来手动跟踪状态。

流程

  1. 要通过应用程序手动跟踪 CR 状态,请将 manageStatus 字段设置为 false 来更新 Watches 文件:

    - version: v1
      group: api.example.com
      kind: Foo
      role: /opt/ansible/roles/Foo
      manageStatus: false
  2. 然后,使用 k8s_status Ansible 模块来更新子资源。例如:要使用键 foo 和值 bar 来更新,可使用 k8s_status,如下所示:

    - k8s_status:
      api_version: app.example.com/v1
      kind: Foo
      name: "{{ meta.name }}"
      namespace: "{{ meta.namespace }}"
      status:
        foo: bar

其他资源

12.2.5.1. 本地测试时使用 k8s_status Ansible 模块

如果 Operator 使用 k8s_status Ansible 模块,而您希望使用 operator-sdk up local 命令在本地测试 Operator,则必须将模块安装至 Ansible 期望的位置。该操作可通过 Ansible 的 library 配置选项完成。

在本示例中,假设用户将第三方 Ansible 模块放至 /usr/share/ansible/library/ 目录中。

流程

  1. 要安装 k8s_status 模块,请将 ansible.cfg 文件设置为在 /usr/share/ansible/library/ 目录中搜索已安装的 Ansible 模块:

    $ echo "library=/usr/share/ansible/library/" >> /etc/ansible/ansible.cfg
  2. k8s_status.py 文件添加到 /usr/share/ansible/library/ 目录中:

    $ wget https://raw.githubusercontent.com/openshift/ocp-release-operator-sdk/master/library/k8s_status.py -O /usr/share/ansible/library/k8s_status.py

12.2.6. 其他资源