NestJS Deployment and DevOps Explained
Deployment Overview
Deployment is the process of moving an application from development to production environment. NestJS applications can be deployed through various methods, including traditional servers, containerized deployment, cloud services, etc.
1. Docker Containerization
Create Dockerfile
dockerfile# Build stage FROM node:18-alpine AS builder WORKDIR /app COPY package*.json ./ RUN npm ci COPY . . RUN npm run build # Production stage FROM node:18-alpine AS runner WORKDIR /app ENV NODE_ENV production COPY package*.json ./ RUN npm ci --only=production COPY /app/dist ./dist EXPOSE 3000 CMD ["node", "dist/main.js"]
Create .dockerignore
shellnode_modules dist .git .env *.log
Build Docker Image
bashdocker build -t nestjs-app .
Run Docker Container
bashdocker run -p 3000:3000 nestjs-app
2. Docker Compose
docker-compose.yml
yamlversion: '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:
Start Services
bashdocker-compose up -d
3. Kubernetes Deployment
Deployment Configuration
yamlapiVersion: 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 Configuration
yamlapiVersion: v1 kind: Service metadata: name: nestjs-app-service spec: selector: app: nestjs-app ports: - protocol: TCP port: 80 targetPort: 3000 type: LoadBalancer
Ingress Configuration
yamlapiVersion: 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 Pipeline
GitHub Actions Configuration
yamlname: 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 Configuration
yamlstages: - 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. Environment Variable Management
Use .env Files
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
Use ConfigModule
typescriptimport { 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
yamlapiVersion: v1 kind: Secret metadata: name: db-secret type: Opaque data: host: bG9jYWxob3N0 port: MzMwNg== user: cm9vdA== password: cGFzc3dvcmQ=
6. Health Checks
Health Check Endpoint
typescriptimport { 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 Health Checks
yamllivenessProbe: 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. Log Management
Structured Logging
typescriptimport { 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' }); // Business logic } }
Use Winston
typescriptimport { 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. Monitoring and Alerting
Use Prometheus
typescriptimport { 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 Dashboard
yamlapiVersion: 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. Load Balancing
Nginx Configuration
nginxupstream 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 Configuration
yamlResources: 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. Disaster Recovery
Database Backup
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 # Keep backups from last 7 days find $BACKUP_DIR -name "db_backup_*.sql.gz" -mtime +7 -delete
Auto Scaling
yamlapiVersion: 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
Deployment Best Practices
- Environment Isolation: Separate development, testing, and production environments
- Automated Deployment: Use CI/CD to automate deployment processes
- Version Control: Include all configuration files in version control
- Monitoring and Alerting: Implement comprehensive monitoring and alerting mechanisms
- Backup Strategy: Regularly backup critical data
- Security Hardening: Implement security best practices
- Documentation: Maintain detailed deployment documentation
- Rollback Plan: Prepare quick rollback plans
- Performance Testing: Conduct performance testing before deployment
- Progressive Release: Use blue-green deployment or canary release
Summary
NestJS deployment and DevOps provides:
- Flexible containerization solutions
- Powerful orchestration support
- Automated CI/CD processes
- Comprehensive monitoring system
- Reliable disaster recovery
Mastering deployment and DevOps is key to successfully delivering NestJS applications to production environments. By properly using containerization, orchestration, CI/CD, and monitoring tools, you can build high-availability, scalable production environments, ensuring stable operation and rapid iteration of applications.