☁️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