Quay lại

Triển khai AWS X-Ray cho Django trên ECS Fargate – Chia sẻ kinh nghiệm thực tế Chuyên mục Devops    2025-10-29    4 Lượt xem    1 Lượt thích    comment-3 Created with Sketch Beta. 0 Bình luận

“Bạn sẽ không thể tối ưu thứ bạn không đo lường được.”
— đó là điều tôi rút ra sau nhiều lần phải mày mò debug performance trên hệ thống Django chạy trong ECS Fargate.

Vì sao tôi cần bật X-Ray cho Django?

Trong hệ thống microservice, việc hiểu được request đi qua đâu, truy cập DB thế nào, API nào chậm… là cực kỳ quan trọng.
Trước đây, tôi chỉ dựa vào CloudWatch logs và một vài metric Prometheus, nhưng nó không giúp tôi trả lời được câu hỏi “request này tốn bao nhiêu thời gian ở đâu?”.

Khi dùng AWS ECS (Fargate) cho backend Django, tôi bắt đầu tìm đến AWS X-Ray — công cụ tracing giúp quan sát request, database query, và external call (S3, SES, DynamoDB, v.v.) theo dạng end-to-end tracing.


Kiến trúc tổng quan

Triển khai X-Ray trên ECS gồm hai phần chính:

  1. X-Ray Daemon container (hoặc Agent): chạy cùng task để nhận dữ liệu trace qua UDP 2000/udp.

  2. Django app: được instrument bằng aws-xray-sdk để gửi trace về daemon.


Cấu hình trong Django

requirements.txt

aws-xray-sdk==2.12.1

Tôi tách cấu hình X-Ray riêng thành file xray_config.py, để dễ quản lý và bật/tắt bằng biến môi trường.

# core/xray_config/xray_config.py
from aws_xray_sdk.core import xray_recorder
from aws_xray_sdk.core import patch_all
import os
from django.conf import settings

ENABLE_XRAY = os.getenv("ENABLE_XRAY", "false").lower() == "true"

if ENABLE_XRAY:
    patch_all()
    xray_recorder.configure(
        context_missing='LOG_ERROR',
        plugins=('ECSPlugin', 'RDSPlugin'),
        daemon_address=settings.XRAY_RECORDER.get('DAEMON_ADDRESS'),
        service=settings.XRAY_RECORDER.get('AWS_XRAY_TRACING_NAME'),
        stream_sql=True  # ⚠️ Không hoạt động đầy đủ với psycopg2
    )

    from aws_xray_sdk.core import patch
    patch(['boto3', 'botocore', 'requests', 'psycopg2'])
else:
    xray_recorder.configure(context_missing='IGNORE_ERROR')

Ở đây tôi dùng ENABLE_XRAY để chủ động tắt X-Ray ở local/dev.
patch_all() sẽ tự động instrument các thư viện phổ biến như boto3, requests, psycopg2, v.v.


Tích hợp middleware trong Django

Trong config/settings/base.py, tôi thêm middleware X-Ray chỉ khi X-Ray bật:

if os.getenv("ENABLE_XRAY", "false").lower() == "true":
    MIDDLEWARE.append('aws_xray_sdk.ext.django.middleware.XRayMiddleware')

XRAY_RECORDER = {
    'AUTO_INSTRUMENT': True,
    'AWS_XRAY_CONTEXT_MISSING': 'LOG_ERROR',
    'AWS_XRAY_TRACING_NAME': os.getenv("AWS_XRAY_TRACING_NAME", "none"),
    'PLUGINS': ('ECSPlugin', 'RDSPlugin'),
    'DAEMON_ADDRESS': os.getenv("AWS_XRAY_DAEMON_ADDRESS", "127.0.0.1:2000"),
    'STREAM_SQL': True,
}

Với cấu hình này, mọi request Django sẽ được gắn trace header (X-Amzn-Trace-Id) và gửi dữ liệu về daemon container.


Import vào Django project

Để mọi thứ load sớm khi app khởi động, tôi import xray_config trong config/__init__.py:

from .celery import app as celery_app

try:
    from core.xray_config.xray_config import *
except ImportError:
    pass

__all__ = ("celery_app",)

Cấu hình ECS Task Definition

Trong ECS, tôi chạy hai container trong cùng một task:

  • my-project-api: container Django

  • xray-daemon: container daemon của AWS

Phần quan trọng nhất là các biến môi trường và cổng UDP 2000:

{
  "containerDefinitions": [
    {
      "name": "liveapp-api",
      "image": "311141543481.dkr.ecr.ap-northeast-1.amazonaws.com/my-project-stage-image-backend-api:702",
      "essential": true,
      "portMappings": [{ "containerPort": 8000, "protocol": "tcp" }],
      "environment": [
        { "name": "ENABLE_XRAY", "value": "true" },
        { "name": "AWS_XRAY_TRACING_NAME", "value": "my-project-api" },
        { "name": "AWS_XRAY_DAEMON_ADDRESS", "value": "127.0.0.1:2000" },
        { "name": "AWS_XRAY_TRACE_SQL", "value": "true" }
      ],
      "logConfiguration": {
        "logDriver": "awslogs",
        "options": {
          "awslogs-group": "/ecs/my-project-task",
          "awslogs-region": "ap-northeast-1",
          "awslogs-stream-prefix": "ecs"
        }
      }
    },
    {
      "name": "xray-daemon",
      "image": "amazon/aws-xray-daemon:latest",
      "essential": false,
      "portMappings": [
        { "containerPort": 2000, "hostPort": 2000, "protocol": "udp" }
      ],
      "memoryReservation": 256,
      "logConfiguration": {
        "logDriver": "awslogs",
        "options": {
          "awslogs-group": "/ecs/stage/xray-daemon",
          "awslogs-region": "ap-northeast-1",
          "awslogs-stream-prefix": "ecs"
        }
      }
    }
  ],
  "networkMode": "awsvpc",
  "requiresCompatibilities": ["FARGATE"],
  "cpu": "2048",
  "memory": "4096"
}

💡 Lưu ý:

  • AWS_XRAY_DAEMON_ADDRESS phải trỏ đến 127.0.0.1:2000 vì daemon và app chạy chung task.

  • Đừng quên mở port UDP 2000 trong cấu hình container.

  • Nên bật CloudWatch Logs cho cả hai container để dễ debug daemon.


Task Role (IAM)

Để X-Ray Daemon có thể gửi dữ liệu tracing lên AWS X-Ray Service, Task Role của ECS Task cần có quyền sau:

Policy Quyền Hạn
AWSXRayDaemonWriteAccess Cho phép gửi dữ liệu (segments) lên X-Ray.

Kết quả

Sau khi deploy, bạn có thể vào AWS X-Ray console → Service Map và sẽ thấy một sơ đồ các segment như thế này:

API → RDS → SES/S3

Từng request sẽ hiển thị trace ID, thời gian xử lý từng thành phần, và cả exception nếu có.


Lưu ý lớn: stream_sql và psycopg2

Một điểm tôi nhận ra khi bật stream_sql=TrueDjango dùng psycopg2 (PostgreSQL driver thuần Python) không được X-Ray hiển thị raw SQL query.
AWS chỉ hỗ trợ SQL tracing đầy đủ với Flask SQLAlchemy qua sqlalchemy plugin.

Vì vậy nếu bạn đang kỳ vọng thấy query trong X-Ray segment, sẽ chỉ thấy “execute” chứ không có text query — hoàn toàn bình thường.


Vấn đề thời gian tới: AWS X-Ray sẽ End-of-Support năm 2027

Đây là thông báo chính thức của AWS:

🕓 End-of-support notice – February 25, 2027
AWS X-Ray sẽ ngừng hỗ trợ các SDK và daemon. Sau ngày này, bạn sẽ không còn nhận được update, fix, hoặc release mới.
👉 AWS khuyến nghị chuyển sang OpenTelemetry.

Xem chi tiết tại: Migrating from X-Ray instrumentation to OpenTelemetry instrumentation

Điều này có nghĩa là:

  • Các hệ thống hiện dùng aws-xray-sdk như Django, Flask, Node.js, Java sẽ vẫn chạy, nhưng không được update.

  • Trong 2025–2026, nên lên kế hoạch chuyển sang OpenTelemetry Collector + AWS Distro for OpenTelemetry (ADOT).


Gợi ý hướng chuyển sang OpenTelemetry (OTel)

Nếu bạn triển khai mới hoặc có kế hoạch dài hạn, hãy cân nhắc:

  • Dùng OpenTelemetry SDK cho Python (opentelemetry-sdk, opentelemetry-instrumentation-django).

  • Dữ liệu trace có thể gửi đến:

    • AWS X-Ray backend (qua ADOT Collector), hoặc

    • Amazon CloudWatch Application Signals, Grafana Tempo, Datadog, v.v.

Cách chuyển dần: chạy song song OTel và X-Ray để so sánh trace trong thời gian đầu.


Bài học rút ra sau khi triển khai

  1. Đặt X-Ray daemon cùng task ECS giúp giảm độ trễ gửi trace và đơn giản network.

  2. Không bật X-Ray ở dev/local, vì SDK sẽ log rất nhiều và làm chậm request.

  3. Dùng biến môi trường (ENABLE_XRAY, AWS_XRAY_TRACING_NAME) để bật/tắt linh hoạt giữa các môi trường.

  4. Không kỳ vọng thấy raw SQL khi dùng psycopg2, đây là giới hạn SDK.

  5. Bắt đầu chuyển sang OpenTelemetry càng sớm càng tốt, vì sau 2027 X-Ray SDK sẽ không còn duy trì.


Kết luận

Việc bật AWS X-Ray cho Django chạy trên ECS Fargate giúp tôi “mở mắt” về cách hệ thống vận hành thật sự.
Tôi nhìn thấy bottleneck, thấy service nào gọi quá nhiều API, thấy transaction chậm vì truy vấn DB nào.

Nhưng quan trọng hơn cả — tôi học được rằng mọi công cụ đều có vòng đời của nó.
X-Ray rất hữu ích, nhưng tương lai sẽ là OpenTelemetry – tiêu chuẩn mở, linh hoạt hơn nhiều.
Nếu bạn đang vận hành hệ thống tương tự, hãy tận dụng X-Ray trong hiện tại, và bắt đầu chuẩn bị cho hành trình OTel ngay từ hôm nay.


✍️ Tác giả: Một DevOps engineer từng mất cả ngày chỉ để tìm vì sao request /api/users/ mất 2 giây trên ECS.
Nếu bạn đang triển khai tracing cho Django trên ECS, hy vọng bài viết này giúp bạn tiết kiệm ít nhất… vài giờ debug 😄


Bạn có muốn tôi tạo thêm bản mở rộng phần “Migration sang OpenTelemetry (OTel)” (gồm ví dụ cấu hình Django + ECS Collector) để bạn đăng thành phần 2 của series blog không?
Nếu bạn đồng ý, tôi có thể viết tiếp phần đó ngay, để bạn có “blog tiếp nối” khi nói về việc rời X-Ray.

Bình luận (0)

Michael Gough
Michael Gough
Michael Gough
Michael Gough
Michael Gough
Michael Gough
Michael Gough
Michael Gough
Michael Gough
Michael Gough
Michael Gough
Michael Gough
Michael Gough
Michael Gough
Michael Gough
Michael Gough

Bài viết liên quan

Learning English Everyday