•10 min read

Distributed Tracing với OpenTelemetry

Distributed Tracing với OpenTelemetry

Khi kiến trúc phần mềm hiện đại ngày càng chuyển dịch sang các mô hình microservices và serverless, sự phức tạp trong việc quan sát, giám sát và gỡ lỗi hệ thống đã tăng vọt. Khi một yêu cầu của người dùng đi qua hàng chục microservices, cơ sở dữ liệu và API bên ngoài, việc xác định nguồn gốc của lỗi hoặc nút thắt cổ chai về hiệu suất bằng cách sử dụng các chỉ số ghi nhật ký truyền thống trở nên gần như không thể. Đây là lúc distributed tracing phát huy tác dụng, và OpenTelemetry đã nổi lên như một tiêu chuẩn mã nguồn mở dứt khoát cho khả năng quan sát (observability).

Trong phần tìm hiểu sâu này, chúng ta sẽ khám phá các nền tảng kiến trúc của OpenTelemetry, hiểu cách distributed tracing hoạt động ở cấp độ cơ bản, và xem xét cách instrument các ứng dụng, truyền tải ngữ cảnh (context), và xuất dữ liệu telemetry để phân tích.

Audio Briefing
0:00 / 0:00

Giải phẫu một Trace

Để hiểu OpenTelemetry, trước tiên chúng ta phải hiểu các thành phần cơ bản của một trace phân tán: Traces và Spans.

Một Trace đại diện cho toàn bộ hành trình của một yêu cầu khi nó di chuyển qua một hệ thống phân tán. Nó là một đồ thị có hướng không chu trình (DAG) của Spans.

Một Span là khối xây dựng cơ bản của một trace. Nó đại diện cho một hoạt động duy nhất trong một trace, chẳng hạn như một truy vấn cơ sở dữ liệu, một yêu cầu HTTP, hoặc một lời gọi hàm. Mỗi span chứa một tập hợp dữ liệu có cấu trúc:

  • Tên hoạt động: Mô tả dễ đọc về hoạt động (ví dụ: GET /users/:id).
  • Thời gian bắt đầu và kết thúc: Dấu thời gian cho biết chính xác thời lượng của hoạt động.
  • Span ID: Một định danh duy nhất cho chính span đó.
  • Trace ID: Một định danh duy nhất cho toàn bộ trace, được chia sẻ bởi tất cả các span trong trace.
  • Parent Span ID: ID của span đã kích hoạt hoạt động này, cho phép hệ thống tracing xây dựng hệ thống phân cấp và mối quan hệ nhân quả (DAG). Nếu một span không có parent, nó được coi là Root Span.
  • Attributes: Các cặp khóa-giá trị cung cấp ngữ cảnh bổ sung (ví dụ: http.status_code, db.statement, user.id).
  • Events: Các bản ghi hoặc chú thích có dấu thời gian được đính kèm vào span, hữu ích để ghi lại các sự kiện cụ thể trong vòng đời của hoạt động.
  • Status: Một chỉ báo về thành công hay thất bại (ví dụ: OK hoặc ERROR).

Bằng cách tập hợp các span này dựa trên Trace ID và mối quan hệ parent-child của chúng, các nền tảng observability có thể hình dung toàn bộ đường dẫn thực thi, xác định độ trễ, các nút thắt cổ chai và lỗi.

Advertisement

Kiến trúc OpenTelemetry

OpenTelemetry (thường được viết tắt là OTel) không phải là một hệ thống backend; nó là một tập hợp các API, SDK và công cụ độc lập với nhà cung cấp, được thiết kế để tạo, thu thập và xuất dữ liệu telemetry (traces, metrics và logs). Kiến trúc cốt lõi bao gồm một số thành phần chính:

1. API (Giao diện lập trình ứng dụng)

API cung cấp một giao diện độc lập với ngôn ngữ được sử dụng để instrument code. Nó định nghĩa các kiểu dữ liệu trừu tượng và các hoạt động cần thiết để tạo span, metrics và logs. Vì API được tách rời khỏi phần triển khai, các tác giả thư viện có thể thêm instrumentation OpenTelemetry vào code của họ mà không buộc người dùng cuối phải sử dụng một triển khai cụ thể.

2. SDK (Bộ công cụ phát triển phần mềm)

SDK là phần triển khai API dành riêng cho từng ngôn ngữ. Nó cung cấp logic cụ thể để xử lý, lấy mẫu và xuất dữ liệu telemetry được tạo bởi API. SDK xử lý các khái niệm như:

  • Sampling: Quyết định trace nào sẽ được thu thập và xuất để giảm chi phí overhead và lưu trữ (ví dụ: Head-based sampling, Tail-based sampling).
  • Processors: Chuyển đổi hoặc lọc các span trước khi chúng được xuất (ví dụ: nhóm các span để cải thiện hiệu quả mạng).
  • Exporters: Gửi dữ liệu đã xử lý đến một backend hoặc collector được chỉ định (ví dụ: OTLP Exporter, Jaeger Exporter, Prometheus Exporter).

3. OpenTelemetry Collector

OpenTelemetry Collector là một proxy rất linh hoạt, độc lập với nhà cung cấp, có thể nhận, xử lý và xuất dữ liệu telemetry. Mặc dù các ứng dụng có thể xuất dữ liệu trực tiếp đến một backend, nhưng việc sử dụng Collector là một mô hình kiến trúc được khuyến nghị. Collector cung cấp một cách thống nhất để xử lý telemetry trên các môi trường đa dạng và tách rời ứng dụng khỏi backend observability.

Collector hoạt động bằng cách sử dụng kiến trúc pipeline:

  • Receivers: Chấp nhận dữ liệu telemetry ở nhiều định dạng khác nhau (ví dụ: OTLP, Jaeger, Zipkin, Prometheus).
  • Processors: Sửa đổi, lọc hoặc làm phong phú dữ liệu đang truyền. Các trường hợp sử dụng phổ biến bao gồm nhóm, loại bỏ thông tin nhận dạng cá nhân (PII) hoặc thêm các thuộc tính môi trường (ví dụ: kubernetes.cluster.name).
  • Exporters: Gửi dữ liệu đã xử lý đến một hoặc nhiều backend observability (ví dụ: DataDog, Honeycomb, AWS X-Ray, Elasticsearch).

Bằng cách triển khai Collector dưới dạng sidecar hoặc gateway trung tâm, các tổ chức có thể chuyển đổi nhà cung cấp observability hoặc gửi dữ liệu đến nhiều đích chỉ với những thay đổi cấu hình đơn giản, mà không cần sửa đổi code ứng dụng.

Context Propagation: Chất kết dính của Distributed Tracing

Một trong những thách thức phức tạp nhất trong distributed tracing là duy trì tính liên tục của một trace qua các ranh giới mạng. Khi Service A thực hiện một yêu cầu HTTP đến Service B, làm thế nào Service B biết nó đang tham gia vào một trace cụ thể?

Câu trả lời là Context Propagation.

Context Propagation là cơ chế mà các định danh trace và trạng thái được truyền giữa các dịch vụ độc lập. Điều này đạt được bằng cách inject metadata tracing vào các header của giao thức truyền thông (ví dụ: HTTP headers, gRPC metadata, Kafka message headers) ở phía client, và trích xuất nó ở phía server.

OpenTelemetry hỗ trợ các định dạng propagation tiêu chuẩn, đáng chú ý nhất là đặc tả W3C Trace Context. W3C Trace Context chuẩn hóa hai HTTP header quan trọng:

  • traceparent: Chứa Trace ID, Parent Span ID và các cờ sampling. Định dạng: 00-{trace-id}-{parent-span-id}-{trace-flags}.
  • tracestate: Cung cấp thông tin tracing dành riêng cho nhà cung cấp, cho phép nhiều hệ thống tracing hoạt động cùng nhau một cách liền mạch.

Ngoài ra, OpenTelemetry hỗ trợ Baggage, một cơ chế để truyền các cặp khóa-giá trị tùy ý (ví dụ: một tenant_id hoặc user_role) trên toàn bộ trace. Không giống như các thuộc tính span, chỉ giới hạn trong một span duy nhất, các mục Baggage chảy xuống tất cả các dịch vụ tiếp theo, cho phép quan sát sâu sắc các ngữ cảnh kinh doanh xuyên suốt.

Instrumenting Ứng dụng

Instrumentation là quá trình tích hợp OpenTelemetry vào một ứng dụng để tạo telemetry. OpenTelemetry cung cấp hai cách tiếp cận chính:

Tự động Instrumentation

Đối với nhiều ngôn ngữ (như Java, Python, Node.js và .NET), OpenTelemetry cung cấp các tác nhân hoặc thư viện tự động instrumentation. Các công cụ này sử dụng thao tác bytecode, monkey-patching hoặc runtime hooks để tự động instrument các framework phổ biến, HTTP client và trình điều khiển cơ sở dữ liệu mà không yêu cầu bất kỳ thay đổi code nào. Điều này mang lại giá trị ngay lập tức, thu thập các trace từ đầu đến cuối một cách dễ dàng.

Thủ công Instrumentation

Mặc dù tự động instrumentation cung cấp một nền tảng vững chắc, nhưng để có được khả năng quan sát sâu sắc, dành riêng cho doanh nghiệp, cần phải instrumentation thủ công. Điều này liên quan đến việc sử dụng trực tiếp OpenTelemetry API trong code ứng dụng để tạo các span tùy chỉnh, thêm các thuộc tính liên quan đến doanh nghiệp và ghi lại các sự kiện cụ thể.

from opentelemetry import trace

# Acquire a tracer
tracer = trace.get_tracer(__name__)

def process_payment(order_id: str, amount: float):
    # Start a new span manually
    with tracer.start_as_current_span("process_payment") as span:
        # Add attributes to the span
        span.set_attribute("payment.order_id", order_id)
        span.set_attribute("payment.amount", amount)
        
        try:
            # Simulate processing logic
            result = invoke_payment_gateway(amount)
            span.set_attribute("payment.status", "success")
            span.add_event("Payment successfully processed by gateway.")
            return result
        except Exception as e:
            # Record the exception and mark the span as failed
            span.record_exception(e)
            span.set_status(trace.StatusCode.ERROR, "Payment processing failed")
            raise

Bằng cách kết hợp tự động instrumentation để bao phủ rộng rãi và thủ công instrumentation để có được thông tin chi tiết sâu sắc, dành riêng cho từng lĩnh vực, các nhóm kỹ thuật có thể đạt được khả năng quan sát toàn diện.

Advertisement

Sức mạnh của Giao thức OTLP

Cốt lõi của khả năng tương tác của OpenTelemetry là Giao thức OpenTelemetry (OTLP). OTLP là giao thức gốc được sử dụng để truyền dữ liệu telemetry giữa SDK, Collector và các backend observability. Được thiết kế để có hiệu suất cao và hiệu quả, OTLP định nghĩa một lược đồ nghiêm ngặt cho traces, metrics và logs, sử dụng các giao thức gRPC hoặc HTTP với Protocol Buffers (protobuf) để tuần tự hóa.

Việc chuẩn hóa trên OTLP ngăn chặn tình trạng khóa nhà cung cấp. Thay vì triển khai các API, SDK và giao thức dành riêng cho nhà cung cấp, một tổ chức chuẩn hóa hoàn toàn trên OTLP. Tình trạng khóa nhà cung cấp chỉ còn lại trong cấu hình của exporter của Collector, đảm bảo rằng code ứng dụng hoàn toàn độc lập với nhà cung cấp.

Kết luận

Distributed tracing với OpenTelemetry không còn là một thứ xa xỉ; nó là một điều tuyệt đối cần thiết để vận hành các hệ thống phân tán, có khả năng phục hồi ở quy mô lớn. Bằng cách hiểu các khái niệm cốt lõi về traces và spans, tận dụng tính linh hoạt của OpenTelemetry Collector, đảm bảo truyền ngữ cảnh mạnh mẽ bằng cách sử dụng các tiêu chuẩn W3C, và kết hợp tự động và thủ công instrumentation, các tổ chức kỹ thuật có thể làm sáng tỏ những góc khuất trong kiến trúc của họ. Khi dự án tiếp tục trưởng thành, mở rộng hỗ trợ mạnh mẽ cho metrics và logging, OpenTelemetry sẵn sàng trở thành Rosetta Stone phổ quát của observability, trao quyền cho các kỹ sư để hiểu, gỡ lỗi và tối ưu hóa hệ thống của họ với sự rõ ràng và tự tin chưa từng có.

Bạn cũng có thể thích

Share this article:

Stay Updated

Get the latest posts delivered straight to your inbox.

Free Developer Utilities

Free In-Browser Developer Tools

Clean AI CLI logs, build cron expressions, decode JWTs, and calculate chmod permissions offline.

Explore Tools
Advertisement
Điện toán lượng tử cho nhà phát triển
tech

Điện toán lượng tử cho nhà phát triển

Hướng dẫn dành cho nhà phát triển về điện toán lượng tử: viết thuật toán lượng tử với Qiskit, hiểu các cổng lượng tử và mô phỏng mạch trên phần cứng cổ điển.

Read more
Hiệu suất WebGL và Three.js
tech

Hiệu suất WebGL và Three.js

Tối ưu hóa hiệu suất web 3D với WebGL và Three.js: nắm vững việc nhóm lệnh vẽ, phân tích shader, tạo thể hiện hình học và quản lý bộ nhớ GPU.

Read more
Kiến trúc mạng Zero Trust
tech

Kiến trúc mạng Zero Trust

Triển khai Kiến trúc mạng Zero Trust trong các đám mây hiện đại: loại bỏ các giả định về vành đai với mTLS, proxy nhận biết danh tính và phân đoạn vi mô.

Read more