Distributed tracing (.NET Core)

Levan Nozadze
TBC Engineering

--

თანამედროვე ტექნოლოგიურ სამყაროში სულ უფრო მეტ პოპულარობას იძენს დისტრიბუციული არქიტექტურა (სერვის-ორიენტირებული, მიკროსერვისული, …), რომელსაც თიბისი ბანკშიც აქტიურად ვიყენებთ და მას ბევრი უპირატესობა გააჩნია, თუმცა მისი იმპლემენტაცია გარკვეულ სირთულეებთან და გამოწვევებთან არის დაკავშირებული. ამ ბლოგში სწორედ ერთ-ერთ ასეთ გამოწვევას განვიხილავ, კერძოდ დისტრიბუციული არქიტექტურის მქონე სისტემაში პრობლემის იდენტიფიკაციის, დიაგნოსტირების სირთულეებს და მათი გადაწყვეტის გზებს .NET Core სერვისების მაგალითზე.

მონოლითურ სისტემაში, სადაც ყველა ფუნქციონალი თავმოყრილია ერთ ფიზიკურ სერვისში თუ აპლიკაციაში (single deployment unit), პრობლემის იდენტიფიკაციისთვის და დიაგნოსტირებისთვის როგორც წესი საკმარისია ლოგირების გამართული ფუნქციონალი, პრობლემის/შეცდომის Stack trace-ით.

დისტრიბუციულ სისტემაში, კონკრეტული ბიზნეს ოპერაციის რეალიზება ხშირ შემთხვევაში გულისხმობს რამდენიმე დამოუკიდებელი სერვისის გამოყენებას და თითოეულ ამ სერვისში შეიძლება მოხდეს შეცდომა, დაფიქსირდეს წარმადობის, გამტარუნარიანობის თუ სხვა პრობლემა, რამაც გამოიწვიოს ამ ბიზნეს ოპერაციის წარუმატებლად დასრულება. ამ ოპერაციის წარუმატებლობის კვლევისას, აუცილებელია, ვიცოდეთ ოპერაციის შესრულებისას რომელი სერვისები და ინფრასტრუქტურული რესურსები (მონაცემთა ბაზა, ქეშირების სისტემა, …) იყო გამოყენებული, მათი გამოძახების მიმდევრობა, მათ მიერ შესრულებული ქვე-ოპერაციების ტიპები, ამ ქვე-ოპერაციების პარამეტრები, დაწყების დრო და ხანგრძლივობა, შეცდომის მესიჯი, Stack trace და სხვა დეტალები. ასევე, რაც მთავარია, გვჭირდება ამ დამოუკიდებელ სერვისებში მომხდარი მოვლენების ერთმანეთთან კორელაცია/დაკავშირება, ბიზნეს ოპერაციის ჭრილში. სწორედ ამ საკითხების გადაწყვეტაში გვეხმარება დისტრიბუციული ტრასირება (Distributed tracing).

დისტრიბუციული ტრასირების მოკლე მიმოხილვა

დისტრიბუციული ტრასირება არის დისტრიბუციული არქიტექტურის მქონე სისტემების მონიტორინგის და დიაგნოსტირების მეთოდი/საშუალება. საკითხის უფრო მარტივად და გასაგებად განსახილველად, მოვიყვანოთ მარტივი დისტრიბუციული სისტემის მაგალითი, რომელიც შედგება სამი სერვისისგან Service A, Service B და Service C. ბიზნეს ოპერაციის შესასრულებლად, ფრონტენდ აპლიკაცია იძახებს Service A-ს, რომელიც თავის მხრივ მიმართავს მონაცემთა ბაზას და ასევე Service B-ს ან Service C-ს, ბიზნეს ლოგიკის შესაბამისად. ეს უკანასკნელები, თავის მხრივ ასევე მიმართავენ მონაცემთა ბაზას და/ან ქეშირების სისტემას.

მარტივი დისტრიბუციული სისტემა

დისტრიბუციულ ტრასირებაში, სრული გზა, რომელსაც გადის ერთი ბიზნეს ოპერაცია Trace-ის სახელით არის ცნობილი, ხოლო ამ გზის ფარგლებში შესრულებული სხვადასხვა ქვე-ოპერაციები, აქტივობები, Span-ის სახელით არის ცნობილი. Span-ებს შორის არის მშობელი-შვილის ტიპის კავშირები. ამ მაგალითში Span 1 არის Span 2-ის და Span 3-ის მშობელი, Span 3 არის Span 4-ის და Span 7-ის მშობელი და ა.შ. ამგვარად იქმნება ერთგვარი იერარქია და ამ იერარქიის თავში მყოფი Span-ს, ანუ Span 1-ს, უწოდებენ Trace Root Span-ს და მისი ხანგრძლივობა ემთხვევა Trace-ის ხანგრძლივობას, რადგან სწორედ ამ ოპერაციით (Service A-ს მიერ ოპერაციის შესრულების მოთხოვნის მიღება) იწყება Trace და ამ ოპერაციის ფარგლებში სრულდება ყველა ქვე-ოპერაცია, Span.

დისტრიბუციული ტრასირების ზოგიერთ სისტემაში (მაგ. Elastic APM) არსებობს ასევე ტრანზაქციის ტიპის Span-ის, ან უბრალოდ ტრანზაქციის (Transaction) ცნებაც, რომელიც წარმოადგენს კონკრეტული სერვისის ფარგლებში Root Span-ს. ამ მაგალითის შემთხვევაში Span 1, Span 4 და Span 7 არის ტრანზაქციის ტიპის Span, რადგან თითოეული მათგანი წარმოადგენს შესაბამის სერვისში, ყველაზე მაღლა მდგომ ოპერაციას, Span-ს და მის ფარგლებში სრულდება ამ სერვისის სხვა აქტივობები, Span-ები.

Span ძირითადად მოიცავს შემდეგ მონაცემებს: Trace-ის იდენტიფიკატორი (Trace ID), Span-ის იდენტიფიკატორი (Span ID), მშობელი Span-ის იდენტიფიკატორი (Parent ID), ოპერაციის დასახელება, დაწყების და დასრულების დრო, ხანგრძლივობა, სტატუსი და ოპერაციასთან დაკავშირებული სხვა ატრიბუტები (მაგ. http request path, request method, sql command, …). მოცემულ მაგალითში კონკრეტული Trace წარმოგვიდგება შემდეგი სახით:

კონკრეტული Trace-ის მაგალითი

Trace-ების და Span-ების შესახებ მონაცემები, თითოეული სერვისიდან იგზავნება დისტრიბუციული ტრასირების სისტემაში (Zipkin, Jaeger, Elastic APM, Prometheus, …), რომელიც აანალიზებს ამ მონაცემებს, Trace-ის ჭრილში აკეთებს მონაცემების აგრეგაციას და შესაძლებლობას იძლევა ზემოთ მოცემული დიაგრამის მსგავსად ვნახოთ Trace-ების ვიზუალიზაცია, Trace-ები და Span-ები მოვძებნოთ მათი ატრიბუტების მიხედვით, შევქმნათ სხვადასხვა სტატისტიკები და მეტრიკები, რაც საშუალებას გვაძლევს ერთი მხრივ გვქონდეს მონიტორინგის და პრევენციული ქმედებების შესაძლებლობა, მეორე მხრივ პრობლემის დაფიქსირების შემთხვევაში, მარტივად მივაგნოთ პრობლემის გამომწვევ მიზეზს, სერვისს, ოპერაციას და მის დეტალებს.

იმისთვის რომ Trace-ის და მის ფარგლებში განხორციელებული Span-ების იდენტიფიკაცია მოხდეს, აუცილებელია მას გააჩნდეს უნიკალური იდენტიფიკატორი (ID). ასევე Span-საც უნდა გააჩნდეს უნიკალური იდენტიფიკატორი (ID) და მისი მშობელი Span-ის იდენტიფიკატორი (Parent ID), რათა მშობელი-შვილი კავშირების იდენტიფიკაცია და ვიზუალიზაცია შეგვეძლოს. ერთი სერვისიდან მეორე სერვისის გამოძახებისას, მოთხოვნის (request) პარამეტრებში აუცილებელია Trace-ის და Span-ის იდენტიფიკატორების გადაცემა, რათა მოთხოვნის მიმღებმა სერვისმა იცოდეს თუ რომელი Trace-ის და Span-ის ფარგლებში მოხდა მისი გამოძახება, მშობელი Trace-ის და Span-ის მონაცემები მიანიჭოს თავის Span-ებს (Trace ID, Parent ID) და ამგვარად გამდიდრებული Span-ების მონაცემები მიაწოდოს დისტრიბუციული ტრასირების სისტემას, რათა ამ უკანასკნელმა სწორად დააიდენტიფიციროს Trace-ის და Span-ის კავშირები, შეძლოს მათი იერარქიის შექმნა და შესაბამისი სტატისტიკების თუ მეტრიკების გენერაცია.

ვინაიდან დისტრიბუციულ არქიტექტურაში, სხვადასხვა სერვისი შეიძლება სხვადასხვა ტექნოლოგიაზე იყოს შემუშავებული, აუცილებელია request-ის Trace ID და Span ID პარამეტრების ერთიან სტანდარტში მოქცევა, რათა თითოეული სერვისისთვის და ტექნოლოგიისთვის გასაგები და კითხვადი იყოს ეს პარამეტრები. სამწუხაროდ, საკმაოდ დიდი ხნის განმავლობაში არ არსებობდა ამ პარამეტრების ერთიანი სტანდარტი და მხოლოდ შარშან გამოჩნდა W3C Trace Context სტანდარტი https://www.w3.org/TR/trace-context/, რომელიც აღწერს HTTP request-ებში Trace-ის მონაცემების გადაცემისთვის სტანდარტულ header პარამეტრებს და მათ ფორმატს. კერძოდ,

  • traceparent — HTTP header პარამეტრით გადაეცემა Trace ID-ის და Span ID-ის მონაცემები შემდეგ ფორმატში:
traceparent: 00–0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331–01

სადაც,
00 - არის სტანდარტის ვერსია,
0af7651916cd43dd8448eb211c80319c - არის Trace ID,
b7ad6b7169203331 - არის Span ID რომელიც აკეთებს ამ HTTP request-ს,
01 - არის Trace flags (დეტალები იხ. სტანდარტის ვებ გვერდზე).

  • tracestate — HTTP header პარამეტრით გადაეცემა Trace-ის სხვადასხვა ატრიბუტი key=value ფორმატში:
tracestate: rojo=00f067aa0ba902b7,congo=t61rcWkgMzE

დისტრიბუციული ტრასირების სისტემებიც (Zipkin, Jaeger, Elastic APM, Prometheus, …) განსხვავდებიან ერთმანეთისგან ტექნოლოგიით, შესაძლებლობებით და ასევე მათთან კომუნიკაციის მესიჯის თუ ტრანსპორტის პროტოკოლით. სამწუხაროდ, საკმაოდ დიდი ხნის განმავლობაში აქაც არ არსებობდა დისტრიბუციული ტრასირების მონაცემთა მოდელების და პროტოკოლის სტანდარტი, საერთო სპეციფიკაციები, რის გამოც სერვისების დეველოპმენტის დროს გვიწევდა კონკრეტული დისტრიბუციული ტრასირების სისტემის შესაბამისი მონაცემთა მოდელების და ტრანსპორტის პროტოკოლის იმპლემენტაცია, რაც მოუქნელს და ზოგჯერ შეუძლებელს ხდიდა დისტრიბუციული ტრასირების ეფექტურად გამართვას, რადგან ტექნოლოგიური თუ ვენდორის სპეციფიური ფაქტორების გამო შეუძლებელი იყო ზოგიერთი სერვისის კოდის ცვლილება და ტრასირების სიტემის პროტოკოლზე მორგება. ასევე დისტრიბუციული ტრასირების სისტემის ჩანაცვლებაც საკმაოდ დიდ დანახარჯებს უკავშირდებოდა, რადგან ყველა სერვისში გვიწევდა ცვლილებები. ამ პრობლემის გადასაჭრელად და საერთო სტანდარტზე შესათანხმებლად შემუშავდა OpenTracing და OpenCensus სპეციფიკაციები, რომლებიც საბოლოოდ გაერთიანდა OpenTelemetry სპეციფიკაციებში.

OpenTelemetry განსაზღვრავს დისტრიბუციული ტრასირების მონაცემთა მოდელების, ტრანსპორტის პროტოკოლის და არქიტექტურის საერთო სპეციფიკაციებს, სტანდარტებს, ინსტრუმენტებს, რაც საშუალებას გვაძლევს კონკრეტული დისტრიბუციული ტრასირების სისტემისგან დამოუკიდებლად დავაინტეგრიროთ ტრასირება ჩვენს სერვისებში. კერძოდ, თითოეული სერვისი, Trace-ების და Span-ების მონაცემებს, სტანდარტული მესიჯის ფორმატით და სტანდარტული პროტოკოლით უგზავნის OpenTelemetry Collector-ს, ხოლო ამ უკანასკნელს შეუძლია მარტივი კონფიგურაციით, ავტომატურად გადათარგმნოს სტანდარტული მესიჯი სხვდასხვა დისტრიბუციული სისტემის სპეციფიურ მესიჯში და შესაბამისი პროტოკოლით მიაწოდოს ამ სისტემას. ამგვარად, დისტრიბუციული ტრასირების სიტემის ჩანაცვლება თუ ახლის დამატება ძალიან მარტივია, მხოლოდ OpenTelemetry Collector-ის კონფიგურაციის ცვლილებას გულისხმობს და სერვისების კოდში არაფრის ცვლილება არ არის საჭირო.

OpenTelemetry პროექტი CNCF-ის მხარდაჭერით ვითარდება და აქვს წამყვანი ტექნოლოგიური კომპანიებს მხარდაჭერა, როგორებიცაა Microsoft, Google, AWS, Red Hat, Dynatrace, Splunk და ა.შ., რომლებსაც აქტიურად შეაქვთ თავიანთი კონტრიბუცია შესაბამისი სპეციფიკაციების თუ ინსტრუმენტების დეველოპმენტში, რის შედეგადაც OpenTelemetry-ის უკვე აქვს ძირითადი პროგრამული ენების შესაბამისი SDK-ები და ძირითად დისტრიბუციულ სისტემებთან ინტეგრაციები, რაც საშუალებას იძლევა ძალიან მარტივად ავაწყოთ დისტრიბუციული ტრასირება ჩვენს დისტრიბუციულ სიტემებში და სერვისებში.

დისტრიბუციული ტრასირება .NET Core სერვისებში

.NET Core-ს 3.0 ვერსიიდან ავტომატურად აქვს W3C Trace Context სტანდარტის მხარდაჭერა, რაც გულისხმობს იმას, რომ ASP.NET Core სერვისებში HTTP თუ gRPC request-ის მიღების დროს, ავტომატურად მოწმდება traceparent და tracestate HTTP header პარამეტრები და მათი არსებობის შემთხვევაში, Trace ID, Span ID და Trace-ის სხვადასხვა ატრიბუტები აისახება Span-ში. ასევე Httpclient-ით თუ gRPC client-ით სხვა სერვისის გამოძახებისას, request-ს ავტომატურად მიყვება traceparent და tracestate HTTP header პარამეტრები. ამ კუთხით, ერთადერთი კონფიგურაცია რაც გვჭირდება ASP.NET Core სერვისებში არის Program.cs-ში Main მეთოდში Activity.DefaultIdFormat-ის განსაზღვრა:

სრული მაგალითი იხილეთ აქ

როგორ შევქმნათ Span-ები .NET სერვისებში?

Span-ის იმპლემენტაცია .NET-ში არის System.Diagnostics.DiagnosticSource ბიბლიოთეკაში არსებული Activity კლასი. ამ კლასს გააჩნია ყველა საჭირო მეთოდი და ველი, რაც Span-ის მონაცემების აღსაწერად არის აუცილებელი. სწორედ Activity კლასის ეგზემპლარი იქმნება ASP.NET Core სერვისებში HTTP თუ gRPC request-ის მიღების დროს, მდიდრდება traceparent და tracestate პარამეტრების მონაცემებით, კერძოდ traceparent იწერება ParentId ველში და tracestate იწერება TraceStateString-ში, რათა Trace-ში Span-ების მშობელი-შვილი კავშირები განისაზღვროს. ეს Activity ხელმისაწვდომია კონტროლერებში თუ სერვისის კოდის სხვა ნაწილში Activity.Current სტატიკური (AsyncLocal) property-ის მეშვეობით და წარმოადგენს ამ სერვისის Root Span-ს, ანუ სერვისში ყველაზე მაღლა მდგომი ოპერაციის Span-ს. თუ request-ს არ მოყვება traceparent და tracestate პარამეტრები, მაშინ შექმნილი Activity კლასის ეგზემპლარი წარმოადგენს Trace Root Span-ს (იხ. ზემოთ).

იმისთვის რომ შევქმნათ სერვისის ქვე-ოპერაციების Span-ები, მაგალითად რეპოზიტორიის მეთოდების, სხვადასხვა ბიზნეს ლოგიკის და გამოთვლითი ოპერაციების თუ ინფრასტრუქტურული ოპერაციების, რათა გვქონდეს ინფორმაცია ამ ოპერაციების ხანგრძლივობის, წარმადობის და სხვა პარამეტრების შესახებ, საჭიროა შევქმნათ შესაბამისი Activity-ები. Activity-ების შექმნა რეკომენდებულია ამავე ბიბლიოთეკის ActivitySource კლასის მეშვეობით, რომლიც Activity-ების შექმნის გარდა, საშუალებას იძლევა დავარეგისტრიროთ ამ Activity-ების მსმენელები (ActivityListener), რომლებსაც გაეგზავნებათ შეტყობინებები (events) ამ Activity-ებში მომხდარი მოვლენების შესახებ, მაგ.: Activity-ის დაწყება, დასრულება და სხვა. სწორედ ActivityListener-ების მეშვეობითაა შესაძლებელი Activity-ების შესახებ მონაცემების მიღება, სტანდარტული ფორმატის მქონე Span-ების შექმნა და დისტრიბუციული ტრასირების სისტემაში მათი გაგზავნა.

იმ კლასში სადაც გვსურს Activity-ების შექმნა, პირველ რიგში ვქმნით ActivitySource სტატიკურ ველს, რომელსაც ვუთითებთ შესაბამის დასახელებას. რეკომენდებულია დასახელებაში მივუთითოთ ამ კლასის სრულ დასახელება (FullName), რათა ActivityListener-ში (ShouldListenTo დელეგატში) უფრო მარტივად განვსაზღვროთ თუ რომელი კლასის Activity-ების მოსმენა გვინდა. ActivitySource სტატიკურ ველის შექმნის შემდეგ, კლასის იმ მეთოდებში, სადაც Activity-ის შექმნა გვინდა, მეთოდის დასაწყისში using-ით ვიძახებთ ActivitySource სტატიკური ველის StartActivity მეთოდს, რომელსაც ვუთითებთ შესაბამისი აქტივობის დასახელებას და ტიპს (როგორც წესი ეს დასახელება ემთხვევა მეთოდის სახელს), შედეგად ვიღებთ Activity ობიექტს რომელიც შეგვიძლია გავამდიდროთ ამ მეთოდის ოპერაცი(ებ)ის დეტალებით, Activity ობიექტის SetTag და AddBaggage მეთოდების მეშვეობით. მეთოდის ბოლოში Activity ობიექტის Dispose ან Stop მეთოდს ვიძახებთ, რათა დავასრულოთ ეს Activity. StartActivity მეთოდით Activity-ის შექმნის დროს, Activity.Current-ის მონაცემები გადაეცემა შექმნილ Activity-ის რათა განისაზღვროს მშობელი Activity (ParentId) და მშობელი-შვილი კავშირი იყოს შენარჩუნებული. ასევე ახალი Activity-ის შექმნის შემდეგ, Activity.Current-ით სწორედ ეს ახალი Activity არის ხელმისაწვდომი სანამ არ მოხდება მისი Dispose ან Stop, რის შემდეგაც Activity.Current-ით კვლავ მისი მშობელი Activity იქნება ხელმისაწვდომი. SampleRepository-ში, GetSampleData მეთოდში დამტებული Activity-ის მაგალითი:

SampleRepository-ის სრული მაგალითი იხილეთ აქ

ActivitySource და ActivityListener კლასების გარდა, .NET-ში დიაგნოსტირებისთვის ასევე გამოიყენება DiagnosticSource და DiagnosticListener კლასები. .NET ბიბლიოთეკების საკმაოდ დიდ ნაწილში (HttpClient, SqlClient, Entity Framework, …) უკვე ჩადებულია დიაგნოსტირების ეს საშუალებები, რაც მნიშვნელოვნად გვიმარტივებს საქმეს და საშუალებას გვაძლევს არსებული დიაგნოსტიკები და event-ები გამოვიყენოთ დისტრიბუციულ ტრასირებაში და ჩვენით არ შევქმნათ ყველა საჭირო Activity.

როგორ გავგზავნოთ Activity-ები/Span-ები .NET სერვისებიდან დისტრიბუციული ტრასირების სისტემაში?

ამ საკითხის მარტივად გადასაწყვეტად შეგვიძლია გამოვიყენოთ ზემოთ ნახსენები სტანდარტული OpenTelemetry სპეციფიკაციები, შესაბამისი .NET ბიბლიოთეკები და OpenTelemetry Collector.

თითოეული სერვისის Startup პროექტში უნდა დავამატოთ შემდეგი NuGet პაკეტები:

OpenTelemetry
OpenTelemetry.Exporter.OpenTelemetryProtocol
OpenTelemetry.Extensions.Hosting
OpenTelemetry.Instrumentation.AspNetCore

ასევე, თუ გვჭირდება .NET ბიბლიოთეკებში უკვე განსაზღვრული დიაგნოსტიკები ავტომატურად აისახოს Trace-ში და Span-ებში, შეგვიძლია შესაბამისი მზა instrumentation ბიბლიოთეკების NuGet პაკეტების დამატებაც, მაგალითად:

SqlClient — OpenTelemetry.Instrumentation.SqlClient
HttpClient — OpenTelemetry.Instrumentation.Http
StackExchangeRedis — OpenTelemetry.Instrumentation.StackExchangeRedis

ჩვენი დისტრიბუციული სისტემის მაგალითისთვის ზემოთ ჩამოთვლილი ბიბლიოთეკები საკმარისია, თუმცა NuGet-ზე შეგიძლიათ მოძებნოთ სხვა instrumentation ბიბლიოთეკებიც “OpenTelemetry.Instrumentation.” პრეფიქსით.

Startup პროექტში, Startup.cs ფაილში, ConfigureServices-ში უნდა დავარეგისტრიროთ დამატებული ბიბლიოთეკების შესაბამისი სერვისები. მაგალითად Service C API პროექტის Startup.cs:

სრული მაგალითი იხილეთ აქ

მაგალითში მოცემული AddOpenTelemetryTracing მეთოდით და შესაბამისი builder-ის მეშვეობით ვამატებთ შემდეგს:

  • SetResourceBuilder — აქ მიეთითება სერვისის და გარემოს დასახელება, რათა დისტრიბუციული ტრასირების სიტემაში შესაბამისად გამოჩნდეს, თუ რომელ სერვისს და გარემოს ეკუთვნის Trace-ები და Span-ები;
  • Add***Instrumentation — ემატება მზა instrumentation ბიბლიოთეკები AspNetCore, HttpClient, SqlClient, StackExchangeRedis ბიბლიოთეკების დიაგნოსტიკების და event-ების Trace-ში და Span-ებში ავტომატური ასახვისთვის;
  • AddSource — აქ ვუთითებთ ჩვენს მიერ განსაზღვრული ActivitySource-ების დასახელებებს (იხ. ზემოთ SampleRepository ActivitySource-ის მაგალითი), რომელთა შესაბამისი Activity-ების ასახვაც გვინდა Trace-ში და Span-ებში. ActivitySource-ების დასახელება შეგვიძლია wildcard-ით მივუთითოთ, როგორც ამ მაგალითში არის ნაჩვენები და მოიცავს ყველა ActivitySource-ს, რომლის დასახელებაც “ServiceC.”-ით იწყება;
  • AddOtlpExporter — აქ ვუთითებთ სტანდარტული OpenTelemetry პროტოკოლის (OTLP) მქონე სერვისის მისამართს, სადაც უნდა გაიგზავნოს Trace-ების და Span-ების მონაცემები. ამ მაგალითის მსგავსად, ძირითადად მიეთითება OpenTelemetry Collector-ის მისამართი. OTLP პროტოკოლი gRPC და HTTP/2 პროტოკოლს იყენებს, ამიტომ თუ Endpoint-ში ვაპირებთ გავუწეროთ არა TLS მისამართი, აუცილებელია AppContext.SetSwitch კონფიგურაციაც, როგორც მაგალითშია მოცემული.

ამგვარად ჩვენი სერვისები მზად არის დისტრიბუციული ტრასირებისთვის. დაგვრჩა მხოლოდ OpenTelemetry Collector-ის კონფიგურაცია, რათა მიღებული Trace-ები და Span-ები გააგზავნოს დისტრიბუციული ტრასირების სისტემაში. თუ დისტრიბუციული ტრასირების სისტემის სახით ვიყენებთ Elastic APM-ს, მაშინ OpenTelemetry Collector-ის კონფიგურაცია იქნება შემდეგი:

OpenTelemetry Collector Elastic APM configuration

სერვისების გაშვების და Service A API-ის, GET /sample/1 მისამართის გამოძახების შემთხვევაში, Elastic APM-ში ვიხილავთ შემდეგ Tace-ებს:

Elastic APM, GET /sample/1 request trace

როგორც ხედავთ საკმაოდ დეტალურად ჩანს GET /sample/1 request-ის ფარგლებში შესრულებული ოპერაციები/Span-ები, როგორც Service A-ში (ლურჯად აღნიშნული), ისე Service C-ში (მწვანედ აღნიშნული), მათი ხანგრძლივობა, თანმიმდევრობა, ტიპი და თითოეულ Span-ზე დაჭერის შემთხვევაში უფრო დეტალური მონაცემების ნახვაც შეგვიძლია. ასევე, აღსანიშნავია, რომ ჩვენს მიერ შექმნილი SampleRepository.GetSampleData Span-ის გარდა, ვხედავთ Http, SQL, Redis ოპერაციების Span-ებსაც, რომლებიც ავტომატურად შეიქმნა ზემოთ დამატებული მზა instrumentation ბიბლიოთეკების მეშვეობით.

გარდა კონკრეტული Trace-ის და მისი Span-ების დეტალების ნახვისა და მათი სხვადასხვა ატრიბუტით ძებნისა, Elastic APM-ში შეგვიძლია მივიღოთ სტატისტიკები და მეტრიკები თითოეული ოპერაციის საშუალო ხანგრძლივობის და მისი განაწილების, Error rate-ის, გამტარუნარიანობის შესახებ. ასევე შეგვიძლია მონიტორინგის Alert-ების განსაზღვრაც, იმ შემთხვევაში თუ ეს სტატისტიკები და მეტრიკები გარკვეულ ზღვრებს სცილდება, რაც მიანიშნებს სისტემის შეფერხებაზე, დაბალ წარმადობაზე თუ შეცდომების ზრდაზე. ამგვარად საშუალება გვეძლევა პროაქტიულად ჩავერთოთ სისტემის პრობლემის აღმოფხვრაში და დროულად მოვაგვაროთ ის.

ბლოგში განხილული დისტრიბუციული სისტემის და სერვისების სამაგალითო პროექტების კოდი და სატესტო გარემოს Docker-ში გამართვის ინსტრუქცია შეგიძლიათ იხილოთ ამ რეპოზიტორიაში:

მადლობა!

--

--

Levan Nozadze
TBC Engineering

Senior Software Developer, Back Systems Software Development Chapter Area Lead at TBC Bank