6-5 bugfixnestjs服务端容器运行测试
验证镜像内容
运行镜像前,先验证镜像内部的文件结构是否正确:
# 运行镜像并执行 ls 命令查看目录结构
docker run --rm nest-starter:1.4 ls -la /home/node/app
# 预期输出:
# dist/
# node_modules/
# prisma/
# 查看 dist 目录内容
docker run --rm nest-starter:1.4 ls -la /home/node/app/dist
# 查看 Prisma 客户端
docker run --rm nest-starter:1.4 ls -la /home/node/app/prisma
# clients/ migrations/ schema.prisma
bash
--rm 参数表示容器运行完成后自动删除。
使用 Docker Compose 运行
创建 docker-compose.yml 文件,方便传递环境变量和配置:
services:
nest-app:
container_name: nest-app
image: nest-starter:1.4
restart: always
ports:
- "10090:3000"
env_file:
- .env
yaml
配置说明:
- 端口映射:
10090:3000将容器内部的 3000 端口映射到宿主机的 10090 端口。注意 3000 是默认配置文件中的端口(非 development 配置的 3030) - env_file:将
.env文件中的环境变量传递给容器 - restart: always:容器异常退出时自动重启
# 启动容器
docker compose up -d
# 查看容器状态
docker ps -a | grep nest-app
# 查看运行日志
docker logs -f nest-app
bash
提示:
grep命令仅在 macOS/Linux 上可用,Windows 用户请使用 Docker Desktop 查看容器状态和日志。
Bug #1:Prisma 客户端缺失
启动后日志报错:Cannot find module prisma/client/postgresql。
排查发现 prisma/clients 目录中只有 mysql 客户端,缺少 postgresql 客户端。原因是 build script 中 tenantMode 设为 false 时,未按 prisma.dbType 生成对应客户端。
修复方案:重构 build script,将客户端生成逻辑抽取为独立函数:
// build-clients.ts
const generateClients = async (result: PrismaConfig, type: string) => {
// 根据 type 生成对应的 Prisma 客户端
// type: 'postgresql' | 'mysql'
};
// 非 multiOM 模式下,默认生成 postgresql 客户端
const result = await readPrismaConfig(prismaFile);
await generateClients(result, 'postgresql');
typescript
修复后验证:
pnpm run build:clients
# 成功生成 postgresql 客户端
bash
Bug #2:Prisma Module 静态导入问题
即使只使用 postgresql,prisma.module.ts 顶部同时静态导入了 mysql 和 postgresql 的客户端:
import { MysqlClient } from './clients/mysql';
import { PgClient } from './clients/postgresql';
typescript
这意味着构建脚本中必须同时生成两种客户端,否则模块加载会报错。
修复方案:在 build script 中确保同时生成所有需要的客户端:
// 同时生成 postgresql 和 mysql 客户端
await generateClients(result, 'postgresql');
await generateClients(result, 'mysql');
typescript
如果数据库类型较多,可以使用数组循环生成。
Bug #3:配置文件读取失败
容器启动后报错:Cannot read property 'split' of undefined(database.module.ts 第 594 行)。
排查步骤:
- 使用
docker inspect验证环境变量是否正确传递到容器 - 在代码中添加
console.log确认parsedConfig的值 - 发现
parsedConfig为空对象{},说明.env文件未被读取
根本原因:utils/get-config.ts 使用 dotenv 读取项目根目录的 .env 文件,但在 Docker 容器中,环境变量通过 env_file 传递,不存在物理 .env 文件。
修复方案:兼容两种配置读取方式:
import fs from 'fs';
let parsedConfig: Record<string, string>;
if (!fs.existsSync('.env')) {
// 容器环境:直接使用 process.env
parsedConfig = process.env as Record<string, string>;
} else {
// 本地开发:读取 .env 文件
parsedConfig = dotenv.parse(fs.readFileSync('.env'));
}
// 后续使用 parsedConfig 读取配置
if (fs.existsSync('.env')) {
// 仅在 .env 文件存在时执行 dotenv 相关逻辑
}
typescript
使用 Volumes 挂载 .env 文件
除了 env_file 方式,还可以使用 volumes 将 .env 文件挂载到容器中:
services:
nest-app:
container_name: nest-app
image: nest-starter:1.7
restart: always
ports:
- "10090:3000"
volumes:
- ./.env:/home/node/app/.env:ro
yaml
这种方式下,应用可以直接读取容器内的 .env 文件。修改本地 .env 后重启容器即可生效,适合开发调试阶段使用。
验证挂载是否生效:
# 修改 .env 中的某个值(如 redis.password)
# 重启容器
docker compose up
# 查看日志,确认配置值已更新
docker logs nest-app
bash
容器网络隔离注意事项
容器内部的网络环境与宿主机隔离。配置文件中 localhost:5432 指向的是容器自身的网络,无法访问宿主机上运行的数据库服务。
解决方法:
- 使用宿主机 IP 地址(如
host.docker.internal) - 或将数据库也放入同一个 Docker 网络中,通过容器名访问
构建 Pnpm 静默模式
如果不希望在构建过程中看到 pnpm install 的详细日志输出,可以添加 --reporter=silent 参数:
RUN pnpm install --frozen-lockfile --reporter=silent
dockerfile
这样在构建镜像时,pnpm install 只会显示一行提示,不会输出大量日志信息。
完整的调试流程
1. docker compose up -> 观察错误日志
2. 定位错误位置 -> 找到具体文件和行号
3. 修复代码 -> 兼容容器环境
4. docker build -t xxx:1.N . -> 重新构建镜像
5. docker compose down -> 删除旧容器
6. 修改 docker-compose.yml -> 更新镜像版本号
7. docker compose up -> 验证修复结果
text
小结
- 使用
docker run --rm快速验证镜像内部文件结构 - 通过
docker compose管理容器运行,方便传递环境变量 - 容器环境中不存在物理
.env文件,需要兼容process.env读取方式 - Prisma 客户端需要在构建时生成所有被静态导入的客户端类型
- 容器网络隔离需要使用
host.docker.internal或 Docker 网络互联访问外部服务 - 使用
volumes挂载.env文件适合开发调试场景
↑