实战:部署Node.js应用
嘿!长安来带你实战了!今天我们从零开始,部署一个完整的Node.js应用!🚀
🎯 项目目标
我们要部署一个完整的Web应用,包括:
- Node.js后端 (Express)
- MongoDB数据库
- Redis缓存
- Nginx反向代理
📁 项目结构
nodejs-docker-app/
├── backend/
│ ├── src/
│ │ ├── index.js
│ │ ├── routes/
│ │ └── models/
│ ├── package.json
│ ├── Dockerfile
│ └── .dockerignore
├── nginx/
│ └── nginx.conf
├── docker-compose.yml
├── .env.example
└── README.md
📝 第一步:创建Node.js应用
backend/package.json
{
"name": "nodejs-docker-app",
"version": "1.0.0",
"description": "长安的Node.js Docker实战项目",
"main": "src/index.js",
"scripts": {
"start": "node src/index.js",
"dev": "nodemon src/index.js"
},
"dependencies": {
"express": "^4.18.2",
"mongoose": "^7.5.0",
"redis": "^4.6.7",
"cors": "^2.8.5",
"dotenv": "^16.3.1"
},
"devDependencies": {
"nodemon": "^3.0.1"
}
}
backend/src/index.js
const express = require('express');
const mongoose = require('mongoose');
const redis = require('redis');
const cors = require('cors');
require('dotenv').config();
const app = express();
const PORT = process.env.PORT || 3000;
// 中间件
app.use(cors());
app.use(express.json());
// MongoDB连接
const MONGO_URI = process.env.MONGO_URI || 'mongodb://mongo:27017/myapp';
mongoose.connect(MONGO_URI)
.then(() => console.log('✅ MongoDB连接成功'))
.catch(err => console.error('❌ MongoDB连接失败:', err));
// Redis连接
const redisClient = redis.createClient({
url: process.env.REDIS_URL || 'redis://redis:6379'
});
redisClient.connect()
.then(() => console.log('✅ Redis连接成功'))
.catch(err => console.error('❌ Redis连接失败:', err));
// 定义模型
const User = mongoose.model('User', {
name: String,
email: String,
createdAt: { type: Date, default: Date.now }
});
// 路由
app.get('/', (req, res) => {
res.json({
message: '嘿!我是长安的Docker应用!',
timestamp: new Date().toISOString()
});
});
// 健康检查
app.get('/health', (req, res) => {
res.json({ status: 'healthy', service: 'nodejs-app' });
});
// 创建用户
app.post('/users', async (req, res) => {
try {
const user = new User(req.body);
await user.save();
// 清除缓存
await redisClient.del('users:all');
res.status(201).json({ success: true, user });
} catch (error) {
res.status(500).json({ success: false, error: error.message });
}
});
// 获取所有用户(带Redis缓存)
app.get('/users', async (req, res) => {
try {
// 先查缓存
const cached = await redisClient.get('users:all');
if (cached) {
console.log('📦 从缓存获取数据');
return res.json({ success: true, users: JSON.parse(cached), cached: true });
}
// 缓存未命中,查数据库
console.log('💾 从数据库获取数据');
const users = await User.find();
// 存入缓存(5分钟)
await redisClient.setEx('users:all', 300, JSON.stringify(users));
res.json({ success: true, users, cached: false });
} catch (error) {
res.status(500).json({ success: false, error: error.message });
}
});
// 获取缓存统计
app.get('/cache/stats', async (req, res) => {
try {
const info = await redisClient.info();
res.json({ success: true, info });
} catch (error) {
res.status(500).json({ success: false, error: error.message });
}
});
// 启动服务器
app.listen(PORT, '0.0.0.0', () => {
console.log(`🚀 服务器运行在 http://0.0.0.0:${PORT}`);
console.log(`👨💻 长安祝你学习愉快!`);
});
// 优雅关闭
process.on('SIGTERM', async () => {
console.log('收到SIGTERM信号,优雅关闭...');
await mongoose.connection.close();
await redisClient.quit();
process.exit(0);
});
🐳 第二步:编写Dockerfile
backend/Dockerfile
# 阶段1:依赖
FROM node:18-alpine AS deps
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
# 阶段2:开发环境(可选)
FROM node:18-alpine AS development
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
CMD ["npm", "run", "dev"]
# 阶段3:生产环境
FROM node:18-alpine AS production
WORKDIR /app
# 创建非root用户
RUN addgroup -g 1001 -S nodejs && \
adduser -S nodejs -u 1001
# 复制依赖
COPY /app/node_modules ./node_modules
COPY . .
# 切换用户
USER nodejs
# 暴露端口
EXPOSE 3000
# 健康检查
HEALTHCHECK \
CMD node -e "require('http').get('http://localhost:3000/health', (r) => {process.exit(r.statusCode === 200 ? 0 : 1)})"
# 启动应用
CMD ["node", "src/index.js"]
backend/.dockerignore
node_modules
npm-debug.log
.env
.git
.DS_Store
*.md
.vscode
.idea
coverage
dist
build
🔧 第三步:配置Nginx
nginx/nginx.conf
events {
worker_connections 1024;
}
http {
upstream backend {
server backend:3000;
}
# 日志格式
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
server {
listen 80;
server_name localhost;
# 日志
access_log /var/log/nginx/access.log main;
error_log /var/log/nginx/error.log;
# 客户端最大body大小
client_max_body_size 10M;
# 代理到后端
location / {
proxy_pass http://backend;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_cache_bypass $http_upgrade;
# 超时设置
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
}
# 健康检查
location /health {
proxy_pass http://backend/health;
access_log off;
}
}
}
🎨 第四步:Docker Compose配置
docker-compose.yml
version: '3.8'
services:
# Nginx反向代理
nginx:
image: nginx:alpine
container_name: myapp-nginx
ports:
- "80:80"
volumes:
- ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
- nginx-logs:/var/log/nginx
depends_on:
backend:
condition: service_healthy
networks:
- frontend
restart: unless-stopped
# Node.js后端
backend:
build:
context: ./backend
target: production
container_name: myapp-backend
environment:
NODE_ENV: production
PORT: 3000
MONGO_URI: mongodb://mongo:27017/myapp
REDIS_URL: redis://redis:6379
depends_on:
mongo:
condition: service_healthy
redis:
condition: service_started
networks:
- frontend
- backend
restart: unless-stopped
healthcheck:
test: ["CMD", "node", "-e", "require('http').get('http://localhost:3000/health')"]
interval: 30s
timeout: 3s
retries: 3
start_period: 40s
deploy:
resources:
limits:
cpus: '1'
memory: 512M
reservations:
cpus: '0.5'
memory: 256M
# MongoDB数据库
mongo:
image: mongo:6
container_name: myapp-mongo
environment:
MONGO_INITDB_DATABASE: myapp
volumes:
- mongo-data:/data/db
- mongo-config:/data/configdb
networks:
- backend
restart: unless-stopped
healthcheck:
test: ["CMD", "mongosh", "--eval", "db.adminCommand('ping')"]
interval: 10s
timeout: 5s
retries: 5
start_period: 40s
# Redis缓存
redis:
image: redis:7-alpine
container_name: myapp-redis
command: redis-server --appendonly yes
volumes:
- redis-data:/data
networks:
- backend
restart: unless-stopped
networks:
frontend:
driver: bridge
backend:
driver: bridge
volumes:
mongo-data:
mongo-config:
redis-data:
nginx-logs:
docker-compose.dev.yml(开发环境)
version: '3.8'
services:
backend:
build:
target: development
volumes:
- ./backend:/app
- /app/node_modules
ports:
- "3000:3000" # 直接暴露端口,方便调试
- "9229:9229" # Node.js调试端口
environment:
NODE_ENV: development
command: npm run dev
mongo:
ports:
- "27017:27017" # 暴露端口,方便使用MongoDB Compass
redis:
ports:
- "6379:6379" # 暴露端口,方便使用Redis CLI
.env.example
# 应用配置
NODE_ENV=production
PORT=3000
# MongoDB
MONGO_URI=mongodb://mongo:27017/myapp
# Redis
REDIS_URL=redis://redis:6379
🚀 第五步:启动应用
开发环境
# 复制环境变量
cp .env.example .env
# 启动开发环境
docker-compose -f docker-compose.yml -f docker-compose.dev.yml up --build
# 查看日志
docker-compose logs -f backend
生产环境
# 启动生产环境
docker-compose up -d --build
# 查看状态
docker-compose ps
# 查看日志
docker-compose logs -f
🧪 第六步:测试应用
1. 健康检查
curl http://localhost/health
2. 创建用户
curl -X POST http://localhost/users \
-H "Content-Type: application/json" \
-d '{
"name": "长安",
"email": "changan@example.com"
}'
3. 获取用户列表
# 第一次(从数据库)
curl http://localhost/users
# 第二次(从Redis缓存)
curl http://localhost/users
4. 查看缓存统计
curl http://localhost/cache/stats
📊 监控和维护
查看资源使用
docker stats
查看日志
# 所有服务
docker-compose logs
# 特定服务
docker-compose logs -f backend
# 最后100行
docker-compose logs --tail=100
进入容器调试
# 进入backend
docker-compose exec backend sh
# 进入mongo
docker-compose exec mongo mongosh
# 进入redis
docker-compose exec redis redis-cli
备份数据
# 备份MongoDB
docker-compose exec mongo mongodump --out=/tmp/backup
docker cp myapp-mongo:/tmp/backup ./mongo-backup
# 备份Redis
docker-compose exec redis redis-cli SAVE
docker cp myapp-redis:/data/dump.rdb ./redis-backup.rdb
🔄 更新应用
# 1. 拉取最新代码
git pull
# 2. 重新构建镜像
docker-compose build backend
# 3. 滚动更新(零停机)
docker-compose up -d --no-deps --build backend
# 4. 查看新容器状态
docker-compose ps
💡 优化建议
1. 使用多实例(负载均衡)
services:
backend:
deploy:
replicas: 3 # 运行3个实例
或手动扩容:
docker-compose up -d --scale backend=3
2. 添加监控
services:
# cAdvisor监控
cadvisor:
image: gcr.io/cadvisor/cadvisor
ports:
- "8080:8080"
volumes:
- /:/rootfs:ro
- /var/run:/var/run:ro
- /sys:/sys:ro
- /var/lib/docker/:/var/lib/docker:ro
3. 配置日志收集
services:
backend:
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
💡 小结
今天长安带你部署了一个完整的Node.js应用:
完成的功能
✅ Express API服务器
✅ MongoDB数据库集成
✅ Redis缓存
✅ Nginx反向代理
✅ Docker多容器编排
✅ 健康检查
✅ 日志管理
✅ 资源限制
学到的技能
- 多容器应用编排
- 服务间通信
- 数据持久化
- 反向代理配置
- 开发生产环境分离
🚀 下一步
学会部署Node.js了!下一章长安教你 搭建数据库环境!
💬 长安的实战经验:
这个实战项目是我真实项目的简化版。
实际生产中,我还会加上:
- SSL证书(HTTPS)
- 环境变量管理
- CI/CD自动部署
- 监控告警
- 日志分析
但对新手来说,这个项目已经很完整了!
建议你:
- 先把这个项目跑起来
- 理解每个部分的作用
- 试着加自己的功能
- 遇到问题多Google,多思考
动手做,才能真正学会!加油!💪
