项目部署
⏱️最后更新: 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"
}
返回结果
状态码 | 状态码含义 | 说明 | 数据模型 |
---|---|---|---|
200 | OK | 服务正常运行 | HealthResponse |
验证点:
- HTTP 状态码必须为 200
status
字段值为"运行正常"uptime
字段为大于 0 的数值类型memoryUsage
对象包含四个数值字段(rss, heapTotal, heapUsed, external)timestamp
为有效的 ISO 8601 格式
GET 2. 测试个性化问候接口
路径GET /api/api/greet
参数
名称 | 位置 | 类型 | 必选 | 说明 |
---|---|---|---|---|
name | query | string | 否 | 问候对象名称,默认为"朋友" |
预期行为 根据提供的名称返回个性化问候语
请求示例
GET http://localhost:3000/api/greet?name=Alice
返回示例
200 OK
json
{
"message": "你好,Alice!",
"timestamp": "2024-05-20T10:00:00.000Z"
}
返回结果
状态码 | 状态码含义 | 说明 | 数据模型 |
---|---|---|---|
200 | OK | 成功返回问候语 | GreetResponse |
验证点:
- 无 name 参数时,默认返回"你好,朋友!"
- 有 name 参数时,返回对应的个性化问候
- 响应包含有效的 ISO 时间戳
- 特殊字符输入需正确转义处理
- XSS 攻击尝试应被过滤或转义
GET 3. 测试代理接口(模拟示例)
路径GET /api/api/proxy
参数
名称 | 位置 | 类型 | 必选 | 说明 |
---|---|---|---|---|
target | query | string | 是 | 要代理的目标 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"
}
返回结果
状态码 | 状态码含义 | 说明 | 数据模型 |
---|---|---|---|
200 | OK | 代理请求成功 | ProxyResponse |
400 | Bad Request | 缺少必要参数 | ErrorResponse |
502 | Bad Gateway | 目标服务不可达 | ErrorResponse |
验证点:
- 必须提供 target 参数,否则返回 400 错误
- 成功请求返回的 status 应为 1
- statusCode 反映目标服务的实际 HTTP 状态码
- latency 字段格式为"数字+ms"
- 无效 URL 应返回 502 错误
- 响应时间不超过设定的超时阈值(如 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"
}
返回结果
状态码 | 状态码含义 | 说明 | 数据模型 |
---|---|---|---|
200 | OK | 成功返回时间信息 | TimeResponse |
验证点:
- serverTime 为有效的 ISO 8601 格式
- timezone 为有效的 IANA 时区标识符
- 服务器时间与客户端时间偏差在合理范围内(±5 秒)
- 时区信息应与服务器配置一致