Koupleless 助力「人力家」实现分布式研发集中式部署,又快又省!

Koupleless 助力「人力家」实现分布式研发集中式部署,又快又省!

作者:赵云兴,葛志刚, 仁励家网络科技(杭州)有限公司架构师,专注 to B 领域架构

背景

人力家由阿里钉钉与人力窝共同孵化,致力于为企业提供以薪酬为核心的一体化 HR SaaS 解决方案,加速对中国人力资源服务行业数字化赋能。

人力资源软件通常由多模块组成,如人力资源规划、招聘、培训、绩效管理、薪酬福利、劳动关系,以及员工和组织管理等。随着业务发展,部分模块进入稳定期,仅需少量维护投入。例如,一个早期有 20 人研发的项目,现在拆分为 5 个应用。尽管产品成熟,但由于客户需求随竞争、业务和政策变化,仍需每年投入部分精力进行迭代。

长时间以来,我们一直面临着以下问题,而苦于没有解决方案:

  • 系统资源浪费:5 个应用支撑的业务,我们在生产环境为每个应用部署了 3 台 2C4G 的 Pod,一共是 15 个 Pod 的资源。
  • 迭代运维成本高:因为业务的复杂性,经常需要多个应用同时改动,部署等待周期长,单应用部署时间在 6 分钟左右。

在过去,我们已经探索过以下方案:

  • 压缩工程:通过排除冗余的 jar 依赖,降低镜像大小。但空间有限,整个应用 jar 包只能从 100+M 减少到 80+M,整个镜像依然有 500M,能节省的部署等待时间有限。
  • 单 ECS 上部署多应用:我们需要为这个应用做特别的定制,譬如监听端口要支持多个;部署脚本也要特别定制,要支持滚动发布,健康检测不同的端口,一段时间以后运维容易搞不清整个部署方案,容易出现运维事故。

初见成效

直到在某个月不黑风不高的夜晚,我们在最大的程序员交友网站上遇到了 Koupleless 团队。看完框架的介绍,我们立刻明白,Koupleless 就是我们要寻找的解决方案。

经过近两个月的敲敲打打,模块成功瘦身了,其中最大的模块的 jar 也只有不到 4M应用部署的体积从 500M 一下子降到了 5M 以下,具体可见下图~

瘦身以后模块的 jar

但高兴不过一天,我们在「如何把 Koupleless 部署到生产环境」上遇到了难题。因为我们没有专门的运维团队,部署都是开发人员通过阿里云的云效流水线,直接把镜像推送到 K8s 的 Pod。但这样改了以后,我们迎来了一连串待解决的问题……

  • 模块要不要流量,还是直接通过基座处理流量?
  • 如何在单独部署模块的时候先把基座的流量摘掉?
  • 发布成功以后如何做健康检查?
  • 如何重新开放基座流量?

Koupleless 的生产环境部署

在这里要特别感谢 Koupleless 团队的伙伴,给了我们很多专业的建议和方案。最终,我们选择了以下部署方案:

整体方案是,在基座上增加监听 oss 文件变化自动更新部署模块,卸载老版本模块安装新版模块,所有流量由 nginx 进入,转发到进程 tomcat (基座和多个模块复用同个 tomcat host),并在基座上控制健康检查和流量的开关,主要工作是在基座上扩充一些能力:

1、基座支持配置自身运行的必要条件,譬如需要模块 A、B、C 都安装成功才能放流量进来;

2、检查 oss 目录,自动安装最新的模块版本,并做健康检查;

3、实现 K8s 的 liveness:用来在基座部署的时候判断基座是否成功启动。只要基座启动成功,即返回成功,这样基座可以走后续的安装模块逻辑;

4、实现 K8s 的 readiness:主要控制外部流量是否可以进来。因此这里需要判断所有必须安装的模块是否健康,并且对应的流量文件 A_status 等是否存在(该文件是一个空文件,在滚动发布的时候开关流量的时候用)

小 Tips:

目前 Koupleless 优化了静态部署和健康检查能力,能够直接满足我们的需求:

  • 静态部署:在基座启动成功后,允许用户通过自定义的方式安装模块;
  • 健康检查:在基座启动成功且用户自定义安装的模块启动后,Koupleless 框架将原生 SpringBoot 的 readiness 配置为 ‘ACCEPTING_TRAFFIC’,用户可以通过探测 readiness 决定是否让流量进入。

以下是 K8s 上的配置图:

  • 就绪检查和启动探测:

  • 在 Pod 上增加云存储的挂载:

模块动态部署时需要考虑两个问题:怎么感知新的模块并部署?在模块部署的前后怎么通过健康检查,控制基座流量?

我们的方案大概如下:

  1. 模块通过流水线,直接 jar 上传到一个 oss 目录;
  2. 基座上增加配置,配置基座承接流量的必须安装的模块,以及对应模块的健康检查地址;
  3. 基座监听 oss 文件夹的变化,来决定是否重新部署模块(譬如有更晚的/更大的版本的模块上传到了 oss)
  4. 基座部署模块前,先摘流量(可以通过删除一个空文件来实现,结合上一步 K8s 的 readiness 逻辑,空文件删除以后,readiness 检测不通过,流量就不会进来。但模块是存活的,防止有耗时的线程还在运行)
  5. 安装好模块以后,把删除的文件写上,就可以开流量了;
  6. 集群下,基座通过 redis 来控制 Pod 不会出现并行安装,保证流量不会断;
  7. 基座提供就绪检查:就绪检查只需要判断基座起来了就可以。

存活检查是比较关键的一步:

a.判断第 4 步的空文件是否存在

b.需要判断所有必须安装的模块都可以访问

小 Tips:

目前 Koupleless 优化了健康检查能力,能够在模块动态安装/卸载之前关闭流量,将 readiness 配置为 REFUSING_TRAFFIC,并允许用户配置模块卸载前的静默时长,让流量在静默时期处于关闭状态。在卸载的静默时长结束、旧模块卸载完成、全部模块安装成功后,readiness 才会配置为 ACCEPTING_TRAFFIC 状态。

总结

在以前,单个模块升级发布一次要 6 分多钟。

而改造后,升级单个模块只需要把编译后的 jar 上传到 oss 即可。

最终的效果,通过一组简单的数字对比就可以看出差异:

  • 在服务器资源上,以前需要 15X2C4G,现在只需要 3X4c8G,节省了 18C36G 的服务器资源
  • 在单个模块的发布时间上,我们从之前的 6 分钟降低到了 3 分钟
  • 在单个模块的部署资源上,我们从 500M 降低到了 5****M

再次感谢 Koupleless 团队伙伴的专业支持,特别是有济、立蓬。当我们在改造过程中遇到一些个性场景的兼容性问题,他们都能快速响应,让我们整个升级改造时间大大缩短。

通过升级 Koupleless 架构,人力家实现了多应用的合并部署、并行研发、轻量集中部署,大大降低了运维成本。

天下架构,分久必合,合久必分。而 Koupleless,让架构演进(分合)更丝滑🥰