×

本指南概述了AWS上的Red Hat OpenShift Service中Operator Lifecycle Manager (OLM)支持的Operator的打包格式。

Bundle格式

Operator的Bundle格式是由Operator Framework引入的一种打包格式。为了提高可扩展性并更好地支持上游用户托管自己的目录,Bundle格式规范简化了Operator元数据的分发。

一个Operator Bundle代表Operator的单个版本。磁盘上的Bundle清单文件被容器化并作为Bundle镜像交付,这是一个不可运行的容器镜像,用于存储Kubernetes清单文件和Operator元数据。然后,使用现有的容器工具(如podmandocker)和容器注册表(如Quay)来管理Bundle镜像的存储和分发。

Operator元数据可以包含:

  • 标识Operator的信息,例如其名称和版本。

  • 驱动UI的附加信息,例如其图标和一些示例自定义资源 (CR)。

  • 必需和提供的API。

  • 相关的镜像。

将清单文件加载到Operator注册表数据库时,将验证以下要求:

  • Bundle必须在注释中至少定义一个通道。

  • 每个Bundle都只有一个集群服务版本 (CSV)。

  • 如果CSV拥有自定义资源定义 (CRD),则该CRD必须存在于Bundle中。

清单文件

Bundle清单文件指的是一组Kubernetes清单文件,这些文件定义了Operator的部署和RBAC模型。

一个Bundle在其/manifests目录中包含每个目录一个CSV,以及通常定义CSV拥有API的CRD。

Bundle格式布局示例
etcd
├── manifests
│   ├── etcdcluster.crd.yaml
│   └── etcdoperator.clusterserviceversion.yaml
│   └── secret.yaml
│   └── configmap.yaml
└── metadata
    └── annotations.yaml
    └── dependencies.yaml

另外支持的对象

以下对象类型也可以可选地包含在Bundle的/manifests目录中:

支持的可选对象类型
  • ClusterRole

  • ClusterRoleBinding

  • ConfigMap

  • ConsoleCLIDownload

  • ConsoleLink

  • ConsoleQuickStart

  • ConsoleYamlSample

  • PodDisruptionBudget

  • PriorityClass

  • PrometheusRule

  • Role

  • RoleBinding

  • Secret

  • Service

  • ServiceAccount

  • ServiceMonitor

  • VerticalPodAutoscaler

当这些可选对象包含在Bundle中时,Operator Lifecycle Manager (OLM)可以从Bundle中创建它们,并与CSV一起管理它们的生存周期。

可选对象的生存周期
  • 删除CSV时,OLM会删除可选对象。

  • 升级CSV时:

    • 如果可选对象的名称相同,OLM会就地更新它。

    • 如果可选对象的名称在版本之间发生了更改,OLM会删除并重新创建它。

注释

Bundle还在其/metadata目录中包含一个annotations.yaml文件。此文件定义更高级别的聚合数据,有助于描述Bundle的格式和包信息,以及如何将Bundle添加到Bundle索引中。

annotations.yaml示例
annotations:
  operators.operatorframework.io.bundle.mediatype.v1: "registry+v1" (1)
  operators.operatorframework.io.bundle.manifests.v1: "manifests/" (2)
  operators.operatorframework.io.bundle.metadata.v1: "metadata/" (3)
  operators.operatorframework.io.bundle.package.v1: "test-operator" (4)
  operators.operatorframework.io.bundle.channels.v1: "beta,stable" (5)
  operators.operatorframework.io.bundle.channel.default.v1: "stable" (6)
1 Operator Bundle的媒体类型或格式。registry+v1格式表示它包含CSV及其关联的Kubernetes对象。
2 镜像中包含Operator清单文件的目录的路径。此标签保留供将来使用,目前默认为manifests/。值manifests.v1意味着Bundle包含Operator清单文件。
3 镜像中包含有关Bundle的元数据文件的目录的路径。此标签保留供将来使用,目前默认为metadata/。值metadata.v1意味着此Bundle具有Operator元数据。
4 Bundle的包名称。
5 添加到Operator注册表时,Bundle订阅的通道列表。
6 从注册表安装时,Operator应该订阅的默认通道。

如果出现不匹配,annotations.yaml文件具有权威性,因为依赖这些注释的集群内Operator注册表只能访问此文件。

依赖项

Operator 的依赖项列在 bundle 的 `metadata/` 文件夹中的 `dependencies.yaml` 文件中。此文件是可选的,目前仅用于指定明确的 Operator 版本依赖关系。

依赖项列表中的每个项目都包含一个 `type` 字段,用于指定依赖项的类型。支持以下类型的 Operator 依赖项:

olm.package

此类型表示对特定 Operator 版本的依赖。依赖信息必须包含包名和包的语义版本 (semver) 格式的版本。例如,您可以指定确切的版本,例如 `0.5.2`,或版本范围,例如 `>0.5.1`。

olm.gvk

使用此类型,作者可以指定具有组/版本/种类 (GVK) 信息的依赖项,类似于 CSV 中现有的基于 CRD 和 API 的用法。这是一种路径,使 Operator 作者能够将所有依赖项(API 或显式版本)整合到同一个位置。

olm.constraint

此类型声明对任意 Operator 属性的通用约束。

在以下示例中,为 Prometheus Operator 和 etcd CRD 指定了依赖项。

dependencies.yaml 文件示例
dependencies:
  - type: olm.package
    value:
      packageName: prometheus
      version: ">0.27.0"
  - type: olm.gvk
    value:
      group: etcd.database.coreos.com
      kind: EtcdCluster
      version: v1beta2

关于 opm CLI

Operator Framework 提供了 `opm` CLI 工具,用于 Operator bundle 格式。此工具允许您从类似于软件存储库的 Operator bundle 列表创建和维护 Operator 目录。结果是一个容器镜像,可以存储在容器注册表中,然后安装在集群上。

目录包含指向 Operator 清单内容的指针数据库,可以通过运行容器镜像时提供的包含的 API 进行查询。在 Red Hat OpenShift Service on AWS 上,Operator 生命周期管理器 (OLM) 可以引用目录源中的镜像(由 `CatalogSource` 对象定义),该对象定期轮询镜像,以便能够频繁更新集群上已安装的 Operator。

  • 有关安装 `opm` CLI 的步骤,请参阅 CLI 工具

重点

基于文件的目录 是 Operator 生命周期管理器 (OLM) 中目录格式的最新迭代。它是早期 SQLite 数据库格式的基于纯文本(JSON 或 YAML)的声明性配置演变,并且完全向后兼容。此格式的目标是启用 Operator 目录编辑、组合和可扩展性。

编辑

使用基于文件的目录,与目录内容交互的用户能够直接更改格式并验证其更改是否有效。由于此格式是纯文本 JSON 或 YAML,因此目录维护者可以轻松地手动或使用广泛已知和支持的 JSON 或 YAML 工具(例如 `jq` CLI)来操作目录元数据。

此可编辑性支持以下功能和用户定义的扩展:

  • 将现有 bundle 推广到新通道

  • 更改包的默认通道

  • 用于添加、更新和删除升级边的自定义算法

组合性

基于文件的目录存储在任意目录层次结构中,这使得目录组合成为可能。例如,考虑两个单独的基于文件的目录:`catalogA` 和 `catalogB`。目录维护者可以通过创建一个新目录 `catalogC` 并将 `catalogA` 和 `catalogB` 复制到其中来创建一个新的组合目录。

这种组合性支持分散式目录。此格式允许 Operator 作者维护特定于 Operator 的目录,并且允许维护者轻松构建由单个 Operator 目录组成的目录。可以通过组合多个其他目录、提取一个目录的子集或这两种方法的组合来组合基于文件的目录。

不允许重复的包和包内的重复 bundle。如果发现任何重复项,`opm validate` 命令将返回错误。

由于 Operator 作者最熟悉他们的 Operator、其依赖项及其升级兼容性,因此他们能够维护他们自己的特定于 Operator 的目录并直接控制其内容。使用基于文件的目录,Operator 作者拥有在目录中构建和维护其包的任务。但是,组合目录维护者只拥有管理其目录中的包并将目录发布给用户这一任务。

可扩展性

基于文件的目录规范是目录的低级表示。虽然可以直接以其低级形式维护它,但目录维护者可以在其之上构建有趣的扩展,这些扩展可以被他们自己的自定义工具用来进行任意数量的更改。

例如,一个工具可以将高级 API(例如 `(mode=semver)`)转换为低级基于文件的目录格式的升级边。或者,目录维护者可能需要通过向满足特定条件的 bundle 添加新属性来自定义所有 bundle 元数据。

虽然这种可扩展性允许在低级 API 之上开发用于未来 Red Hat OpenShift Service on AWS 版本的额外官方工具,但主要好处是目录维护者也具有此功能。

从 Red Hat OpenShift Service on AWS 4.11 开始,默认的 Red Hat 提供的 Operator 目录以基于文件的目录格式发布。Red Hat OpenShift Service on AWS 4.6 到 4.10 的默认 Red Hat 提供的 Operator 目录以已弃用的 SQLite 数据库格式发布。

与 SQLite 数据库格式相关的 `opm` 子命令、标志和功能也已弃用,并将在此版本的未来版本中删除。这些功能仍然受支持,并且必须用于使用已弃用的 SQLite 数据库格式的目录。

许多用于处理 SQLite 数据库格式的 `opm` 子命令和标志(例如 `opm index prune`)不适用于基于文件的目录格式。有关使用基于文件的目录的更多信息,请参阅 管理自定义目录

目录结构

基于文件的目录可以存储和加载自基于目录的文件系统。`opm` CLI 通过遍历根目录并递归进入子目录来加载目录。CLI 会尝试加载它找到的每个文件,如果发生任何错误则会失败。

可以使用 `.indexignore` 文件忽略非目录文件,其模式和优先级规则与 `.gitignore` 文件相同。

`.indexignore` 文件示例
# Ignore everything except non-object .json and .yaml files
**/*
!*.json
!*.yaml
**/objects/*.json
**/objects/*.yaml

目录维护者可以灵活地选择他们想要的布局,但建议将每个包的基于文件的目录 blob 存储在单独的子目录中。每个单独的文件可以是 JSON 或 YAML;目录中的每个文件都不需要使用相同的格式。

基本的推荐结构
catalog
├── packageA
│   └── index.yaml
├── packageB
│   ├── .indexignore
│   ├── index.yaml
│   └── objects
│       └── packageB.v0.1.0.clusterserviceversion.yaml
└── packageC
    └── index.json
    └── deprecations.yaml

此推荐结构具有目录层次结构中的每个子目录都是一个自包含的目录的属性,这使得目录组合、发现和导航成为简单易行的文件系统操作。该目录也可以通过将其复制到父目录的根目录来包含在父目录中。

模式

基于文件的目录使用一种基于CUE 语言规范的格式,可以扩展任意模式。以下_Meta CUE 模式定义了所有基于文件的目录 Blob 必须遵守的格式。

_Meta 模式
_Meta: {
  // schema is required and must be a non-empty string
  schema: string & !=""

  // package is optional, but if it's defined, it must be a non-empty string
  package?: string & !=""

  // properties is optional, but if it's defined, it must be a list of 0 or more properties
  properties?: [... #Property]
}

#Property: {
  // type is required
  type: string & !=""

  // value is required, and it must not be null
  value: !=null
}

本规范中列出的 CUE 模式不应被认为是详尽无遗的。opm validate 命令具有其他难以或不可能在 CUE 中简洁表达的验证。

运营商生命周期管理器 (OLM) 目录当前使用三种模式 (olm.packageolm.channelolm.bundle),它们对应于 OLM 现有的包和捆绑包概念。

目录中的每个运营商包都需要恰好一个 olm.package Blob,至少一个 olm.channel Blob 和一个或多个 olm.bundle Blob。

所有 olm.* 模式都保留用于 OLM 定义的模式。自定义模式必须使用唯一的前缀,例如您拥有的域名。

olm.package 模式

olm.package 模式定义运营商的包级元数据。这包括其名称、描述、默认通道和图标。

olm.package 模式
#Package: {
  schema: "olm.package"

  // Package name
  name: string & !=""

  // A description of the package
  description?: string

  // The package's default channel
  defaultChannel: string & !=""

  // An optional icon
  icon?: {
    base64data: string
    mediatype:  string
  }
}

olm.channel 模式

olm.channel 模式定义包中的通道、作为通道成员的捆绑包条目以及这些捆绑包的升级边缘。

如果捆绑包条目在多个 olm.channel Blob 中表示边缘,则它每个通道只能出现一次。

条目的 replaces 值引用在此目录或其他目录中找不到的另一个捆绑包名称是有效的。但是,所有其他通道不变式必须成立,例如通道不能有多个头部。

olm.channel 模式
#Channel: {
  schema: "olm.channel"
  package: string & !=""
  name: string & !=""
  entries: [...#ChannelEntry]
}

#ChannelEntry: {
  // name is required. It is the name of an `olm.bundle` that
  // is present in the channel.
  name: string & !=""

  // replaces is optional. It is the name of bundle that is replaced
  // by this entry. It does not have to be present in the entry list.
  replaces?: string & !=""

  // skips is optional. It is a list of bundle names that are skipped by
  // this entry. The skipped bundles do not have to be present in the
  // entry list.
  skips?: [...string & !=""]

  // skipRange is optional. It is the semver range of bundle versions
  // that are skipped by this entry.
  skipRange?: string & !=""
}

使用 skipRange 字段时,跳过的运营商版本将从更新图中修剪,并且使用Subscription对象的spec.startingCSV属性的用户不再可以安装它们。

您可以通过同时使用 skipRangereplaces 字段来增量更新运营商,同时让以前安装的版本可供用户将来安装。确保 replaces 字段指向相关运营商版本的紧前版本。

olm.bundle 模式

olm.bundle 模式
#Bundle: {
  schema: "olm.bundle"
  package: string & !=""
  name: string & !=""
  image: string & !=""
  properties: [...#Property]
  relatedImages?: [...#RelatedImage]
}

#Property: {
  // type is required
  type: string & !=""

  // value is required, and it must not be null
  value: !=null
}

#RelatedImage: {
  // image is the image reference
  image: string & !=""

  // name is an optional descriptive name for an image that
  // helps identify its purpose in the context of the bundle
  name?: string & !=""
}

olm.deprecations 模式

可选的 olm.deprecations 模式定义目录中包、捆绑包和通道的弃用信息。运营商作者可以使用此模式向从目录运行这些运营商的用户提供有关其运营商的相关消息,例如支持状态和推荐的升级路径。

定义此模式后,AWS 上的 Red Hat OpenShift 服务 Web 控制台将在运营商的预安装和后安装页面上显示受影响的运营商元素的警告徽章,包括任何自定义弃用消息。

olm.deprecations 模式条目包含以下一个或多个 reference 类型,指示弃用范围。安装运营商后,任何指定的邮件都可以在相关的 Subscription 对象上显示为状态条件。

表 1. 弃用 reference 类型
类型 范围 状态条件

olm.package

表示整个包

PackageDeprecated

olm.channel

表示一个通道

ChannelDeprecated

olm.bundle

表示一个捆绑包版本

BundleDeprecated

每个 reference 类型都有其自身的要求,详情请参见以下示例。

包含每个 reference 类型的示例 olm.deprecations 模式
schema: olm.deprecations
package: my-operator (1)
entries:
  - reference:
      schema: olm.package (2)
    message: | (3)
    The 'my-operator' package is end of life. Please use the
    'my-operator-new' package for support.
  - reference:
      schema: olm.channel
      name: alpha (4)
    message: |
    The 'alpha' channel is no longer supported. Please switch to the
    'stable' channel.
  - reference:
      schema: olm.bundle
      name: my-operator.v1.68.0 (5)
    message: |
    my-operator.v1.68.0 is deprecated. Uninstall my-operator.v1.68.0 and
    install my-operator.v1.72.0 for support.
1 每个弃用模式都必须具有 package 值,并且该包引用在整个目录中必须唯一。不能有相关的 name 字段。
2 olm.package 模式不得包含 name 字段,因为它由模式前面定义的 package 字段确定。
3 任何 reference 类型的 message 字段都必须是非零长度的,并表示为不透明文本 Blob。
4 olm.channel 模式的 name 字段是必需的。
5 olm.bundle 模式的 name 字段是必需的。

弃用功能不考虑重叠的弃用,例如包与通道与捆绑包。

运营商作者可以将 olm.deprecations 模式条目保存为包的 index.yaml 文件所在目录中的 deprecations.yaml 文件。

包含弃用的目录的示例目录结构
my-catalog
└── my-operator
    ├── index.yaml
    └── deprecations.yaml

属性

属性是可以附加到基于文件的目录模式的任意元数据片段。type 字段是一个字符串,有效地指定了 value 字段的语义和语法含义。该值可以是任何任意 JSON 或 YAML。

OLM 定义了一些属性类型,同样使用保留的 olm.* 前缀。

olm.package 属性

olm.package 属性定义包名称和版本。这是捆绑包上的必需属性,并且必须只有一个此类属性。packageName 字段必须与捆绑包的第一类 package 字段匹配,version 字段必须是有效的语义版本。

olm.package 属性
#PropertyPackage: {
  type: "olm.package"
  value: {
    packageName: string & !=""
    version: string & !=""
  }
}

olm.gvk 属性

olm.gvk 属性定义此捆绑包提供的 Kubernetes API 的组/版本/种类 (GVK)。OLM 使用此属性来解析具有此属性的捆绑包,作为将相同 GVK 列为所需 API 的其他捆绑包的依赖项。GVK 必须遵守 Kubernetes GVK 验证。

olm.gvk 属性
#PropertyGVK: {
  type: "olm.gvk"
  value: {
    group: string & !=""
    version: string & !=""
    kind: string & !=""
  }
}

olm.package.required

olm.package.required 属性定义此捆绑包所需的另一个包的包名称和版本范围。对于捆绑包列出的每个所需包属性,OLM 都会确保集群上安装了列出的包和所需版本范围内的运营商。

olm.package.required 属性
#PropertyPackageRequired: {
  type: "olm.package.required"
  value: {
    packageName: string & !=""
    versionRange: string & !=""
  }
}

olm.gvk.required

olm.gvk.required 属性定义此捆绑包所需的 Kubernetes API 的组/版本/种类 (GVK)。对于捆绑包列出的每个所需 GVK 属性,OLM 都会确保集群上安装了提供它的运营商。GVK 必须遵守 Kubernetes GVK 验证。

olm.gvk.required 属性
#PropertyGVKRequired: {
  type: "olm.gvk.required"
  value: {
    group: string & !=""
    version: string & !=""
    kind: string & !=""
  }
}

示例目录

使用基于文件的目录,目录维护者可以专注于运营商管理和兼容性。由于运营商作者已经为其运营商生成了运营商特定的目录,因此目录维护者可以通过将每个运营商目录呈现到目录根目录的子目录来构建其目录。

构建基于文件的目录有很多方法;以下步骤概述了一种简单的方法。

  1. 为目录维护单个配置文件,其中包含目录中每个运营商的镜像引用。

    示例目录配置文件
    name: community-operators
    repo: quay.io/community-operators/catalog
    tag: latest
    references:
    - name: etcd-operator
      image: quay.io/etcd-operator/index@sha256:5891b5b522d5df086d0ff0b110fbd9d21bb4fc7163af34d08286a2e846f6be03
    - name: prometheus-operator
      image: quay.io/prometheus-operator/index@sha256:e258d248fda94c63753607f7c4494ee0fcbe92f1a76bfdac795c9d84101eb317
  2. 运行一个解析配置文件并根据其引用创建新目录的脚本。

    示例脚本
    name=$(yq eval '.name' catalog.yaml)
    mkdir "$name"
    yq eval '.name + "/" + .references[].name' catalog.yaml | xargs mkdir
    for l in $(yq e '.name as $catalog | .references[] | .image + "|" + $catalog + "/" + .name + "/index.yaml"' catalog.yaml); do
      image=$(echo $l | cut -d'|' -f1)
      file=$(echo $l | cut -d'|' -f2)
      opm render "$image" > "$file"
    done
    opm generate dockerfile "$name"
    indexImage=$(yq eval '.repo + ":" + .tag' catalog.yaml)
    docker build -t "$indexImage" -f "$name.Dockerfile" .
    docker push "$indexImage"

指南

维护基于文件的目录时,请考虑以下指南。

不变的捆绑包

运营商生命周期管理器 (OLM) 的一般建议是,捆绑包镜像及其元数据应视为不变的。

如果已将损坏的 Bundle 推送到目录中,则必须假设至少有一名用户已升级到该 Bundle。基于此假设,必须发布另一个 Bundle,其中包含从损坏的 Bundle 到新 Bundle 的升级路径,以确保安装了损坏 Bundle 的用户能够收到升级。如果目录中 Bundle 的内容已更新,OLM 不会重新安装已安装的 Bundle。

但是,在某些情况下,更改目录元数据更可取。

  • 渠道推广:如果您已经发布了一个 Bundle,后来决定将其添加到另一个渠道,则可以在另一个olm.channel Blob 中添加您的 Bundle 条目。

  • 新的升级路径:例如,如果您发布了新的1.2.z Bundle 版本,例如1.2.4,但1.3.0 已经发布,则可以更新1.3.0 的目录元数据以跳过1.2.4

源代码控制

目录元数据应存储在源代码控制中,并视为真相来源。更新目录镜像应包括以下步骤:

  1. 使用新的提交更新源代码控制的目录。

  2. 构建并推送目录镜像。使用一致的标签分类法,例如:latest:<target_cluster_version>,以便用户可以在目录更新可用时接收更新。

CLI 使用

有关使用opm CLI 创建基于文件的目录的说明,请参阅管理自定义目录

有关与管理基于文件的目录相关的opm CLI 命令的参考文档,请参阅CLI 工具

自动化

鼓励操作符作者和目录维护者使用 CI/CD 工作流自动化其目录维护。目录维护者可以通过构建 GitOps 自动化来进一步改进这一点,以完成以下任务:

  • 检查拉取请求 (PR) 作者是否有权进行请求的更改,例如更新其包的镜像引用。

  • 检查目录更新是否通过opm validate 命令。

  • 检查更新的 Bundle 或目录镜像引用是否存在,目录镜像是否在集群中成功运行,以及来自该包的操作符是否可以成功安装。

  • 自动合并通过先前检查的 PR。

  • 自动重新构建和重新发布目录镜像。