AWS Lambda 的冷启动问题真的能让人抓狂。上周刚上线了一个新版本的 API 网关函数,结果一压测,冷启动时间直奔 6 秒,部分请求直接抛出 Gateway Timeout。去 AWS CloudWatch 日志里排查,发现系统加载依赖的 Init Duration 占了整整 5.5 秒。登入 CI 机器上一看,好家伙,由于开发同学直接把 node_modules 里的全部依赖包括 AWS SDK、大量的 lodash 辅助函数,甚至一些本地开发测试工具都一股脑压缩进 zip 包,整个部署包体积达到了惊人的 85MB。
要知道,AWS Lambda 初始化时需要下载这个 zip 包并解压。包体积越大,拉取和解压消耗的时间就呈线性增长。更别提由于 node.js 的 require 机制是同步磁盘 I/O,加载数千个小文件直接把 CPU 跑满,导致冷启动慢得像蜗牛。对于敏感的 C 端接口来说,这绝对是灾难。
为了解决这个问题,我们必须给部署包瘦身,定下了一个“Lambda 打包体积预算(Bundle Budget)”目标:解压前 zip 包控制在 5MB 以内。我们废弃了原先无脑 zip 的打包方案,改用 Esbuild 进行单文件打包和 Treeshaking 树摇。
这里直接贴出我们最终上线并稳定运行的 esbuild.config.js 自动化打包脚本。它不仅能自动排除 AWS SDK(因为 Lambda 运行时环境自带),还能自动压缩混淆,精简最终输出。
import esbuild from 'esbuild';import { builtinModules } from 'module';
const nativeModules = [ '@aws-sdk/client-s3', '@aws-sdk/client-dynamodb', 'aws-sdk', // 兼容老版本 sdk ...builtinModules];
async function runBuild() { console.time('Build completed in'); try { const result = await esbuild.build({ entryPoints: ['src/handlers/api.ts'], bundle: true, minify: true, sourcemap: true, platform: 'node', target: 'node18', format: 'esm', // 采用 ESM 格式输出,提升加载速度 outfile: 'dist/index.mjs', external: nativeModules, // 关键:排除运行时已有的原生模块和 AWS SDK treeShaking: true, // 开启树摇,剔除未使用的辅助函数 metafile: true, // 生成分析文件,用于可视化包体积 define: { 'process.env.NODE_ENV': '"production"', }, banner: { // 解决 ESM 下 __dirname 和 __filename 找不到的报错问题 js: ` import { createRequire } from 'module'; const require = createRequire(import.meta.url); `, }, });
console.log('⚡ Build succeeded.'); console.timeEnd('Build completed in');
// 打印打包分析摘要 if (result.metafile) { const text = await esbuild.analyzeMetafile(result.metafile); console.log(text); } } catch (error) { console.error('❌ Build failed:', error); process.exit(1); }}
runBuild();通过这套脚本,我们把原本 85MB 的依赖怪物,直接浓缩成了一个只有 820KB 的单个 index.mjs 文件。
上线后的效果非常显著:Init Duration 从 5.5 秒暴跌到 280 毫秒,整体冷启动时间稳定在 400 毫秒以内,用户端再也没有遇到超时报错。而且,精简后的包也加快了我们的 CI/CD 部署速度,从原本打包上传到 AWS 耗时 2 分钟缩短到了 10 秒以内,开发体验也得到了极大提升。
所以,做 Serverless 架构设计时,千万别拿传统主机的依赖思路去写 Lambda。对于这种瞬时启动、按需计算的场景,每一个 KB 的依赖都是实打实的成本和延迟。
你们在写 Lambda 或者 Serverless 函数时,是用什么构建工具做压缩的?有遇到过哪些因为第三方依赖引入的莫名其妙的冷启动大坑吗?