Skip to content

项目部署

⏱️最后更新: 2025/07/27

下载配套资源

markdown
├── docker-compose.yml # 根目录(与 nodejs 目录同级)
├── .env # 环境配置文件(根目录)
└── nodejs/ # Node.js 项目目录
├────── Dockerfile # 优化后的 Dockerfile(大写)
├────── .dockerignore # 忽略冗余文件
├────── package.json # 项目依赖配置
├────── src/ # 代码目录
├────────── index.js # 主程序
├────── config/ # 配置目录(如 allowed-hosts.json)
├────────── allowed-hosts.json # 白名单主机列表(whitelist 模式用)
└── nginx/ # 新增 Nginx 目录
├────── default.conf # Nginx 反向代理配置
├────── logs/ # 可选:Nginx 日志目录(挂载到主机)

测试项目

src/index.js

js
"use strict";

const express = require("express");

// Constants
const PORT = 3001;
const HOST = "0.0.0.0";

// App
const app = express();
// 基础路由 - 欢迎信息
app.get("/", (req, res) => {
  res.json({
    message: "欢迎使用简单Node.js服务!",
    endpoints: [
      { path: "/", description: "欢迎页面" },
      { path: "/api/greet", description: "个性化问候" },
      { path: "/api/time", description: "获取服务器时间" },
      { path: "/api/health", description: "服务健康检查" },
    ],
  });
});

// 个性化问候API
app.get("/api/greet", (req, res) => {
  const name = req.query.name || "朋友";
  res.json({
    message: `你好,${name}!`,
    timestamp: new Date().toISOString(),
  });
});

// 服务器时间API
app.get("/api/time", (req, res) => {
  res.json({
    serverTime: new Date().toISOString(),
    timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
  });
});

// 健康检查API
app.get("/api/health", (req, res) => {
  res.json({
    status: "运行正常",
    uptime: process.uptime(),
    memoryUsage: process.memoryUsage(),
    timestamp: new Date().toISOString(),
  });
});
// 新增健康检查端点
app.get("/api/health", (req, res) => {
  res.json({ status: "ok", timestamp: Date.now() });
});

app.listen(PORT, HOST, () => {
  console.log(`Running on http://${HOST}:${PORT}`);
});

package.json

json
{
  "name": "docker_web_app",
  "version": "1.0.0",
  "description": "Node.js on Docker",
  "author": "First Last <first.last@example.com>",
  "main": "src/index.js",
  "scripts": {
    "start": "node src/index.js"
  },
  "dependencies": {
    "express": "^4.16.1"
  }
}

创建部署配置文件

创建 default.conf

添加 Nginx 反向代理 ,将/api 路径的请求转发到 Node.js 服务

bash
# nginx/default.conf
server {
    listen 80;                          # 监听主机80端口(HTTP默认)
    server_name localhost;               # 可修改为你的域名(如example.com)

    # 核心:反向代理所有/api开头的请求到Node.js服务
    location /api/ {
        proxy_pass http://nodejs:3001/;  # 转发到Node.js服务的3001端口(保持/api路径)
        proxy_set_header Host $host;          # 传递原始主机名(用于Node.js识别请求来源)
        proxy_set_header X-Real-IP $remote_addr;  # 传递客户端真实IP
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;  # 传递代理链IP
        proxy_set_header X-Forwarded-Proto $scheme;  # 传递原始协议(http/https)

        proxy_connect_timeout 10s;       # 连接超时时间(根据需求调整)
        proxy_read_timeout 10s;          # 读取超时时间
        proxy_send_timeout 10s;          # 发送超时时间
    }

    # 可选:处理静态文件(若有前端项目,可添加此配置)
    # location / {
    #     root /usr/share/nginx/html;    # 静态文件目录(容器内路径)
    #     index index.html index.htm;    # 默认索引文件
    #     try_files $uri $uri/ /index.html;  # 单页应用(SPA)配置
    # }

    # 错误页面配置(可选)
    error_page 500 502 503 504 /50x.html;
    location = /50x.html {
        root /usr/share/nginx/html;      # 错误页面路径(容器内)
    }
}

创建 Dockerfile

检查主机网络连接

bash
ping registry.npmmirror.com  # 主机能否访问淘宝镜像源?
curl -I https://registry.npmmirror.com  # 能否获取响应?
FROM node:18-alpine

# 设置合理的工作目录(避免覆盖系统文件)
WORKDIR /app

# 先复制依赖文件(利用Docker缓存,加速构建)
COPY package*.json ./
RUN npm install --omit=dev --registry=https://registry.npmmirror.com

# 复制所有后端代码(包括src目录)
COPY . .

# 暴露后端端口(与docker-compose.yml一致)
EXPOSE 3001

# 启动命令(相对路径,正确指向src/index.js)
CMD ["node", "src/index.js"]

创建 docker-compose.yml

yml
# docker-compose.yml(根目录)
services:
  # Node.js服务(保持不变,去掉主机端口映射)
  nodejs:
    build:
      context: ./nodejs
      dockerfile: Dockerfile
    restart: unless-stopped
    ports:
      - "3001:3001"

  # 新增Nginx服务(反向代理)
  nginx:
    image: nginx:alpine # 使用轻量的Alpine版本
    ports:
      - "80:80" # 主机80端口映射到容器80端口(对外暴露)
    volumes:
      - ./nginx/default.conf:/etc/nginx/conf.d/default.conf:ro # 挂载自定义配置(只读)
      - ./nginx/logs:/var/log/nginx:rw # 可选:挂载日志到主机(方便查看)
    depends_on:
      - nodejs # 确保Nginx在Node.js之后启动
    restart: unless-stopped

启动与验证

1. 构建并启动服务

bash
# 格式化文件
sudo dos2unix .env
sudo dos2unix ./nodejs/Dockerfile
cd ..
# 常规
sudo docker-compose up -d
# --build:强制重新构建镜像(若修改了Dockerfile)
sudo docker-compose up -d --build

2. 验证服务状态

  • 健康检查curl http://localhost/api/health,应返回status: "ok"
  • 代理功能curl http://localhost/api/,应返回Hello world
  • 容器状态docker-compose ps,查看nodejs服务的State是否为Up (healthy)(健康检查通过)。
GET 1. 测试健康检查接口

路径GET /api/api/health

预期行为 验证服务健康状态,返回运行状态、服务器信息及资源使用情况

请求示例 GET http://localhost:3000/api/health

返回示例 200 OK

json
{
  "status": "运行正常",
  "uptime": 123.45,
  "memoryUsage": {
    "rss": 12345678,
    "heapTotal": 1234567,
    "heapUsed": 123456,
    "external": 12345
  },
  "timestamp": "2024-05-20T10:00:00.000Z"
}
返回结果
状态码状态码含义说明数据模型
200OK服务正常运行HealthResponse

验证点:

  1. HTTP 状态码必须为 200
  2. status字段值为"运行正常"
  3. uptime字段为大于 0 的数值类型
  4. memoryUsage对象包含四个数值字段(rss, heapTotal, heapUsed, external)
  5. timestamp为有效的 ISO 8601 格式

GET 2. 测试个性化问候接口

路径GET /api/api/greet

参数

名称位置类型必选说明
namequerystring问候对象名称,默认为"朋友"

预期行为 根据提供的名称返回个性化问候语

请求示例 GET http://localhost:3000/api/greet?name=Alice

返回示例 200 OK

json
{
  "message": "你好,Alice!",
  "timestamp": "2024-05-20T10:00:00.000Z"
}
返回结果
状态码状态码含义说明数据模型
200OK成功返回问候语GreetResponse

验证点:

  1. 无 name 参数时,默认返回"你好,朋友!"
  2. 有 name 参数时,返回对应的个性化问候
  3. 响应包含有效的 ISO 时间戳
  4. 特殊字符输入需正确转义处理
  5. XSS 攻击尝试应被过滤或转义

GET 3. 测试代理接口(模拟示例)

路径GET /api/api/proxy

参数

名称位置类型必选说明
targetquerystring要代理的目标 URL

预期行为 模拟代理请求到目标 URL 并返回响应指标

请求示例 GET http://localhost:3000/api/proxy?target=https://registry.npmjs.org

返回示例 200 OK

json
{
  "url": "https://registry.npmjs.org",
  "latency": "100ms",
  "status": 1,
  "statusCode": 200,
  "timestamp": "2024-05-20T10:00:00.000Z"
}
返回结果
状态码状态码含义说明数据模型
200OK代理请求成功ProxyResponse
400Bad Request缺少必要参数ErrorResponse
502Bad Gateway目标服务不可达ErrorResponse

验证点:

  1. 必须提供 target 参数,否则返回 400 错误
  2. 成功请求返回的 status 应为 1
  3. statusCode 反映目标服务的实际 HTTP 状态码
  4. latency 字段格式为"数字+ms"
  5. 无效 URL 应返回 502 错误
  6. 响应时间不超过设定的超时阈值(如 3000ms)

GET 4. 测试服务器时间接口

路径GET /api/api/time

预期行为 返回服务器当前时间和时区信息

请求示例 GET http://localhost:3000/api/time

返回示例 200 OK

json
{
  "serverTime": "2024-05-20T10:00:00.000Z",
  "timezone": "Asia/Shanghai"
}
返回结果
状态码状态码含义说明数据模型
200OK成功返回时间信息TimeResponse

验证点:

  1. serverTime 为有效的 ISO 8601 格式
  2. timezone 为有效的 IANA 时区标识符
  3. 服务器时间与客户端时间偏差在合理范围内(±5 秒)
  4. 时区信息应与服务器配置一致