简介: OLM(Operator Lifecycle Manager) 作为 Operator Framework 的一部分,可以帮助用户进行 Operator 的自动安装,升级及其生命周期的管理。同时 OLM 自身也是以 Operator 的形式进行安装部署。本文我们将来了解一下 OLM 的基本架构和安装使用。
作者 | 匡大虎、阚俊宝
导读:OLM(Operator Lifecycle Manager) 作为 Operator Framework 的一部分,可以帮助用户进行 Operator 的自动安装,升级及其生命周期的管理。同时 OLM 自身也是以 Operator 的形式进行安装部署,可以说它的工作方式是以 Operators 来管理 Operators,而它面向 Operator 提供了声明式 (declarative) 的自动化管理能力也完全符合 Kubernetes 交互的设计理念。本文我们将来了解一下 OLM 的基本架构和安装使用。
OLM 的出现是为了帮助没有如大数据,云监控等领域知识的用户能够自助式地部署并管理像 etcd、大数据分析或监控服务等复杂的分布式应用。因此从它的设计目标来说,OLM 官方希望实现面向云原生应用提供以下几个方向上的通用管理能力,包括:
上述在设计上的目标可以归结为下面几个方向上的需求:
基于上述设计目标,OLM 在实现中面向 Operator 定义了如下模型和组件。
首先,OLM 自身包含两个 Operator:OLM Operator 和 Catalog Operator。它们分别管理了如下几个 OLM 架构中扩展出的基础 CRD 模型:
在 Operator 安装管理的生命周期中 Deployment,Serviceaccount,RBAC 相关的角色和角色绑定是通过 OLM operator 创建的;Catalog Operator 负责 CRDs 和 CSVs 等资源的创建。
在介绍 OLM 的两个 Operator 之前,我们先来看下 ClusterServiceVersion 的定义,作为 OLM 工作流程中的基本元素,它定义了在 OLM 管理下用户业务应用的元数据和运行时刻信息的集合,包括了:
在对 ClusterServiceVersion 的概念有了基本了解后,我们来看下 OLM Operator。
首先 OLM Operator 的工作会基于 ClusterServiceVersion,一旦 CSV 中声明的依赖资源都已经在目标集群中注册成功,OLM Operator 就会负责去安装这些资源对应的应用实例。注意这里 OLM Operator 并不会去关注 CSV 中声明的依赖资源对应的 CRD 模型的创建注册等工作,这些动作可以由用户的手工 kubectl 操作或是由 Catalog Opetator 来完成。这样的设计也给了用户一个逐步适应 OLM 架构并最终应用起来的熟悉过程。另外,OLM Operator 对依赖资源对应自定义模型的监听可以是全局 all namespaces 的,也可以只限定在指定的 namespace 下。
接着我们来认识一下 Catalog Operator,它主要负责解析 CSV 中声明的依赖资源定义,同时它通过监听 catalog 中安装包对应 channels 的版本定义完成 CSV 对应的版本更新。
用户可以通过创建 Subscription 模型来设置 channel 中所需安装包和更新的拉取源,当一个可用更新被发现时,一个用户对应的 InstallPlan 模型会在相应的 namespace 被创建出来。当然用户也可以手动创建 InstallPlan,InstallPlan 实例中会包含目标 CSV 的定义和相关的 approval 审批策略,Catalog Operator 会创建相应的执行计划去创建 CSV 所需的依赖资源模型。一旦用户完成审批,Catalog Operator 就会创建 InstallPlan 中的相关资源,此时刚才提及的 OLM Operator 关注的依赖资源条件得到满足,CSV 中定义的 Operator 实例会由 OLM Operator 完成创建。
在上一小节中我们了解了 OLM 的基本组件模型和相关定义,本小节我们就来介绍一下它的基本架构,如下图所示:
首先在 Operator Framework 中提供了两个重要的元 Operator 和相应的扩展资源(如上节中介绍的 ClusterServiceVersion,InstallPlan 等),用于进行用户应用 Operator 的生命周期管理。在自定义的 CSV 模型中定义了用户部署 Operator 的各类资源组合,包括 Operator 是如何部署的,Operator 对应管理的自定义资源类型是什么以及使用了哪些 K8s 原生资源等。
在上节的定义中我们也了解到 OLM Operator 在安装对应的 Operator 实例前要求其管理的自定义资源模型已经被注册在目标安装集群中,而这个动作可以来自于集群管理员手动 kubectl 方式的创建,也可以利用 Catalog Operator 完成,Catalog Operator 除了可以完成目标CRD模型的注册,还负责资源模型版本的自动升级工作。其工作流程包括:
监听用户创建的未解析 InstallPlans:
一旦 OLM Operator 监听到 CSV 模板中安装所需依赖资源已经注册或是变更,就会启动应用 Operator 的安装和升级工作,并最终启动 Operator 自身的工作流程,在 Kubernetes 集群中创建和管理对应的自定义资源实例模型。
在了解了 OLM 的基础架构后,我们首先来看下 OLM 的安装。在社区代码中我们找到 OLM 各项部署资源对应的模板,用户可以方便的通过修改相应部署参数完成定制化的 OLM 安装。
在官方的发布公告中我们可以找到最新的发布版本和各版本对应的安装说明。
这里以 0.13.0 版本为例,通过以下命令执行自动化安装脚本:
curl -L https://github.com/operator-framework/operator-lifecycle-manager/releases/download/0.13.0/install.sh -o install.sh
chmod +x install.sh
./install.sh 0.13.0
手动安装 OLM 所需部署模板命令:
kubectl apply -f https://github.com/operator-framework/operator-lifecycle-manager/releases/download/0.13.0/crds.yaml
kubectl apply -f https://github.com/operator-framework/operator-lifecycle-manager/releases/download/0.13.0/olm.yaml
在通过 clone OLM 代码仓库到本地后,用户可以执行 make run-local
命令启动 minikube,并通过 minikube 自带 docker daemon 在本地 build OLM 镜像,同时该命令会基于仓库 deploy 目录下的 local-values.yaml
作为配置文件构建运行本地 OLM,通过 kubectl -n local get deployments
可以验证 OLM 各组件是否已经成功安装运行。
另外针对用户的定制化安装需求,OLM 支持通过配置如下模板指定参数来生成定制化的部署模板并安装。下面是其支持配置的模板参数:
# sets the apiversion to use for rbac-resources. Change to `authorization.openshift.io` for openshift
rbacApiVersion: rbac.authorization.k8s.io
# namespace is the namespace the operators will _run_
namespace: olm
# watchedNamespaces is a comma-separated list of namespaces the operators will _watch_ for OLM resources.
# Omit to enable OLM in all namespaces
watchedNamespaces: olm
# catalog_namespace is the namespace where the catalog operator will look for global catalogs.
# entries in global catalogs can be resolved in any watched namespace
catalog_namespace: olm
# operator_namespace is the namespace where the operator runs
operator_namespace: operators
# OLM operator run configuration
olm:
# OLM operator doesn't do any leader election (yet), set to 1
replicaCount: 1
# The image to run. If not building a local image, use sha256 image references
image:
ref: quay.io/operator-framework/olm:local
pullPolicy: IfNotPresent
service:
# port for readiness/liveness probes
internalPort: 8080
# catalog operator run configuration
catalog:
# Catalog operator doesn't do any leader election (yet), set to 1
replicaCount: 1
# The image to run. If not building a local image, use sha256 image references
image:
ref: quay.io/operator-framework/olm:local
pullPolicy: IfNotPresent
service:
# port for readiness/liveness probes
internalPort: 8080
用户可以通过以下方式进行模板的定制化开发和在指定集群中的安装:
my-values.yaml
的配置模板,用户可以参考上述模板配置所需参数;my-values.yaml
模板,使用 package_release.sh
生成指定部署模板;# 第一个参数为系统兼容的helm chart目标版本
# 第二个参数为模板指定的输出目录
# 第三个参数为指定的配置文件路径
./scripts/package_release.sh 1.0.0-myolm ./my-olm-deployment my-values.yaml
kubectl apply -f ./my-olm-deployment/templates/
;最后,用户可以通过环境变量 GLOBAL_CATALOG_NAMESPACE
定义 catalog operator 监听全局 catalogs 的指定 namespace,默认情况下安装过程会创建 olm 命名空间并部署 catalog operator。
如同 apt/dkpg 和 yum/rpm 对于系统组件包的管理一样,OLM 在管理 Operator 版本时也会遇到依赖解析和正在运行的 operator 实例的升级管理等问题。为了保证所有 operators 在运行时刻的可用性,OLM 在依赖解析和升级管理流程中需要保证:
下面我们通过一些示例来了解下当前 OLM 是如何处理版本迭代下的依赖解析:
首先介绍一下 CRD 的升级,当一个待升级的 CRD 只属于单个 CSV 时,OLM 会立即对 CRD 进行升级;当 CRD 属于多个 CSV 时,CRD 的升级需要满足下列条件:
当你需要添加一个新版本的 CRD 时,官方推荐的步骤是:
v1alpha1
,此时你希望添加一个新版本 v1beta1
并且将其置为新的 storage 版本,如下:versions:
- name: v1alpha1
served: true
storage: false
- name: v1beta1
served: true
storage: true
owned
字段所引用的 CRD 版本是新的,如下所示:customresourcedefinitions:
owned:
- name: cluster.example.com
version: v1beta1
kind: cluster
displayName: Cluster
当我们需要弃用或是删除一个 CRD 版本时,OLM 不允许立即删除一个正在使用中的 CRD 版本,而是需要首先通过将 CRD 中的 serverd
字段置为 false
来弃用该版本,然后这个不被使用的版本才会在接下来的 CRD 升级过程中被删除。官方推荐的删除或弃用一个 CRD 指定版本的步骤如下:
serverd
字段标记为 false, 表示不再使用该版本同时会在下次升级时删除此版本,如:versions:
- name: v1alpha1
served: false
storage: true
storage
字段为 true,需要将其置为 false 同时将新版本的 storage
对应字段置为 true,比如:versions:
- name: v1alpha1
served: false
storage: false
- name: v1beta1
served: true
storage: true
versions:
- name: v1beta1
served: true
storage: true
注意在删除指定版本的 CRD 过程中,我们需要保证该版本同时在 CRD status 中的 storedVersion
字段队列中被删除。当 OLM 发现某 storedversion 在新版本 CRD 中不会再使用时会帮助我们完成相应的删除动作。另外我们需要保证 CSV 中关联引用的 CRD 版本在老版本被删除时及时更新。
下面我们来看一下两个会引发升级失败的示例以及 OLM 的依赖解析逻辑:
示例 1:假如我们有 A 和 B 两个不同类型的 CRD。
这样的升级得到的结果是 B 对应的 CRD 版本没有了对应使用它的 Operator 或 APIService,同时依赖它的 A 也将无法工作。
示例 2:假如我们有 A 和 B 两个自定义 API。
此时如果我们只尝试升级 A 而没有同步地升级 B,即使系统可以找到适用的升级版本,也无法完成对应 Operator 的版本升级。
为了避免上述版本迭代中可能遇到的问题,OLM 所采用的依赖解析逻辑如下。
假设我们有运行在某一个 namespace 下的一组 operator:
在了解了 OLM 的依赖解析和升级管理的基本原理后,我们来看下 OLM 升级相关的工作流程。首先从上文中我们已经有所了解,ClusterServiceVersion(CSV),CatalogSource 和 Subscription 是 OLM 框架中和升级紧密相关的三种扩展模型。在 OLM 的生态系统中,我们通过 CatalogSource
来存储如 CVS 这样的 operator 元数据;OLM 会基于 CatalogSources,使用下 Operator 仓库相关 API 去查询可用或可升级的 operators;而在 CatalogSource
中,operators 通过 channels
来标识封装好的不同版本安装包。
当用户希望升级某个 operator 时,可以通过 Subscription
来订阅具体需要安装哪个 channel 中指定版本的软件包。如果订阅中指定的包还没有被安装在目标集群中时,OLM 会安装在 catalog/package/channel 等下载源的最新版本 operator。
在一个 CSV 定义中,我们可以通过 replaces
字段声明需要替换的 operator,OLM 在收到请求后会从不同的 channels 中寻找能够被安装的 CSV 定义并最终将它们构建出一个 DAG(有向无环图),在这个过程中 channels 可以被认为是更新 DAG 的入口。在升级过程中,如果 OLM 发现在可升级的最新版本和当前版本之间还有未安装的中间版本,系统会自动构建出一条升级路径并保证路径上中间版本的安装。比如当前我们有一个正在运行的 operator,它的运行版本是 0.1.1,此时 OLM 在收到更新请求后通过订阅的 channel 找到了 0.1.3 的最新可升级版本,同时还找到了 0.1.2 这个中间版本,此时 OLM 会首先安装 0.1.2 版本 CSV 中对应的 operator 替换当前版本,并最终安装 0.1.3 替换安装成功后的 0.1.2 版本。
当然在某些状况下,比如我们遇到了一个存在严重安全漏洞的中间版本时,这样迭代升级每个版本的方式并不是一种合理和安全的方式。此时我们可以通过 skips
字段定制化安装路径以跳过指定的中间版本,如下所示:
apiVersion: operators.coreos.com/v1alpha1
kind: ClusterServiceVersion
metadata:
name: etcdoperator.v0.9.2
namespace: placeholder
annotations:
spec:
displayName: etcd
description: Etcd Operator
replaces: etcdoperator.v0.9.0
skips:
- etcdoperator.v0.9.1
如果需要忽略多个版本的安装,我们可以在 CSV 中使用如下定义:
olm.skipRange:
其中版本范围的定义可参考 semver,一个 skipRange 的 CSV 示例如下:
apiVersion: operators.coreos.com/v1alpha1
kind: ClusterServiceVersion
metadata:
name: elasticsearch-operator.v4.1.2
namespace: placeholder
annotations:
olm.skipRange: '>=4.1.0 <4.1.2'
在 OLM 中,我们可以通过对 CatalogSource 模型来定义 InstallPlan 从哪里完成安装包的自动下载和依赖解析,同时 Subscription 通过对 channel 的订阅也可以从 CatalogSource 来拉取最新版本的安装包。本小节中我们以官方社区的 operator-registry 为例介绍一下 CatalogSource 的安装和基本使用方法。
operator-registry 主要由下列三部分组成:
关于 operator manifes 的格式定义,在 operator-registry 中把在上传目录中包含的每一个 CSV 定义单元称为一个“bundle”,每个典型的 bundle 由单个 CSV(ClusterServiceVersion)和包含其相关接口定义的单个或多个 CRD 组成,如下所示:
# bundle示例
0.6.1
├── etcdcluster.crd.yaml
└── etcdoperator.clusterserviceversion.yaml
当导入 manifests 到数据库时会包含如下的格式校验:
replaces
定义中被其他 CSV 取代,则对应的新旧 CSV 均需要存在于 package 中。对于 manifests 中不同软件包对应的 bundle 目录格式,原则上最好要保持一个清晰的目录结构,这里我们来看官方的一个 manifest 示例,其他 manifest 示例请见:
manifests
├── etcd
│ ├── 0.6.1
│ │ ├── etcdcluster.crd.yaml
│ │ └── etcdoperator.clusterserviceversion.yaml
│ ├── 0.9.0
│ │ ├── etcdbackup.crd.yaml
│ │ ├── etcdcluster.crd.yaml
│ │ ├── etcdoperator.v0.9.0.clusterserviceversion.yaml
│ │ └── etcdrestore.crd.yaml
│ ├── 0.9.2
│ │ ├── etcdbackup.crd.yaml
│ │ ├── etcdcluster.crd.yaml
│ │ ├── etcdoperator.v0.9.2.clusterserviceversion.yaml
│ │ └── etcdrestore.crd.yaml
│ └── etcd.package.yaml
└── prometheus
├── 0.14.0
│ ├── alertmanager.crd.yaml
│ ├── prometheus.crd.yaml
│ ├── prometheusoperator.0.14.0.clusterserviceversion.yaml
│ ├── prometheusrule.crd.yaml
│ └── servicemonitor.crd.yaml
├── 0.15.0
│ ├── alertmanager.crd.yaml
│ ├── prometheus.crd.yaml
│ ├── prometheusoperator.0.15.0.clusterserviceversion.yaml
│ ├── prometheusrule.crd.yaml
│ └── servicemonitor.crd.yaml
├── 0.22.2
│ ├── alertmanager.crd.yaml
│ ├── prometheus.crd.yaml
│ ├── prometheusoperator.0.22.2.clusterserviceversion.yaml
│ ├── prometheusrule.crd.yaml
│ └── servicemonitor.crd.yaml
└── prometheus.package.yaml
通过官方提供的 Dockerfile 我们可以构建出一个包含了 initializer 和 registry-server 的最小集 operator-registry 镜像。
下面我们来看下 operator-registry 与 OLM 的集成,这里我们需要创建一个 CatalogSource
对象并指定使用我们 operator-registry
对应镜像,如下所示:
apiVersion: operators.coreos.com/v1alpha1
kind: CatalogSource
metadata:
name: example-manifests
namespace: default
spec:
sourceType: grpc
image: example-registry:latest
当上面的 example-manifest 完成启动后,我们可以通过 pod 日志查看相应的 gRPC 后端服务是否已建立:
$ kubectl logs example-manifests-wfh5h -n default
time="2019-03-18T10:20:14Z" level=info msg="serving registry" database=bundles.db port=50051
同时一旦 catalog 完成加载,OLM 中 package-server
组件就会开始读取目录中定义好的 Operators 软件包,通过下面的命令我们可以 Watch 当前可用的 Operator package:
$ watch kubectl get packagemanifests
[...]
NAME AGE
prometheus 13m
etcd 27m
同时我们可以使用下面的命令查看一个指定 Operator package 使用的默认 channel:
$ kubectl get packagemanifests etcd -o jsonpath='{.status.defaultChannel}'
alpha
通过上面获取到的 Operator 软件包名称,channel 和运行 catalog 的命名空间等信息,我们可以通过创建上文介绍过的 OLM 订阅对象(Subscription)启动从指定 catalog 源中安装或升级 Operator,一个 Subscription 示例如下所示:
apiVersion: operators.coreos.com/v1alpha1
kind: Subscription
metadata:
name: etcd-subscription
namespace: default
spec:
channel: alpha
name: etcd
source: example-manifests
sourceNamespace: default
另外通过支持 gRPC 协议的命令行通讯工具 gRPCurl,我们可以在本地向指定的 catalog 服务端发送请求,从而方便地进行软件包目录信息的查看。
本章我们介绍了 Operator Lifecycle Manager 的基本架构和使用方法,通过本章的学习,我们对 OLM 的工作原理、架构设计都有了较为清晰的认识。同时通过一些示例代码,加深了读者对 OLM 应用实践的认识,为工作实战中通过 Operator Framework 实现产品能力扩展提供了指导基础。
匡大虎 阿里云高级技术专家,从事 Kubernetes 和容器相关产品的开发。尤其关注云原生安全,是阿里云容器服务云原生安全核心成员。
阚俊宝 阿里云容器服务技术专家,专注 Kubernetes、Docker、云存储领域,是阿里云 CSI 项目的核心维护者。
原文链接
本文为阿里云原创内容,未经允许不得转载。