You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 08:14:52