第 190 章:部署与运维
学习目标
- 学会 Docker 容器化部署
- 掌握 PM2 进程管理
- 部署到云平台
- 学会监控与日志
一、Docker 化
1.1 多阶段 Dockerfile
dockerfile
# 第一阶段:构建
FROM node:20-alpine AS builder
WORKDIR /app
# 复制依赖文件
COPY package*.json pnpm-lock.yaml ./
# 安装 pnpm
RUN npm install -g pnpm
# 安装依赖
RUN pnpm install --frozen-lockfile
# 复制源码
COPY . .
# 构建
RUN pnpm run build
# 第二阶段:运行
FROM node:20-alpine
ENV NODE_ENV=production
WORKDIR /app
COPY package*.json pnpm-lock.yaml ./
RUN npm install -g pnpm && pnpm install --prod --frozen-lockfile
COPY --from=builder /app/dist ./dist
EXPOSE 3000
CMD ["node", "dist/main.js"]1.2 .dockerignore
node_modules
dist
.git
.env
*.log
coverage
.vscode
.idea1.3 构建和运行
bash
docker build -t myapp:1.0.0 .
docker run -d \
--name myapp \
-p 3000:3000 \
--env-file .env \
myapp:1.0.0二、Docker Compose
yaml
# docker-compose.yml
version: '3.8'
services:
app:
build: .
ports:
- '3000:3000'
environment:
- NODE_ENV=production
- DB_HOST=postgres
- DB_PORT=5432
- DB_USER=admin
- DB_PASS=secret
- DB_NAME=myapp
- REDIS_HOST=redis
- REDIS_PORT=6379
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
restart: unless-stopped
networks:
- app-net
postgres:
image: postgres:15-alpine
environment:
POSTGRES_USER: admin
POSTGRES_PASSWORD: secret
POSTGRES_DB: myapp
volumes:
- pgdata:/var/lib/postgresql/data
ports:
- '5432:5432'
healthcheck:
test: ['CMD-SHELL', 'pg_isready -U admin']
interval: 10s
timeout: 5s
retries: 5
networks:
- app-net
redis:
image: redis:7-alpine
ports:
- '6379:6379'
volumes:
- redisdata:/data
networks:
- app-net
nginx:
image: nginx:alpine
ports:
- '80:80'
- '443:443'
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf
- ./certs:/etc/nginx/certs
depends_on:
- app
networks:
- app-net
volumes:
pgdata:
redisdata:
networks:
app-net:
driver: bridge启动:
bash
docker-compose up -d
docker-compose logs -f app三、Nginx 反向代理
nginx
# nginx.conf
upstream nestjs {
server app:3000;
}
server {
listen 80;
server_name api.example.com;
client_max_body_size 10M;
location / {
proxy_pass http://nestjs;
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_set_header X-Forwarded-Proto $scheme;
proxy_cache_bypass $http_upgrade;
}
location /static/ {
alias /var/www/static/;
expires 7d;
}
}HTTPS 配置:
nginx
server {
listen 443 ssl http2;
server_name api.example.com;
ssl_certificate /etc/nginx/certs/fullchain.pem;
ssl_certificate_key /etc/nginx/certs/privkey.pem;
location / {
proxy_pass http://nestjs;
# ... 同上
}
}四、PM2(传统服务器)
4.1 安装
bash
npm install -g pm24.2 ecosystem.config.js
javascript
module.exports = {
apps: [{
name: 'myapp',
script: 'dist/main.js',
instances: 'max', // 进程数 = CPU 核心数
exec_mode: 'cluster',
env: {
NODE_ENV: 'development',
PORT: 3000,
},
env_production: {
NODE_ENV: 'production',
PORT: 3000,
},
max_memory_restart: '500M',
error_file: 'logs/err.log',
out_file: 'logs/out.log',
log_date_format: 'YYYY-MM-DD HH:mm:ss',
watch: false,
}],
};4.3 常用命令
bash
pm2 start ecosystem.config.js --env production
pm2 list
pm2 logs
pm2 restart myapp
pm2 reload myapp # 零停机重启
pm2 stop myapp
pm2 delete myapp
pm2 monit # 监控面板
pm2 startup # 开机自启
pm2 save五、健康检查
5.1 探针接口
typescript
@Controller('health')
export class HealthController {
constructor(
private dataSource: DataSource,
@InjectRedis() private redis: Redis,
) {}
@Get()
async check() {
const checks = await Promise.all([
this.checkDb(),
this.checkRedis(),
]);
const allOk = checks.every(c => c.status === 'ok');
return {
status: allOk ? 'ok' : 'degraded',
uptime: process.uptime(),
timestamp: Date.now(),
checks,
};
}
private async checkDb() {
try {
await this.dataSource.query('SELECT 1');
return { name: 'database', status: 'ok' };
} catch (e) {
return { name: 'database', status: 'fail', error: e.message };
}
}
private async checkRedis() {
try {
await this.redis.ping();
return { name: 'redis', status: 'ok' };
} catch (e) {
return { name: 'redis', status: 'fail', error: e.message };
}
}
}5.2 探针配置
typescript
// main.ts
async function bootstrap() {
const app = await NestFactory.create(AppModule, {
logger: ['error', 'warn', 'log'],
// 优雅关闭
abortOnError: false,
});
app.enableShutdownHooks(); // 监听 SIGTERM
await app.listen(3000);
}5.3 Docker 健康检查
yaml
services:
app:
healthcheck:
test: ['CMD', 'curl', '-f', 'http://localhost:3000/health']
interval: 30s
timeout: 5s
retries: 3
start_period: 40s六、配置生产环境
6.1 关闭调试
typescript
// main.ts
const app = await NestFactory.create(AppModule, {
logger: ['error', 'warn'], // 生产关掉 log/debug
});
// 关掉 sync
synchronize: false,6.2 启用 HTTPS
typescript
import * as fs from 'fs';
async function bootstrap() {
const httpsOptions = {
key: fs.readFileSync('certs/privkey.pem'),
cert: fs.readFileSync('certs/fullchain.pem'),
};
const app = await NestFactory.create(AppModule, { httpsOptions });
await app.listen(3000);
}6.3 启用压缩
typescript
import * as compression from 'compression';
app.use(compression());6.4 安全头
typescript
import helmet from 'helmet';
app.use(helmet());6.5 限流
typescript
ThrottlerModule.forRoot([{
ttl: 60_000,
limit: 100,
}])七、部署平台
7.1 Railway / Render
bash
# 登录
railway login
# 部署
railway upjson
// railway.json
{
"$schema": "https://railway.app/railway.schema.json",
"build": { "builder": "NIXPACKS" },
"deploy": {
"startCommand": "node dist/main.js",
"healthcheckPath": "/health",
"restartPolicyType": "ON_FAILURE"
}
}7.2 Fly.io
toml
# fly.toml
app = "myapp"
primary_region = "sin"
[build]
dockerfile = "Dockerfile"
[env]
NODE_ENV = "production"
[[services]]
internal_port = 3000
protocol = "tcp"
[[services.ports]]
port = 80
handlers = ["http"]
force_https = true
[[services.ports]]
port = 443
handlers = ["tls", "http"]
[services.concurrency]
type = "connections"
hard_limit = 100
[[services.http_checks]]
interval = "10s"
timeout = "2s"
grace_period = "10s"
method = "get"
path = "/health"7.3 阿里云 ECS
bash
# 1. SSH 到服务器
ssh root@your-server
# 2. 安装 Docker
curl -fsSL https://get.docker.com | sh
# 3. 复制文件
scp -r ./dist ./node_modules ./package.json root@server:/app
# 4. 启动
cd /app && pm2 start ecosystem.config.js --env production八、CI/CD(GitHub Actions)
yaml
# .github/workflows/deploy.yml
name: Deploy
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Build and push
uses: docker/build-push-action@v5
with:
push: true
tags: user/myapp:${{ github.sha }}
- name: Deploy to server
uses: appleboy/ssh-action@v1
with:
host: ${{ secrets.SERVER_HOST }}
username: ${{ secrets.SERVER_USER }}
key: ${{ secrets.SSH_PRIVATE_KEY }}
script: |
cd /app
docker-compose pull
docker-compose up -d
pnpm migration:run九、监控
9.1 Prometheus + Grafana
bash
pnpm add @willsoto/nestjs-prometheus prom-clienttypescript
import { PrometheusModule } from '@willsoto/nestjs-prometheus';
@Module({
imports: [
PrometheusModule.register({
defaultMetrics: { enabled: true },
}),
],
})
export class AppModule {}9.2 健康监控
- UptimeRobot:HTTP 探针
- Sentry:异常捕获
- New Relic / Datadog:APM
bash
pnpm add @sentry/nodetypescript
import * as Sentry from '@sentry/node';
Sentry.init({
dsn: process.env.SENTRY_DSN,
environment: process.env.NODE_ENV,
});
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.useGlobalFilters(new SentryExceptionFilter());
await app.listen(3000);
}十、性能调优清单
| 项目 | 配置 |
|---|---|
| Node.js | --max-old-space-size=4096 |
| PM2 | instances: 'max' |
| 数据库连接池 | connectionLimit: 20 |
| Redis | 持久化 + 内存淘汰策略 |
| Nginx | worker_processes auto |
| HTTP/2 | ✅ |
| Gzip | ✅ |
| CDN | 静态资源 |
| HTTP 缓存 | ETag / Cache-Control |
十一、本章小结
| 方式 | 特点 |
|---|---|
| Docker Compose | 单机/小团队 |
| K8s | 大规模/集群 |
| PM2 | 单机传统部署 |
| Serverless | 流量波动大 |
| Railway/Fly.io | PaaS 快速部署 |
| 监控 | 用途 |
|---|---|
| Prometheus | 指标 |
| Grafana | 可视化 |
| Sentry | 异常 |
| ELK | 日志聚合 |
动手练习
- 写一个 Dockerfile,多阶段构建
- 用 Docker Compose 启动 app + db + redis
- 配置 Nginx 反向代理 + HTTPS
- 部署到 Railway / Fly.io
推荐阅读
恭喜你完成了 NestJS 全部 25 章的学习! 🎉
全书总结:你已经掌握了从 NestJS 基础到生产部署的完整技能栈,包括模块化架构、依赖注入、数据库集成、认证授权、缓存队列、容器化部署。
下一步建议:
- 🛠️ 实战:做一个完整的 SaaS 项目
- 📖 进阶:GraphQL / 微服务 / WebSocket
- 🤖 AI:集成 OpenAI API 做智能应用