从零掌握:用 Docker 和 Docker Compose 部署 Node.js 微服务
引言:为何选择容器化部署?
在微服务架构中,Node.js 以其轻量级、高并发和非阻塞 I/O 的特性成为热门选择。然而,传统部署方式(如直接在物理机或虚拟机上运行)面临环境不一致、依赖冲突、扩展困难等问题。容器化技术(如 Docker)通过将应用及其依赖打包为独立单元,解决了这些问题:
- 环境一致性:开发、测试、生产环境完全一致。
- 资源隔离:每个微服务独立运行,避免依赖冲突。
- 快速扩展:通过水平扩展应对流量高峰。
- 简化运维:结合 Docker Compose 定义多服务依赖关系。
本文将通过一个完整的 Node.js 微服务示例,演示如何使用 Docker 和 Docker Compose 实现从开发到部署的全流程。
一、Docker 基础:构建 Node.js 微服务的容器镜像
1.1 编写 Node.js 微服务代码
假设我们有一个简单的 RESTful API 服务,使用 Express 框架实现:
// app.jsconst express = require('express');const app = express();const PORT = 3000;app.get('/', (req, res) => {res.send('Hello from Node.js Microservice!');});app.listen(PORT, () => {console.log(`Server running on port ${PORT}`);});
依赖文件 package.json:
{"name": "node-microservice","version": "1.0.0","main": "app.js","scripts": {"start": "node app.js"},"dependencies": {"express": "^4.18.2"}}
1.2 创建 Dockerfile
Dockerfile 是构建镜像的脚本,定义如何将代码打包为容器镜像:
# 使用官方 Node.js 镜像作为基础FROM node:18-alpine# 设置工作目录WORKDIR /usr/src/app# 复制 package.json 和 package-lock.json(如果有)COPY package*.json ./# 安装依赖RUN npm install# 复制应用代码COPY . .# 暴露端口EXPOSE 3000# 定义启动命令CMD ["npm", "start"]
关键点解释:
FROM node:18-alpine:使用轻量级的 Alpine Linux 基础镜像,减少镜像体积。WORKDIR:设置容器内的工作目录,避免使用绝对路径。COPY:分步复制文件,利用 Docker 缓存机制加速构建(依赖变化时只需重新运行npm install)。EXPOSE:声明容器监听的端口(非必须,但便于文档化)。CMD:指定容器启动时运行的命令。
1.3 构建 Docker 镜像
在项目根目录执行以下命令:
docker build -t node-microservice .
-t:指定镜像名称和标签(如node-microservice:latest)。.:表示使用当前目录的 Dockerfile。
构建完成后,可通过 docker images 查看镜像列表。
1.4 运行容器
启动容器并映射端口:
docker run -p 4000:3000 -d node-microservice
-p 4000:3000:将宿主机的 4000 端口映射到容器的 3000 端口。-d:后台运行容器。
访问 http://localhost:4000,应看到 Hello from Node.js Microservice!。
二、Docker Compose:管理多服务依赖
实际项目中,微服务通常依赖数据库、缓存等其他服务。Docker Compose 通过 YAML 文件定义多容器应用,简化依赖管理。
2.1 添加 Redis 依赖
修改 app.js,添加 Redis 缓存:
const express = require('express');const redis = require('redis');const app = express();const PORT = 3000;// 创建 Redis 客户端const client = redis.createClient({url: 'redis://redis:6379' // 通过服务名连接});client.on('error', (err) => console.log('Redis Client Error', err));await client.connect();app.get('/', async (req, res) => {await client.set('key', 'value');const value = await client.get('key');res.send(`Hello from Node.js! Redis value: ${value}`);});app.listen(PORT, () => {console.log(`Server running on port ${PORT}`);});
更新 package.json 添加 Redis 依赖:
"dependencies": {"express": "^4.18.2","redis": "^4.6.0"}
2.2 创建 docker-compose.yml
定义 Node.js 服务和 Redis 服务的依赖关系:
version: '3.8'services:node-app:build: .ports:- "4000:3000"depends_on:- redisenvironment:- NODE_ENV=productionredis:image: redis:alpineports:- "6379:6379"
关键点解释:
version:指定 Compose 文件版本(需与 Docker 引擎兼容)。services:定义多个服务。node-app:使用当前目录的 Dockerfile 构建。redis:直接使用官方 Redis 镜像。
depends_on:声明服务启动顺序(但不会等待 Redis 完全就绪,需应用层处理)。environment:设置环境变量(如生产模式)。
2.3 启动多容器应用
在项目根目录执行:
docker-compose up -d
-d:后台运行。
访问 http://localhost:4000,应看到 Redis 缓存的值。
三、进阶优化与最佳实践
3.1 多阶段构建:减小镜像体积
优化 Dockerfile,分离构建环境和生产环境:
# 构建阶段FROM node:18-alpine AS builderWORKDIR /usr/src/appCOPY package*.json ./RUN npm installCOPY . .RUN npm run build # 如果有构建步骤(如 TypeScript)# 生产阶段FROM node:18-alpineWORKDIR /usr/src/appCOPY --from=builder /usr/src/app/node_modules ./node_modulesCOPY --from=builder /usr/src/app .EXPOSE 3000CMD ["npm", "start"]
3.2 使用 .dockerignore 排除无关文件
创建 .dockerignore 文件,避免将 node_modules、日志文件等无关内容复制到镜像中:
node_modules.git.envlogs/
3.3 健康检查与重启策略
在 docker-compose.yml 中添加健康检查和自动重启:
services:node-app:build: .ports:- "4000:3000"healthcheck:test: ["CMD", "curl", "-f", "http://localhost:3000"]interval: 30stimeout: 10sretries: 3restart: unless-stopped
3.4 环境变量与配置管理
使用环境变量文件(.env)管理敏感配置:
# .envREDIS_URL=redis://redis:6379NODE_ENV=production
在 docker-compose.yml 中引用:
services:node-app:env_file:- .env
四、常见问题与解决方案
4.1 端口冲突
问题:宿主机端口已被占用。
解决:修改 docker-compose.yml 中的 ports 映射,如 "4001:3000"。
4.2 依赖安装失败
问题:npm install 报错。
解决:
- 确保
package.json和package-lock.json版本一致。 - 检查网络连接(可尝试使用国内镜像源)。
4.3 服务启动顺序问题
问题:Node.js 服务启动时 Redis 未就绪。
解决:
- 在应用代码中添加重试逻辑(如
client.connect()失败时延迟重试)。 - 使用
wait-for-it.sh等工具(需自定义脚本)。
五、总结与扩展
通过本文,您已掌握:
- 使用 Docker 构建 Node.js 微服务的容器镜像。
- 通过 Docker Compose 管理多服务依赖。
- 优化镜像体积、健康检查和配置管理。
下一步建议:
- 学习 Kubernetes 编排容器化应用(适合大规模生产环境)。
- 探索 CI/CD 流水线(如 GitHub Actions)自动化构建和部署。
- 研究服务网格(如 Istio)管理微服务通信。
容器化是现代云原生开发的核心技能,掌握 Docker 和 Docker Compose 将显著提升开发效率和部署可靠性。