Trace ID trong Istio

·

11 min read

Trong Istio, các Trace ID được sử dụng để theo dõi và quản lý các yêu cầu xuyên suốt trong hệ thống phân tán, như các microservices, giúp theo dõi toàn bộ vòng đời của một request từ dịch vụ này sang dịch vụ khác. Đây là cách Istio định nghĩa và sử dụng Trace ID:

1. Trace ID là gì trong Istio?

Trace ID là một mã định danh duy nhất cho mỗi yêu cầu (request) đi qua hệ thống, giúp liên kết tất cả các bước (span) của yêu cầu đó. Mỗi yêu cầu được gửi từ một client đến server sẽ được gắn một Trace ID, và Trace ID này sẽ được truyền qua nhiều dịch vụ khi yêu cầu đi qua hệ thống.

Các Trace ID được sử dụng để:

  • Liên kết các logs, metrics, và traces với nhau.

  • Giúp DevOps hoặc SREs hiểu rõ luồng dữ liệu của một request.

  • Dễ dàng xác định và debug các vấn đề về hiệu năng hoặc lỗi của hệ thống.

2. Cách Trace ID được tạo và phân phối trong Istio

Istio sử dụng Distributed Tracing để gán Trace ID cho các yêu cầu và phân phối chúng trong hệ thống. Istio không tự tạo Trace ID mà phụ thuộc vào các công cụ tracing như Jaeger, Zipkin, hoặc OpenTelemetry để tạo và quản lý Trace ID.

a. Envoy Proxy:

Mỗi sidecar Envoy proxy trong mesh sẽ tự động chèn hoặc duy trì Trace ID trong header HTTP của các request. Envoy sẽ thực hiện các bước sau:

  • Nhận yêu cầu đầu tiên: Khi một request đi vào hệ thống, nếu nó chưa có Trace ID (VD: đến từ bên ngoài), Envoy sẽ tạo mới Trace ID.

  • Truyền Trace ID: Khi yêu cầu được truyền qua các dịch vụ khác, Trace ID sẽ được gắn vào các header HTTP để Envoy trong từng service có thể theo dõi.

  • Thực hiện span cho mỗi hop: Envoy cũng tạo ra các span cho từng hop của yêu cầu. Một Trace ID có thể chứa nhiều span, mỗi span là một đoạn của quá trình xử lý yêu cầu.

b. Header thông dụng để truyền Trace ID:

Envoy proxy gắn Trace ID vào header của các request để duy trì tracing xuyên suốt. Các header này bao gồm:

  • x-request-id: Một ID để theo dõi request.

  • x-b3-traceid: Trace ID được tạo theo chuẩn B3 (Zipkin, Jaeger, OpenTelemetry).

  • x-b3-spanid: ID của một span, một phần nhỏ của trace toàn bộ.

  • x-b3-parentspanid: Span ID của span cha (nếu có).

  • x-b3-sampled: Biểu thị việc trace có được sampling hay không.

  • x-b3-flags: Đánh dấu việc trace có được ghi lại bắt buộc hay không.

Nếu một request không có các header này khi đến Envoy (ví dụ từ một client bên ngoài), Envoy sẽ tạo mới các header này và gửi chúng dọc theo các hop.

3. Quy trình sử dụng Trace ID trong Istio

Dưới đây là quy trình làm việc của Trace ID trong Istio:

a. Khi một request bắt đầu:

  • Khi một request đi từ một client bên ngoài vào một service trong mesh, Envoy sidecar của service đầu tiên sẽ kiểm tra xem header của request có chứa Trace ID không.

    • Nếu không có Trace ID, Envoy sẽ tạo mới Trace ID và gắn vào header HTTP (chẳng hạn x-b3-traceid).

    • Nếu đã có Trace ID (VD: request từ một dịch vụ bên ngoài như một hệ thống khác đã bật tracing), Envoy sẽ tiếp tục sử dụng Trace ID này.

b. Truyền Trace ID giữa các dịch vụ:

  • Khi request được chuyển từ service này sang service khác trong mesh, Envoy sidecar tại mỗi hop sẽ truyền Trace ID và Span ID qua các header HTTP.

  • Mỗi Envoy sidecar ghi lại các span (đoạn thời gian của mỗi request) và liên kết chúng lại với Trace ID.

  • Mỗi khi request đi qua Envoy proxy của một service mới, một span mới được tạo ra và gắn vào Trace ID hiện có. Các spans này cung cấp thông tin chi tiết về hành trình của request, như thời gian xử lý, nơi xảy ra sự cố, hoặc bất kỳ chậm trễ nào.

c. Ghi nhận và phân tích trace:

  • Các traces và spans được gửi tới hệ thống thu thập trace như Jaeger, Zipkin, hoặc OpenTelemetry để lưu trữ và phân tích.

  • Tại đây, các Trace ID được tập hợp lại, giúp DevOps hoặc SRE có thể xem toàn bộ luồng request, thời gian phản hồi của từng dịch vụ, và dễ dàng xác định các điểm nghẽn trong hệ thống.

4. Điều gì xảy ra khi Trace ID không hợp lệ hoặc thiếu?

  • Thiếu Trace ID: Nếu một request không có Trace ID (hoặc các header liên quan như x-b3-traceid), Envoy sẽ tự động tạo mới Trace ID khi request đầu tiên đi vào mesh.

  • Trace ID không hợp lệ: Nếu Trace ID không hợp lệ hoặc không được công nhận, Envoy sẽ bỏ qua nó và tự tạo Trace ID mới. Việc này đảm bảo rằng tracing luôn hoạt động ngay cả khi request từ các hệ thống không tương thích hoặc không bật tracing.

5. Tính năng Sampling của Trace ID trong Istio

Trong môi trường sản xuất với số lượng lớn request, việc trace mọi request có thể gây tốn tài nguyên. Do đó, Istio hỗ trợ tính năng sampling để chỉ trace một phần các yêu cầu dựa trên tỷ lệ sampling.

  • Envoy sẽ sử dụng header x-b3-sampled để kiểm soát việc trace request có được kích hoạt hay không.

  • Sampling rate có thể được cấu hình tại Envoy để quyết định bao nhiêu phần trăm request sẽ được traced. Điều này giúp giảm tải tài nguyên nhưng vẫn thu thập được dữ liệu đủ cho việc phân tích.

6. Ví dụ về Trace ID trong Istio

Khi một request di chuyển qua nhiều microservices trong Istio mesh:

  1. Dịch vụ A nhận request từ client → Envoy proxy của A tạo Trace ID.

  2. Request được chuyển từ Dịch vụ A sang Dịch vụ B → Envoy của A truyền Trace ID trong header request sang Envoy của B.

  3. Envoy của Dịch vụ B nhận request, tiếp tục gắn span mới với Trace ID hiện tại, rồi chuyển request tới Dịch vụ C.

Mỗi hop đều được ghi lại thông qua một span, và tất cả các span đều được gắn với cùng một Trace ID, giúp bạn có thể xem toàn bộ hành trình của request.

Vai trò của Jaeger và Zipkin trong Istio

JaegerZipkin là các hệ thống Distributed Tracing được sử dụng để thu thập, lưu trữ, và hiển thị các trace từ các ứng dụng trong môi trường microservices. Trong Istio, chúng đóng vai trò quan trọng trong việc giám sát và phân tích hiệu suất, bằng cách thu thập các traces từ các dịch vụ trong mesh. Cụ thể:

  • JaegerZipkin giúp theo dõi các yêu cầu (request) khi chúng di chuyển qua các dịch vụ khác nhau trong mesh.

  • Cả hai công cụ này đều hiển thị một cách chi tiết các traces, giúp người dùng hiểu rõ được luồng yêu cầu, phát hiện các vấn đề về độ trễ hoặc lỗi.

  • Chúng cung cấp giao diện để phân tích các traces, xác định các điểm nghẽn trong hệ thống, và tối ưu hóa hiệu suất ứng dụng.

Jaeger:

  • Là một hệ thống tracing mã nguồn mở, ban đầu được phát triển bởi Uber.

  • Hỗ trợ tracing toàn bộ các thành phần trong ứng dụng từ client đến server.

  • Giúp xác định các bottleneck trong hệ thống và theo dõi luồng dữ liệu trong thời gian thực.

Zipkin:

  • Là một hệ thống tracing phân tán được phát triển bởi Twitter.

  • Được xây dựng trên chuẩn B3 propagation để truyền Trace ID, Span ID giữa các dịch vụ.

  • Tương tự như Jaeger, Zipkin giúp hiển thị, phân tích traces và cung cấp các công cụ để debug vấn đề hiệu suất.

Cách thức Envoy Proxy kết nối với Jaeger và Zipkin

Envoy proxy, chạy dưới dạng sidecar trong Istio Service Mesh, đóng vai trò là người thu thập các trace khi một yêu cầu đi qua các dịch vụ. Envoy sau đó sẽ gửi các thông tin tracing (trace data) đến hệ thống Jaeger hoặc Zipkin. Quá trình này diễn ra như sau:

a. Cấu hình Envoy để gửi traces đến Jaeger hoặc Zipkin

Trong Istio, Envoy proxy được cấu hình tự động để gửi dữ liệu trace đến Jaeger hoặc Zipkin thông qua cấu hình từ Istio Control Plane. Bạn có thể chỉ định hệ thống tracing mà bạn muốn sử dụng (Jaeger, Zipkin, hoặc OpenTelemetry) thông qua cài đặt cấu hình của Istio.

Các bước cấu hình kết nối:
  1. Chỉ định endpoint của Jaeger hoặc Zipkin:

    • Envoy cần biết địa chỉ của Jaeger hoặc Zipkin để có thể gửi trace data đến đúng nơi.

    • Bạn cần cấu hình Tracing Service URL cho Envoy bằng cách chỉnh sửa file cấu hình hoặc qua Helm Chart khi cài đặt Istio.

Ví dụ:

  1. Kích hoạt tính năng tracing trong Istio: Trong Istio, bạn có thể bật hoặc tắt tính năng tracing thông qua istioctl hoặc file cấu hình IstioOperator

  2. Envoy gắn Trace ID vào mỗi yêu cầu: Khi request đến từ client và đi qua Envoy proxy, nếu không có Trace ID trong header, Envoy sẽ tạo mới Trace ID. Sau đó, Trace ID này sẽ được truyền qua các dịch vụ bằng các header HTTP (chẳng hạn x-b3-traceid cho Zipkin).

  3. Gửi trace data tới Jaeger hoặc Zipkin:

    • Envoy thu thập tất cả các trace của một request và gửi chúng tới Jaeger hoặc Zipkin dựa trên cấu hình tracing đã được thiết lập.

    • Envoy sử dụng HTTP POST để gửi trace data tới collector của Jaeger hoặc Zipkin. Collector sau đó sẽ xử lý và lưu trữ các trace.

Dữ liệu trace bao gồm thông tin như:

  • Trace ID: ID đại diện cho toàn bộ trace.

  • Span ID: ID cho từng bước (span) trong trace.

  • Thời gian bắt đầu và kết thúc của từng span.

  • Metadata bổ sung như trạng thái HTTP, lỗi, hoặc thông tin bổ sung về dịch vụ.

b. Sampling và cấu hình tần suất tracing:

Envoy có thể được cấu hình để chỉ gửi một phần nhỏ các trace (gọi là sampling) thay vì gửi tất cả các yêu cầu để tránh quá tải hệ thống. Tỷ lệ sampling này có thể được định cấu hình qua Istio, ví dụ:

apiVersion: install.istio.io/v1alpha1
kind: IstioOperator
spec:
  values:
    pilot:
      traceSampling: 1.0  # 100% requests are traced

Nếu muốn tracing chỉ một phần request (VD: 10%):

apiVersion: install.istio.io/v1alpha1
kind: IstioOperator
spec:
  values:
    pilot:
      traceSampling: 0.1  # Only 10% of requests are traced

Các bước cài đặt Jaeger hoặc Zipkin trong Istio

a. Cài đặt Jaeger

Nếu bạn sử dụng Istio demo profile, Jaeger sẽ được cài đặt sẵn. Nếu không, bạn có thể cài đặt Jaeger bằng cách sử dụng Helm hoặc istioctl.

Ví dụ cài đặt Jaeger cùng với Istio:

istioctl install --set values.tracing.enabled=true \
  --set values.tracing.provider=jaeger \
  --set values.global.tracer.jaeger.address=jaeger.istio-system:14268

b. Cài đặt Zipkin

Tương tự như Jaeger, bạn có thể cài đặt Zipkin bằng istioctl:

istioctl install --set values.tracing.enabled=true \
  --set values.tracing.provider=zipkin \
  --set values.global.tracer.zipkin.address=zipkin.istio-system:9411

4. Quy trình kết nối giữa Envoy Proxy và Jaeger/Zipkin

  1. Envoy Proxy tại mỗi service nhận request, chèn hoặc duy trì Trace ID.

  2. Các trace được thu thập và gửi tới Jaeger hoặc Zipkin thông qua các HTTP POST request từ Envoy đến collector của Jaeger/Zipkin.

  3. Jaeger hoặc Zipkin nhận các trace, lưu trữ chúng và cung cấp giao diện trực quan để phân tích.

  4. Người dùng có thể truy cập giao diện Jaeger/Zipkin để theo dõi các trace, xem chi tiết từng span và đánh giá hiệu năng của hệ thống.

Để liên kết Trace ID của Istio với các log của dịch vụ, bạn cần đảm bảo rằng các log do dịch vụ tạo ra có chứa Trace IDIstio gán cho mỗi yêu cầu. Khi có sự liên kết này, bạn có thể dễ dàng truy vết luồng yêu cầu từ logs đến các trace tương ứng trong hệ thống như Jaeger hoặc Zipkin. Dưới đây là các bước chính để thực hiện việc này:

Trace ID được truyền qua các request header

Khi một yêu cầu đi qua các proxy Envoy trong Istio, hệ thống sẽ tự động tạo ra hoặc duy trì một Trace ID cho yêu cầu đó. Trace ID được gắn vào các request header dưới dạng:

  • x-request-id

  • x-b3-traceid (cho Zipkin/B3 propagation)

  • x-b3-spanid

  • x-b3-sampled

  • x-ot-span-context (cho OpenTracing)

Bạn có thể tận dụng các header này để ghi lại Trace ID trong log của dịch vụ.

Cách liên kết Trace ID với log của dịch vụ

a. Truy xuất Trace ID từ header trong ứng dụng

Trong mỗi dịch vụ, bạn cần đảm bảo rằng các Trace ID từ header HTTP được trích xuất và chèn vào các log. Các framework như Spring Boot, Django, Flask, hoặc Node.js đều có khả năng truy cập các request header.

Ví dụ trong Python với Flask:

from flask import request
import logging

@app.route("/some-endpoint", methods=["GET"])
def some_endpoint():
    trace_id = request.headers.get('x-b3-traceid')  # Lấy trace ID từ header
    logging.info(f"Trace ID: {trace_id} - Log message here")
    return "OK"

b. Ghi Trace ID vào log

Khi bạn trích xuất được Trace ID từ header, bạn có thể ghi nó vào các log của dịch vụ. Điều này sẽ giúp bạn liên kết log của dịch vụ với Trace ID mà Envoy và hệ thống tracing đang theo dõi.

c. Sử dụng logger tích hợp Trace ID (Structured Logging)

Một số framework hỗ trợ structured logging, cho phép tự động gắn Trace ID vào log mà không cần phải xử lý thủ công. Ví dụ:

  • Spring Sleuth trong Spring Boot tự động chèn Trace ID vào log.

  • Flask-Log-Request-ID trong Flask cũng có tính năng tương tự.