在之前的《Blog部署演进之路》中,我曾提到博客目前是借助 Gitea Action 实现项目部署。过去一年里,它出色地完成了“提交→编译→部署”这一流程。然而从上次提交了编写的图片压缩插件后,项目部署耗时便大幅增加,有些时候甚至会导致任务卡死。
编译部署的耗时翻了几倍
正好端午这几天,杭州阴雨不断,我可以在家细细琢磨,给博客部署提提速。
01 切换Npm镜像
我先随意点开了几个任务,查看哪个步骤最耗时。经过排查,发现主要问题是 npm 拉取项目依赖包的过程耗时太久。
npm install的耗时占比约65%
这个问题其实很好解决,只要在拉取前设置好 npm 镜像地址就行(调整后的 workflows 配置见下方)。配置好镜像地址后,拉取时间从10分钟缩短至2分钟,提速约80%。
1 2 3 4 5 6 7 8
|
- name: Install Npm Package run: | npm config set registry https://registry.npmmirror.com #提前切换到淘宝镜像地址 npm install
|
拉取步骤的提速非常明显
02 添加optionalDependencies(可选依赖)
在开头的图片中,可以看到Build Hexo的步骤也比较耗时(约占30%)。于是我点开了任务日志,发现有一项报错:
无法在Linux中安装sharp
sharp是图片压缩插件所依赖的库,而该插件是以命令行形式运行的,在项目编译阶段其实并不需要它。也就是说,我得想个办法,在编译阶段把它给“屏蔽”掉。
经过一番资料查找,我发现了“optionalDependencies(可选依赖)”这个功能,它能完美解决我的问题。只要按下列操作进行,就能实现安装依赖时自动忽略可选依赖项啦。
- 在
package.json
里,先把那些非必须引用的插件,从“dependencies”挪到“optionalDependencies”中;
1 2 3 4 5 6 7 8
| { "dependencies": { "pluginName": "1.0.0", }, "optionalDependencies": { "sharp": "^0.32.6", } }
|
- 然后在工作流文件中,把“npm install”命令调整为“npm install –omit=optional”。
1 2 3 4 5 6 7 8
|
- name: Install Npm Package run: | npm config set registry https://registry.npmmirror.com npm install --omit=optional #提前切换到淘宝镜像地址
|
需要注意的是:Hexo会在编译/运行时自动注册插件,如果在插件注册时找不到对应依赖,项目编译时仍会报错!所以需要修改插件compress-images.js
内容,增加环境判断条件才能完全解决问题!
1 2 3 4 5 6 7 8 9
| const os = require('os'); const platform = os.platform();
if (platform === 'win32') { hexo.extend.console.register('compress-images', '...', {}, () => { }) }
|
到这一步时,整个编译部署时间已经缩短到了5~8分钟(小有成效😁)。
忽略加载一部分npm包和插件后,Build时间缩短了一半!
03 使用@actions/cache缓存依赖包
虽然前面我已经设置了 npm 镜像地址,但网络波动仍会导致拉取时间偶尔变慢。于是,我琢磨着有没有办法缓存 npm 包,好在查阅资料后,还真让我找到了解决方法:
- 使用 @actions/setup-node 的
cache
属性简单开启npm包缓存功能;
1 2 3 4 5 6 7 8 9
|
- name: Setup Node uses: actions/setup-node@v4 with: node-version: '18' cache: 'npm'
|
- 使用 @actions/cache 来缓存和恢复npm包(下方代码来自Gitea官方案例);
1 2 3 4 5 6 7 8 9 10 11
|
- name: Cache node modules uses: actions/cache@v3 with: path: node_modules key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} restore-keys: | ${{ runner.os }}-node-
|
两者的区别可以参考下表。由于我的工作流里已经用 container.image
属性,指定了自带Node版本的容器镜像,如果再用 @actions/setup-node 重复拉取一次Node就有点多余了,所以我采用了第二种办法。
功能 |
@actions/setup-node |
@actions/cache |
主要用途 |
设置 Node.js 环境 |
缓存依赖项和构建输出 |
功能细节 |
下载并缓存指定版本的 Node.js 添加 Node.js 到系统环境变量 缓存 npm、yarn 或 pnpm 依赖 配置 GPR 或 npm 认证 |
缓存指定路径下的文件 通过 key 和 restore-keys 控制缓存的命中和恢复 |
适用场景 |
在 CI/CD 流程中自动化设置 Node.js 环境 测试不同 Node.js 版本对项目的影响 需要缓存依赖项以提高构建效率 |
缓存依赖项,减少重复下载时间 缓存构建输出,加速后续构建 |
配置复杂度 |
相对简单,主要配置 Node.js 版本和缓存策略 |
需要指定缓存路径、key 和 restore-keys 等 |
执行报错“::error::Input required and not supplied: key”的解决办法:
我尝试通过echo输出runner.os
和hashFiles()
的结果,发现runner.os
正常、hashFiles
无法获取结果导致key生成失败。于是改为使用sha256sum
来生成hash(步骤见下方代码)。
1 2 3 4 5 6 7 8 9 10 11 12
| - name: Calculate hash manually id: hash run: | echo "hash=$(sha256sum package-lock.json | cut -d ' ' -f 1)" >> $GITHUB_OUTPUT
- name: Cache Npm Package uses: https://gitea.com/actions/cache@v3 with: path: node_modules key: ${{ runner.os }}-node-${{ steps.hash.outputs.hash }}
|
经过这一步缓存 npm 包的操作,每次编译部署时,系统都会自动比对package-lock.json
文件的 hash 值。如果文件内容没有变动,就会优先从缓存中读取所需数据,这样一来,拉取 npm 包的速度又得到了显著提升——从原来的 2 分钟缩短到了 30 秒。
耗时缩短至30秒内,提速约75%;
04 移除未使用的依赖包
打开package.json
后,我又逐项对比并移除了项目中未使用的四款插件,这在一定程度上能减少项目依赖的npm包数量,小幅提升项目编译的速度。
相比第二步时,Build耗时又缩短了60%
05 替换为npm ci
npm ci
相比npm install
优势显著:它严格按照package-lock.json
安装依赖,确保版本一致,避免因版本范围不同导致最终差异。另一方面它能有效利用缓存,减少从远程仓库的下载次数,这使得其在工作流阶段的安装速度更快。
1 2 3 4
| npm install --omit=optional
# 替换为 npm ci --omit=optional
|
尾:最终效果
通过上述方法调整后,目前博客的编译部署时间已经从十多分钟缩短至三分钟左右,整体优化效果还是非常明显的,也不再出现编译部署时任务卡死的情况了,舒服了❤。
耗时2分4秒,舒服了