发布系统从0到1

最近的新版发布系统项目的开发基本收尾,趁着热乎劲记录一下思路和心得。

整体思路

目标

同时支持K8S(容器)和VM(虚拟机)两种发布引擎。

流程

按3大阶段划分:配置,构建,发布。

对于业务研发来说,只需要操作构建与发布。

全局视角

下面是项目之初我构思的一个整体架构:

发布系统与应用CMDB隔离为2套系统:

  • 发布系统:提供所有发布平台的所有功能
  • CMDB:restful api接口,维护公司核心的应用基础信息,供发布系统调用。

下面拆开每个部分简单说一下思路。

设计与实现

CI – 持续构建

发布平台支持php&vm、php&k8s、java&vm、java&k8s等多种应用架构。

这一块的理念就是把要发布的东西打包到一起:

  • 对于K8S来说,就是生成镜像,推送到Harbor。
  • 对于VM来说,就是生成tar.gz包,推送到对象存储。

设计时要克服的难点在于:

  • 不同的技术栈的构建流程不同,比如PHP、JAVA、GO在编译代码方面差异是很大的。
  • 同一个技术栈,不同的项目其玩法也各不相同,不同团队还有不同的项目结构和组织风格。
  • VM和K8S的构建流程本身就不同。

针对上述问题,我的解决方案如下:

  • 不同技术栈的构建流程做区分设计,建立接入标准,推动业务适配。
  • VM和K8S构建流程做区分设计。

简单的总结一下,不同的技术栈与运行环境(VM or K8S)分别设计一套构建流程和标准,强约束用户只能按照固定套路玩耍。

以PHP+K8S的构建配置为例,前台表单是定制化设计的,这意味着JAVA+K8S也许就不长这样:

表单之所以长这样,完全是因为PHP+K8S架构的应用的构建流程需要这些信息,一些过于灵活配置的东西就通过一个YAML来表达了,否则前台表单太复杂。

虽然不同架构的表单和构建配置各不相同,但是在数据库中都是一个大JSON字段保存,不同的应用架构对该JSON有对应的实现逻辑。

发布平台会触发Jenkins对应的pipeline完成构建,pipeline是按应用架构划分的,流程均基于Python开发,会完成代码下载、编译、打包/镜像等流程,细节不在此展开。

无论如何,CI阶段的核心设计就是build_order构建订单,最终产物就存放在image_list镜像列表(K8S用)或者package_list包列表(VM用)。

下面是php&k8s构建时的样子:

选择commit以及依赖项目的Commit,最终触发构建就可以得到编译结果了。

CD – 持续发布

这一块的理念就是把CI的产物分发到线上:

  • K8S就是deployment部署image
  • VM就是ansible playbook部署package

CD阶段的设计基本是通用的,可以适用于所有的技术栈(PHP or JAVA or …)和运行时环境(VM or K8S)

对于K8S来说,我们需要配置好Deployment的YAML模板文件,发布的时候只需要替换image name即可。

对于VM来说,需要配置诸如代码部署到哪个目录,部署到哪些机器,如何重启服务等配置。

这里以K8S的发布配置为例:

当然,具体发布到哪些K8S集群也是需要地方配置的,需要和该应用产生关联,就不展示了。

最终发布就很简单,只需要选镜像并点击发布:

CD阶段的核心设计是publish_order。

发布K8S很容易遇到不一致问题,比如创建了订单,但是推送K8S失败,那么到底以谁的状态为准呢?我的思想就是以K8S为准。

为了实现发布系统和K8S的一致性,我在推送deployment yaml给K8S的时候,在annotations中记录了关联的publish order ID。

这样做的好处是,我只需要调用K8S获取它运行中的deployment yaml,并从中找到publish order ID,然后再去publish_order表中找到对应的发布订单即可。

每个发布订单记录2个重要信息:

  • 上一个订单ID是多少?
  • 操作类型是什么? 发布 or 回滚。

这里一个重要的设计思想就是:把回滚当作一次发布。

因此,回滚的逻辑是找到当前订单的前一个订单,然后Copy它的内容生成一个新的发布订单,其类型等于回滚,然后推送它的YAML到K8S。

这种回滚即发布的设计,对于VM也同样重要,它可以令你的设计非常简单可靠。

下图是查看POD列表的样子:

RBAC权限

在RBAC中的难题就是权限的作用范围:

  • 全局权限
  • 具体某个应用下的权限

在我的设计中,权限permission表本身没有额外设计,但是在设计角色Role的时候增加了类型字段,用以区分是全局角色和应用级角色。

整个系统只允许给用户user分配角色,不允许直接分配权限permission。

当我们给用户user分配角色的时候,如果角色是应用级角色,那么需要关联到某个应用ID。

框架提供了检查权限的方法和装饰器,其逻辑是:

如果传入应用ID,则获取user关联在该应用ID下的角色以及关联的全局角色,然后分别获取这些角色的权限,最终检查是否权限充足。

大家有不明白的可以加我微信交流。

最后

整个发布平台还有RBAC,工单等设计,就不一一展开了,希望对即将做类似系统的朋友有所启发。

如果文章帮助您解决了工作难题,您可以帮我点击屏幕上的任意广告,或者赞助少量费用来支持我的持续创作,谢谢~