云原生下的 DevOps 平台
1. 什么是云原生
云原生是一个快速发展的领域。
2013 年, Pivotal 提出云原生的概念,并不断对其进行解读。下面是 Pivotal 对云原生架构的特征描述:
-
2015年,12-Factor、面向微服务、抗脆弱 -
2017年,可观测性、模块化、可替代性、可处理性 -
2019年,DevOps、持续交付、微服务、容器
很多人接触云原生,可能是从 CNCF 开始的。CNCF 项目、全景图是云原生绕不开的焦点,下面是 CNCF 对云原生的特征描述:
-
2015年,容器化、微服务、编排调度 -
2018年,不可变基础设施、声明式 API、服务网格
这些定义基本上,能给出云原生的骨架。下面是我对云原生的一些认识:
随着互联网的高速发展,业务规模、数量、复杂度急剧增长,各种研发相关的框架一波又一波,但运维领域并没有发生大的变化,继续写 shell、Ansible,能用 Django 写页面就已经很不错了。
转折点来自 Docker 对容器技术的推广,之后 Kubernetes 又统一了容器编排领域。大家意识到 Kubernetes 是一个共识的操作系统,在分布式的资源之上构建了统一的控制平面,可以对标到 Linux。而云原生的描述更多是在与传统的架构划分界限。以前需要登录机器进行变更,现在是不可变的基础设施;以前是过程式的一系列动作,现在是声明式的描述;以前是单体服务,现在是微服务。这些新的特性能让业务更快迭代、更平稳健壮、更可靠访问,反过来又促进了云原生进一步地发展。
2. 什么是 DevOps
云原生离不开 DevOps,每个人心中对 DevOps 都有着不同的定义。DevOps 是一个被泛化的概念,涵盖了从需求端到上线的全过程,接管了整个研发流程。但理解 DevOps 只需记住一句话,端到端的价值交付。
传统的工作方式更像是大工厂,每个人只交付自己负责的环节。如上图,如果一个过程有三个步骤 A、B、C,那么就需要三个人各自负责。一旦某个环节出错,将影响整个交付。
DevOps 提倡的是面向价值的交付,只有同时完成了 A4、B6、C4,交付给用户之后,产出才有意义。因此,DevOps 更看重的是团队的协作,完整制品的快速交付,拒绝半成品。
从开发者提交代码、构建镜像、进行代码扫描、执行单元测试、构件制品,最终部署上线,就是一个端到端的交付过程。这种重复性很高的流程,我们通常需要借助一定的工具完成,那就是 CICD 工具。
3. 云原生下集成 DevOps 平台
3.1 如何开发一个 CICD 工具
首先思考一个问题,如何设计一个 CICD 工具?定义一套 DSL 语义。
我们可以将 DSL 分为两个部分,Outer DSL 和 Inner DSL。Inner DSL 就是 CICD 引擎,用于执行具体的逻辑,例如 Jenkins、GitLab CI 等。另一部分 Outer DSL 用于提供给用户描述 CICD 流程,例如 Jenkins 中的 Groovy、GitLab CI 中的 Yaml。
底层封装复杂性,向上层提供服务。Inner DSL 实现流程的解析,提供与 CICD 紧密相关的功能,用户不需要太多关注。Outer DSL 用于表达用户的意图,易于学习和掌握,针对 CICD 场景提供表达能力。
3.2 Operator 连接万物
在云原生背景下,我们通常采用 Operator 模式进行扩展。也就是使用 CRD 定义 Scheme 字段,将对象数据存储为 CR ,Informer watch 到变化之后通过 Controller 不断地 Reconcile ,最终达到预期的状态。
可以从两个角度描述这一模式。
-
声明式,是云原生的特征之一。如果需要将一个组件的副本数设置为 3,传统的方式是 +1、+1、+1。但在 Kubernetes 下,只需要声明副本的数量为 3 即可,系统会帮用户完成 +1、+1、+1 的过程。Operator 就是将传统的过程式转变为声明式。
-
替代人工。Operator 字面意思操作者,已经很清楚地表述了其作用,用于替代人工。Operator 融入了运维领域的知识,将人的技能固化到程序代码中,通过读取 CR 数据进行指定的操作,达到与人工运维同等效果。
Operator 可以连接一切外部的组件,替代人工运维过程, 比如部署 Redis 集群、对接 Jenkins 等。
3.3 DevOps Operator
针对国内的调研数据显示,超过 50 % 的用户使用的是 Jenkins 作为其 CICD 引擎。因此,KubeSphere DevOps 选择的也是 Jenkins。
在 KubeSphere DevOps 中,我们对其进行了如下抽象。
产品概念 | Kubernetes 对象 | Jenkins 对象 |
---|---|---|
DevOps 工程 | DevopsProject | 文件夹 |
流水线 | Pipeline | 流水线/多分支流水线 |
凭证 | Credential | 文件夹下的凭据 |
下面是处理流程示意图:
通过前端页面,我们将 CR 对象写入 Etcd 中,然后不断地 watch 通过 Controller 将数据同步到 Jenkins 。用户通过页面触发 Jenkins 的执行操作,Jenkins 在 Kubernetes 中创建 Pod 作为 Agent 用于构建镜像,最后发布到环境中。
在平台中,我们内置了 Python、Go、Nodejs、Java 构建客户端,通过内置或 Yaml 定义的方式,也可以很容易地添加其他类型的 Agent。
4. 挑战与展望
-
DevOps 与 Jenkins 耦合紧密
虽然我们通过 Operator 对 DevOps 相关的数据和操作进行了自动化管理,但是请看下面这个流水线对象的描述。
apiVersion: devops.kubesphere.io/v1alpha3
kind: Pipeline
metadata:
annotations:
kubesphere.io/creator: admin
creationTimestamp: "2021-03-24T20:04:22Z"
finalizers:
- pipeline.finalizers.kubesphere.io
generation: 2
name: test
namespace: testxxs9t
resourceVersion: "41074824"
selfLink: /apis/devops.kubesphere.io/v1alpha3/namespaces/testxxs9t/pipelines/test
uid: 22fad924-8f13-400c-a249-7c792ce9ea68
spec:
pipeline:
discarder:
days_to_keep: "7"
num_to_keep: "10"
jenkinsfile: echo "hello"
name: test
type: pipeline
status: {}
从 Yaml 中可以看到 Spec 中相关的字段与 Jenkins 紧密相关,这其实非常不利于扩展,如果以后需要对接其他流程引擎,基本上只能重写一套 Operator。
-
CRD == DataBase ?
将 CRD 等同 DataBase 确实可以帮助研发快速写代码逻辑,但是缺乏抽象与设计将会导致灾难。推翻重构不是最难的,难的是在高速公路上去做,而且得保证平稳切换。数据的迁移是这个过程中,具有挑战的地方。在设计时,就需要考虑数据迁移的问题。
-
统一的 Out DSL
最后,我比较期待的是一个通用的抽象模型,能够对接主流的 CICD 工具。用户通过定义执行流中的 Stage 和 上下文 Context,就可以对接各种流程引擎,而在底层只需要实现同一套 Interface 即可。