乐闻世界logo
搜索文章和话题

NestJS 部署和 DevOps 如何实现?

2月21日 17:11

NestJS 部署和 DevOps 详解

部署概述

部署是将应用程序从开发环境转移到生产环境的过程。NestJS 应用程序可以通过多种方式部署,包括传统服务器、容器化部署、云服务等。

1. Docker 容器化

创建 Dockerfile

dockerfile
# 构建阶段 FROM node:18-alpine AS builder WORKDIR /app COPY package*.json ./ RUN npm ci COPY . . RUN npm run build # 生产阶段 FROM node:18-alpine AS runner WORKDIR /app ENV NODE_ENV production COPY package*.json ./ RUN npm ci --only=production COPY --from=builder /app/dist ./dist EXPOSE 3000 CMD ["node", "dist/main.js"]

创建 .dockerignore

shell
node_modules dist .git .env *.log

构建 Docker 镜像

bash
docker build -t nestjs-app .

运行 Docker 容器

bash
docker run -p 3000:3000 nestjs-app

2. Docker Compose

docker-compose.yml

yaml
version: '3.8' services: app: build: . ports: - "3000:3000" environment: - NODE_ENV=production - DATABASE_HOST=db - DATABASE_PORT=3306 - DATABASE_USER=root - DATABASE_PASSWORD=password - DATABASE_NAME=nestjs depends_on: - db restart: unless-stopped db: image: mysql:8.0 environment: - MYSQL_ROOT_PASSWORD=password - MYSQL_DATABASE=nestjs ports: - "3306:3306" volumes: - mysql_data:/var/lib/mysql restart: unless-stopped volumes: mysql_data:

启动服务

bash
docker-compose up -d

3. Kubernetes 部署

Deployment 配置

yaml
apiVersion: apps/v1 kind: Deployment metadata: name: nestjs-app spec: replicas: 3 selector: matchLabels: app: nestjs-app template: metadata: labels: app: nestjs-app spec: containers: - name: nestjs-app image: nestjs-app:latest ports: - containerPort: 3000 env: - name: NODE_ENV value: "production" - name: DATABASE_HOST valueFrom: secretKeyRef: name: db-secret key: host resources: requests: memory: "256Mi" cpu: "250m" limits: memory: "512Mi" cpu: "500m" livenessProbe: httpGet: path: /health port: 3000 initialDelaySeconds: 30 periodSeconds: 10 readinessProbe: httpGet: path: /health port: 3000 initialDelaySeconds: 5 periodSeconds: 5

Service 配置

yaml
apiVersion: v1 kind: Service metadata: name: nestjs-app-service spec: selector: app: nestjs-app ports: - protocol: TCP port: 80 targetPort: 3000 type: LoadBalancer

Ingress 配置

yaml
apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: nestjs-app-ingress annotations: nginx.ingress.kubernetes.io/rewrite-target: / spec: rules: - host: api.example.com http: paths: - path: / pathType: Prefix backend: service: name: nestjs-app-service port: number: 80

4. CI/CD 管道

GitHub Actions 配置

yaml
name: CI/CD Pipeline on: push: branches: [ main, develop ] pull_request: branches: [ main ] jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Setup Node.js uses: actions/setup-node@v3 with: node-version: '18' cache: 'npm' - name: Install dependencies run: npm ci - name: Run tests run: npm run test - name: Run lint run: npm run lint - name: Build run: npm run build build-and-push: needs: test runs-on: ubuntu-latest if: github.ref == 'refs/heads/main' steps: - uses: actions/checkout@v3 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v2 - name: Login to Docker Hub uses: docker/login-action@v2 with: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} - name: Build and push uses: docker/build-push-action@v4 with: context: . push: true tags: username/nestjs-app:latest,username/nestjs-app:${{ github.sha }} cache-from: type=registry,ref=username/nestjs-app:latest cache-to: type=inline deploy: needs: build-and-push runs-on: ubuntu-latest if: github.ref == 'refs/heads/main' steps: - name: Deploy to Kubernetes uses: azure/k8s-deploy@v4 with: manifests: | k8s/deployment.yaml k8s/service.yaml images: | username/nestjs-app:${{ github.sha }} kubeconfig: ${{ secrets.KUBE_CONFIG }}

GitLab CI 配置

yaml
stages: - test - build - deploy variables: NODE_ENV: test test: stage: test image: node:18 script: - npm ci - npm run test - npm run lint cache: paths: - node_modules/ build: stage: build image: docker:latest services: - docker:dind script: - docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA . - docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA only: - main deploy: stage: deploy image: bitnami/kubectl:latest script: - kubectl set image deployment/nestjs-app nestjs-app=$CI_REGISTRY_IMAGE:$CI_COMMIT_SHA only: - main

5. 环境变量管理

使用 .env 文件

bash
# .env.production NODE_ENV=production PORT=3000 DATABASE_HOST=localhost DATABASE_PORT=3306 DATABASE_USER=root DATABASE_PASSWORD=password DATABASE_NAME=nestjs JWT_SECRET=your-secret-key

使用 ConfigModule

typescript
import { Module } from '@nestjs/common'; import { ConfigModule } from '@nestjs/config'; @Module({ imports: [ ConfigModule.forRoot({ isGlobal: true, envFilePath: `.env.${process.env.NODE_ENV}`, }), ], }) export class AppModule {}

Kubernetes Secrets

yaml
apiVersion: v1 kind: Secret metadata: name: db-secret type: Opaque data: host: bG9jYWxob3N0 port: MzMwNg== user: cm9vdA== password: cGFzc3dvcmQ=

6. 健康检查

健康检查端点

typescript
import { Controller, Get } from '@nestjs/common'; import { HealthCheck, HealthCheckService, TypeOrmHealthIndicator } from '@nestjs/terminus'; @Controller('health') export class HealthController { constructor( private health: HealthCheckService, private db: TypeOrmHealthIndicator, ) {} @Get() @HealthCheck() check() { return this.health.check([ () => this.db.pingCheck('database'), ]); } }

Kubernetes 健康检查

yaml
livenessProbe: httpGet: path: /health port: 3000 initialDelaySeconds: 30 periodSeconds: 10 timeoutSeconds: 5 failureThreshold: 3 readinessProbe: httpGet: path: /health port: 3000 initialDelaySeconds: 5 periodSeconds: 5 timeoutSeconds: 3 failureThreshold: 3

7. 日志管理

结构化日志

typescript
import { Logger } from '@nestjs/common'; export class AppService { private readonly logger = new Logger(AppService.name); async getData() { this.logger.log('Fetching data', { userId: 123, action: 'fetch' }); // 业务逻辑 } }

使用 Winston

typescript
import { WinstonModule } from 'nest-winston'; import * as winston from 'winston'; @Module({ imports: [ WinstonModule.forRoot({ transports: [ new winston.transports.Console({ format: winston.format.combine( winston.format.timestamp(), winston.format.json(), ), }), new winston.transports.File({ filename: 'logs/error.log', level: 'error', }), new winston.transports.File({ filename: 'logs/combined.log', }), ], }), ], }) export class AppModule {}

8. 监控和告警

使用 Prometheus

typescript
import { Controller, Get } from '@nestjs/common'; import { MetricsService } from './metrics.service'; @Controller('metrics') export class MetricsController { constructor(private metricsService: MetricsService) {} @Get() getMetrics() { return this.metricsService.getMetrics(); } }

Grafana 仪表板

yaml
apiVersion: v1 kind: ConfigMap metadata: name: grafana-dashboard data: dashboard.json: | { "dashboard": { "title": "NestJS Application", "panels": [ { "title": "Request Rate", "targets": [ { "expr": "rate(http_requests_total[5m])" } ] } ] } }

9. 负载均衡

Nginx 配置

nginx
upstream nestjs_backend { server nestjs-app-1:3000; server nestjs-app-2:3000; server nestjs-app-3:3000; } server { listen 80; server_name api.example.com; location / { proxy_pass http://nestjs_backend; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } }

AWS ALB 配置

yaml
Resources: LoadBalancer: Type: AWS::ElasticLoadBalancingV2::LoadBalancer Properties: Name: nestjs-alb Subnets: - !Ref SubnetA - !Ref SubnetB SecurityGroups: - !Ref SecurityGroup Type: application TargetGroup: Type: AWS::ElasticLoadBalancingV2::TargetGroup Properties: Name: nestjs-tg Port: 3000 Protocol: HTTP VpcId: !Ref VPC Targets: - Id: !Ref EC2Instance1 - Id: !Ref EC2Instance2

10. 灾难恢复

数据库备份

bash
#!/bin/bash # backup.sh DATE=$(date +%Y%m%d_%H%M%S) BACKUP_DIR="/backups" DATABASE="nestjs" mysqldump -u root -p$MYSQL_ROOT_PASSWORD $DATABASE | gzip > $BACKUP_DIR/db_backup_$DATE.sql.gz # 保留最近7天的备份 find $BACKUP_DIR -name "db_backup_*.sql.gz" -mtime +7 -delete

自动扩展

yaml
apiVersion: autoscaling/v2 kind: HorizontalPodAutoscaler metadata: name: nestjs-hpa spec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment name: nestjs-app minReplicas: 2 maxReplicas: 10 metrics: - type: Resource resource: name: cpu target: type: Utilization averageUtilization: 70 - type: Resource resource: name: memory target: type: Utilization averageUtilization: 80

部署最佳实践

  1. 环境隔离:开发、测试、生产环境分离
  2. 自动化部署:使用 CI/CD 自动化部署流程
  3. 版本控制:所有配置文件纳入版本控制
  4. 监控告警:实施全面的监控和告警机制
  5. 备份策略:定期备份关键数据
  6. 安全加固:实施安全最佳实践
  7. 文档化:维护详细的部署文档
  8. 回滚计划:准备快速回滚方案
  9. 性能测试:部署前进行性能测试
  10. 渐进式发布:使用蓝绿部署或金丝雀发布

总结

NestJS 部署和 DevOps 提供了:

  • 灵活的容器化方案
  • 强大的编排支持
  • 自动化的 CI/CD 流程
  • 完善的监控体系
  • 可靠的灾难恢复

掌握部署和 DevOps 是将 NestJS 应用程序成功交付到生产环境的关键。通过合理使用容器化、编排、CI/CD 和监控工具,可以构建出高可用、可扩展的生产环境,确保应用程序的稳定运行和快速迭代。

标签:NestJS