Đã làm OpenTelemetry hoạt động, nhưng tại sao lại phức tạp đến vậy?
(iconsolutions.com)- Yêu cầu hỗ trợ OpenTelemetry từ khách hàng cho thấy khả năng quan sát hiện có dựa trên Prometheus, Jaeger và OpenTracing không hoàn toàn trùng với các yêu cầu về metric, log và trace của OTel
- Log và metric được chuyển đổi khá dễ bằng cách thêm appender và cấu hình, nhưng truy vết phân tán thì khó hơn nhiều vì Spring và Akka phải chia sẻ ngữ cảnh truy vết trong cùng một JVM
- OTel và Lightbend Telemetry/OpenTracing sử dụng các Tracer API và cách triển khai SpanContext khác nhau, khiến cùng một giao dịch bị tách thành các trace riêng biệt
- Cách giải quyết là inject ngữ cảnh OTel vào Java
Map, extract nó thành Jaeger SpanContext, rồi kích hoạt thủ công bằngGlobalExtendedTracer.get().local().activateContext()của Lightbend - OTel giúp chuẩn hóa khả năng quan sát, nhưng khi trộn với hệ thống đo đạc sẵn có dựa trên Akka và Jaeger thì có thể cần nối ngữ cảnh một cách tường minh
Các tín hiệu quan sát mà OTel muốn hợp nhất
- OpenTelemetry (OTel) là một framework và bộ công cụ cho khả năng quan sát, nhằm chuẩn hóa các mảng công cụ vốn tách rời trước đây thành ba tín hiệu: metric, log và trace
- Tổ hợp cũ được chia theo vai trò
- Prometheus: metric
- Các bộ gom tập trung như Logstash và Elasticsearch: log
- OpenTracing: truy vết phân tán
- OTel không chỉ cung cấp đặc tả mà còn có các thành phần cốt lõi
- OpenTelemetry Protocol (OTLP): giao thức để ứng dụng báo cáo dữ liệu telemetry
- OpenTelemetry Collector: thành phần tiếp nhận, xử lý và xuất dữ liệu telemetry theo cách trung lập với nhà cung cấp
- SDK cho hơn 10 ngôn ngữ: triển khai OTLP và export telemetry
- Ứng dụng có thể gửi tín hiệu tới OTel Collector thông qua auto-instrumentation, API và SDK ngôn ngữ; hạ tầng cũng có thể gửi tín hiệu tới Collector
Hỗ trợ sẵn có của IPF và yêu cầu từ khách hàng
- IPF vốn đã có tích hợp ELK Stack, giám sát và tài liệu về khả năng quan sát, và khách hàng cũng đã dùng trong môi trường vận hành
- Do tính chất framework, không thể biết ứng dụng dùng thư viện sẽ được triển khai trong môi trường nào, nên cần tránh ép buộc một vendor cụ thể mà thay vào đó cung cấp abstraction và các giá trị mặc định có thể ghi đè
- Khuyến nghị mặc định là gửi log tới các bộ gom như Elasticsearch hoặc LogScale, còn metric thì bật và hỗ trợ Prometheus, vốn gần như đã trở thành tiêu chuẩn thực tế
- Khách hàng gần như đồng thời gửi ba yêu cầu về tracing dựa trên OTel, và từ đó cũng xuất hiện xu hướng chuyển cả log lẫn metric sang OTel
- Log và metric được xử lý khá dễ bằng cách thêm appender và cấu hình mới, nhưng tracing vẫn là một bài toán riêng
Vì sao truy vết phân tán cần truyền ngữ cảnh
- Để theo dõi một giao dịch trong hệ thống phân tán, cần truyền thông tin liên kết các lời gọi và giao dịch cụ thể giữa các hệ thống; điều này được gọi là context propagation
- Trace là cấu trúc cha bao quanh nhiều đơn vị công việc gọi là span; có thể có nhiều span và chúng có thể lồng nhau
- Trong một website thương mại điện tử, luồng bấm nút “buy now” có thể được gom vào một trace
- Span khi frontend giao tiếp với backend
- Các span con khi backend giao tiếp với dịch vụ thanh toán, vận chuyển và quản lý đơn hàng
- Các span con bổ sung khi từng dịch vụ downstream lại giao tiếp với hệ thống khác
- Khi dùng OTel, mỗi dịch vụ phân tán sẽ báo cáo phần trace của riêng mình tới OTel Collector, và OTel sẽ dựng toàn bộ luồng dưới một trace ID duy nhất
Bốn tiêu chuẩn truyền ngữ cảnh và dấu vết của OpenTracing
- OTel phải hỗ trợ bốn cách triển khai context propagation
- Phần Akka của IPF đã hỗ trợ OpenTracing, vốn đang trên lộ trình bị loại bỏ, và sử dụng hỗ trợ OpenTracing của Lightbend Telemetry
- Lightbend Telemetry 2.20.0 đã bổ sung hỗ trợ log/event và metric của OpenTelemetry, nhưng lại thiếu hỗ trợ tracing, vốn là phần quan trọng
- Về lý thuyết, tracing của OTel gần giống như một lần đổi thương hiệu của OpenTracing, và OpenTracing của Lightbend Telemetry cũng hỗ trợ bốn kiểu truyền ngữ cảnh nói trên, nên có vẻ như nó phải hoạt động, nhưng kết quả thực tế lại khác
Trace bị đứt giữa Spring và Akka
- IPF dùng đồng thời Spring và Akka
- Spring Boot và Spring IoC: bootstrap ứng dụng, cấu hình, thiết lập phụ thuộc
- Akka: event sourcing, scheduling, clustering, sharding, integration, v.v.
- Khách hàng có thể bắt đầu luồng thanh toán từ Spring REST controller hoặc từ phương thức gắn
@KafkaListener - Trước OTel, ba mảng này hoạt động mà không va chạm lớn
- Metrics: Spring và Akka xuất các endpoint Prometheus khác nhau
- Logs: cả hai framework đều dùng SLF4J và Logback
- Traces: cả hai đều dùng OpenTracing
- Khi dùng OTel, metric và log được gửi đi bình thường vì Spring và Akka hoạt động độc lập, nhưng tracing thì cần chia sẻ cùng một trace context bên trong JVM
- Vấn đề thực sự là bên trong ứng dụng, cùng một khái niệm truy vết lại được biểu diễn bằng hai API khác nhau, và hai API đó không giao tiếp với nhau
- Hành vi đúng lẽ ra là Akka HTTP client tái sử dụng trace ID
123hiện có và chỉ tạo span ID mới để biểu diễn một đơn vị công việc mới trong cùng trace, nhưng trên thực tế lại sinh ra hai trace không liên quan
opentracing-shim và xung đột implementation
- Phía OTel có
opentracing-shim, giúp OTelTracertrông giống OpenTracingTracer - Nhưng Lightbend Telemetry dùng implementation Tracer tùy biến, nên cả shim lẫn Jaeger đều thất bại
- Log in ra các lỗi sau
Expected to have an OpenTelemetry Span but got cinnamon.opentracing.TraceLocal$ContextOnlySpanExpected to have a JaegerSpanContext but got io.opentelemetry.opentracingshim.SpanContextShim
- Jaeger thất bại khi nhận ngữ cảnh từ shim của OTel, còn OTel cũng thất bại khi nhận ngữ cảnh từ Lightbend
Khảo sát Java Agent và truy vết nội bộ của Lightbend
- Cả instrumentation của OTel lẫn của Lightbend Telemetry đều dùng Java Agent để hook vào lời gọi của các phương thức cụ thể trong các class cụ thể và báo cáo hoạt động tới tracer
- Instrumentation của Lightbend Telemetry không phải mã nguồn mở, nên để hiểu cách nó hoạt động, phải khảo sát mã tracer đã decompile
- Một ví dụ tái hiện tối giản đã được tạo ra để xác định chính xác điểm mà trace context OTel ở phía Spring bị đứt trước khi sang được phía Akka
- Điểm Jaeger thất bại là ở đoạn mã này trong
JaegerTracer.java - Bên gọi là
OpenTracingAkkaPersistenceActorInstrumentation.class, một class instrumentation độc quyền của Lightbend Telemetry - Trong mã đã decompile, điều kiện cốt lõi là trạng thái của
var5- Nếu
var5, tức OpenTracingSpanContext, không phải null thì span mới sẽ được gắn làm span con của span đang active hiện tại - Nếu
var5là null thì span đó sẽ không liên quan tới trace hiện có - Nếu
var5không phải Jaeger SpanContext thì Jaeger sẽ thất bại
- Nếu
Nối OTel và OpenTracing bằng chuyển đổi thủ công
SpanContextđược lấy từthis.traceLocal.currentContext(), vàtraceLocalđược khởi tạo từExtendedTracercủa Lightbend- Tài liệu của Lightbend có cách truy cập
ExtendedTracertheo kiểu globalGlobalExtendedTracer.get()
GlobalExtendedTracer.get()trả vềExtendedTracer, vàlocal()trả về cùngTraceLocalmà thư viện instrumentation đang dùngTraceLocalnày có phương thứcactivateContextnhận OpenTracingSpanContext- Luồng giải pháp gồm bốn bước
- Không dùng OTel shim
- Inject OpenTelemetry Context hiện tại vào một Java
Map - Dùng các giá trị trong
Mapđó để extract Jaeger SpanContext - Kích hoạt Jaeger SpanContext vào
TraceLocalcủa Lightbend trước khi đi vào Akka
- Các thao tác API propagators của OTel được dùng là inject and extract operations
- Mã cốt lõi đi theo luồng sau
- Tạo một
HashMaprỗng - Inject OTel context vào map bằng
GlobalOpenTelemetry.get().getPropagators().getTextMapPropagator().inject(...) - Tạo JaegerSpanContext bằng
new TextMapCodec(false).extract(new TextMapAdapter(otelContext)) - Kích hoạt context trước khi vào Akka bằng
GlobalExtendedTracer.get().local().activateContext(openTracingContext)
- Tạo một
Hành vi đã được xác nhận và khuyến nghị vận hành
- Sau khi nối thủ công, trace được nối tiếp đúng như kỳ vọng
- Toàn bộ luồng bắt đầu từ lời gọi REST
/submitcủa Spring được liên kết thành một trace duy nhất - Có thể trộn lẫn phần được instrumentation bằng OTel API với phần được instrumentation bằng OpenTracing API
- Trace vẫn được truyền qua biên giới HTTP
- Toàn bộ luồng bắt đầu từ lời gọi REST
- Trong ví dụ, tác giả dùng các tên khác nhau để phân biệt hai kiểu instrumentation, nhưng trong môi trường khách hàng thực tế, khuyến nghị đặt
otel.service.namevàcinnamon.applicationgiống nhau để từ bên ngoài trông như một ứng dụng duy nhất
Vì sao lại phức tạp và những lo ngại còn lại
- Để viết lại Lightbend Telemetry bằng OTel API, cần port một lượng lớn instrumentation hiện đang gắn với Jaeger API sang OTel API, nên đây có thể là một khối lượng công việc lớn
- OTel Collector hỗ trợ thu thập trace theo định dạng Zipkin legacy, nên Lightbend có thể dựa vào hỗ trợ legacy để cung cấp mức hỗ trợ OTel hoàn chỉnh hơn
- Nguyên nhân trực tiếp của độ phức tạp là nỗ lực kết hợp hai hệ thống instrumentation dùng các thư viện tracing khác nhau
- Dự án OTel đang cố gắng sắp xếp lại lĩnh vực khả năng quan sát bằng các nỗ lực chuẩn hóa như semantic conventions; dù ban đầu có phần phức tạp để nắm bắt, đây vẫn được đánh giá là một dự án FOSS hữu ích
- Vẫn còn lo ngại về việc Akka có truyền trace context đúng cách giữa các thread bên trong actor model hay không
- Trong các bài kiểm thử tải quy mô nhỏ, nó hoạt động đúng như kỳ vọng
- Một ticket liên quan đã được mở phía Akka
1 bình luận
Các ý kiến trên Hacker News
Suốt thời gian học và port Otel, tôi có cảm giác như quay lại thế giới Java. Mỗi lần lần theo từng dòng code đều giống EnterpriseFizzBuzz, hoàn toàn không có khả năng tự khám phá, và hệ thuật ngữ riêng của nó trông như do những người đang phê thứ gì đó tạo ra
Trên NodeJS, mức dùng CPU cao gấp khoảng 4 lần so với StatsD, nên cuối cùng chúng tôi tự làm phần tổng hợp để giảm mức sử dụng và cũng giảm tình trạng bùng nổ tag. StatsD vẫn ổn khi nhiều process báo cáo cùng một tag, nhưng OTEL thì ghi đè mất
Ở tải đỉnh, một CPU chạy ở mức sử dụng 60–80%, và trước khi có gì đó thay đổi thì thậm chí không thể scale dọc. Với các ngôn ngữ dùng một process trên mỗi core, OTEL gần như chủ động đối nghịch, nghe như chuyện đùa. Cứ dùng Prometheus thì hơn, và thực tế cũng không có đối thủ nào khác
Khi thử cấu hình otel đơn giản trong .NET, sau vài giờ đọc tài liệu của nhà cung cấp mà tổ chức đã chọn vẫn không hiểu, nên tôi vào Discord do một đồng nghiệp vận hành. Một phần mô hình kinh doanh của bên đó là “muốn dùng otel đúng cách trong sản phẩm open source thì trả tiền”, và tôi ngay lập tức cảm thấy dù chi phí bao nhiêu cũng đáng
Thà không có kinh nghiệm trước đó mà viết thêm một thư viện event/pub-sub ổn định còn hơn triển khai OTEL
Cấu hình tối thiểu thực sự cũng khá nhỏ: chỉ cần web UI, một instance server và một DB bạn đã biết là đủ: https://skywalking.apache.org/docs/main/latest/en/setup/back...
Nó có vài điểm giống Zabbix trong mảng monitoring. Cả hai đều không làm ai phải trầm trồ, nhưng đủ thực dụng
process.vpid. Có thể điều phối bằng một thứ như global object để giá trị đó là duy nhất trong suốt thời gian app còn sốngSau đó chỉ cần có thứ gì đó cộng tổng rồi loại bỏ thuộc tính đó. Với statsd/delta, nếu mất một lần truyền tín hiệu thì toàn bộ dữ liệu bị méo, nhưng với cách tích lũy thì chỉ mất độ chính xác
Trường hợp của tôi là metric dạng push đến từ các công cụ “batch”, chứ không phải các long-running process có thể scrape
Lý do Otel trông phức tạp là vì nhiều nhà cung cấp observability đã khiến việc triển khai observability trở nên rất dễ bằng SDK, agent và API riêng của họ. Otel đang cố giải quyết vấn đề này, và tôi nghĩ những người tạo ra nó đang làm rất tốt
Việc Grafana chấp nhận OpenTelemetry như một thành phần hạng nhất của hệ sinh thái cũng đáng khen
Trong vài năm tôi đã thúc đẩy dùng Datadog, nhưng với quy mô giữa doanh nghiệp vừa và doanh nghiệp lớn, giá trở nên khó kham nổi. Theo thời gian, khi OpenTelemetry API và SDK ổn định hơn, chúng tôi đã lấy nó làm chuẩn observability cho ứng dụng
Tuy vậy, nhìn chung tài liệu vẫn có thể tốt hơn, và việc tài liệu onboarding khác nhau theo từng ngôn ngữ là không lý tưởng
Hiện team dùng stack NodeJS/Typescript, và đã tạo một gói các package cùng stack Grafana ví dụ để có thể bắt đầu nhanh với OpenTelemetry: https://github.com/zonneplan/open-telemetry-js
Vấn đề lớn nhất với mọi nhà cung cấp APM là ngay khi bạn có các kernel hook thông qua một agent kiểu ma thuật, đủ thứ chuyện xảy ra mà developer không giải thích được
Trước đây ở một công ty khác, chúng tôi đưa Dynatrace vào; may là app đã có đủ metric tích hợp sẵn đến mức lead SRE xem đó là “hình mẫu” về instrumentation. Nhưng ngay khi cài agent Dynatrace lên host của app, xuất hiện nhiều heisenbug cần restart node, và hiệu năng suy giảm cũng được đo trực tiếp
Trớ trêu là nhờ metric mà chúng tôi tránh được đau đớn, nhưng không ai biết cách sửa. Tệ nhất là trường hợp khi cập nhật MSSQL, failover làm bẩn connection pool ADO.NET theo cách kỳ lạ
Đây là một cấu trúc chỉ phức tạp đến mức cần thiết. Có thể tránh các tính năng kiểu “ma thuật”, và chỉ dùng tập con có giá trị nhất trong ngữ cảnh đồng thời dễ hiểu nhất
Ở đội của chúng tôi thì rất đơn giản. Chúng tôi dùng một thư viện chỉ gửi trace, trace mang lại giá trị lớn nhất cho việc quan sát ứng dụng và cũng có thể chứa các loại dữ liệu khác. Về cơ bản giống như dùng hashmap thay vì chuỗi/số thực
Chúng tôi dùng instrumentation thủ công thay vì tự động, chủ động quyết định sẽ quan sát gì và hiểu rõ đoạn code nào phát ra span. Cũng có quy ước đặt tên phù hợp với cấu trúc code
Backend dùng kết hợp một dịch vụ bên thứ ba giá rẻ và bản cài Jaeger all-in-one cho phát triển local. Cái sau chỉ cần chạy một file thực thi hoặc một Docker container, và không lưu span xuống đĩa. Chủ yếu dùng để giúp các thành viên trong đội yên tâm rằng họ sẽ không làm dịch vụ bên thứ ba bị quá tải
Giám sát hạ tầng thì đã có cấu hình sẵn, và trong trường hợp của chúng tôi, việc thu thập toàn bộ log và metric hạ tầng không đem lại nhiều giá trị. Tôi cho rằng metric và log của OTEL vẫn còn ở giai đoạn đầu, nhưng các vendor thì không nói vậy
Tôi vẫn đang tìm một endpoint để gửi các metric đơn giản dùng một lần từ một số phần hạ tầng hiện chưa thể scrape được
Otel có thể ổn với dự án mới, nhưng bật nó trên một dịch vụ production đã có telemetry rồi thì giống như thay lốp xe khi xe đang chạy
Tôi tin rằng nếu là một dự án greenfield chỉ dùng otel và không có framework ngoài otel thì có thể tốt. Nhưng tôi vẫn chưa sống trong thế giới đó
Một trong những vấn đề lớn nhất là trải nghiệm phát triển local. Tôi muốn hỗ trợ log, trace, metric ở local, nhưng không muốn phải bật cả đống Docker image chỉ để làm việc đó. Tôi muốn xem trong log các metric, trace, baggage, activity span trông như thế nào trước khi deploy
Gần đây đội .NET đã ra mắt .NET Aspire và nó rất tốt. Trong stack phát triển local, rất dễ trực quan hóa mọi thứ ở một nơi, và nó hoạt động như một orchestrator bằng code
Khi deploy lên k8s, chỉ cần trỏ OTEL endpoint tới DataDog Agent là mọi thứ tự chạy. Chúng tôi tránh thư viện trace tùy chỉnh và SDK của DataDog, chỉ dùng OTEL
Giờ trải nghiệm phát triển đã khá tốt
https://learn.microsoft.com/en-us/dotnet/aspire/fundamentals...
https://docs.datadoghq.com/opentelemetry/#overview
Thiết lập lần đầu trên máy phát triển local mất 5 phút, sau đó chỉ cần chạy
/path/to/openobservetrong một tab terminal riêng là xong. Nếu muốn tránh sự phức tạp khổng lồ của một binary liên kết tĩnh duy nhất, họ cũng cung cấp Docker image để chạy local/từ xa :PĐây là backend OpenTelemetry all-in-one có các biểu đồ đẹp, và trong các dự án của tôi nó chưa từng hỏng theo cách có thể nhận thấy
Dùng Aspire sẽ thêm độ phức tạp không cần thiết ở cấp app và trói bạn vào một hệ sinh thái hẹp. Với phát triển local có nhiều lựa chọn đã được kiểm chứng như docker compose, và Aspire cũng không hẳn dễ hơn nhiều so với docker compose cùng biến môi trường
Nếu làm otel trong Python thì nên dùng client của Logfire. Ngay cả khi bạn không dùng dịch vụ Logfire cũng vậy
Nó là mã nguồn mở và có thể gửi tới bất kỳ endpoint tương thích otel nào. Hơn nữa, client do đội pydantic làm tốt hơn và đơn giản hơn thư viện otel chính thức gấp 10 lần
Cũng có một cuộc phỏng vấn thú vị trong đó Samuel Colvin giải thích họ đã đi đến đây như thế nào: https://www.bitecode.dev/p/samuel-colvin-on-logfire-mixing-p...
Vì vậy tôi đã bắt đầu một dự án mã nguồn mở tập trung vào việc làm cho việc áp dụng OpenTelemetry dễ như một dòng lệnh: https://github.com/odigos-io/odigos
Gần đây, nhiều web framework đã lo phần lớn việc instrumentation thay bạn. Ví dụ, nếu dùng opentelemetry-js và tự host một thứ như https://signoz.io thì có thể dựng lên trong chưa đầy một giờ, và thu được rất nhiều dữ liệu mà không cần viết code tùy chỉnh
OpenTelemetry phát triển lên từ trace, nhưng metric và log thì tốt hơn nhiều nếu để cho các giải pháp chuyên biệt xử lý
Cảm giác giống vấn đề “abstraction bị rò rỉ” hoặc “framework bị rò rỉ”. Nếu muốn nhét mọi thứ dưới cùng một chiếc ô, cơ sở dữ liệu SQL cũng có thể làm tất cả những việc này cùng lúc. Nhưng điều đó không có nghĩa là nên làm vậy
https://cra.mr/the-problem-with-otel/
Dù vậy, mỗi lần phải làm việc với OTel tôi vẫn thấy ghét
Đọc đến cuối sẽ thấy phần lớn nỗi đau là tự mình tạo ra. Với stack tiêu chuẩn của Python (mysql, flask, redis, requests, v.v.) thì rất dễ. Chỉ cần thêm vài dòng import ở đầu service là chúng tự động nối vào và trace mọi thứ mà không ồn ào gì
aiohttphiện có phiên bản mới nhất là 3.11.X, auto-instrumentation ghi là hỗ trợ3.X[0], nhưng kết quả thực tế phụ thuộc vào việc bảnaiohttpbạn dùng mới đến đâu và trạng thái phía auto-instrumentation ra saoKhi mọi thứ khớp hoàn hảo thì nó hoạt động như phép màu, nhưng lỗ kim đó khá hẹp
[0]: https://github.com/open-telemetry/opentelemetry-python-contr...
Trace thì chạy được, spanmetrics exporter cũng đã cấu hình, và nếu query trực tiếp prometheus thì thấy spanmetrics. Nhưng làm đủ mọi cách nó vẫn không xuất hiện trong tab “monitor” của jaeger
Sau khi mất 3 ngày cho chuyện này, sếp tôi bảo “hay cứ instrument thủ công rồi gửi tất cả vào SQL Server và làm dashboard Grafana đi”, nhưng tôi cũng không muốn làm vậy
Đây là use case đơn giản nhất mà vẫn không được. Có phải phải chồng thêm Grafana nữa không?
Lý do nó phức tạp không phải là vì các kỹ sư triển khai, mà vì nó được thiết kế cho những công ty bán phần mềm tương thích Otel
Ngược lại, tôi nghĩ các backend đã khá chậm trong việc áp dụng
Nghĩa của từ thay đổi là chuyện bình thường, nhưng thú vị là giờ dường như đã mất đi từ để chỉ việc viết phần mềm theo đặc tả, chỉ còn lại “việc triển khai phần mềm có sẵn lên server”