1.2 Operator 介绍
在 Kubernetes 中我们经常使用 Deployment、DaemonSet、Service、ConfigMap 等资源,这些资源都是 Kubernetes 的内置资源,它们的创建、更新、删除等均由 Controller Manager 负责管理,触发相应的动作来满足期望状态(Spec),这种声明式的方式简化了用户的操作,用户在使用时只需关心应用程序的最终状态即可。随着 Kubernetes 的发展,在大数据、人工智能等领域出现了一些场景更为复杂的分布式应用系统,原生 Kubernetes内置资源在这些场景下就显得有些力不从心。
(1)不同应用平台需要管理的目标各有差异,如何在 Kubernetes 中兼容定义管理的目标?
(2)如何管理和备份系统的应用数据,协调各应用之间不同生命周期的状态?
(3)能否用同样的 Kubectl 命令来管理自己定义的复杂分布式应用?
在这些场景下,Kubernetes 自身基础模型已经无法支撑不同业务领域下的自动化场景。为了满足这些需求,谷歌提出了 Third Party Resources(TPR)概念,允许开发者根据业务需求的变化自定义资源和控制器,用于编写面向领域知识的业务逻辑控制,这就是Operator 的核心概念。
Operator 是 一 种 封 装、 部 署 和 管 理 Kubernetes 应 用 的 方 法, 用 户 可 以 使 用 Kubernetes API 和 Kubectl 工 具 在 Kubernetes 上 部 署 并 管 理 Kubernetes 应 用。Operator 基于基本 Kubernetes 资源和控制器概念构建,但又涵盖了特定领域或应用的知识,用于实现其所管理软件的整个生命周期的自动化,它是一种特定于应用的控制器,可扩展 Kubernetes API 的功能,是 Kubernetes 用户创建、配置和管理复杂应用的实例。
在 Kubernetes 内置资源中,Controller Manager 实施控制循环,反复比较集群中的期望状态和实际状态,如果集群的实际状态和期望状态不一致,则采取措施使二者一致。Operator 使用自定义资源(CR)管理应用,CR 引入新的对象类型后,Operator 监视 CR 类型,并采取特定于应用的操作,确保 CR 对象当前实际状态和期望状态一致,用户可通过在Operator 中编写自定义规则来扩展新功能和更新现有功能,可以像操作 Kubernetes 原生组件一样,通过声明式的方式定义一组业务应用的期望状态,监控操作自定义资源,该特性使Operator 几乎可以在 Kubernetes 中执行任何操作,包括扩展复杂的应用、版本升级、管理有状态的服务等。这种设计使应用维护人员只需要关注配置自身应用的期望运行状态,而无须投入大量的精力在部署应用或业务运行过程中频繁操作可能出错的运维命令。
1.2.1 Operator 简介
1. Operator 历史
Kubernetes(k8s)作为云原生应用的基石,已然成为当今容器编排的事实标准,为用户提供了很多拿来即用的基础资源组件,如 Deployment、Pod、Service、Configmap 等,这些基础组件为用户应用的部署和运维提供了极大的方便,Operator 的出现更是将 Kubernetes在容器化道路上推向了一个新的高度。现在,让我们先来了解 Kubernetes 和 Operator 的历史。
Docker 在 2014—2015 年是容器领域的佼佼者。Docker 公司前身是一个平台即服务的初创公司 dotCloud,它们发现,许多应用程序在管理依赖关系和二进制文件时需要做大量的工作,因此它们将 Linux Cgroups 和 Namespaces 的一些功能组合成一个包,这个包就是 Docker 镜像,它可以让应用程序在任何基础设施上持续运行,并提供简单的命令操作 Docker。随着 Docker 的风靡,Docker 容器上的应用越来越多,如何协调编排这些容器成为一个新的研究方向,因此,涌现了一批容器编排工具,包括 Nomad、Kubernetes、DockerSwarm 等,各种编排工具都可以使用容器来部署、管理和扩展应用程序,但是每个工具侧重点不同,最终 Kubernetes 脱颖而出。
谷 歌 于 2015 年 2 月 发 布 Kubernetes 并 于 2016 年 3 月 将 其 捐 赠 给 了 CNCF。Kubernetes 对应用开发人员非常有吸引力,它减少了对基础设施的依赖,并为开发人员提供了强大的工具来编排无状态的 Docker 容器。Kubernetes 项目是容器编排的核心,在于一个叫作“控制器模式”的机制中,它通过对 ETCD 中的 API 对象的变化进行监听,并在Controller 中对这些变化进行响应,无论是 Pod 等应用对象,还是存储设备、网络等服务对象,任何一个 API 对象发生变化时,Kubernetes 都会调用对应的 Controller 执行响应动作,实现编排动作。
2016 年,原 CoreOS 工程师邓洪超和他的同事在编程过程中突然有了一个想法,作为 Kubernetes 项目的用户,我们能不能编写一个 Controller 来定义自己所期望的编排动作呢?于是,这两位工程师将这个小项目称为 Operator,旨在通过扩展 Kubernetes原生 API 的方式,在 Kubernetes 中添加一个新的 API 对象,自定义该对象的运维动作,就可以为 Kubernetes 应用提供创建、配置和管理应用生命周期能力。这个小项目一经公布,引起了大量 Kubernetes 开发者的热烈追捧,很快大量的分布式项目都通过 Operator 运行起来。
相比 Helm 描述静态关系的编排工具,Operator 定义的是应用运行起来后整个集群的动态逻辑。得益于 Kubernetes 良好的 API 设计范式,Operator 在保证自由度的同时,还可以展现出清晰的架构和设计逻辑,开发者们可以专注于填写自己的业务逻辑,只需很小的开发量即可完成一个复杂的分布式系统的运维工作。
虽 然 Operator 一 经 面 世 就 受 到 热 烈 追 捧, 但 是 它 的 发 展 并 不 是 一 帆 风 顺 的。Operator 的诞生使得 Kubernetes 项目的负责人 Google 团队极为不适应,对于他们来说,Controller 应该是隐藏在 Kubernetes 内部实现的核心机制,即使开放了,也应该按照 Kubernetes 现有 API 规范成为 Controller Manager 管理下的一部分,Google 不希望失去 Kubernetes 生态系统的主导权。随着 Kubernetes 项目的发起人之一 Brendan Burns加入 Red Hat,Google 团队和 RedHad 在社区推广 UAS(User Aggregated APIServer),它允许用户编写一个自定义的 APIServer,在这里面添加自定义 API,就可以与原生的APIServer 绑定,部署在一起统一提供服务。并且 Red Hat 和 Google 还建议废弃 TPR,也就是 Operator 依赖的第三方接口资源,Operator 面临被关闭的风险。
在这种困境下,CoreOS 公司在 GitHub 上发布了一个帖子,让社区的开发者发声,挽救 TPR 和 Operator,由于 Operator 的用户太多,在来自社区的压力下,Google 和 Red Hat 最终选择了让步,Operator 从绝境中重生。后来 Kubernetes 使用 CRD 替代了 TPR,这两种机制除了名称,其他方面并没有什么变化。
2018 年,RedHad 完成了对 CoreOS 公司的收购,并推出了 Operator 框架,进一步完善了 Operator 相关工具,使 Operator 的地位得到了稳固。
2. Operator 组成
简单来说 Operator=Controller+CRD,Operator 是由 Kubernetes 自定义资源(CRD)和控制器(Controller)构成的云原生拓展服务,其中 CRD 定义了每个 Operator 需要创建和管理的自定义资源对象,底层实际就是通过 APIServer 接口在 ETCD 中注册一种新的资源类型,注册完成后就可以创建该资源类型的对象了。但是仅注册资源和创建资源对象是没有任何实际意义的,CRD 最重要的是需要配合对应的 Controller 来实现自定义资源的功能,达到自定义资源期望的状态,比如内置的 Deployment Controller 用来控制 Deployment资源的功能,根据配置生成特定数量的 Pod 监控其状态,并根据事件做出相应的动作。
3. Operator 使用
用户想为自己的自定义资源构建一个 Kubernetes Operator,有很多工具可供选择,比如 Operator SDK、Kubebuilder,甚至可以使用 Operator SDK(Helm、Ansible、Go)。这些工具创建 Kubernetes Operator 用来监控自定义资源,并且根据资源的变化调整资源状态,如图 1-9 所示。
图 1-9 Operator 使用
Operator 作 为 自 定 义 扩 展 资 源 以 Deployment 的 方 式 部 署 到 k8s 中, 通 过 ListWatch 方式监听对应资源的变化,当用户修改自定义资源中的任何内容时,Operator 会监控资源的更改,并根据更改内容执行特定的操作,这些操作通常会对 Kubernetes API 中某些资源进行调用。