22.02-Node.js部署

要点

  • Node.js 是最经典的 JS 运行时,部署方式成熟:PM2、Docker、systemd
  • Hono 在 Node.js 上用 @hono/node-server 适配器跑
  • 传统服务器部署的核心问题:进程管理、日志、重启、扩缩容
  • 适合长时间运行、重计算的场景,但运维复杂度比 Serverless 高

内容

1. Hono 在 Node.js 上跑

Hono 提供了 Node.js 适配器 @hono/node-server

// terminal
npm install @hono/node-server
// src/index.ts
import { serve } from '@hono/node-server'
import { Hono } from 'hono'
 
const app = new Hono()
 
app.get('/', (c) => c.text('Hello Hono on Node.js!'))
app.get('/api/users', (c) => c.json({ users: [] }))
 
serve(app, (info) => {
  console.log(`Server running on port ${info.port}`)
})

@hono/node-server 的作用是把 Node.js 的原生 http 模块桥接到 Hono 的 Web Standards API 上。你写代码时还是用 c.json()c.text(),适配器帮你翻译成 Node.js 的 Request/Response。

2. 构建和启动

Node.js 项目通常需要先编译 TypeScript 再运行:

// terminal
# 编译
npx tsc
 
# 或者用 tsx 直接运行(开发期)
npx tsx src/index.ts

package.json 里的 scripts:

{
  "scripts": {
    "dev": "tsx watch src/index.ts",
    "build": "tsc",
    "start": "node dist/index.js"
  }
}

3. 进程管理:PM2

直接 node dist/index.js 跑的问题是:进程挂了不会自动重启,终端关了进程也就结束了。

PM2 是 Node.js 最常用的进程管理器:

// terminal
# 安装
npm install -g pm2
 
# 启动
pm2 start dist/index.js --name my-api
 
# 查看状态
pm2 status
 
# 查看日志
pm2 logs my-api
 
# 重启
pm2 restart my-api
 
# 停止
pm2 stop my-api
 
# 开机自启
pm2 startup
pm2 save

PM2 的核心功能:

功能说明
自动重启进程挂了自动拉起
日志管理自动收集 stdout/stderr
集群模式利用多核 CPU,pm2 start -i max
零停机重启pm2 reload,先启动新进程再杀旧进程
开机自启系统重启后自动启动

3.1 集群模式

// terminal
# 启动和 CPU 核心数相同的实例
pm2 start dist/index.js -i max --name my-api
 
# 查看状态
pm2 status
# ┌─────────────┬────┬─────────┬──────┬───────┬──────────┐
# │ App name    │ id │ mode    │ pid  │ status│ restart  │
# ├─────────────┼────┼─────────┼──────┼───────┼──────────┤
# │ my-api      │ 0  │ cluster │ 1234 │ online│ 0        │
# │ my-api      │ 1  │ cluster │ 1235 │ online│ 0        │
# │ my-api      │ 2  │ cluster │ 1236 │ online│ 0        │
# │ my-api      │ 3  │ cluster │ 1237 │ online│ 0        │
# └─────────────┴────┴─────────┴──────┴───────┴──────────┘

集群模式下,PM2 会在多个实例之间做负载均衡。适合 CPU 密集型的应用。

4. Docker 容器化部署

Docker 是另一种主流的部署方式。先把应用容器化,然后在任何支持 Docker 的平台上运行。

# Dockerfile
FROM node:22-alpine AS builder
 
WORKDIR /app
COPY package.json pnpm-lock.yaml ./
RUN npm install -g pnpm && pnpm install --frozen-lockfile
 
COPY . .
RUN pnpm build
 
# ---
 
FROM node:22-alpine AS runner
 
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/package.json ./
 
EXPOSE 3000
CMD ["node", "dist/index.js"]
// terminal
# 构建镜像
docker build -t my-api .
 
# 运行
docker run -p 3000:3000 --name my-api my-api
 
# 后台运行
docker run -d -p 3000:3000 --restart always --name my-api my-api

Docker 的优势:

  1. 环境一致:开发、测试、生产用同一个镜像
  2. 可移植:在任何支持 Docker 的平台上运行
  3. 隔离性:每个应用在自己的容器里,互不影响
  4. 编排:用 Docker Compose 或 K8s 管理多个服务

5. systemd 部署

如果不用 Docker 和 PM2,可以直接用 Linux 的 systemd 管理进程:

# /etc/systemd/system/my-api.service
[Unit]
Description=My Hono API
After=network.target
 
[Service]
Type=simple
User=www-data
WorkingDirectory=/var/www/my-api
ExecStart=/usr/bin/node dist/index.js
Restart=on-failure
RestartSec=5
Environment=NODE_ENV=production
Environment=PORT=3000
 
[Install]
WantedBy=multi-user.target
// terminal
# 启用
sudo systemctl enable my-api
 
# 启动
sudo systemctl start my-api
 
# 查看状态
sudo systemctl status my-api
 
# 查看日志
sudo journalctl -u my-api -f

systemd 是 Linux 原生的进程管理,不需要额外安装 PM2。但配置相对复杂。

6. 反向代理:Nginx

生产环境通常不会让 Node.js 直接暴露 3000 端口,而是用 Nginx 做反向代理:

用户 → Nginx (80/443) → Node.js (3000)
# /etc/nginx/sites-available/my-api
upstream my-api {
    server 127.0.0.1:3000;
}
 
server {
    listen 80;
    server_name api.example.com;
 
    location / {
        proxy_pass http://my-api;
        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_cache_bypass $http_upgrade;
    }
}

Nginx 的作用:

  1. SSL 终止:在 Nginx 层处理 HTTPS
  2. 负载均衡:多个 Node.js 实例时做负载均衡
  3. 静态文件:静态资源直接由 Nginx 返回
  4. 限流和安全:防 DDoS、限速率

7. 传统部署的运维清单

Node.js 传统服务器部署需要关注:

事项工具
进程管理PM2 / systemd
日志收集PM2 logs / journald / 文件日志
SSL 证书Let's Encrypt / Certbot
反向代理Nginx / Caddy
监控PM2 Plus / Datadog / Prometheus
备份数据库定时备份脚本
安全防火墙、SSH 密钥、fail2ban

8. 和边缘计算的对比

传统 Node.js 部署 vs Workers 边缘部署:

维度Node.js 传统部署Cloudflare Workers
延迟取决于服务器位置全球边缘,极低延迟
扩缩容手动配置或自动扩缩自动
运维需要管服务器不需要
成本固定月租按请求计费
运行时完整 Node.jsV8 isolate
适用场景长时间运行、重计算低延迟 API、全球服务

对于 AI 项目来说,如果主要逻辑是调用外部 AI API(网络 I/O 密集),Workers 的延迟优势很明显。如果有大量的本地计算(如模型推理),传统服务器可能更合适。

9. 小结

Node.js 部署的核心:

  1. Hono 用 @hono/node-server 跑在 Node.js 上
  2. PM2 管理进程(自动重启、日志、集群)
  3. Docker 容器化(环境一致、可移植)
  4. Nginx 反向代理(SSL、负载均衡)
  5. systemd 是 Linux 原生的替代方案

传统部署适合需要完全控制、长时间运行的场景,但运维复杂度比 Serverless 和边缘计算高。