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: - internalorder-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: - proxyuser-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 - internalproduct-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 - internalorder-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 - internalnetworks: 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_passkong: 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_passorder-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: trueuser-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: - internalapi-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: 40sorder-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-netuser-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