六维教程

Node.js 部署与日志

代码写完只算完成一半,让它在服务器上稳定跑起来、出了问题能查日志,才是完整的交付。这篇讲部署的完整流程,用 PM2 保持进程常驻,用 Winston 记录日志,最后加一个健康检查接口。

部署前的准备

生产环境安装依赖

服务器上安装依赖要用 npm ci,它按 package-lock.json 里的精确版本安装,速度快且结果可复现,不会因为依赖升级引入意外。npm 安装的细节回顾 Node.js npm 入门

npm ci --omit=dev

--omit=dev 跳过开发依赖,生产环境只需要 express、prisma 这些运行时包。

环境变量

数据库地址、JWT 密钥这些配置在服务器上要用生产值,.env 文件不要提交到 git,部署时在服务器上手动创建。多环境配置的做法见 Node.js 环境变量与配置

# 服务器上的 .env
PORT=3000
DATABASE_URL="mysql://user:密码@localhost:3306/blog"
JWT_SECRET="换成一段足够长的随机字符串"
NODE_ENV=production

编译数据库

生产库要执行迁移建表

npx prisma migrate deploy

PM2 进程管理

直接 node app.js 启动的进程有两个问题,终端一关进程就死,代码一抛未捕获异常进程直接退出。PM2 是 Node.js 最常用的进程管理器,进程常驻、崩溃自动重启、日志统一管理。

# 全局安装
npm install -g pm2

# 启动应用
pm2 start src/server.js --name blog-api

# 查看进程状态
pm2 status

# 实时查看日志
pm2 logs blog-api

# 重启 / 停止
pm2 restart blog-api
pm2 stop blog-api

服务器重启后要能自动拉起进程,执行一次保存和开机自启配置

pm2 save
pm2 startup

pm2 startup 会输出一条需要 root 权限执行的命令,复制执行一次即可。

ecosystem 配置文件

项目里放一份 ecosystem.config.js,把启动参数固化下来,团队其他人部署时不用猜

// ecosystem.config.js
module.exports = {
  apps: [
    {
      name: 'blog-api',
      script: 'src/server.js',
      instances: 1,
      autorestart: true,
      max_memory_restart: '300M',
      env: {
        NODE_ENV: 'production'
      }
    }
  ]
}

启动时用 pm2 start ecosystem.config.js 即可。

Winston 日志

日志的目的是出问题时能还原现场。Winston 是 Node.js 最流行的日志库,支持分级、多种输出、按天切分文件。

npm install winston
// src/utils/logger.js
const winston = require('winston')

const logger = winston.createLogger({
  level: process.env.NODE_ENV === 'production' ? 'info' : 'debug',
  format: winston.format.combine(
    winston.format.timestamp(),
    winston.format.printf(({ timestamp, level, message }) => {
      return `${timestamp} [${level}] ${message}`
    })
  ),
  transports: [
    new winston.transports.Console(),
    new winston.transports.File({ filename: 'logs/app.log' })
  ]
})

module.exports = logger

开发环境日志级别是 debug,方便调试,生产环境是 info,减少噪音。日志级别从低到高是 debug、info、warn、error,级别越高输出越少。代码里这样用

const logger = require('../utils/logger')

logger.info('用户登录成功', userId)
logger.error('数据库连接失败', err.message)

访问日志用 morgan 记录每个请求的方法、路径、状态码和耗时,先装包

npm install morgan
const morgan = require('morgan')
const logger = require('./utils/logger')

app.use(morgan('combined', {
  stream: { write: (msg) => logger.info(msg.trim()) }
}))

morgan 的用法回顾 Node.js Express 中间件,这里把输出流接到 Winston,访问日志和业务日志统一落盘。

健康检查接口

运维监控、负载均衡都要一个探活接口。返回 200 说明进程活着,再顺手查一下数据库连通性,避免”进程活着但数据库挂了”的假健康。

// src/controllers/health.controller.js
const { PrismaClient } = require('@prisma/client')

const prisma = new PrismaClient()

async function health(req, res) {
  try {
    await prisma.$queryRaw`SELECT 1`
    res.json({ status: 'ok', db: 'ok', time: new Date().toISOString() })
  } catch (err) {
    res.status(503).json({ status: 'error', db: 'down' })
  }
}

module.exports = { health }
// app.js 里挂载
app.get('/health', healthController.health)

Nginx 反向代理

Node 应用默认监听 3000 端口,用户直接访问 3000 端口体验不好也不安全。标准做法是 Nginx 监听 80 端口,把请求转发给 Node,顺带代理静态资源和启用 HTTPS。

server {
    listen 80;
    server_name api.example.com;

    location / {
        proxy_pass http://127.0.0.1:3000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}

HTTPS 证书用 Let’s Encrypt 免费申请,配置好后全站强制跳转 https,安全相关的要求回顾 Node.js Web 安全

完整部署流程

把博客 API 部署到一台 Ubuntu 云服务器,完整命令序列

# 1. 安装 Node 20 LTS(版本管理用 nvm)
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash
nvm install 20

# 2. 拉取代码
git clone https://你的仓库地址/blog-api.git
cd blog-api

# 3. 安装生产依赖并建表
npm ci --omit=dev
npx prisma migrate deploy

# 4. 创建 .env 并填入生产配置
vi .env

# 5. PM2 启动并设置开机自启
npm install -g pm2
pm2 start ecosystem.config.js
pm2 save
pm2 startup

# 6. 验证
curl http://localhost:3000/health

常见坑

问题 原因 解决
端口被占用 上次进程没停干净 pm2 stop 后检查 lsof -i:3000
.env 没生效 文件没传到服务器 确认 .env 在项目根目录且被 .gitignore 忽略
数据库连不上 密码或地址不对 检查 DATABASE_URL,确认 MySQL 已启动
日志文件无限增长 没有轮转策略 配置 maxsize 和 maxFiles 限制文件大小
代码更新不生效 改了代码没重启 pm2 restart blog-api

学习路线回顾

到这里整个 Node.js 教程就结束了,25 篇从 Node.js 是什么 的环境搭建、核心模块、异步编程、事件循环,到 Express 框架、数据库、认证安全、实战部署,已经形成一条完整的后端开发路线。接着可以按兴趣往这几个方向深入,Stream 处理大文件读写、Docker 容器化部署、TypeScript 给项目加类型、微服务拆分架构,每一条都够再学一套课程。

上一篇
Node.js 博客 API 实战
下一篇
EJS 介绍