FORMA

部署与运维

生产环境:nest build 输出 dist,用进程管理器与反向代理部署。

将 NestJS 应用部署到生产环境,不仅需要构建出优化后的产物,还需配置合适的运行参数、进程管理、健康检查、日志聚合和安全策略。以下是各个关键环节的最佳实践。

一、构建:nest build 输出 dist 目录

NestJS CLI 内置了打包功能,可将 TypeScript 编译为 JavaScript 并输出到 dist 目录。

bash
nest build

该命令会:

  • 读取 tsconfig.json(通常为 tsconfig.build.json)编译项目。
  • 将生成的 JS 文件输出到 dist 文件夹。
  • 可选地支持 webpack 模式(nest build --webpack),用于微服务或部署包优化,但多数情况下默认配置即可。

生产环境构建优化

  • 设置 NODE_ENV=production 环境变量,使某些依赖(如 TypeORM、Mongoose)使用生产配置。
  • 确保 tsconfig.build.jsoncompilerOptions.removeCommentsdeclaration 选项按需设置(生产可移除注释,不生成 .d.ts)。
  • 使用 --optimization 标志启用 webpack 的优化(如代码压缩、作用域提升等),但仅在使用了 --webpack 时才生效。

二、生产环境 Node 配置

Node.js 运行时参数对稳定性和性能至关重要。

1. NODE_ENV=production

  • 明确设置为 production,使 NestJS 和第三方库(如 TypeORM、Express)启用生产模式:
    • 禁用调试日志、缓存模板、减少错误堆栈细节。
    • 某些库会进行性能优化(如连接池调优)。

2. 内存限制:--max-old-space-size

Node.js 默认内存限制约为 1.4GB(64位系统),对于大型应用可能不足。可通过参数调整老生代内存大小:

bash
node --max-old-space-size=2048 dist/main.js

建议

  • 根据服务器可用内存和应用负载设置,通常设置为服务器内存的 70%~80%,避免 OOM Killer 介入。
  • 在容器环境(Docker/K8s)中,应将此值与容器内存限制(memory limit)匹配,预留一部分给其他进程。

3. 其他有用标志

  • --enable-source-maps:生产环境启用源码映射,便于错误堆栈定位(需确保 source map 文件不被泄露)。
  • --trace-warnings:跟踪警告来源,辅助排查潜在问题。
  • --max-http-header-size:若客户端发送较大头部,可适当增加默认 16KB 限制。

三、进程管理

保证 Node.js 进程在崩溃后自动重启、多核服务器负载均衡。

1. PM2(常用进程管理器)

安装并启动:

bash
npm install -g pm2
pm2 start dist/main.js --name my-app --instances 2 --max-memory-restart 500M

常用配置ecosystem.config.js):

js
module.exports = {
  apps: [
    {
      name: "nest-app",
      script: "dist/main.js",
      instances: "max", // 启用所有 CPU 核心
      exec_mode: "cluster", // 集群模式
      max_memory_restart: "1G",
      env: { NODE_ENV: "production" },
      error_file: "./logs/err.log",
      out_file: "./logs/out.log",
      merge_logs: true,
      kill_timeout: 5000, // 强制杀死前等待时间(秒)
      listen_timeout: 3000, // 应用监听超时
    },
  ],
};

2. Docker + Kubernetes(容器化)

Dockerfile 示例(多阶段构建,保证镜像小):

dockerfile
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production && npm cache clean --force
COPY . .
RUN npm run build

FROM node:20-alpine
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
EXPOSE 3000
CMD ["node", "--max-old-space-size=512", "dist/main.js"]

Kubernetes 部署关键点

  • 设置 Deploymentreplicas 数量(水平扩展)。
  • 添加 livenessProbereadinessProbe(利用健康检查端点)。
  • 使用 ConfigMapSecret 管理环境变量。
  • 设置 resources.limits.memory,配合 --max-old-space-size 预留内存。

四、健康检查:@nestjs/terminus 模块

NestJS 官方提供的 Terminus 模块可以方便地集成健康检查端点,用于容器探针或负载均衡监控。

安装:

bash
npm install @nestjs/terminus

@nestjs/terminus 没有 TerminusModule.forRoot({ healthChecks: {...} }) 这种配置式 API;正确用法是把 TerminusModule 直接导入模块,再用 HealthCheckService + 各类 HealthIndicatorHttpHealthIndicatorTypeOrmHealthIndicatorMemoryHealthIndicatorDiskHealthIndicator 等)在控制器中组合检查项:

typescript
// health.module.ts
import { Module } from "@nestjs/common";
import { TerminusModule } from "@nestjs/terminus";
import { HttpModule } from "@nestjs/axios";
import { HealthController } from "./health.controller";

@Module({
  imports: [TerminusModule, HttpModule],
  controllers: [HealthController],
})
export class HealthModule {}
typescript
// health.controller.ts
import { Controller, Get } from "@nestjs/common";
import {
  HealthCheckService,
  HealthCheck,
  HttpHealthIndicator,
  TypeOrmHealthIndicator,
  MemoryHealthIndicator,
} from "@nestjs/terminus";

@Controller("health")
export class HealthController {
  constructor(
    private health: HealthCheckService,
    private http: HttpHealthIndicator,
    private db: TypeOrmHealthIndicator,
    private memory: MemoryHealthIndicator,
  ) {}

  // 就绪探针:检查依赖(数据库、下游服务等)是否可用
  @Get("readiness")
  @HealthCheck()
  readiness() {
    return this.health.check([
      () => this.db.pingCheck("database", { timeout: 1500 }),
      () => this.http.pingCheck("downstream-api", "https://api.example.com/ping"),
    ]);
  }

  // 存活探针:只做轻量自检,不依赖外部服务
  @Get("liveness")
  @HealthCheck()
  liveness() {
    return this.health.check([() => this.memory.checkHeap("memory_heap", 300 * 1024 * 1024)]);
  }
}

@HealthCheck() 会让路由返回标准的 Terminus 响应格式:所有检查通过时状态码 200,任一检查失败则为 503

在 Kubernetes 中使用

yaml
livenessProbe:
  httpGet:
    path: /health/liveness
    port: 3000
  initialDelaySeconds: 10
  periodSeconds: 10
readinessProbe:
  httpGet:
    path: /health/readiness
    port: 3000
  initialDelaySeconds: 5
  periodSeconds: 5

五、日志聚合

生产环境需要收集来自多个实例的日志,并集中分析。

1. 推荐使用结构化日志(如 pino 或 winston)

见前面“日志记录”章节,生产应输出 JSON 格式,便于工具解析。

2. 日志聚合方案

方案适用场景特点
ELK Stack (Elasticsearch, Logstash, Kibana)大规模、高复杂度功能强大,支持全文检索和可视化
Loki + Grafana轻量级,与 Kubernetes 集成好日志与 Prometheus 指标共享标签,查询高效
Datadog商业 SaaS,全栈监控开箱即用,集成 APM、日志、基础设施监控

实现方式

  • 将日志输出到 stdout/stderr,由容器运行时收集并转发到日志代理(如 Filebeat、Fluentd、Promtail)。
  • 在代码中使用 JSON 格式打印:
typescript
this.logger.info({ reqId: uuid(), userId }, "User action");
  • 确保使用异步日志驱动(如 pino),避免阻塞事件循环。

六、环境变量确保安全

禁止在代码中硬编码任何凭证、API 密钥、数据库密码等敏感信息。

1. 使用 .env 文件(本地开发)

配合 @nestjs/config 模块,但 .env 文件不应提交到版本控制。

2. 生产环境注入方式

  • Docker/K8s:通过 envsecret 对象注入。例如:
yaml
env:
  - name: DB_PASSWORD
    valueFrom:
      secretKeyRef:
        name: db-secret
        key: password
  • PM2:在 ecosystem.config.js 中设置 env 字段。
  • 系统环境变量:直接在启动命令前设置(安全性较低,不推荐多租户环境)。

3. 使用密钥管理服务(Vault)

对于高安全要求场景,可集成 HashiCorp Vault、AWS Secrets Manager 或 Azure Key Vault,应用启动时动态获取密钥。

4. 校验环境变量

在应用启动时,使用 Joi 或 class-validator 验证所有必需的环境变量是否已设置且格式正确,避免运行时因缺失配置而崩溃。

typescript
// main.ts 或 config.module.ts
const requiredEnv = ["DB_HOST", "DB_PASSWORD", "API_KEY"];
requiredEnv.forEach((key) => {
  if (!process.env[key]) throw new Error(`Missing env ${key}`);
});

总结表

环节关键措施
构建nest build,设置 NODE_ENV=production,使用 webpack 优化(可选)
Node 配置--max-old-space-size 设置内存上限,开启 --enable-source-maps
进程管理PM2 集群模式,或 Docker + K8s 编排(设置管理探针)
健康检查@nestjs/terminus 提供 /health 端点,支持 liveness/readiness
日志聚合使用 JSON 格式日志,集成 ELK/Loki/Datadog 集中收集
环境变量安全禁止硬编码,通过 Secrets、Vault 注入;启动时强制校验

遵循这些实践,可以确保 NestJS 应用在生产环境中稳定、安全、可观测。

参考文献

以下链接在编写时均可正常访问:

资料说明
NestJS 文档官方
Deployment本章主题
Request lifecycle执行顺序