☁️Deployment, DevOps & Observability

Prepare your NestJS application for production with CI/CD, logging, and monitoring.

🐳 Dockerizing a NestJS App

Welcome to Dockerizing your NestJS Application! In this chapter, you'll learn how to containerize your NestJS app using Docker, write optimized multi-stage Dockerfiles, and orchestrate services with Docker Compose. Let's dive into the world of containerization!

💡 Why Docker?

  • Consistent environments across development, testing, and production.
  • 轻松部署和扩展应用程序.
  • 快速构建和发布优化的容器镜像.

✅ Setting Up Your Dockerfile

Let's start by creating a basic Dockerfile for your NestJS application:

FROM node:14
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
CMD ["npm", "run", "start"]

This is a good starting point, but we can optimize it further!

💡 Optimizing with Multi-Stage Builds

FROM node:14 AS builder
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build

FROM node:14
WORKDIR /app
COPY --from=builder /app/dist ./dist
CMD ["npm", "run", "start:prod"]

The multi-stage approach helps keep your final image lean and secure by separating the build environment from the production environment.

✅ Using Docker Compose for Local Development

Docker Compose allows you to define and run multi-container applications. Here's an example setup:

version: '3'
services:
  app:
    build: .
    ports:
      - "3000:3000"
    depends_on:
      - db
  db:
    image: postgres:12
    environment:
      POSTGRES_USER: nest
      POSTGRES_PASSWORD: nest
      POSTGRES_DB: nest

This setup will start your NestJS app and a PostgreSQL database, linked together seamlessly.

💡 Best Practices for Docker Images

  • Separate development and production stages using multi-stage builds.
  • Always use a non-root user when possible.
  • Keep your base images up to date.
  • Use .dockerignore to exclude unnecessary files.

❌ Common Mistakes to Avoid

  • Avoid bundling development dependencies in production images.
  • Don't use large base images (e.g., node:14 vs node:14-alpine).
  • Don't expose sensitive credentials in your Dockerfiles.
  • Avoid unnecessary layers to keep your images small.

💡 Real-World Example

FROM node:14-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm install --production
COPY . .
RUN npm run build

FROM node:14-alpine
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY package.json .
RUN npm install --production
CMD ["npm", "run", "start:prod"]

This example demonstrates an optimized production build using Alpine Linux, resulting in a smaller and more secure image.

💡 Conclusion

You've now learned how to Dockerize your NestJS application effectively. By following best practices and avoiding common pitfalls, you can ensure smooth deployment and scalability of your applications.

🧠 Logging, Metrics & Health Checks

Welcome to the world of logging, metrics, and health checks in NestJS! These tools are essential for maintaining a healthy, observable application. In this chapter, we'll explore how to implement structured logging with Pino or Winston, expose health endpoints using @nestjs/terminus, integrate metrics with Prometheus and Grafana, and apply best practices for production diagnostics.

💡 Structured Logging

Structured logging is a modern approach to capturing application logs in a machine-readable format. Unlike traditional plain-text logs, structured logs allow for easier searching, filtering, and analysis.

  • Use Pino for fast, flexible logging with JSON output
  • Alternatively, use Winston for a widely adopted logging solution
  • Implement structured logs by including context such as timestamps, request IDs, and error codes
import { Logger } from 'pino';

const logger = Logger({
  name: 'my-app',
  level: 'info'
});

logger.info('Application started', {
  version: '1.0.0',
  environment: 'production'
});

💡 Health Checks & Liveness Endpoints

Health checks are critical for monitoring application uptime and performance. NestJS provides a simple way to implement health checks using the @nestjs/terminus package.

import { HealthCheck, HealthCheckService } from '@nestjs/terminus';

@HealthCheck()
public async checkStatus(): Promise<string> {
  try {
    await this.databaseService.ping();
    return 'All systems operational';
  } catch (error) {
    throw new Error('Database connection failed');
  }
}

💡 Metrics & Observability

Monitoring your application's performance with metrics is essential for proactive maintenance. Prometheus and Grafana provide a powerful combination for collecting, visualizing, and alerting on metrics.

  • Install @nestjs/microservices for built-in Prometheus support
  • Expose metrics endpoints in your application
  • Set up Grafana dashboards to visualize your data
  • Implement alerts based on metric thresholds
import { Module } from '@nestjs/common';
import { MonitoringModule } from '@nestjs/microservices';

@Module({
  imports: [
    MonitoringModule.forRoot({
      name: 'my-app',
      path: '/metrics'
    })
  ]
})
export class AppModule {}

💡 Best Practices for Observability

  • Always implement logging at the application level
  • Use consistent log formats across your services
  • Monitor both application and infrastructure metrics
  • Set up automated alerts based on critical metrics
  • Regularly review and optimize your observability setup

❌ Common Mistakes to Avoid

  • Don't ignore error logging in production environments
  • Avoid overloading logs with unnecessary debug information
  • Don't rely solely on application-level metrics - monitor infrastructure as well
  • Never store sensitive data in logs or metrics

Quiz

Question 1 of 10

What is the primary benefit of using multi-stage builds in Docker?

  • Reduced image size
  • Faster build times
  • Both A and B
  • None of the above