一个极简的 CD 工具

是什么
对 K8S api 进行封装,借助 FRP 对封装后的接口进行公网暴露,同时使用 Token 进行身份验证,配合CI Pipeline最终实现代码的自动发布上线。
使用时,只需要在前端选择对应的Deployment,点击创建按钮,然后复制对应的命令即可。得到的命令是一个curl语句,可以和任意的系统进行集成。如果和CI进行集成,那么当代码构建完之后就能实现自动的发布更新。
前端

Curl

CI 集成

为什么
入学之后组里想探索一下新的方向,做一些平台之类的东西;因此搭建了 K8S,在此之上进行开发。

随着开发的模块越来越多,每天的频繁发版上线成了一件非常折磨人的事情:每次都需要 本地构建镜像 -> 上传docker hub -> 登录 rancher -> 修改镜像Tag,非常的繁琐,某些时间每天会有接近20+次的上线,如果完全依靠人工来做会做到崩溃。
因此搭建了一个 CD 系统,借助 Azure Devops 完成 CI 部分;使用 webhook+K8S api;写完代码 Push 至仓库后自动开始构建自动发布,省去所有人工步骤。
为什么不使用开源的方案
在之前实习的时候做过一段时间的 CICD,因此最初遇到这个问题时想的同样是使用开源方案来解决。但我们的代码托管在 Github 等地方,我们希望直接使用这些服务商的 CI 能力而不是在本地单独搭建一套 CI 系统,因此像Jenkins这类工具就显得太重了。
我们的诉求很简单:当 CI 系统构建出来镜像之后以某种方式通知到 K8S 即可。
Helm 和 ArgoCD 都可以解决这个问题:
- Helm 可以和 CI 集成,当构建完产物后通过
update来更新镜像即可,然而我们的 K8S 是不能直接外网访问的,这样 Helm 就无能为力了。 - ArgoCD 可以完美的解决这个问题,它会通过监听某个仓库的变化并将变化应用到集群;但是这种方案需要单独维护一个或多个仓库,同时需要手动/自动的更新这些仓库的内容,学习成本太高&和 CI 集成的难度比较大。
因此最后没有选择这些方案,自己简单实现了一下。
如何实现

FRP 实现内网穿透
在构建 Docker 镜像时,直接将
frp的客户端打包进去,然后在后端的init函数中拉起。
func init() { ...... // 开一个协程拉起 if runtime.GOOS != "windows" { go startFrp() }}安全起见,FRP 的配置通过环境变量注入
后端
使用 Go 实现,为了适配不同的数据库,抽象出了一些接口。
在修改Jaeger时学习到了这种设计模式,于是就应用了一下。
type Factory interface { Initialize() error CreateReader() (Reader, error) CreateWriter() (Writer, error)}
type Reader interface { Get(key string) (KV, error)}
type Writer interface { Set(kv KV) error Remove(key string) error}进程内缓存,当 Token 不存在时先去数据库里查,查询到更新至缓存中。
前端
使用angular实现
使用效果

平均每月 600-800 分钟的构建时长,大约100次左右的发布,除网络问题外无任何异常。
仅 2-3 月里自动完成了约500次发布,4 月份消耗了 1800 分钟的构建时长,平均每天 10 次发布,成功率100%,极大的减少了手动发布的痛苦。


意外收获
目前使用 Azure Devops 作为代码托管和 CI,Azure 的 Agent 可以无视 GFW,因此在构建镜像的时候非常丝滑;同时,我们使用阿里云的 ACR 作为镜像中心,Azure 也可以很顺畅的将镜像推至 ACR 中。
一些缺点
并发限制
免费的 CI Pipeline 有并发限制,如果出现多个任务,需要排队;但我们团队只有两个人,这个问题可以忽略不计。
构建速度
虽然不存在网络的问题,但是因为构建镜像的 Agent 是随机的,因此无法将一些可以缓存的东西缓存下来,每次构建都是全新的,平均构建一个镜像需要 3-5 分钟,相比本地要慢一下;但是网络优势又弥补了这个缺点。