Spring Boot, Jaeger, Distributed Tracing Sinhala Guide | Zipkin Replacement
ආයුබෝවන් යාළුවනේ! ඩිවලොපර් කෙනෙක් විදියට ඔයා දන්නවා ඇති නේ, අද කාලේ ඇප්ලිකේෂන්ස් හැදෙන්නේ කොහොමද කියලා. මොනොලිතික් (Monolithic) ආකෘතියෙන් මිදිලා, අපි දැන් මයික්රෝසර්විසස් (Microservices) පැත්තට ගිහින් තියෙනවා. මේකෙන් ගොඩක් වාසි තියෙනවා, ඒත් පොඩි අවුලකුත් තියෙනවා - ඩිබග් (Debug) කරන එක හරිම අමාරුයි! එක ඉල්ලීමක් (request) සර්විසස් කීපයක් අතරින් යනකොට, කොහෙද අවුල තියෙන්නේ කියලා හොයන එක "කැලේට ගිය අලියා හොයනවා" වගේ වැඩක්.
ඔන්න ඕකට තියෙන සුපිරි විසඳුමක් තමයි ඩිස්ට්රිබියුටඩ් ට්රේසින් (Distributed Tracing) කියන්නේ. මේ ටියුටෝරියල් එකෙන් අපි බලමු Spring Boot ඇප්ලිකේෂන් එකක ඩිස්ට්රිබියුටඩ් ට්රේසින් implement කරන්නේ කොහොමද කියලා, ඒ වගේම Open Source ටූල් එකක් වෙන Jaeger භාවිතයෙන් කොහොමද මේ ට්රේසස් (traces) බලන්නේ කියලා. විශේෂයෙන්ම, ඔයා දැනට Zipkin පාවිච්චි කරනවා නම්, ඒක Jaeger එකට මාරු කරන්නේ කොහොමද කියලත් අපි කතා කරනවා. එහෙනම්, අපි පටන් ගමු!
Distributed Tracing කියන්නේ මොකක්ද? (What is Distributed Tracing?)
මයික්රෝසර්විසස් ආකෘතියේදී, එක ඇප්ලිකේෂන් එකක් සර්විසස් (services) කීපයකින් හැදිලා තියෙන්න පුළුවන්. උදාහරණයක් විදියට, ඊ-කොමර්ස් (e-commerce) ඇප්ලිකේෂන් එකක් ගත්තොත්, "User Service", "Product Service", "Order Service", "Payment Service" වගේ සර්විසස් ගොඩක් තියෙන්න පුළුවන්. Client කෙනෙක්ගෙන් එන එක request එකක් මේ සර්විසස් කීපයක් හරහා ගිහින් තමයි completed වෙන්නේ.
මේ වගේ වෙලාවක, request එකක් ෆේල් (fail) වුනොත් හරි, ස්ලෝ (slow) වුනොත් හරි, ඒක කොහෙද වෙන්නේ කියලා හොයන එක හරිම අමාරුයි. සාමාන්ය ලොග් (logs) පාවිච්චි කරලා, සර්විස් එකෙන් සර්විස් එකට පැන පැන බැලීම අපා දුකක්.
ඩිස්ට්රිබියුටඩ් ට්රේසින් කියන්නේ මේ ප්රශ්නයට තියෙන සුපිරි විසඳුමක්. මේකෙන් වෙන්නේ, Client එකෙන් එන මුල්ම request එකට Unique ID එකක් (Trace ID) දෙනවා. ඊට පස්සේ, ඒ request එක යන හැම සර්විස් එකක්ම, ඒ service එක ඇතුලේ වෙන හැම ක්රියාවලියකටම (operation) Span ID එකක් දෙනවා. මේ Span ID එකට Parent Span ID එකකුත් තියෙනවා.
සරලවම කිව්වොත්, Trace ID එක තමයි මුළු request එකේම "කතාව". Span ID එක කියන්නේ ඒ කතාවේ "එක චරිතයක්" (හෝ එක සිද්ධියක්). මේ හැම Span එකකම, request එකට කොච්චර වෙලාවක් ගියාද, මොනවද කරපු operations, මොන සර්විස් එකද මේක handle කළේ වගේ ගොඩක් වැදගත් තොරතුරු අඩංගු වෙනවා. මේ තොරතුරු එකතු කරලා, අපිට request එකක සම්පූර්ණ ගමනක් (end-to-end flow) ග්රැෆිකල් (graphical) විදියට බලන්න පුළුවන්. ඒකෙන් අපිට request එක කොහෙද ස්ලෝ වුනේ, කොහෙද ෆේල් වුනේ කියලා ලේසියෙන් හොයාගන්න පුළුවන්. ඒක හරියට X-ray එකක් වගේ!
Jaeger Setup එක (Setting up Jaeger)
Jaeger කියන්නේ OpenTracing API එක implement කරන, Distributed Tracing System එකක්. Uber වලින් හදලා Open Source කරපු මේක, Go වලින් ලියලා තියෙන්නේ. මේකේ ප්රධාන කොටස් ටිකක් තියෙනවා:
- Jaeger Client Libraries: මේවා ඇප්ලිකේෂන් එකට එකතු කරනවා. මේවා තමයි Spans හදන්නේ, ඒවා Context එකට දාන්නේ, ඊට පස්සේ Agent එකට යවන්නේ.
- Jaeger Agent: සර්විස් එකට ළඟින්ම තියෙන Network Daemon එකක්. Client Library එකෙන් එන UDP packets collect කරලා, Collector එකට යවනවා.
- Jaeger Collector: Agent වලින් එන traces collect කරලා, processing කරලා Storage එකට යවනවා.
- Storage: Cassandra, Elasticsearch, Kafka වගේ දේවල් පාවිච්චි කරන්න පුළුවන්.
- Query Service: Storage එකෙන් traces අරගෙන, Jaeger UI එකට පෙන්නනවා.
- Jaeger UI: Traces බලන්න පුළුවන් Web UI එක.
අපි Jaeger Run කරන්න, පහසුම ක්රමය තමයි Docker පාවිච්චි කරන එක. Development environment එකකට නම් all-in-one කන්ටේනර් එක හොඳටම ඇති.
පහත කමාන්ඩ් එක Run කරන්න:
docker run -d --name jaeger \
-e COLLECTOR_ZIPKIN_HOST_PORT=:9411 \
-e COLLECTOR_OTLP_ENABLED=true \
-p 6831:6831/udp \
-p 6832:6832/udp \
-p 5778:5778 \
-p 16686:16686 \
-p 4317:4317 \
-p 4318:4318 \
-p 14268:14268 \
jaegertracing/all-in-one:latest
මේ කමාන්ඩ් එකෙන් Jaeger all-in-one කන්ටේනර් එක Run වෙනවා.
-p 16686:16686: මේ Port එකෙන් තමයි Jaeger UI එකට යන්න පුළුවන්. Browser එකේhttp://localhost:16686කියලා ටයිප් කරලා බලන්න. ඔයාට Jaeger UI එක පේන්න ඕනේ.-p 6831:6831/udp: Agent UDP Port එක.COLLECTOR_ZIPKIN_HOST_PORT=:9411: Zipkin compatible API එක enable කරනවා.
දැන් Jaeger ready. අපි Spring Boot project එකට යමු.
Spring Boot Project එකට Jaeger එකතු කරගමු (Integrating Jaeger into a Spring Boot Project)
Spring Cloud Sleuth කියන්නේ Spring Boot ඇප්ලිකේෂන් වලට Distributed Tracing එකතු කරන්න උදව් වෙන සුපිරි ලයිබ්රරි එකක්. මේකෙන් අපිට අතින් මුකුත් ලියන්න ඕනේ නැතුවම, HTTP requests, messaging, scheduled tasks වගේ දේවල් වලට traces generate කරගන්න පුළුවන්. Spring Cloud Sleuth, OpenTracing සහ Brave කියන tracing APIs දෙකටම සපෝට් කරනවා. අපි OpenTracing API එකයි, Jaeger Client එකයි එක්ක Sleuth භාවිතා කරමු.
මුලින්ම, ඔයාගේ Spring Boot Project එකේ pom.xml එකට මේ dependencies ටික එකතු කරගන්න:
<dependencies>
<!-- Spring Boot Starter Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Spring Cloud Sleuth for Distributed Tracing -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>
<!-- Jaeger Tracer (via OpenTracing) -->
<dependency>
<groupId>io.jaegertracing</groupId>
<artifactId>jaeger-client</artifactId>
<version>1.8.1</version> <!-- නවතම stable version එක බලන්න -->
</dependency>
<dependency>
<groupId>io.opentracing.contrib</groupId>
<artifactId>opentracing-spring-cloud-starter</artifactId>
<version>0.1.15</version> <!-- නවතම stable version එක බලන්න -->
</dependency>
<dependency>
<groupId>io.opentracing.contrib</groupId>
<artifactId>opentracing-spring-web-starter</artifactId>
<version>0.3.9</version> <!-- නවතම stable version එක බලන්න -->
</dependency>
<!-- Spring Cloud Context (needed for Sleuth) -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-context</artifactId>
</dependency>
</dependencies>
<!-- Spring Cloud Dependency Management -->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>2021.0.7</version> <!-- ඔයාගේ Spring Boot version එකට ගැලපෙන එකක් දාන්න -->
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
spring-cloud-dependencies version එක ඔයාගේ Spring Boot version එකට ගැලපෙන්න දාන්න මතක තියාගන්න.
දැන්, application.properties (හෝ application.yml) එකේ Jaeger configuration ටික දාමු.
# Service Name එක (Jaeger UI එකේ පෙන්වයි)
spring.application.name=my-spring-boot-service
# Jaeger Agent Host සහ Port
opentracing.jaeger.udp-sender.host=localhost
opentracing.jaeger.udp-sender.port=6831
# Tracing Sampling Strategies
# Probabilistic (සියලු requests වලින් 1% trace කරනවා)
opentracing.jaeger.sampler.type=probabilistic
opentracing.jaeger.sampler.param=0.01
# හෝ Constant (හැම request එකක්ම trace කරනවා - development වලදී හොඳයි)
# opentracing.jaeger.sampler.type=const
# opentracing.jaeger.sampler.param=1
# Debugging සඳහා logs enable කරන්න
logging.level.org.springframework.cloud.sleuth=DEBUG
logging.level.io.jaegertracing=DEBUG
spring.application.name කියන්නේ Jaeger UI එකේ ඔයාගේ සර්විස් එක අඳුරගන්න පාවිච්චි කරන නම. opentracing.jaeger.udp-sender.host සහ port කියන්නේ Jaeger Agent එක Run වෙන තැන. අපි Docker එකේ localhost එකේ 6831 Port එකේ Run කරපු නිසා මේ විදියට දානවා. sampler එකෙන් කියන්නේ කොච්චර requests ප්රමාණයක් trace කරන්න ඕනෙද කියලා. const sampler එක 1 ට සෙට් කළොත් හැම request එකක්ම trace වෙනවා. Production වලදී probabilistic එක හොඳයි.
දැන් ඔයාගේ Spring Boot ඇප්ලිකේෂන් එක Run කරලා, ඒකට HTTP request කීපයක් යවන්න. උදාහරණයක් විදියට, Controller එකක් හදාගමු:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
@RestController
public class MyController {
private static final Logger logger = LoggerFactory.getLogger(MyController.class);
@Autowired
private RestTemplate restTemplate; // RestTemplate bean එකක් AutoConfigure වෙනවා Sleuth එක්ක
@GetMapping("/hello")
public String hello() {
logger.info("Hello endpoint called");
return "Hello from My Spring Boot Service!";
}
@GetMapping("/call-another")
public String callAnotherService() {
logger.info("Calling another service...");
// මේක ඇත්තටම වෙන service එකකට call කරනවා වගේ simulated එකක්.
// ඔයාට වෙන Spring Boot service එකක් තියෙනවා නම් ඒකට call කරන්න පුළුවන්.
// String response = restTemplate.getForObject("http://localhost:8081/another-endpoint", String.class);
try {
Thread.sleep(100); // Simulate some work
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
logger.info("Another service call finished.");
return "Called another service!";
}
}
RestTemplate එකක් Autowired කරලා ඒකෙන් වෙන service එකකට call කරාම, Sleuth automatically ඒ call එකත් Trace එකට එකතු කරනවා. මේක තමයි Sleuth වල සුපිරිම දේ. ඔයාට වෙන service එකක් නැත්නම්, Thread.sleep(100) වගේ දෙයක් දාලා පොඩි Span එකක් හදන්න පුළුවන්.
දැන්, http://localhost:8080/hello සහ http://localhost:8080/call-another වගේ URLs වලට requests කීපයක් යවලා, ඊට පස්සේ Jaeger UI එකට (http://localhost:16686) ගිහින් බලන්න. ඔයාගේ my-spring-boot-service එකේ traces පේන්න ඕනේ.
Zipkin වෙනුවට Jaeger (Replacing Zipkin with Jaeger)
දැනට Zipkin පාවිච්චි කරන project එකක් තියෙනවා නම්, Jaeger එකට මාරු කරන එක හරිම ලේසියි. මොකද Spring Cloud Sleuth, Trace reporting සඳහා abstraction එකක් සපයන නිසා.
සාමාන්යයෙන් Zipkin එක්ක වැඩ කරනකොට ඔයාගේ pom.xml එකේ මේ වගේ dependency එකක් තියෙන්න පුළුවන්:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zipkin</artifactId>
</dependency>
නැත්නම්, Sleuth එකේ Brave implementation එක එක්ක সরাসরি Zipkin Reporter එක පාවිච්චි කරන්න පුළුවන්.
Jaeger එකට මාරු වෙන්න නම්, ඔයාට කරන්න තියෙන්නේ spring-cloud-starter-zipkin dependency එක අයින් කරලා, Section 3 එකේ අපි කතා කරපු Jaeger related dependencies ටික එකතු කරන එක විතරයි.
pom.xml වෙනස:
පැරණි Zipkin Dependencies (මේවා අයින් කරන්න):
<!-- Don't include these for Jaeger -->
<!--
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zipkin</artifactId>
</dependency>
<dependency>
<groupId>io.zipkin.reporter2</groupId>
<artifactId>zipkin-reporter-brave</artifactId>
</dependency>
-->
අලුතින් එකතු කළ යුතු Jaeger Dependencies (Section 3 වලින්):
<!-- Jaeger Tracer (via OpenTracing) -->
<dependency>
<groupId>io.jaegertracing</groupId>
<artifactId>jaeger-client</artifactId>
<version>1.8.1</version>
</dependency>
<dependency>
<groupId>io.opentracing.contrib</groupId>
<artifactId>opentracing-spring-cloud-starter</artifactId>
<version>0.1.15</version>
</dependency>
<dependency>
<groupId>io.opentracing.contrib</groupId>
<artifactId>opentracing-spring-web-starter</artifactId>
<version>0.3.9</version>
</dependency>
ඒ වගේම application.properties එකේ Zipkin configuration ටික අයින් කරලා, Section 3 එකේ අපි කතා කරපු Jaeger configuration ටික දාන්න ඕනේ.
පැරණි Zipkin Configuration (මේවා අයින් කරන්න):
# Don't include these for Jaeger
# spring.zipkin.base-url=http://localhost:9411
# spring.sleuth.sampler.probability=1.0 # Or other value
අලුතින් එකතු කළ යුතු Jaeger Configuration (Section 3 වලින්):
# Jaeger Agent Host සහ Port
opentracing.jaeger.udp-sender.host=localhost
opentracing.jaeger.udp-sender.port=6831
# Tracing Sampling Strategies
opentracing.jaeger.sampler.type=const
opentracing.jaeger.sampler.param=1
මේ වෙනස්කම් කරලා project එක Run කරාම, Sleuth automatically traces ටික Jaeger Agent එකට යවන්න පටන් ගනීවි. මොකද OpenTracing API එක සහ Jaeger Client එක classpath එකේ තියෙන නිසා Sleuth එකෙන් ඒක අඳුරගෙන ඒකට අනුව ක්රියා කරනවා. හරිම සරලයි, නේද?
Traces බලමු (Viewing Traces in Jaeger UI)
ඔයාගේ Spring Boot ඇප්ලිකේෂන් එක Run කරලා, requests ටිකක් යැව්වට පස්සේ, http://localhost:16686 තියෙන Jaeger UI එකට යන්න.
- Search Section එක: වම් පැත්තේ තියෙන "Service" කියන dropdown එකෙන් ඔයාගේ service name එක (අපි කලින්
my-spring-boot-serviceකියලා දැම්මා) තෝරන්න. - Operations: ඊට පස්සේ "Operation" කියන dropdown එකෙන් specific endpoint එකක් (උදාහරණයක් විදියට
/helloහෝ/call-another) තෝරන්න පුළුවන්. - Find Traces: "Find Traces" බටන් එක ක්ලික් කරන්න.
දැන් ඔයාට requests වලට අදාළ traces ලැයිස්තුවක් පෙනෙන්න ඕනේ. එක trace එකක් ක්ලික් කරලා, ඒකේ සම්පූර්ණ ගමනක් (full end-to-end flow) බලන්න පුළුවන්. Graph එකක් විදියට පෙන්නන නිසා, request එකේ කවර කොටසටද වැඩිම වෙලාවක් ගියේ, මොන service එකේද ප්රශ්නයක් තියෙන්නේ වගේ දේවල් එක බැල්මකින් හොයාගන්න පුළුවන්.
ඒ වගේම, හැම Span එකක්ම ක්ලික් කරලා, ඒ Span එකට අදාළ Tags, Logs වගේ විස්තර බලාගන්නත් පුළුවන්. මේ Tags වලට අපිට තව custom data එකතු කරන්නත් පුළුවන්, ඊටත් වඩා විස්තරාත්මක Tracing එකක් සඳහා.
Conclusion:
ඉතින් යාළුවනේ, අපි මේ ටියුටෝරියල් එකෙන් ඩිස්ට්රිබියුටඩ් ට්රේසින් කියන්නේ මොකක්ද, ඒක මයික්රෝසර්විසස් ආකෘතියේදී කොච්චර වැදගත්ද, සහ Spring Boot ඇප්ලිකේෂන් එකක Jaeger එක්ක ඒක implement කරන්නේ කොහොමද කියලා විස්තරාත්මකව ඉගෙන ගත්තා. Zipkin වලින් Jaeger එකට මාරු වෙන එකත් හරිම ලේසියි කියලා අපි දැක්කා.
Jaeger වගේ ටූල්ස් පාවිච්චි කරනකොට, ඔයාගේ ඇප්ලිකේෂන් වල performance issues හොයන එක, bugs fix කරන එක හරිම ලේසි වෙනවා. ඩිවලොපර් කෙනෙක් විදියට ඔයාට මේක හරිම වැදගත් skill එකක් වෙයි.
ඉතින්, මේක ඔයාගේ ඊළඟ Spring Boot project එකේදී implement කරලා බලන්න. ඔයාගේ අත්දැකීම් කොහොමද කියලා කමෙන්ට් සෙක්ෂන් එකේ අපිට කියන්නත් අමතක කරන්න එපා. මොකද ඔයාගේ අත්දැකීම තවත් කෙනෙක්ට ගොඩක් වැදගත් වෙන්න පුළුවන්! සුභ ගමන්!