Redian新闻
>
加 3 行代码减少 80% 构建时间

加 3 行代码减少 80% 构建时间

公众号新闻

推荐关注↓

作者:Whilconn

https://juejin.cn/post/7135756687134162980

背景

最近接手的BI项目在Jenkins的构建机上构建耗时比较久,日常构建耗时都在 20min 以上,即使改动一行代码也要构建这么久。构建耗时截图如下:

构建耗时较长导致日常测试和正式发版都会浪费很多时间等待,对研发流程影响较大(主要是我忍不了)。因此需要对构建速度进行优化。

优化思路分析

要优化项目的构建速度,得先了解构建流程:

  • 开发人员推送代码到 Gitlab,触发 Gitlab 服务器的 Push Events
  • Push Events被触发后,会调用提前配置好的 Jenkins webhooks
  • Jenkins webhooks被调用后,会执行对应项目的构建任务
  • 构建任务开始后先拉取项目源码到构建机,再使用docker build构建镜像
  • docker 构建镜像分为两个阶段,先使用npm scripts构建前端项目,然后把构建产物拷贝到nginx基础镜像

在这个流程中,可以优化的环节只有构建docker镜像这一步,其他环节的耗时基本可以忽略不计。而在不大改项目的情况下能起到明显提速效果的方案是:缓存策略。构建docker镜像时可以用到的缓存包括两类:docker层缓存应用层缓存

docker层缓存是指docker build所产生的可重用镜像层,只要Dockerfile中的命令及相关的源文件未改变,就能直接使用这些镜像缓存。这种缓存策略在代码不改变的情况下效果很好,构建耗时甚至可以控制在 10 秒内。而对于日常开发情况下,代码频繁变化,如果应用本身构建时间又很长,则需要使用应用层缓存。(docker 层缓存相关介绍,也可以看看官方文档[1]中文文档[2],本文不再赘述)

应用层缓存是指应用构建所产生的中间产物,这些中间产物主要是node_modules目录中的物理文件,其中包括npm install下载的依赖包和npm run build产生的.cache目录文件。

docker build每次都会初始化全新的环境用于构建,新环境中不存在node_modules目录,因此每次都是重新写入而无法复用,得想办法复用该目录下的文件;另外npm run build需要开启缓存功能,才会输出缓存文件到node_modules/.cache目录。

综上,优化思路主要是两点:1、开启应用层构建缓存(如webpack cache);2、持久化node_modules目录,确保每次npm installnpm run build都能复用该目录下的文件。

开启应用层构建缓存

项目使用的技术是React,构建主要依靠[email protected],底层实际调用的是[email protected],应用构建缓存主要来自webpackwebpack需要手工开启缓存功能(官方文档传送门[3]),配置cache属性为true即可。

实际操作只有 1 步, 找到webpack.config.js设置cache:true,代码如下:

module.exports = {
  //...
  cachetrue
};

本地首次npm run build构建,无缓存的情况下,耗时 13min 左右。

启用缓存后在本地进行二次构建,有缓存的情况下,无论是否修改源码构建耗时均为 4min 左右,比优化前的 13min 有明显提升。 构建耗时截图如下:

实际上,webpack@4的缓存只在watchdevelopment模式下生效,在上述构建测试中其实不起作用。 实测删除wepack中的cache:true配置,或者配置为cache:false,二次构建时间也是 4min 左右。

之所以构建速度提升了那么多,是因为react-scriptswebpack配置[4]中开启了babel-loadereslint-webpack-plugin的缓存功能,另外terser-webpack-plugin配置[5]也默认开启了缓存功能。从缓存目录node_modules/.cache中也能看到它们的缓存文件。

所以,这一步其实啥也不用做,如果想进一步提速可以升级到webpack@5

持久化  node_modules目录

想在docker build环境中持久化node_modules需要使用到BuildKitmount功能,该功能有几个前置条件:

  • docker 版本必须高于 18.09
  • BuildKit需要手工启用[6],可在docker build命令前添加环境变量DOCKER_BUILDKIT=1启用
  • 如果前两个条件不满足,则需要具备Jenkins和构建机的读写权限,以调整构建环境参数
  • 修改Dockerfile,使用RUN --mount=type=cache运行npm installnpm run build指令(--mount=type=cache说明文档传送门[7]

开启BuildKit还有其他特性[8],比如输出日志更友好,基本每一步都会输出耗时,就这一条,值了!

实际操作分为 2 步

1、修改Jenkins配置,在docker build命令前加上环境变量。修改后镜像构建命令长这样:

 DOCKER_BUILDKIT=1 docker build .

2、修改Dockerfile,将RUN npm installRUN npm run build指令改为RUN --mount=type=cache npm xxx。修改后Dockerfile长这样:

FROM node:alpine as builder

WORKDIR /app

COPY package.json /app/

RUN --mount=type=cache,target=/app/node_modules,id=my_app_npm_module,sharing=locked \
    --mount=type=cache,target=/root/.npm,id=npm_cache \
    npm i --registry=https://registry.npm.taobao.org


COPY src /app/src

RUN --mount=type=cache,target=/app/node_modules,id=my_app_npm_module,sharing=locked \
    npm run build

文档说由于 BuildKit 为实验特性,需要在 Dockerfile 文件开头加上如下代码:# syntax = docker/dockerfile:experimental。在Docker 20.10环境下,加了上述代码反而构建报错,原因是加载外网资源失败,删除后构建成功。这不就是玄学吗?🤡

优化结果

在配置好缓存策略后,模拟日常开发修改项目代码触发自动构建流程,构建耗时从 20min+下降到 4min+,总体耗时减少 80%。整个优化过程修改了Jenkins的一行配置,另外在Dockerfile中添加了3行代码,改动很少但效果很不错。

参考资料

[1]

官方文档: https://docs.docker.com/develop/develop-images/dockerfile_best-practices/#leverage-build-cache

[2]

中文文档: https://vuepress.mirror.docker-practice.com/appendix/best_practices/#%E6%9E%84%E5%BB%BA%E7%BC%93%E5%AD%98

[3]

传送门: https://v4.webpack.js.org/configuration/other-options/#cache

[4]

配置: https://github.com/facebook/create-react-app/blob/v4.0.3/packages/react-scripts/config/webpack.config.js#L459

[5]

配置: https://github.com/webpack-contrib/terser-webpack-plugin/tree/v4.2.3#cache

[6]

手工启用: https://docs.docker.com/develop/develop-images/build_enhancements/#to-enable-buildkit-builds

[7]

传送门: https://vuepress.mirror.docker-practice.com/buildx/buildkit/#run-mount-type-cache

[8]

其他特性: https://docs.docker.com/develop/develop-images/build_enhancements/#new-docker-build-command-line-build-output


- EOF -




推荐阅读  点击标题可跳转

0、极客专属:几十款程序员秒懂的卫衣

1、没有几十年功力,写不出这一行“看似无用”的代码!!

2、Linus 已决定将 Rust 语言加入 Linux 内核

3、虾皮光速大裁员!上个厕所的功夫,瞬间查无此人...


关注「程序员的那些事」加星标,不错过圈内事

点赞和在看就是最大的支持❤️

微信扫码关注该文公众号作者

戳这里提交新闻线索和高质量文章给我们。
相关阅读
儿童的火车乐园谁动了我的代码:代码混淆剖析0行代码拿210万年薪,ChatGPT催生新型「程序员」岗:工作纯靠和AI聊天独立完整工业体系完成文革期间重写 50 万行代码,从 0 自研的云原生 HSTAP 能否成为数据库的未来?| Q推荐据说这是减少代码错误的有效办法是什么让一段20行代码的性能提升了10倍三行代码解决长尾不平衡类别分类!间隔校准算法Margin Calibration来了!3行代码建模,训练速度提升200%?这款时序开源神器PaddleTS太强了!用1个月重构了同事写的烂代码,我总结出了15条重写烂代码的经验!红牛车队违反预算上限规则,被罚款700万美元并减少10%研发时间【报告】中国企业低代码/无代码产品应用与实践研究——构建数字化作业体系的变速齿轮 | 甲子光年智库PyTorch 2.0 来了!100% 向后兼容,一行代码将训练提速 76%早盘的国债殖利率飙升到3.062城市闹病农村吃药!【友情转发】MITCSSA年度巨献|一行代码,告别光棍节如何快速构建Prometheus监控体系,架构、指标、数据、告警… | 极客时间仅花200行代码,如何将60万行的RocksDB改造成协程写国际化的嵌入式代码,时间问题如何处理?Htmx意外走红,我们从React“退回去”后:代码行数减少 67%,JS 依赖项从 255 下降到 9业务负责人“无证上岗”?这家银行代销基金遭警示几行代码就能价值千万美金?丨1024程序员节Power Platform 产品大更新,微软:以无代码、低代码方式全面支持企业数字化转型瞧不上 C++ 和 D 语言,国外程序员将 5.8 万行代码迁移到 Jai 语言,到底图什么?10亿美元20年!亚城华人最爱的中超附近重建时间表!不写代码,就能快速构建精准的机器学习模型PyTorch 2.0来了!100%向后兼容,一行代码将训练提速76%!如何构建一个在线绘图工具:Feakin 是如何设计与构建的?《代码英雄》第五季(2):写代码的地方 | Linux 中国在北京,几行代码实现看房自由!一行代码12倍加速Bert推理,OpenAI编程语言加持的引擎火了读懂HikariCP一百行代码,多线程就是个孙子!研究了代码质量后,开发速度提高了 2 倍,bug 减少了 15 倍网站都变成灰色,几行代码就搞定了!出游, 击碎一个孩子的美梦
logo
联系我们隐私协议©2024 redian.news
Redian新闻
Redian.news刊载任何文章,不代表同意其说法或描述,仅为提供更多信息,也不构成任何建议。文章信息的合法性及真实性由其作者负责,与Redian.news及其运营公司无关。欢迎投稿,如发现稿件侵权,或作者不愿在本网发表文章,请版权拥有者通知本网处理。