使用pnpm构建Docker容器时node_modules文件为0字节的问题
问题描述
我有一个基于TypeScript的pnpm monorepo,本地开发环境运行正常。部署时尝试将服务端Docker化,遇到以下问题:镜像构建成功,node_modules文件夹显示所有依赖项,但每个文件都是0字节,Docker容器启动后退出并报错:
Error [ERR_MODULE_NOT_FOUND]: Cannot find package 'express' imported from /prod/server/src/main.js at new NodeError (node:internal/errors:399:5) at packageResolve (node:internal/modules/esm/resolve:794:9) at moduleResolve (node:internal/modules/esm/resolve:843:20) at defaultResolve (node:internal/modules/esm/resolve:1058:11) at nextResolve (node:internal/modules/esm/hooks:654:28) at Hooks.resolve (node:internal/modules/esm/hooks:309:30) at ESMLoader.resolve (node:internal/modules/esm/loader:312:26) at ESMLoader.getModuleJob (node:internal/modules/esm/loader:172:38) at ModuleWrap.<anonymous> (node:internal/modules/esm/module_job:76:40) at link (node:internal/modules/esm/module_job:75:36) { code: 'ERR_MODULE_NOT_FOUND' }
我怀疑问题和pnpm deploy命令有关,因为本地按install > build > node dist/main.js的顺序手动执行命令,能正常构建并运行服务端,但尚未确定具体原因。

相关文件信息
由于是monorepo,参考了pnpm官方的Docker化monorepo指南。
Dockerfile
FROM node:19-slim AS base ARG service ENV PNPM_HOME="/pnpm" ENV PATH="$PNPM_HOME:$PATH" RUN corepack enable COPY . /usr/application FROM base AS deps ARG service WORKDIR /usr/application/${service} RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --prod --frozen-lockfile FROM base as build ARG service WORKDIR /usr/application/${service} RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --frozen-lockfile RUN pnpm prisma generate RUN pnpm run build RUN pnpm deploy --filter=${service} --prod /prod/${service} FROM base as server COPY --from=deps /usr/application/${service}/node_modules /prod/${service}/node_modules COPY --from=build /prod/${service}/dist /prod/${service} COPY --from=build /prod/${service}/package.json /prod/${service} WORKDIR /prod/${service} EXPOSE 3000 CMD [ "pnpm", "run", "start:prod" ]
构建命令:
docker build -t mono-image . --file Dockerfile --build-arg="service=server"
镜像构建正常,但Docker Desktop启动容器时出现上述错误。
pnpm-workspace.yaml
packages: - server - app - shared
服务端工作区package.json
{ "name": "server", "version": "1.0.0", "description": "", "main": "index.js", "engines": { "node": ">= 19 < 20" }, "type": "module", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "build": "./node_modules/.bin/tsc -p ./tsconfig.json", "start": "tsx src/main.ts", "start:dev": "tsx watch src/main.ts", "start:prod": "node src/main.js" }, "keywords": [], "author": "", "license": "ISC", "devDependencies": { "@types/connect-pg-simple": "^7.0.1", "@types/cors": "^2.8.14", "@types/express": "^4.17.18", "@types/express-session": "^1.17.8", "@types/node": "^18.17.4", "@types/passport": "^1.0.13", "prisma": "^5.3.1", "typescript": "^5.3.2" }, "dependencies": { "connect-pg-simple": "^9.0.0", "cors": "^2.8.5", "express": "^4.18.2", "express-session": "^1.17.3", "iso-3166-2": "^1.0.0", "node-fetch": "^3.3.2", "passport": "^0.6.0", "passport-magic-link": "^2.1.0", "passport-magic-login": "^1.2.2", "pg": "^8.11.3", "postmark": "3.0.0", "reflect-metadata": "^0.1.13", "shared": "workspace:^", "simple-xml-to-json": "^1.1.7", "socket.io": "^4.7.2", "tslib": "~2.6", "tsx": "^3.12.10" } }
项目根目录package.json
{ "name": "pnpm-monorepo", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "start": "pnpm --stream -r start", "start:dev": "pnpm --stream -r run start:dev" }, "dependencies": { "app": "workspace:*", "server": "workspace:*" }, "keywords": [], "author": "", "license": "ISC", "devDependencies": { "@prisma/client": "^5.3.1", "prisma": "^5.3.1", "typescript": "^5.0.2" } }
服务端tsconfig.json
{ "compilerOptions": { "target": "ES6", "useDefineForClassFields": true, "lib": ["ES2020", "DOM", "DOM.Iterable"], "module": "Node16", "skipLibCheck": true, "outDir": "dist", "esModuleInterop": true, // added for error related to "export module = " /* Bundler mode */ // "allowImportingTsExtensions": true, "allowSyntheticDefaultImports": true, // "resolveJsonModule": true, "moduleResolution": "Node16", "isolatedModules": true, "noEmitOnError": false, /* Linting */ "strict": true, "noUnusedLocals": true, "noUnusedParameters": true, "noFallthroughCasesInSwitch": true }, "include": ["src", "config.ts"], "exclude": ["node_modules", "test", "dist", "**/*spec.ts"] // "references": [{ "path": "./tsconfig.node.json" }] }
另外,欢迎针对2023年Express服务给出优化建议。
内容的提问来源于stack exchange,提问作者Dan Engel
相关产品推荐
相关产品推荐

