Turborepo+Docker部署Lambda时无法找到内部包@repo/image的问题
解决Turborepo单仓中Lambda依赖内部包找不到的问题
问题根源
你的Dockerfile在runner阶段只复制了resize服务自身的代码,完全没包含它依赖的内部包@repo/image的构建产物。虽然Turborepo的turbo prune和构建步骤已经在installer阶段生成了所有依赖的文件,但runner镜像里缺少@repo/image的代码,导致Lambda运行时Node.js无法解析这个内部包。
解决方案1:保留工作区结构,复制内部包产物
修改Dockerfile的runner阶段,把内部包的构建产物和项目根目录的package.json一起复制到镜像里,保持monorepo的目录结构,让Node.js能通过工作区配置找到依赖:
FROM public.ecr.aws/lambda/nodejs:20 as base FROM base AS builder WORKDIR ${LAMBDA_TASK_ROOT} RUN npm i turbo --global COPY . . RUN turbo prune resize --docker FROM base AS installer WORKDIR ${LAMBDA_TASK_ROOT} COPY .gitignore .gitignore COPY --from=builder /${LAMBDA_TASK_ROOT}/out/json/ . COPY --from=builder /${LAMBDA_TASK_ROOT}/out/package-lock.json ./package-lock.json RUN npm i COPY --from=builder /${LAMBDA_TASK_ROOT}/out/full/ . COPY turbo.json turbo.json RUN npm run build -- --filter=resize... FROM base AS runner WORKDIR ${LAMBDA_TASK_ROOT} # 复制根目录package.json(用于解析工作区依赖) COPY --from=installer /${LAMBDA_TASK_ROOT}/package.json . # 复制内部包image的完整构建产物 COPY --from=installer /${LAMBDA_TASK_ROOT}/packages/image ./packages/image # 复制resize服务的代码和配置 COPY --from=installer /${LAMBDA_TASK_ROOT}/services/resize/package.json ./services/resize/package.json COPY --from=installer /${LAMBDA_TASK_ROOT}/services/resize/dist ./services/resize/dist # 切换到resize服务目录,确保handler路径正确 WORKDIR ${LAMBDA_TASK_ROOT}/services/resize CMD [ "dist/index.handler" ]
原理
Node.js会读取根目录package.json里的workspaces配置,自动映射@repo/image到packages/image路径,这样导入语句就能正常解析。
解决方案2:打包为单文件(更适合Lambda场景)
如果不想在镜像里保留复杂的目录结构,可以用esbuild把resize服务和所有依赖(包括内部包)打包成单个文件,这样只需要复制这个文件到镜像即可:
步骤1:给resize服务添加esbuild配置
在services/resize/package.json中添加打包脚本和依赖:
{ "scripts": { "build": "esbuild src/index.ts --bundle --platform=node --target=node20 --outfile=dist/index.js" }, "devDependencies": { "esbuild": "^0.20.0" } }
步骤2:简化Dockerfile的runner阶段
FROM public.ecr.aws/lambda/nodejs:20 as base FROM base AS builder WORKDIR ${LAMBDA_TASK_ROOT} RUN npm i turbo --global COPY . . RUN turbo prune resize --docker FROM base AS installer WORKDIR ${LAMBDA_TASK_ROOT} COPY .gitignore .gitignore COPY --from=builder /${LAMBDA_TASK_ROOT}/out/json/ . COPY --from=builder /${LAMBDA_TASK_ROOT}/out/package-lock.json ./package-lock.json RUN npm i COPY --from=builder /${LAMBDA_TASK_ROOT}/out/full/ . COPY turbo.json turbo.json RUN npm run build -- --filter=resize... FROM base AS runner WORKDIR ${LAMBDA_TASK_ROOT} # 只复制打包好的单文件和package.json(可选) COPY --from=installer /${LAMBDA_TASK_ROOT}/services/resize/dist/index.js . COPY --from=installer /${LAMBDA_TASK_ROOT}/services/resize/package.json . CMD [ "index.handler" ]
优势
- 镜像体积大幅缩小,不需要复制整个内部包目录。
- 彻底避免路径解析问题,Lambda直接加载单文件即可运行。
验证与部署
修改后重新构建镜像:
docker build --platform linux/amd64 -f services/resize/Dockerfile . -t resizeservice --no-cache
可以先本地运行镜像测试依赖是否正常:
docker run -it resizeservice node -e "require('./index')"
用CDK部署时,直接使用DockerImageFunction引用镜像即可:
import { DockerImageFunction, DockerImageCode } from 'aws-cdk-lib/aws-lambda'; import { Construct } from 'constructs'; import { Stack, StackProps } from 'aws-cdk-lib'; export class ResizeLambdaStack extends Stack { constructor(scope: Construct, id: string, props?: StackProps) { super(scope, id, props); new DockerImageFunction(this, 'ResizeFunction', { code: DockerImageCode.fromImageAsset('./services/resize', { platform: 'linux/amd64', }), }); } }
内容的提问来源于stack exchange,提问作者Kevin
相关产品推荐
相关产品推荐

