我总是喜欢一些比喻,这样可以让我们更加形象地认识事物。
Erda 是一个 PaaS 平台,底层用到的技术曾经从 marathon mesos 切换到现在的 K8s,它们一般被认为是“容器层”。Erda 在“容器层”之上又堆叠了 CI/CD Pipeline、集群和部署管理、应用监控、自动化测试等等能力,这样分层的体现非常像网络的分层,每一层各司其职,不过我更喜欢将其比喻为「编程语言」。
封装是为了减少“失误”
一门高阶编程语言拥有更符合人类理解的语法,其通过编译成汇编或者机器码来实现运行,或者是编译成低阶语言再进行二次编译。
与此类似,Erda 通过对“容器层”的封装,对我们的用户呈现了具有“Erda 理念”的功能设计和使用体验。而所谓封装最重要的就是减少人为的“失误”,就好像高阶语言通过受限和优雅的语法、智能的编译提示以及丰富的类库,大大减少开发者的心智负担,可以轻松地写出健壮的代码。而在其之上的程序设计方法、最佳实践,为高速交付实现提供理论支撑。
何为制品
Erda 的身骨是以「应用」为中心打造的,假设 Erda 只能剩下一个功能的话,那就是应用的“交付”。
交付可以非常简单,我称之为“两步走”:
- 将代码编译成应用安装包
- 在客户要求的环境中安装应用
实际的情况会稍显复杂,但不会改变这个核心的步骤,大抵是多几步的问题。
如果要追究细节的话,这两步可以一直向下展开。Erda 做的事情就是让使用者无须深究这些细节,就像我们使用个人电脑观看视频时无须知道编码、传输和解码的过程,也无须知道有损和无损的区别。
当然,做这样的比喻不代表 Erda 有意阻拦用户进行深入探究,相反若懂得底层原理,更能够加深对 Erda 功能设计的理解!
具体而言,Erda 规范了可在云上交付的“软件安装包”格式,这样的安装包我们称之为“Erda 制品”(下文称之为“制品”),我们简单罗列一下制品的特性,这样大家可以有一个总体的印象:
- 制品是对 docker 镜像的进一步抽象,类似 docker-compose.yml,涵盖了多个镜像(服务)的总体描述,以及互相之间的依赖关系
- 制品是对应用交付环境的声明(结构化、可被系统识别的软件安装部署说明书),不仅声明每个镜像服务的资源限制,也能够声明所需要的中间件(比如 mysql)
需要补充一下,由于 Erda 是一个多应用架构(核心的主库 erda、前端应用 erda-ui、监控相关的 telegraf、fluentbit 等),所以 Erda 交付的时候是多个应用共同交付,我们称之为项目制品。项目制品在包含多个应用制品的同时还支持分批部署等特性。
这是 Erda 自己的制品(项目制品):
我们聚焦一下 Erda 主应用的制品内部,也就是分组 Group 3 中的 erda 主库的应用制品。
由于 Erda 主应用是微服务架构(多服务),所以我们能看到一串微服务容器列表:
以及最核心的 dice.yml
:
version: "2.0"
envs:
ETCDCTL_API: "3"
services:
cluster-agent:
cmd: /app/cluster-agent
deployments:
replicas: 1
envs:
DEBUG: "false"
resources:
cpu: ${request_cpu:1}
max_cpu: 1
max_mem: 1024
mem: ${request_mem:1024}
addons:
mysql:
plan: mysql:basic
为了方便阅读我们只列出了关键的信息,如果大家需要看到完整的 dice.yml
,可以在开源代码中找到:
https://github.com/erda-project/erda/blob/master/erda.yml
在这个例子中(也就是 Erda 自身的应用制品),大家可以充分感受到 Erda 是如何将“软件交付”进行封装的。
得益于 docker 对“执行环境”的封装,再组合上 dice.yml
对微服务/多应用整体“交付环境”的封装(实例个数、环境变量、资源消耗、中间件依赖),我们可以很自信地说:“开发者只需要关心其必要知晓的,其他由平台代为负责”。
交付制品
虽然刚介绍完制品的来龙去脉,我们仍想再回顾一下制品的关键组成部分:
- 镜像列表(docker images)
dice.yml
声明了各个服务(镜像列表所对应的)部署所需的资源,以及需要的中间件
镜像(docker)的知识我们暂且按下,“开发者只需要关心其必要知晓的”且只有 dice.yml
的语法规范和配置了。
了解 Erda 的实现,可以知道平台在部署制品时,读取 dice.yml
内容,并最终转换成“容器层”认知的结构(如果是 K8s 则是 Deployment、Service、Ingress,以及 StatefulSet 或者 Operator),进而交由“容器层”施展部署动作。熟悉 K8s 会知道,如果让用户手工编写这些配置,需要理解许多本不用知晓的知识(大多是运维相关),并且容易出错。
PS:不过针对 Addons(或者说中间件)的部署机制相对复杂,考虑到比如 Rds 等云厂商提供的外部能力,Erda 单独提供了一套部署和扩展能力
就像开篇讲的,dice.yml
似乎是一门“高阶语言”,而 K8s yml 则是“低阶语言”(我们这里所指高阶和低阶,并非认为“高阶”一定是“优秀”和“正确”的,而是指相对于方便人类认知而言,是更倾向于易于理解和防止出错的,而且恰恰是“低阶”在性能、灵活度、控制力以及正确逻辑方面是更有优势的),Erda 经过一次复杂的“编译”,将用户(我们不妨说业务研发人员)更容易理解的配置和声明格式或者语法,转变成实际部署的工作负载(Workload)和内生亦或外部的服务(Addons)。
聊了这么多,很容易就能得出 Erda 是如何交付制品的结论:一键部署。
当然真实的生产环境部署相对复杂,但不会改变核心的一键部署流程。Erda 在此之外展开了非常多额外的流程和功能:比如制品能够导出和导入,我们同时也开发了 Gallery(集市)方便制品的传播(Gallery 同时也承载了 Addons 以及 Pipeline 的 Action);制品也内置了 migration 的能力,能够在部署的过程中进行数据迁移;等等。
当然最重要的是 Pipeline 也就是流水线的能力,通过其编排的核心 CI/CD 逻辑:“代码 -> 制品 -> 部署”,能够从流程上控制生产环境(也包括测试或者其他环境)的准入,Pipeline 的 Action 扩展机制为方便对接外部流程节点提供可能,当然 Erda 内置提供了非常多开箱即用的能力诸如质量扫描、单元测试、自动化测试等等。
最后
本文只是从一个很小的侧面:制品,讲述了 Erda 如何交付自身,也包括如何交付各个其他软件,但“制品”又是在 Erda 中最为重要最为核心的概念,也可以说是 Erda 至此不变的“理念”。这是 Erda 的起点、原点,此前 Erda 未有身骨,只是内部无名的打包部署平台;此后 Erda 繁舟聚汇,不终不止。