前置知识点:
Dockerfile: Dockerfile需要先了解
docker-compose: docker-componse 需要先了解

1. 创建Dockerfile
采用多阶段构建方式,包含构建阶段和运行阶段:
- 构建阶段:安装所有依赖并构建项目
- 运行阶段:仅安装生产依赖,复制构建产物
- 暴露3000端口,配置容器启动命令
ts
# 构建阶段
# 构建阶段
FROM node:20-alpine AS builder
WORKDIR /app
# 复制package.json和package-lock.json
COPY package*.json ./
# 安装依赖
RUN npm ci
# 复制源代码
COPY . .
# 构建项目
RUN npm run build
# 运行阶段
FROM node:20-alpine AS runner
WORKDIR /app
# 复制package.json和package-lock.json
COPY package*.json ./
# 安装生产依赖
RUN npm ci --only=production
# 复制构建产物
COPY --from=builder /app/dist ./dist
# 复制GraphQL类型定义文件
COPY src/graphql ./src/graphql
# 复制环境变量示例文件(如果有)
COPY .env.development ./.env
# 暴露端口
EXPOSE 3000
# 运行应用
CMD ["node", "dist/main"]
2. 创建docker-compose.yml
配置了完整的服务栈:
- 应用服务:使用构建的Docker镜像
- MySQL数据库:版本8.0,配置持久化卷
- Redis服务:版本7.0-alpine,配置持久化卷
- 所有服务通过Docker网络连接
ts
version: '3.8'
services:
app:
build: .
ports:
- '3000:3000'
environment:
- NODE_ENV=production
- PORT=3000
- DB_HOST=host.docker.internal
- DB_PORT=3306
- DB_USER=root
- DB_PASSWORD=123456
- DB_NAME=care_of_children
- REDIS_HOST=host.docker.internal
- REDIS_PORT=6379
networks:
- app-network
networks:
app-network:
driver: bridge
3. 创建.dockerignore
忽略不必要的文件和目录,加快构建速度:
- 依赖目录(node_modules)
- 构建产物(dist)
- 日志目录(logs)
- 环境变量文件(.env)
- SQL文件和图片文件
ts
# 依赖目录
node_modules/
# 构建输出目录
dist/
# 日志目录
logs/
# 环境变量文件(只保留示例文件)
.env
.env.local
.env.*.local
# IDE配置
.vscode/
.idea/
# 测试文件
test/
# 操作系统文件
.DS_Store
Thumbs.db
# 包管理器锁文件(可选,根据需要)
# package-lock.json
# yarn.lock
# pnpm-lock.yaml
# SQL文件
*.sql
# 图片文件
*.png
*.jpg
*.jpeg
*.gif
4. 测试与优化
- 成功构建Docker镜像
- 解决了容器启动问题(移除了对cross-env的依赖)
- 确保应用能正常运行
使用方法
- 构建镜像 :
docker build -t care-of-children-server . - 运行单个容器 :
docker run -p 3000:3000 care-of-children-server - 启动完整服务栈 :
docker-compose up
通过以上配置,您的NestJS项目已成功Docker化,可以在任何支持Docker的环境中轻松部署和运行。
以下是我遇到的问题
1. 发现端口映射问题
通过 docker ps 命令发现,之前的应用容器没有正确的端口映射 配置,只有 3000/tcp(内部端口),没有映射到物理机的端口(如 0.0.0.0:3000->3000/tcp)。
2. 解决端口冲突
发现 MySQL (3306端口) 和 Redis (6379端口) 已经在物理机上运行(容器名为 sweet_blackburn 和 redis),导致 docker-compose 尝试启动新实例时出现端口冲突。
3. 修改 docker-compose.yml
- 移除了内部服务:删除了 docker-compose.yml 中的 MySQL 和 Redis 服务配置
- 更新环境变量 :将
DB_HOST和REDIS_HOST改为host.docker.internal,让容器连接到物理机上已运行的数据库和Redis服务 - 保留端口映射 :确保应用容器的
3000:3000端口映射配置正确
4. 配置应用监听地址
修改了 src/main.ts 文件,让 NestJS 应用监听 0.0.0.0 地址:
typescript
await app.listen(process.env.PORT ?? 3000, '0.0.0.0');
这确保应用接受来自所有网络接口的连接,而不仅仅是容器内部。
5. 重新启动应用容器
使用 docker-compose up -d 重新启动应用容器,确保所有配置生效。
最终结果
现在应用容器正确映射了端口(0.0.0.0:3000->3000/tcp),并且能够:
- 通过
http://localhost:3000从物理机访问 - 连接到物理机上已运行的 MySQL 和 Redis 服务
- 提供 Swagger API 文档(
http://localhost:3000/api)和 GraphQL Playground(http://localhost:3000/graphql)
核心原理
容器与物理机通信的关键在于:
- 端口映射:将容器端口暴露到物理机
- 监听地址 :应用必须监听
0.0.0.0而非仅localhost - 服务连接 :使用
host.docker.internal让容器访问物理机上的服务