Chuyển đến nội dung chính

Bài 18: Docker cho Microservices Architecture

Thiết kế microservices với Docker, service discovery, API gateway với Traefik và Kong, distributed tracing với Jaeger, circuit breaker pattern, event-driven architecture, sidecar pattern và real-world microservices project.

🔒 DevSecOps — Bài 18 Bài 18: Docker cho Microservices Architecture

Docker từ Cơ bản đến Nâng cao

Phần 5: Production và Advanced Topics

xdev.asia

1. Microservices với Docker - Tổng quan

Docker là nền tảng lý tưởng cho microservices architecture vì mỗi service được đóng gói trong container riêng biệt, có thể develop, deploy và scale độc lập.

Monolith vs Microservices

Monolith:                    Microservices:
┌──────────────────┐         ┌───────┐  ┌───────┐
│   User Module    │         │ User  │  │ Order │
│   Order Module   │   →     │Service│  │Service│
│   Product Module │         └───────┘  └───────┘
│   Payment Module │         ┌───────┐  ┌───────┐
│   Notification   │         │Product│  │Payment│
└──────────────────┘         │Service│  │Service│
   1 container               └───────┘  └───────┘
                              4+ containers

2. Microservices Project Structure

ecommerce-microservices/
├── docker-compose.yml          # Orchestration
├── docker-compose.dev.yml      # Dev overrides
├── docker-compose.prod.yml     # Production overrides
├── .env
├── services/
│   ├── api-gateway/
│   │   ├── Dockerfile
│   │   ├── nginx.conf
│   │   └── ...
│   ├── user-service/
│   │   ├── Dockerfile
│   │   ├── package.json
│   │   └── src/
│   ├── product-service/
│   │   ├── Dockerfile
│   │   ├── requirements.txt
│   │   └── app/
│   ├── order-service/
│   │   ├── Dockerfile
│   │   ├── pom.xml
│   │   └── src/
│   └── notification-service/
│       ├── Dockerfile
│       └── ...
├── infrastructure/
│   ├── postgres/
│   ├── redis/
│   ├── rabbitmq/
│   └── monitoring/
└── scripts/
    ├── init-db.sh
    └── seed-data.sh

3. Service Discovery

Docker Compose cung cấp built-in DNS service discovery:

# docker-compose.yml
services:
  user-service:
    build: ./services/user-service
    networks:
      - internal

order-service: build: ./services/order-service environment: USER_SERVICE_URL: http://user-service:3000 PRODUCT_SERVICE_URL: http://product-service:8000 networks: - internal

product-service: build: ./services/product-service networks: - internal

networks: internal: driver: bridge

Consul Service Discovery

services:
  consul:
    image: hashicorp/consul:1.18
    ports:
      - "8500:8500"
    command: agent -server -bootstrap-expect=1 -ui -client=0.0.0.0

user-service: build: ./services/user-service environment: CONSUL_HTTP_ADDR: consul:8500 depends_on: - consul

4. API Gateway với Traefik

# docker-compose.yml
services:
  traefik:
    image: traefik:v3.1
    command:
      - "--api.insecure=true"
      - "--providers.docker=true"
      - "--providers.docker.exposedByDefault=false"
      - "--entrypoints.web.address=:80"
      - "--entrypoints.websecure.address=:443"
      - "--certificatesresolvers.letsencrypt.acme.httpchallenge=true"
      - "--certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web"
      - "[email protected]"
      - "--certificatesresolvers.letsencrypt.acme.storage=/acme/acme.json"
    ports:
      - "80:80"
      - "443:443"
      - "8080:8080"  # Dashboard
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - acme-data:/acme
    networks:
      - proxy

user-service: build: ./services/user-service labels: - "traefik.enable=true" - "traefik.http.routers.user.rule=PathPrefix(/api/users)" - "traefik.http.routers.user.entrypoints=web" - "traefik.http.services.user.loadbalancer.server.port=3000" networks: - proxy - internal

product-service: build: ./services/product-service labels: - "traefik.enable=true" - "traefik.http.routers.product.rule=PathPrefix(/api/products)" - "traefik.http.routers.product.entrypoints=web" - "traefik.http.services.product.loadbalancer.server.port=8000" deploy: replicas: 2 networks: - proxy - internal

order-service: build: ./services/order-service labels: - "traefik.enable=true" - "traefik.http.routers.order.rule=PathPrefix(/api/orders)" - "traefik.http.routers.order.entrypoints=web" - "traefik.http.services.order.loadbalancer.server.port=8080" networks: - proxy - internal

networks: proxy: driver: bridge internal: driver: bridge

volumes: acme-data:

5. API Gateway với Kong

services:
  kong-database:
    image: postgres:16-alpine
    environment:
      POSTGRES_DB: kong
      POSTGRES_USER: kong
      POSTGRES_PASSWORD_FILE: /run/secrets/kong_db_pass
    volumes:
      - kong-db:/var/lib/postgresql/data
    secrets:
      - kong_db_pass

kong: image: kong:3.7 environment: KONG_DATABASE: postgres KONG_PG_HOST: kong-database KONG_PG_USER: kong KONG_PROXY_ACCESS_LOG: /dev/stdout KONG_ADMIN_ACCESS_LOG: /dev/stdout KONG_PROXY_ERROR_LOG: /dev/stderr KONG_ADMIN_ERROR_LOG: /dev/stderr KONG_ADMIN_LISTEN: "0.0.0.0:8001" ports: - "8000:8000" # Proxy - "8001:8001" # Admin API depends_on: - kong-database

volumes: kong-db:

6. Event-Driven Architecture

# Message queue cho async communication
services:
  rabbitmq:
    image: rabbitmq:3.13-management-alpine
    ports:
      - "5672:5672"     # AMQP
      - "15672:15672"   # Management UI
    environment:
      RABBITMQ_DEFAULT_USER: admin
      RABBITMQ_DEFAULT_PASS_FILE: /run/secrets/rabbitmq_pass
    volumes:
      - rabbitmq-data:/var/lib/rabbitmq
    secrets:
      - rabbitmq_pass

order-service: build: ./services/order-service environment: RABBITMQ_URL: amqp://admin:${RABBITMQ_PASS}@rabbitmq:5672 depends_on: - rabbitmq

notification-service: build: ./services/notification-service environment: RABBITMQ_URL: amqp://admin:${RABBITMQ_PASS}@rabbitmq:5672 depends_on: - rabbitmq

Event Flow

Order Created:
┌─────────────┐    publish     ┌──────────┐    consume    ┌──────────────────┐
│ Order       │ ──────────→    │ RabbitMQ │ ──────────→   │ Notification     │
│ Service     │  order.created │ Exchange │               │ Service          │
└─────────────┘                │  Queue   │ ──────────→   │ (email/sms)      │
                               └──────────┘  consume      └──────────────────┘
                                             │             ┌──────────────────┐
                                             └──────────→  │ Inventory        │
                                               consume     │ Service          │
                                                           └──────────────────┘

7. Distributed Tracing với Jaeger

services:
  jaeger:
    image: jaegertracing/all-in-one:1.55
    ports:
      - "16686:16686"  # Jaeger UI
      - "4318:4318"    # OTLP HTTP
    environment:
      COLLECTOR_OTLP_ENABLED: true

user-service: build: ./services/user-service environment: OTEL_EXPORTER_OTLP_ENDPOINT: http://jaeger:4318 OTEL_SERVICE_NAME: user-service

order-service: build: ./services/order-service environment: OTEL_EXPORTER_OTLP_ENDPOINT: http://jaeger:4318 OTEL_SERVICE_NAME: order-service

8. Circuit Breaker Pattern

// Sử dụng opossum (Node.js circuit breaker)
const CircuitBreaker = require('opossum');

const options = { timeout: 3000, // 3 second timeout errorThresholdPercentage: 50, // Open circuit at 50% failures resetTimeout: 10000 // Try again after 10 seconds };

const breaker = new CircuitBreaker(callUserService, options);

breaker.on('open', () => console.log('Circuit OPEN - calls blocked')); breaker.on('halfOpen', () => console.log('Circuit HALF-OPEN - testing')); breaker.on('close', () => console.log('Circuit CLOSED - normal'));

// Fallback khi circuit open breaker.fallback(() => ({ status: 'service unavailable', data: getCachedData() }));

async function getUser(userId) { return breaker.fire(userId); }

9. Sidecar Pattern

# Sidecar cho logging, proxy, etc.
services:
  api:
    build: ./services/api
    networks:
      - internal

api-sidecar: image: envoyproxy/envoy:v1.30-latest volumes: - ./envoy.yaml:/etc/envoy/envoy.yaml network_mode: "service:api"

log-collector: image: fluent/fluent-bit:3.0 volumes: - /var/lib/docker/containers:/var/lib/docker/containers:ro networks: - internal

10. Health Checks cho Microservices

services:
  user-service:
    build: ./services/user-service
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 40s

order-service: build: ./services/order-service healthcheck: test: ["CMD", "wget", "--spider", "-q", "http://localhost:8080/actuator/health"] interval: 30s timeout: 10s retries: 3 depends_on: user-service: condition: service_healthy postgres: condition: service_healthy

postgres: image: postgres:16-alpine healthcheck: test: ["CMD-SHELL", "pg_isready -U postgres"] interval: 10s timeout: 5s retries: 5

11. Database per Service

# Mỗi service có database riêng
services:
  user-db:
    image: postgres:16-alpine
    environment:
      POSTGRES_DB: users
      POSTGRES_USER: user_svc
    volumes:
      - user-db-data:/var/lib/postgresql/data
    networks:
      - user-net

user-service: build: ./services/user-service environment: DATABASE_URL: postgresql://user_svc:pass@user-db:5432/users networks: - user-net - internal

product-db: image: mongo:7.0 volumes: - product-db-data:/data/db networks: - product-net

product-service: build: ./services/product-service environment: MONGO_URI: mongodb://product-db:27017/products networks: - product-net - internal

order-db: image: postgres:16-alpine environment: POSTGRES_DB: orders volumes: - order-db-data:/var/lib/postgresql/data networks: - order-net

networks: user-net: product-net: order-net: internal:

volumes: user-db-data: product-db-data: order-db-data:

12. Tổng kết

Trong bài này, bạn đã học:

  • Thiết kế microservices project structure

  • Service discovery với Docker DNS và Consul

  • API Gateway với Traefik và Kong

  • Event-driven architecture với RabbitMQ

  • Distributed tracing (Jaeger), circuit breaker

  • Sidecar pattern, health checks, database per service

Bài tiếp theo: Docker với Kubernetes - Migration Path