×

本指南概述了 OpenShift Dedicated 中运算符生命周期管理器 (OLM) 支持的运算符的打包格式。

Bundle 格式

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

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

运算符元数据可以包括:

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

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

  • 所需的和提供的 API。

  • 相关的镜像。

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

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

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

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

清单文件

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

一个 Bundle 包含每个目录一个 CSV,并且通常在其/manifests目录中包含定义 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 中时,运算符生命周期管理器 (OLM) 可以从 Bundle 中创建它们,并与 CSV 一起管理它们的生存周期。

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

  • 升级 CSV 时:

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

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

注解

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

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 包的媒体类型或格式。registry+v1格式表示它包含一个 CSV 文件及其关联的 Kubernetes 对象。
2 镜像中包含 Operator 清单文件的目录的路径。此标签预留供将来使用,目前默认为manifests/。值manifests.v1表示包包含 Operator 清单文件。
3 镜像中包含关于包的元数据文件的目录的路径。此标签预留供将来使用,目前默认为metadata/。值metadata.v1表示此包具有 Operator 元数据。
4 包的包名称。
5 添加到 Operator 注册表时,包订阅的通道列表。
6 从注册表安装时,Operator 应订阅的默认通道。

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

依赖项

Operator 的依赖项列在包的metadata/文件夹中的dependencies.yaml文件中。此文件是可选的,目前仅用于指定显式的 Operator 版本依赖项。

依赖项列表为每个项目包含一个type字段,用于指定这是什么类型的依赖项。支持以下类型的 Operator 依赖项:

olm.package

此类型表示特定 Operator 版本的依赖项。依赖信息必须包含包名和包的语义版本号格式的版本。例如,您可以指定确切的版本,例如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 包格式。此工具允许您根据类似于软件存储库的 Operator 包列表创建和维护 Operator 目录。结果是一个容器镜像,可以存储在容器注册表中,然后安装到集群上。

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

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

亮点

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

编辑

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

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

  • 将现有包提升到新通道

  • 更改包的默认通道

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

组合性

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

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

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

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

可扩展性

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

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

这种可扩展性允许在低级 API 之上为未来的 OpenShift Dedicated 版本开发额外的官方工具,但其主要好处在于目录维护者也具备此能力。

从 OpenShift Dedicated 4.11 开始,默认的 Red Hat 提供的 Operator 目录使用基于文件的目录格式发布。OpenShift Dedicated 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 中简洁地表达。

Operator Lifecycle Manager (OLM) 目录当前使用三种模式(olm.packageolm.channelolm.bundle),它们对应于 OLM 现有的包和捆绑包概念。

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

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

olm.package 模式

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

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 字段时,跳过的 Operator 版本将从更新图中删除,并且使用 Subscription 对象的 spec.startingCSV 属性的用户将不再能够安装它们。

您可以通过同时使用 skipRangereplaces 字段来增量更新 Operator,同时保持以前安装的版本可供用户将来安装。确保 replaces 字段指向相关 Operator 版本的直接前一个版本。

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 模式定义了目录中包、捆绑包和通道的弃用信息。Operator 作者可以使用此模式向从目录运行这些 Operator 的用户提供有关其 Operator 的相关消息,例如支持状态和推荐的升级路径。

定义此模式后,OpenShift Dedicated Web 控制台会在 Operator 的受影响元素(包括任何自定义弃用消息)上显示警告徽章,这些徽章位于 OperatorHub 的安装前和安装后页面上。

olm.deprecations 模式条目包含以下一个或多个 reference 类型,这表示弃用范围。安装 Operator 后,任何指定的邮件都可以作为相关 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 字段是必需的。

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

Operator 作者可以将 olm.deprecations 模式条目保存为 deprecations.yaml 文件,该文件与包的 index.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 确保集群中已安装列出的包,且版本在所需范围内。versionRange 字段必须是有效的语义版本 (semver) 范围。

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

olm.gvk.required

olm.gvk.required 属性定义了此捆绑包所需的 Kubernetes API 的组/版本/种类 (GVK)。对于捆绑包列出的每个必需 GVK 属性,OLM 确保集群中已安装提供它的 Operator。GVK 必须符合 Kubernetes GVK 验证。

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

示例目录

使用基于文件的目录,目录维护者可以专注于 Operator 的管理和兼容性。因为 Operator 作者已经为他们的 Operator 生成了特定于 Operator 的目录,所以目录维护者可以通过将每个 Operator 目录渲染到目录根目录的子目录中来构建他们的目录。

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

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

    示例目录配置文件
    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"

指导原则

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

不可变捆绑包

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

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

但是,在某些情况下,更改目录元数据是更好的选择。

  • 频道升级:如果已发布捆绑包,后来决定将其添加到另一个频道,则可以在另一个olm.channel blob 中添加捆绑包的条目。

  • 新的升级边缘:例如,如果发布了新的1.2.z 捆绑包版本,例如1.2.4,但1.3.0 已经发布,则可以更新1.3.0 的目录元数据以跳过1.2.4

源代码管理

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

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

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

CLI 使用

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

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

自动化

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

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

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

  • 检查更新的捆绑包或目录镜像引用是否存在,目录镜像是否可以在集群中成功运行,以及是否可以成功安装该包中的 Operator。

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

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