Skip to content

Gateway Routing

1. Tổng quan

Routing trong BANA dùng mẫu Service-Owned Prefix. Mỗi dịch vụ tự đặt base path (/v1/api/<service>) và khai báo route Traefik của nó qua Docker labels. Không tồn tại cấu hình route trung tâm.

Nguồn: Docker labels từ infrastructure/deployments/develop/*/docker-compose.yml, config tĩnh từ packages/gateway/config/traefik.yml

2. Pipeline Edge → Traefik → Middleware

Traffic vào qua Nginx (TLS termination), rồi Traefik nhận nó trên port :30080. Hai provider cấu hình cấp cho Traefik: Docker Provider đọc label container cho router/service, File Provider đọc middlewares.yml cho định nghĩa middleware dùng chung.

ComponentNguồnChi tiết
NginxLớp edge (:80/:443)TLS termination, thêm X-Forwarded-For/X-Real-IP
EntrypointTraefik web (:80)Nhận HTTP từ Nginx trên host port :30080
rate-limit@filemiddlewares.yml200 req/s theo IP, burst: 400, ipStrategy.depth: 1
circuit-breaker@filemiddlewares.ymlnet error > 10% hoặc P95 latency > 3s (vế tỷ lệ 5xx bị comment)
security-headers@filemiddlewares.ymlXSS filter, nosniff, frame deny, strip Server header
Docker Provider/var/run/docker.sockĐọc label traefik.*, tự tạo router + service
File Providerconfig/dynamic/middlewares.ymlMiddleware dùng chung, router dashboard + basic auth, hot reload

3. Ánh xạ Router → Service

Sau khi đi qua chuỗi middleware, Traefik khớp request với một trong các router đã đăng ký. Mỗi router được tự khám phá từ Docker labels. Router khớp forward tới dịch vụ backend tương ứng trên mạng Docker.

RouterRuleMiddlewaresGhi chú
identityPathPrefix(/v1/api/identity)rate-limit-auth@file, circuit-breaker@file, security-headers@fileRate limit auth nghiêm hơn
commercePathPrefix(/v1/api/commerce)rate-limit@file, circuit-breaker@file, security-headers@file
salePathPrefix(/v1/api/sale)rate-limit@file, circuit-breaker@file, security-headers@file
financePathPrefix(/v1/api/finance)rate-limit@file, circuit-breaker@file, security-headers@file
inventoryPathPrefix(/v1/api/inventory)rate-limit@file, circuit-breaker@file, security-headers@file
paymentPathPrefix(/v1/api/payment)rate-limit@file, circuit-breaker@file, security-headers@file
signal-restPathPrefix(/v1/api/signal)rate-limit@file, circuit-breaker@file, security-headers@file
signal-wsPathPrefix(/stream)WebSocket passthrough
payment-webhookHost(hook.bana.nexpando.vn)payment-add-prefix@docker, security-headers@filepriority: 100, path rewrite
portalPathPrefix(/)dashboard-auth@filepriority: 1, Portal dashboard

4. Components Traefik

ComponentNguồnMô tả
Docker Provider/var/run/docker.sockTheo dõi container đang chạy để tìm label traefik.*. Tự tạo router và service khi một container khởi động, và gỡ chúng khi nó dừng.
File Providerconfig/dynamic/middlewares.ymlNạp định nghĩa middleware dùng chung (rate-limit, circuit-breaker, security-headers, dashboard-auth). Theo dõi file để tìm thay đổi — chỉnh sửa có hiệu lực mà không restart Traefik.
RouterDocker labelsKhớp request đến theo rule (PathPrefix, Host). Mỗi container dịch vụ khai báo router riêng. Traefik đánh giá rule theo thứ tự priority (priority mặc định = độ dài chuỗi rule). Dùng priority tường minh khi nhiều router có thể khớp cùng request.
MiddlewareFile provider (@file) hoặc Docker labels (@docker)Xử lý request trước khi tới backend. Middleware dùng chung nằm trong file provider và phải được tham chiếu với hậu tố @file từ Docker labels.
ServiceDocker labelsĐích backend — Traefik phân giải IP container qua mạng Docker và forward tới port đã khai báo. Mỗi dịch vụ bao gồm cấu hình health check.
Health CheckDocker labelsTraefik chủ động poll /v1/api/<service>/health mỗi 30 giây. Nếu một dịch vụ fail, Traefik gỡ nó khỏi routing cho đến khi sức khỏe được khôi phục.

5. Vòng đời Request

6. Ba Mẫu Routing

BANA dùng ba mẫu routing riêng biệt tùy theo loại traffic:

Mẫu 1: REST API chuẩn

Mẫu phổ biến nhất. Một router khớp theo path prefix và áp dụng chuỗi middleware đầy đủ.

Client → Nginx → Traefik
  → Router: PathPrefix(/v1/api/commerce)
  → Middlewares: rate-limit@file → circuit-breaker@file → security-headers@file
  → Service: dev-nx-commerce:3000

Dùng bởi: identity, commerce, sale, finance, inventory, payment, signal (REST).

Mẫu 2: WebSocket (Không Middleware)

Kết nối WebSocket yêu cầu kết nối bền vững và không được gián đoạn bởi rate limiting hay circuit breaking. Router signal-ws không có middleware.

Client → Nginx (Connection: upgrade) → Traefik
  → Router: PathPrefix(/stream)
  → Middlewares: (không)
  → Service: dev-nx-signal:3000

Traefik hỗ trợ WebSocket nguyên bản — các header Connection: UpgradeUpgrade: websocket được forward tự động.

Mẫu 3: Webhook bên thứ 3 với Path Rewrite

Cho tích hợp bên thứ 3 legacy nơi URL đã đăng ký không thể đổi. Một router dựa-trên-Host khớp theo domain, và một middleware replacepathregex rewrite path để thêm prefix dịch vụ.

VNPAY → Nginx (Host: hook.bana.nexpando.vn) → Traefik
  → Router: Host(hook.bana.nexpando.vn), priority: 100
  → Middlewares: payment-add-prefix@docker → security-headers@file
  → Path rewrite: /v1/api/payments/* → /v1/api/payment/payments/*
  → Service: dev-nx-payment:3000

Middleware payment-add-prefix được định nghĩa trong Docker labels (không phải file provider) vì nó riêng cho chỉ dịch vụ payment.

Vì sao priority: 100? PathPrefix của Traefik là khớp prefix thuần — PathPrefix(/v1/api/payment) cũng khớp /v1/api/payments/.../v1/api/payment là prefix của /v1/api/payments. Không có priority tường minh, router payment (PathPrefix) và router payment-webhook (Host) có priority mặc định tương tự (tính từ độ dài chuỗi rule), và Traefik có thể route request webhook qua sai router — bỏ qua path rewrite. Đặt priority: 100 đảm bảo router webhook dựa-trên-Host luôn thắng khi có Host: hook.bana.nexpando.vn.

7. Cấu hình Docker Label

Dịch vụ chuẩn (Identity)

Nguồn: infrastructure/deployments/develop/identity/docker-compose.yml

yaml
services:
  dev-nx-identity:
    labels:
      - "traefik.enable=true"
      # Router
      - "traefik.http.routers.identity.rule=PathPrefix(`/v1/api/identity`)"
      - "traefik.http.routers.identity.entrypoints=web"
      - "traefik.http.routers.identity.middlewares=rate-limit-auth@file,circuit-breaker@file,security-headers@file"
      # Service
      - "traefik.http.services.identity.loadbalancer.server.port=3000"
      - "traefik.http.services.identity.loadbalancer.healthcheck.path=/v1/api/identity/health"
      - "traefik.http.services.identity.loadbalancer.healthcheck.interval=30s"

Dịch vụ WebSocket (Signal)

Nguồn: infrastructure/deployments/develop/signal/docker-compose.yml

Dịch vụ signal cần hai router — một cho REST API, một cho WebSocket:

yaml
services:
  dev-nx-signal:
    labels:
      - "traefik.enable=true"
      # REST API
      - "traefik.http.routers.signal-rest.rule=PathPrefix(`/v1/api/signal`)"
      - "traefik.http.routers.signal-rest.entrypoints=web"
      - "traefik.http.routers.signal-rest.middlewares=rate-limit@file,circuit-breaker@file,security-headers@file"
      # WebSocket
      - "traefik.http.routers.signal-ws.rule=PathPrefix(`/stream`)"
      - "traefik.http.routers.signal-ws.entrypoints=web"
      # Service dùng chung
      - "traefik.http.services.signal.loadbalancer.server.port=3000"
      - "traefik.http.services.signal.loadbalancer.healthcheck.path=/v1/api/signal/health"
      - "traefik.http.services.signal.loadbalancer.healthcheck.interval=30s"

Router Webhook bên thứ 3 (Payment)

Nguồn: infrastructure/deployments/develop/payment/docker-compose.yml

Dịch vụ payment có hai router dùng chung một service. Router thứ hai xử lý webhook bên thứ 3 từ một domain legacy (hook.bana.nexpando.vn) nơi URL đã đăng ký không thể đổi. Một middleware replacepathregex rewrite path để bao gồm prefix /payment.

yaml
services:
  dev-nx-payment:
    labels:
      - "traefik.enable=true"
      # Router — traffic API thông thường
      - "traefik.http.routers.payment.rule=PathPrefix(`/v1/api/payment`)"
      - "traefik.http.routers.payment.entrypoints=web"
      - "traefik.http.routers.payment.middlewares=rate-limit@file,circuit-breaker@file,security-headers@file"
      # Router — webhook bên thứ 3 (path legacy không có prefix /payment)
      # Priority 100 đảm bảo cái này thắng router PathPrefix khi Host khớp
      - "traefik.http.routers.payment-webhook.rule=Host(`hook.bana.nexpando.vn`)"
      - "traefik.http.routers.payment-webhook.priority=100"
      - "traefik.http.routers.payment-webhook.entrypoints=web"
      - "traefik.http.routers.payment-webhook.middlewares=payment-add-prefix@docker,security-headers@file"
      # Middleware — rewrite /v1/api/* thành /v1/api/payment/*
      - "traefik.http.middlewares.payment-add-prefix.replacepathregex.regex=^/v1/api/(.*)"
      - "traefik.http.middlewares.payment-add-prefix.replacepathregex.replacement=/v1/api/payment/$$1"
      # Service (dùng chung bởi cả hai router)
      - "traefik.http.services.payment.loadbalancer.server.port=3000"
      - "traefik.http.services.payment.loadbalancer.healthcheck.path=/v1/api/payment/health"
      - "traefik.http.services.payment.loadbalancer.healthcheck.interval=30s"

Luồng request cho webhook bên thứ 3:

API Portal (Catch-all, priority thấp nhất)

Nguồn: infrastructure/deployments/develop/gateway/docker-compose.yml

Portal là một dashboard Astro + React hiển thị sức khỏe dịch vụ, endpoint OpenAPI, thời gian phản hồi, và tổng quan hệ thống. Nó chạy như một container nginx phục vụ output tĩnh đã build và bắt mọi route không khớp.

yaml
services:
  dev-nx-portal:
    image: nginx:1.27-alpine
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.portal.rule=PathPrefix(`/`)"
      - "traefik.http.routers.portal.entrypoints=web"
      - "traefik.http.routers.portal.priority=1"
      - "traefik.http.routers.portal.middlewares=dashboard-auth@file"
      - "traefik.http.services.portal.loadbalancer.server.port=80"

Middleware dashboard-auth@file áp dụng cùng HTTP Basic Auth như Traefik dashboard (user: nx.eventry).

8. Poll Health Check

Traefik chủ động poll endpoint health của mỗi dịch vụ backend mỗi 30 giây. Dịch vụ không khỏe bị gỡ khỏi routing cho đến khi hồi phục. Việc này chạy độc lập với xử lý request.

9. Luồng Tự Khám phá

Khi một container dịch vụ mới khởi động với traefik.enable=true, điều này xảy ra tự động:

Không cần restart Traefik. Không có cấu hình route trung tâm để cập nhật. Dịch vụ khai báo route riêng và Traefik nhận nó trong vài giây.

10. Namespace Provider Middleware

Middleware được định nghĩa trong packages/gateway/config/dynamic/middlewares.yml (file provider) phải được tham chiếu với hậu tố @file trong Docker labels. Không có hậu tố, Traefik tìm middleware trong provider @docker và fail âm thầm.

yaml
# Đúng — tham chiếu middleware từ file provider
- "traefik.http.routers.commerce.middlewares=rate-limit@file,circuit-breaker@file,security-headers@file"

# Sai — Traefik tìm "rate-limit" trong Docker provider (không tồn tại)
- "traefik.http.routers.commerce.middlewares=rate-limit,circuit-breaker,security-headers"

11. Priority Router & Bẫy PathPrefix

PathPrefix của Traefik là khớp prefix thuần — nó không bắt buộc ranh giới /. Điều này có nghĩa:

Rule PathPrefixPath RequestKhớp?
PathPrefix(/v1/api/payment)/v1/api/payment/webhook
PathPrefix(/v1/api/payment)/v1/api/payments/vnpay/ipn (không ranh giới /!)
PathPrefix(/v1/api/sale)/v1/api/sale-orders (cùng vấn đề)

Khi nhiều router khớp cùng request, Traefik chọn cái có priority cao nhất. Priority mặc định được tính từ độ dài chuỗi rule, có thể dẫn đến kết quả khó lường.

Khi nào đặt priority tường minh:

  • Khi một router dựa-trên-Host trùng lặp với một router PathPrefix (như payment-webhook vs payment)
  • Khi một router catch-all (như SPA client với PathPrefix(/)) phải có priority thấp nhất
  • Khi hai rule PathPrefix trùng lặp (ví dụ /v1/api/sale vs /v1/api/sale-orders giả định)
yaml
# Số cao hơn = priority cao hơn (thắng)
- "traefik.http.routers.payment-webhook.priority=100"   # Thắng router PathPrefix
- "traefik.http.routers.client.priority=1"               # Thua mọi thứ khác

12. Cấu hình Base Path Dịch vụ

Mỗi dịch vụ đặt base path qua biến môi trường:

bash
# infrastructure/deployments/develop/<service>/.env.development
APP_ENV_SERVER_BASE_PATH=/v1/api/<service>
Dịch vụAPP_ENV_SERVER_BASE_PATH
identity/v1/api/identity
commerce/v1/api/commerce
sale/v1/api/sale
finance/v1/api/finance
inventory/v1/api/inventory
payment/v1/api/payment
signal/v1/api/signal

13. Thêm một Dịch vụ Mới

  1. Đặt APP_ENV_SERVER_BASE_PATH=/v1/api/<new-service> trong .env.development của dịch vụ
  2. Thêm Traefik labels vào docker-compose.yml của dịch vụ:
    yaml
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.<new-service>.rule=PathPrefix(`/v1/api/<new-service>`)"
      - "traefik.http.routers.<new-service>.entrypoints=web"
      - "traefik.http.routers.<new-service>.middlewares=rate-limit@file,circuit-breaker@file,security-headers@file"
      - "traefik.http.services.<new-service>.loadbalancer.server.port=3000"
      - "traefik.http.services.<new-service>.loadbalancer.healthcheck.path=/v1/api/<new-service>/health"
      - "traefik.http.services.<new-service>.loadbalancer.healthcheck.interval=30s"
  3. Thêm file compose vào mảng COMPOSE_FILES infrastructure/deployments/develop/dc
  4. Traefik tự khám phá dịch vụ mới — không cần đổi config gateway

14. Giao tiếp Liên-Dịch-vụ

Các dịch vụ gọi nhau (ví dụ commerce → identity) KHÔNG nên đi qua Traefik. Chúng giao tiếp trực tiếp qua mạng Docker:

bash
# Trực tiếp container-tới-container (không đi qua gateway)
APP_ENV_IDENTITY_SERVICE_BASE_URL=http://dev-nx-identity:3000/v1/api/identity
APP_ENV_COMMERCE_SERVICE_BASE_URL=http://dev-nx-commerce:3000/v1/api/commerce

15. Lộ trình Migrate K8s

Docker labels chuyển đổi cơ học sang Traefik IngressRoute CRDs:

yaml
# Docker label
traefik.http.routers.identity.rule=PathPrefix(`/v1/api/identity`)

# Tương đương K8s IngressRoute CRD
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
spec:
  routes:
    - match: PathPrefix(`/v1/api/identity`)
      services:
        - name: identity-service
          port: 3000

16. Trang liên quan

Tài liệuMô tả
Tổng quan GatewayThẻ định danh + service catalog
Kiến trúcGóc nhìn C4, luồng request-routing
MiddlewaresRate limiting, circuit breaker, security headers
ResilienceTrạng thái circuit breaker, health check, retry
Cấu hìnhBảng route Nginx cục bộ, hằng số
Quyết địnhADR-0001 (label routing), ADR-0002 (dev parity)

Proprietary and Confidential. Unauthorized copying, distribution, or use of this software is strictly prohibited.