长安的Docker教程长安的Docker教程
首页
快速开始
编程指南
首页
快速开始
编程指南
  • 🎯 Docker入门篇

    • 快速开始
    • Docker是什么?
    • 为什么要用Docker?
    • 安装Docker
    • 第一个容器
  • 📦 Docker基础篇

    • Docker镜像详解
    • 容器操作详解
    • 编写Dockerfile
    • 数据卷(Volumes)
    • Docker网络
  • ⚡ Docker进阶篇

    • Docker Compose
    • 多阶段构建
    • Docker性能优化
    • Docker最佳实践
  • 🚀 实战项目

    • 实战:部署Node.js应用
    • 实战:搭建数据库环境
    • 实战:微服务架构
  • 💡 常见问题

    • 常见问题排查
    • 实用技巧

实战:部署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 --from=deps --chown=nodejs:nodejs /app/node_modules ./node_modules
COPY --chown=nodejs:nodejs . .

# 切换用户
USER nodejs

# 暴露端口
EXPOSE 3000

# 健康检查
HEALTHCHECK --interval=30s --timeout=3s --start-period=10s --retries=3 \
  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多容器编排
✅ 健康检查
✅ 日志管理
✅ 资源限制

学到的技能

  1. 多容器应用编排
  2. 服务间通信
  3. 数据持久化
  4. 反向代理配置
  5. 开发生产环境分离

🚀 下一步

学会部署Node.js了!下一章长安教你 搭建数据库环境!


💬 长安的实战经验:

这个实战项目是我真实项目的简化版。

实际生产中,我还会加上:

  • SSL证书(HTTPS)
  • 环境变量管理
  • CI/CD自动部署
  • 监控告警
  • 日志分析

但对新手来说,这个项目已经很完整了!

建议你:

  1. 先把这个项目跑起来
  2. 理解每个部分的作用
  3. 试着加自己的功能
  4. 遇到问题多Google,多思考

动手做,才能真正学会!加油!💪

在 GitHub 上编辑此页
Next
实战:搭建数据库环境