๐ŸŒŠ โ€œ์š”์ฒญ์€ ์ง€๋‚˜๊ฐ€๊ณ  ํ”์ ์€ ๋‚จ๋Š”๋‹คโ€ โ€” MSA ๋ถ„์‚ฐํ…Œ์ŠคํŠธ์™€ Trace ์‹œ๊ฐํ™”

๋‚˜๋‚˜'s Brainยท2025๋…„ 9์›” 11์ผ
0

๊ฐœ๋…Study

๋ชฉ๋ก ๋ณด๊ธฐ
24/25
post-thumbnail

์™œ โ€˜ํ๋ฆ„โ€™์ด ํ•„์š”ํ–ˆ์„๊นŒ?

๋ชจ๋†€๋ฆฌ์‹ ํ™˜๊ฒฝ์—์„  ํ˜ธ์ถœ์ด ํ•œ ๋ฉ์–ด๋ฆฌ๋ผ ๋น„๊ต์  ์ง๊ด€์ ์ด๋‹ค.
๋กœ๊ทธ ๋ช‡ ์ค„๋งŒ ๋ด๋„ โ€œ์–ด๋””์„œ ๋ฌด์Šจ ์ผ์ด ๋‚ฌ๋Š”์ง€โ€ ๊ฐ์ด ์˜จ๋‹ค.

ํ•˜์ง€๋งŒ ์„œ๋น„์Šค๊ฐ€ ๋‚˜๋‰˜๋Š” ์ˆœ๊ฐ„, ๋กœ๊ทธ๋Š” ์„ž์ด๊ณ  ํƒ€์ž„๋ผ์ธ์€ ํํŠธ๋Ÿฌ์ง„๋‹ค.
โ€œ200 OKโ€๋งŒ ๋ณด๊ณ ๋Š” ํ๋ฆ„ ์ „์ฒด๋ฅผ ์‹ ๋ขฐํ•  ์ˆ˜ ์—†๋Š” ์ƒํ™ฉ์ด ๋ฐœ์ƒํ•œ๋‹ค.

๊ทธ๋ž˜์„œ ์„ค๊ณ„ ๋ชฉํ‘œ๋ฅผ ์ด๋ ‡๊ฒŒ ์žก์•˜๋‹ค:

  • ๋กœ๊ทธ๋Š” ๋ˆˆ์œผ๋กœ ๋”ฐ๋ผ๊ฐ€๊ธฐ ์‰ฝ๊ฒŒ (MDC ์ „ํŒŒ)
  • ํŠธ๋ ˆ์ด์Šค๋Š” ํ™”๋ฉด์—์„œ ์ถ”์  ๊ฐ€๋Šฅํ•˜๊ฒŒ (OTLP Exporter โ†’ Collector โ†’ Tempo)
  • ๋ฉ”ํŠธ๋ฆญ์Šค๋Š” ์ง€ํ‘œ ๊ธฐ๋ฐ˜์œผ๋กœ ํ™•์ธ ๊ฐ€๋Šฅํ•˜๊ฒŒ (Micrometer โ†’ Prometheus โ†’ Grafana)
  • ์ฆ‰, ๋กœ๊ทธยทํŠธ๋ ˆ์ด์Šคยท๋ฉ”ํŠธ๋ฆญ์„ ํ•˜๋‚˜์˜ ์‚ฌ๊ฑด์œผ๋กœ ๋ฌถ๋Š” ๊ฒƒ

์ด๋ฒˆ ๊ธ€์—์„œ๋Š” ์ด๋Ÿฐ ๋ฐฐ๊ฒฝ ์œ„์—์„œ, ์‹ค์ œ๋กœ ์–ด๋–ป๊ฒŒ ํ…Œ์ŠคํŠธํ•˜๊ณ  ๊ฒ€์ฆํ–ˆ๋Š”์ง€๋ฅผ ์ค‘์‹ฌ์œผ๋กœ ํ’€์–ด๊ฐ€๋ ค ํ•œ๋‹ค.


๐Ÿ’ฃ ์‹œ์ž‘์€ ๋‹จ์ˆœ UUID์˜€๋‹ค

์ดˆ๊ธฐ์—” ์š”์ฒญ๋งˆ๋‹ค UUID ํ•˜๋‚˜๋ฅผ ๋ถ™์˜€๋‹ค. ๋ชจ๋†€๋ฆฌ์‹์—์„  ์ถฉ๋ถ„ํ•˜๋‹ค๊ณ  ์ƒ๊ฐํ–ˆ๋‹ค.
ํ•˜์ง€๋งŒ MSA์—์„  ์š”์ฒญ์ด A โ†’ B โ†’ C๋กœ ํ˜๋Ÿฌ๊ฐ„๋‹ค.
์„œ๋น„์Šค ๊ฒฝ๊ณ„๋ฅผ ๋„˜์„ ๋•Œ๋งˆ๋‹ค UUID๊ฐ€ ์ƒˆ๋กœ ์ƒ๊ธฐ๋ฉด, ํ•œ ๋ฒˆ์˜ ์‚ฌ์šฉ์ž ์š”์ฒญ์„ ํ•˜๋‚˜์˜ ํƒ€์ž„๋ผ์ธ์œผ๋กœ ๋ณผ ์ˆ˜ ์—†๋‹ค.

์ฆ‰, MSA์—์„  ์„œ๋น„์Šค ๊ฒฝ๊ณ„๋ฅผ ๋„˜๋Š” ์ˆœ๊ฐ„, ๋‹จ์ผ ID๋งŒ์œผ๋ก  ์ „์ฒด ํ˜ธ์ถœ ํŠธ๋ฆฌ ์‹œ๊ฐํ™”๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์—†๋‹ค๋Š” ๊ฒƒ์ด๋‹ค.

๊ทธ๋ž˜์„œ ์ˆ˜๋™ UUID๋Š” ๋ฒ„๋ฆฌ๊ณ , Micrometer Tracing์„ ๋„์ž…ํ•ด traceId/spanId๋ฅผ ์ž๋™ ์ƒ์„ฑยท์ „ํŒŒํ•˜๋„๋ก ํ–ˆ๋‹ค.

  • traceId: ์ „์ฒด ์š”์ฒญ์˜ ํ๋ฆ„์„ ํ•˜๋‚˜๋กœ ๋ฌถ๋Š” ID
  • spanId: ๊ทธ ์š”์ฒญ ๋‚ด ๊ฐ ๋‹จ์œ„ ์ž‘์—…(HTTP ์š”์ฒญ, DB ์ฟผ๋ฆฌ ๋“ฑ)์— ๋Œ€ํ•œ ID
  • MDC ์ „ํŒŒ: ๋กœ๊ทธ๊ฐ€ ์–ด๋””์„œ ์ฐํžˆ๋“  ๊ฐ™์€ ์–ธ์–ด๋กœ ๊ธฐ๋ก
์˜ˆ์‹œ :

[traceId: AAA]  
โ”œโ”€โ”€ spanId: A1 (Gateway)  
โ”‚   โ”œโ”€โ”€ spanId: B1 (Service A: Controller)
โ”‚   โ”‚   โ””โ”€โ”€ spanId: C1 (Service A: DB)
โ”‚   โ””โ”€โ”€ spanId: B2 (Service B: External API)
โ””โ”€โ”€ spanId: A2 (Service C) 

โœ… `traceId`: AAA "์š”์ฒญ ์ „์ฒด ์‹๋ณ„์ž"
โœ… ๊ฐ๊ฐ์˜ `spanId`: "์‹ค์ œ ์ž‘์—…์˜ ๊ตฌ์ฒด์  ์œ„์น˜"

โ†’ ํ•˜๋‚˜์˜ traceId ์•ˆ์—๋Š” ์—ฌ๋Ÿฌ spanId๊ฐ€ ์žˆ๊ณ , ์ด๋“ค์€ ํŠธ๋ฆฌ ๊ตฌ์กฐ๋กœ ์—ฐ๊ฒฐ๋จ (parent/child)

๋˜ํ•œ MdcKeys ํ‚ค๋Š” enum ๋ง๊ณ  public final class MdcKeys๋กœ ๋ฌธ์ž์—ด ์ƒ์ˆ˜๋กœ ๊ด€๋ฆฌํ•˜์—ฌ ๋ณ„๋„ ๋ถ„๋ฆฌํ•ด์ฃผ๊ณ , ํ•„ํ„ฐ์—์„œ ๊ฐ€์ ธ๋‹ค๊ฐ€ ์‚ฌ์šฉํ•˜์˜€๋‹ค.
โžก๏ธ ์žฅ์  : ๋กœ๊ทธ๋ฐฑ ํŒจํ„ด%X{}๋Š” ๋ฌธ์ž์—ด ํ‚ค๋งŒ ๋ฐ›๊ธฐ๋„ ํ•˜๊ณ , ํ•œ ๊ตฐ๋ฐ๋งŒ ๊ณ ์น˜๋ฉด ์ „์—ญ ๋ฐ˜์˜ ๊ฐ€๋Šฅ

MDC๋Š” ๋‚ด๋ถ€์ ์œผ๋กœ๋Š” ThreadLocal<Map<String, String>> ๊ตฌ์กฐ๋กœ ๋™์ž‘ํ•œ๋‹ค.

๋งˆ์ง€๋ง‰์œผ๋กœ MDC.put()์œผ๋กœ ์ฃผ์ž…ํ•ด์ฃผ๊ณ , ์š”์ฒญ์ด ๋๋‚œ ์‹œ์ ์—์„œ ThreadLocal ๊ฐ’์„ ๋ช…์‹œ์ ์œผ๋กœ ๋น„์›Œ์ค˜์•ผ ๋‹ค์Œ ์š”์ฒญ์—์„œ ์ž˜๋ชป๋œ ๊ฐ’์ด ์„ž์ด๋Š” ๋ฒ„๊ทธ + ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜๋ฅผ ๋ฐฉ์ง€ํ•˜๊ธฐ์œ„ํ•ด MDC.clear()๋กœ ๋งˆ๋ฌด๋ฆฌํ•˜์˜€๋‹ค.


โ“ traceId๋Š” ์ƒ์„ฑํ–ˆ์œผ๋‹ˆ, ์ด์ œ ์ „ํŒŒ์™€ ์‹œ๊ฐํ™”๋Š” ์–ด๋–ป๊ฒŒ ํ• ๊นŒ

๊ทธ๋ž˜์„œ ์ž๋™ traceId ๊ด€๋ฆฌ์—์„œ ํ•œ ๊ฑธ์Œ ๋” ๋‚˜์•„๊ฐ€, ์„œ๋น„์Šค ๊ฐ„ ํ˜ธ์ถœ ํ๋ฆ„ ์ถ”์ ๊ณผ ์‹œ๊ฐํ™”๊นŒ์ง€ ๊ณ ๋ คํ•ด Micrometer Tracing + OpenTelemetry๋ฅผ ๋„์ž…ํ–ˆ๋‹ค.

์ฒ˜์Œ์—” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ Tempo ๊ฐ™์€ ๋ฐฑ์—”๋“œ๋กœ ์ง์ ‘ ์ „์†ก์„ ๊ณ ๋ฏผํ–ˆ๋‹ค.
๊ณง๋ฐ”๋กœ ๋ง‰ํ˜”๋‹ค. ๋ฐฑ์—”๋“œ๋ฅผ ๋ฐ”๊พธ๊ฑฐ๋‚˜, ์—ฌ๋Ÿฌ ๊ตฐ๋ฐ๋กœ ๋™์‹œ์— ๋ณด๋‚ด๊ฑฐ๋‚˜, ๋„คํŠธ์›Œํฌ ์ด์Šˆ์— ๋ฐฉ์–ด์ ์œผ๋กœ ๋Œ€์‘ํ•˜๊ธฐ๊ฐ€ ๋งŒ๋งŒ์น˜ ์•Š์•˜๋‹ค.

๊ทธ๋ž˜์„œ Exporter๋ฅผ ๋ฐ”๋กœ ๋ถ™์ด์ง€ ์•Š๊ณ , ์ค‘๊ฐ„์— OpenTelemetry Collector๋ฅผ ์„ธ์› ๋‹ค.

  • ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ โ†’ OTLP(HTTP 4318 / gRPC 4317) โ†’ Collector
  • Collector์—์„œ ์ง‘๊ณ„ยท์ƒ˜ํ”Œ๋งยท๋ผ์šฐํŒ…ยทํฌ๋งท ๋ณ€ํ™˜
  • ๋’ท๋‹จ์€ Grafana Tempo๋กœ ์‹œ๊ฐํ™” (์ดˆ๊ธฐ Zipkin์—์„œ ์ „ํ™˜)

๋ฐฑ์—”๋“œ๋ฅผ ๊ฐˆ์•„๋ผ์šฐ๊ฑฐ๋‚˜ ์ „์†ก ์ •์ฑ…์„ ์กฐ์ •ํ•  ๋•Œ ์•ฑ ์ˆ˜์ •์ด ํ•„์š” ์—†๊ฒŒ ๋งŒ๋“œ๋Š” ๊ฒŒ ํฌ์ธํŠธ์˜€๋‹ค.

App โ†’ OTLP(4317/4318) โ†’ Collector โ†’ Tempo (or Zipkin, Jaeger)

Collector๋ฅผ ์“ฐ๋‹ˆ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์€ โ€œํ๋ฆ„ ๋งŒ๋“ค๊ธฐโ€์—๋งŒ ์ง‘์ค‘ํ•  ์ˆ˜ ์žˆ์—ˆ๊ณ ,
๋ฐฑ์—”๋“œ๋ฅผ ๊ฐˆ์•„๋ผ์šฐ๊ฑฐ๋‚˜ ์ „์†ก ์ •์ฑ…์„ ๋ฐ”๊ฟ€ ๋•Œ๋„ ์•ฑ ์ˆ˜์ • ์—†์ด Collector ์„ค์ •๋งŒ ๋ณ€๊ฒฝํ•˜๋ฉด ๋๋‹ค.

์ถ”๊ฐ€๋กœ, Collector๋Š” ๋™์‹œ์— ์—ฌ๋Ÿฌ Exporter๋กœ ์ „์†กํ•  ์ˆ˜ ์žˆ์–ด ์šด์˜ ํ™˜๊ฒฝ์—์„œ๋Š”
Tempo + Jaeger ๊ฐ™์€ ๋ฉ€ํ‹ฐ ์ „์†ก๋„ ๊ฐ€๋Šฅํ•˜๋‹ค.

์ฆ‰, Collector๋Š” ๋‹จ์ˆœ ์ค‘๊ณ„๊ธฐ๊ฐ€ ์•„๋‹ˆ๋ผ ํ™•์žฅ์„ฑ๊ณผ ์œ ์—ฐ์„ฑ์„ ๋ณด์žฅํ•˜๋Š” ์ค‘์•™ ํ—ˆ๋ธŒ์˜€๋‹ค.


๐Ÿ›ฐ๏ธ MSA ํ˜ธ์ถœ, ๋‹จ์ผ ์•ฑ์—์„œ ๋‘ ์„œ๋น„์Šค๋กœ ํ™•์žฅํ•˜๊ธฐ

์ฒ˜์Œ์—๋Š” ๋‹จ์ผ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋‚ด๋ถ€์—์„œ Aโ†’B ํ˜ธ์ถœ์„ ํ‰๋‚ด ๋‚ด๋ฉฐ ํ…Œ์ŠคํŠธํ–ˆ๋‹ค.

๐Ÿ“‚ ํด๋” ๊ตฌ์กฐ (๋‹จ์ผ ์•ฑ ์•ˆ์—์„œ A์™€ B๋ฅผ ๋‚˜๋ˆˆ ํ˜•ํƒœ)

tracing/
 โ”œโ”€ client/
 โ”‚   โ””โ”€ BServiceClient
 โ”œโ”€ controller/
 โ”‚   โ”œโ”€ AController
 โ”‚   โ””โ”€ BController
 โ”œโ”€ service/
 โ”‚   โ””โ”€ AService
config/
 โ””โ”€ tracing/
     โ””โ”€ TracingWebClientConfig

โžก๏ธ ํ•˜๋‚˜์˜ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์•ˆ์ด์ง€๋งŒ, ํŒจํ‚ค์ง€๋ฅผ ๋ถ„๋ฆฌํ•ด A์™€ B ์„œ๋น„์Šค์ฒ˜๋Ÿผ ๊ตฌ์„ฑํ–ˆ๋‹ค.


๐Ÿ”€ ํ˜ธ์ถœ ํ๋ฆ„ (์š”์ฒญ์ด ์‹ค์ œ๋กœ ํ˜๋Ÿฌ๊ฐ€๋Š” ๊ฒฝ๋กœ)

GET /a/call-b
  โ†’ AController
    โ†’ AService
      โ†’ BServiceClient (WebClient)
         โ†’ GET /b/hello
            โ†’ BController
  • WebClient(๋ฆฌ์•กํ‹ฐ๋ธŒ, Spring WebFlux ๊ธฐ๋ฐ˜) ๋กœ Aโ†’B ํ˜ธ์ถœ์„ ๊ตฌํ˜„ํ–ˆ๋‹ค.
  • Micrometer Tracing ์œผ๋กœ traceId/spanId๋ฅผ ์ž๋™ ์ „ํŒŒํ–ˆ๋‹ค.
  • MDC ๋ฅผ ํ†ตํ•ด ๋กœ๊ทธ์—๋„ ๋™์ผ ID๋ฅผ ์ฃผ์ž…ํ–ˆ๋‹ค.

๋•๋ถ„์— Postman ํ˜ธ์ถœ๋งŒ์œผ๋กœ๋„ ๋กœ๊ทธ + Tempo ์‹œ๊ฐํ™”๋ฅผ ๋™์‹œ์— ํ™•์ธํ•  ์ˆ˜ ์žˆ์—ˆ๋‹ค.

ํ•˜์ง€๋งŒ ๊ณง ํ•œ๊ณ„๊ฐ€ ๋ณด์˜€๋‹ค.
โ†’ ๊ฐ™์€ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋‚ด๋ถ€์—์„œ๋งŒ ํ˜ธ์ถœ์„ ํ‰๋‚ด ๋‚ด๋‹ค ๋ณด๋‹ˆ, ์‹ค์ œ MSA์ฒ˜๋Ÿผ ์„œ๋น„์Šค ๊ฒฝ๊ณ„๋ฅผ ๋„˜๋Š” ํ๋ฆ„์„ ์˜จ์ „ํžˆ ์ฆ๋ช…ํ•˜๊ธด ์–ด๋ ค์› ๋‹ค.

๋” ๋ช…ํ™•ํ•œ ํ…Œ์ŠคํŠธ๋ฅผ ์œ„ํ•ด ์‹ค์ œ MSA์ฒ˜๋Ÿผ ์„œ๋น„์Šค๋ฅผ ๋ถ„๋ฆฌํ•œ ๊ตฌ์กฐ์—์„œ ๋ณด๋Š” ๊ฒƒ์ด ๋” ์ ํ•ฉํ•˜๋‹ค๊ณ  ํŒ๋‹จํ–ˆ๋‹ค.

๐Ÿ‘‰๐Ÿป ๊ทธ๋ž˜์„œ ํ˜ธ์ถœ ๊ตฌ์กฐ๋Š” ๊ทธ๋Œ€๋กœ ๋‘๊ณ , 8081(logsentry-api)์™€ 8082(service-b) ๋‘ ์„œ๋น„์Šค๋กœ ๋‚˜๋ˆ„์–ด ํ…Œ์ŠคํŠธ๋ฅผ ์ง„ํ–‰ํ–ˆ๋‹ค.

[Before: ๋‹จ์ผ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜]
  logsentry-api (8081)
    โ”œโ”€ AController
    โ”œโ”€ AService
    โ”œโ”€ BServiceClient
    โ””โ”€ BController
    (TraceId/SpanId ๋™์ผ JVM ๋‚ด ์ „ํŒŒ)

----------------------------

[After: ์„œ๋น„์Šค ๋ถ„๋ฆฌ]
  Service-A (8081, logsentry-api)
    โ””โ”€ /a/call-b
         โ””โ”€ WebClient โ†’ Service-B ํ˜ธ์ถœ

  Service-B (8082, service-b)
    โ””โ”€ /b/hello

โš™๏ธ ์„œ๋น„์Šค ๋ถ„๋ฆฌ ๊ตฌ์„ฑ

๋จผ์ € ๊ธฐ์กด service-a(logsentry-api)์—์„œ service-b๋ฅผ ํ˜ธ์ถœํ•  ์ˆ˜ ์žˆ๋„๋ก ๊ตฌ์กฐ๋ฅผ ๋ฐ”๊ฟจ๋‹ค.
๋‚˜๋Š” ๋ชจ๋“  ์„œ๋น„์Šค๋ฅผ Docker ํ™˜๊ฒฝ์—์„œ ์‹คํ–‰ํ–ˆ๊ธฐ ๋•Œ๋ฌธ์—, service-b ์ปจํ…Œ์ด๋„ˆ๋ฅผ ๋จผ์ € ์ถ”๊ฐ€ํ•˜์˜€๋‹ค.

1. service-b ๋„์ปค ์ปจํ…Œ์ด๋„ˆ ์ถ”๊ฐ€

# docker-compose.yml
service-b:
  build:
    context: ./service-b
    dockerfile: Dockerfile
  container_name: service-b
  ports:
    - "8082:8082"
  environment:
    - SPRING_PROFILES_ACTIVE=${SPRING_PROFILES_ACTIVE}
  networks:
    - logsentry
  restart: unless-stopped
  healthcheck:
    test: ["CMD", "wget", "--spider", "-q", "http://localhost:8082/actuator/health"]
    interval: 30s
    timeout: 10s
    retries: 10
    start_period: 60s

2. service-a โ†’ service-b ํ˜ธ์ถœ ๊ตฌ์กฐ ๋ณ€๊ฒฝ ๋ฐ ํ™˜๊ฒฝ ์„ค์ • ์ถ”๊ฐ€

application-prod.yml

b:
  base-url: ${B_BASE_URL:http://localhost:8082}

BProps (ํ™˜๊ฒฝ๋ณ€์ˆ˜ ์ฃผ์ž…)

@ConfigurationProperties(prefix = "b")
public record BProps(String baseUrl) {}

TracingWebClientConfig

@Configuration
@RequiredArgsConstructor
@Slf4j
public class TracingWebClientConfig {

    private final BProps props;
    private final Tracer tracer;

    @Bean
    WebClient webClient(WebClient.Builder builder) {
        return builder
            .baseUrl(props.baseUrl())
            .filter((request, next) -> {
                var context = tracer.currentTraceContext().context();
                if (context != null) {
                    log.info("[Tracing] traceId={}, spanId={}", context.traceId(), context.spanId());
                }
                return next.exchange(request);
            })
            .build();
    }
}

BServiceClient

@Component
@RequiredArgsConstructor
public class BServiceClient {

    private final WebClient webClient;

    @Observed(name = "bServiceClient.callB", contextualName = "WebClient โ†’ B")
    public Mono<String> callB() {
        return webClient.get()  // http://localhost:8081/b/hello์—์„œ ์ƒ๋Œ€ ๊ฒฝ๋กœ๋กœ ๋ณ€๊ฒฝ โ†’ service-b ํ˜ธ์ถœ
                .uri("/b/hello")
                .retrieve()
                .bodyToMono(String.class);
    }
}

โœ… service-b ์„œ๋น„์Šค์—๋Š” BController์™€ ํ•จ๊ป˜, Tempo/Otel ์ „์†ก ๊ตฌ์กฐ yml๊ณผ Dockerfile ์ •๋„์˜ ์ตœ์†Œ ์„ค์ •๋งŒ ๋‘์—ˆ๋‹ค.

BController

@RestController
@RequestMapping("/b")
public class BController {

    private static final Logger log = LoggerFactory.getLogger(BController.class);

    @Observed(name = "bController.hello", contextualName = "HTTP /b/hello")
    @GetMapping("/hello")
    public Mono<String> hello() {
        log.info("[B] /b/hello ์š”์ฒญ ์ฒ˜๋ฆฌ");
        return Mono.just("Hello from B!");
    }
}

๐Ÿ” Grafana Tempo์—์„œ ํ™•์ธ

๋‘ ์„œ๋น„์Šค๋ฅผ ๊ตฌ๋ถ„ํ•˜๊ธฐ ์œ„ํ•ด spring.application.name ์„ ๊ฐ๊ฐ ๋‹ค๋ฅด๊ฒŒ ์„ค์ •ํ–ˆ๋‹ค.

  • service-a โ†’ logsentry-api
  • service-b โ†’ service-b

Tempo ์ฟผ๋ฆฌ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ง€์ •ํ–ˆ๋‹ค.

{resource.service.name=~"logsentry-api|service-b"}

์ด ์ƒํƒœ์—์„œ /a/call-b ์š”์ฒญ์„ ๋ณด๋‚ด๋ฉด, Tempo๋Š” ์•„๋ž˜์ฒ˜๋Ÿผ span ํŠธ๋ฆฌ๋ฅผ ๋ณด์—ฌ์ค€๋‹ค.
๊ทธ๋ฆฌ๊ณ , ์„œ๋น„์Šค๋ณ„๋กœ ์ƒ‰์ด ๋‹ค๋ฅด๊ฒŒ ํ‘œ์‹œ๋˜์–ด Aโ†’B ํ˜ธ์ถœ ํ๋ฆ„์„ ํ•œ๋ˆˆ์— ๊ตฌ๋ถ„ํ•  ์ˆ˜ ์žˆ์—ˆ๋‹ค.

  1. logsentry-api (์„œ๋น„์Šค A)

    • http get /a/call-b โ†’ ์ตœ์ดˆ ์š”์ฒญ, root span
    • ๋‚ด๋ถ€์—์„œ WebClient๋กœ /b/hello ํ˜ธ์ถœ ์‹œ ์ƒˆ๋กœ์šด child span ์ƒ์„ฑ
  2. service-b (์„œ๋น„์Šค B)

    • http get /b/hello โ†’ service-a์˜ WebClient ํ˜ธ์ถœ์„ ์ฒ˜๋ฆฌ
    • ๊ฐ™์€ traceId๋ฅผ ๊ณต์œ ํ•˜๋ฏ€๋กœ Tempo์—์„œ child span์œผ๋กœ ์ด์–ด์ง

  • root span: /a/call-b (service-a)
  • child span: /b/hello (service-b)

โ†’ traceId๋Š” ๋™์ผํ•˜๊ณ , spanId๋งŒ ์ƒˆ๋กœ ์ƒ์„ฑ๋˜์–ด ๋ถ€๋ชจ-์ž์‹ ๊ด€๊ณ„๋กœ ์—ฐ๊ฒฐ๋๋‹ค.

๐Ÿ‘‰ ๊ฒฐ๊ณผ์ ์œผ๋กœ, /a/call-b (logsentry-api) ์š”์ฒญ์ด traceId๋ฅผ ์œ ์ง€ํ•œ ์ฑ„ /b/hello (service-b)๊นŒ์ง€ ์ด์–ด์ง€๋Š” ํ๋ฆ„์„ ์‹œ๊ฐ์ ์œผ๋กœ ์ฆ๋ช…ํ•  ์ˆ˜ ์žˆ์—ˆ๋‹ค.

๋‹จ์ผ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋‚ด์—์„œ๋„ traceId ์ „ํŒŒ ๊ฒ€์ฆ์€ ๊ฐ€๋Šฅํ–ˆ๋‹ค.
ํ•˜์ง€๋งŒ ์„œ๋น„์Šค๋ฅผ ์‹ค์ œ๋กœ ๋‘ ๊ฐœ ๋ถ„๋ฆฌํ•˜๋‹ˆ, Aโ†’B ํ˜ธ์ถœ ํ๋ฆ„์ด ํ›จ์”ฌ ๋ช…ํ™•ํ•˜๊ฒŒ ๋“œ๋Ÿฌ๋‚ฌ๋‹ค.


๐Ÿ”ฅ ๋งˆ๋ฌด๋ฆฌํ•˜๋ฉฐ

์ด๋ฒˆ ๊ธ€์˜ ํฌ์ธํŠธ๋Š” ์š”์ฒญ์ด ์–ด๋””์„œ ์‹œ์ž‘ํ•ด ์–ด๋””๊นŒ์ง€ ๊ฐ”๋Š”์ง€, ๊ทธ ๊ธธ์„ ๋ˆˆ๊ณผ ํ™”๋ฉด์œผ๋กœ ๋™์‹œ์— ๋”ฐ๋ผ๊ฐˆ ์ˆ˜ ์žˆ๊ฒŒ ๋งŒ๋“œ๋Š” ์„ค๊ณ„์˜€๋‹ค.

  • ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์€ Micrometer Tracing์œผ๋กœ traceId/spanId๋ฅผ MDC์™€ ํ—ค๋”์— ์ž๋™ ์ „ํŒŒํ–ˆ๋‹ค.
  • ์ „์†ก์€ OTLP โ†’ Collector๋กœ ์ค‘์•™ ์ง‘์ค‘ํ™”ํ–ˆ๋‹ค.
  • ์‹œ๊ฐํ™”๋Š” Grafana Tempo์—์„œ ์„œ๋น„์Šค๋ณ„ ํŠธ๋ฆฌ ๊ตฌ์กฐ๋กœ ํ™•์ธํ–ˆ๋‹ค.
  • ํ…Œ์ŠคํŠธ๋Š” ์ฒ˜์Œ์—” ๋‹จ์ผ ์•ฑ ๋‚ด๋ถ€์—์„œ, ์ดํ›„ 8081โ†”8082 ๋‘ ์„œ๋น„์Šค๋กœ ๋ถ„๋ฆฌํ•˜์—ฌ ์‹ค์ œ MSA์ฒ˜๋Ÿผ ๊ฒ€์ฆํ–ˆ๋‹ค.

์ด์ œ โ€œ200 OK๋ฉด ๋โ€์ด ์•„๋‹ˆ๋ผ, โ€œํ๋ฆ„์„ ํ™•์ธํ•˜๋Š” ์Šต๊ด€" ์ด ์ •๋ง ์ค‘์š”ํ•˜๋‹ค๊ณ  ์ฒด๊ฐํ–ˆ๋‹ค.

_

๐Ÿ“˜ ์šฉ์–ด ์ •๋ฆฌ

Micrometer Tracing: Spring Boot 3.x์˜ Sleuth ํ›„์† ๋ถ„์‚ฐ ์ถ”์  ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ, traceId/spanId ์ž๋™ ์ƒ์„ฑยท์ „ํŒŒ
OpenTelemetry (OTEL): CNCF ํ‘œ์ค€ ๊ด€์ธก ํ”„๋ ˆ์ž„์›Œํฌ, ๋กœ๊ทธยท๋ฉ”ํŠธ๋ฆญยทํŠธ๋ ˆ์ด์Šค๋ฅผ ํ†ตํ•ฉ ๊ด€๋ฆฌ
OTLP Exporter: ํŠธ๋ ˆ์ด์Šค ๋ฐ์ดํ„ฐ๋ฅผ OTLP ํ”„๋กœํ† ์ฝœ(HTTP/gRPC)๋กœ Collector์— ์ „๋‹ฌํ•˜๋Š” ๋ชจ๋“ˆ
Collector: Exporter์—์„œ ๋ฐ›์€ ๋ฐ์ดํ„ฐ๋ฅผ ์ง‘๊ณ„ยท๋ณ€ํ™˜ยท๋ผ์šฐํŒ…ํ•ด Zipkin, Tempo ๋“ฑ ๋ฐฑ์—”๋“œ๋กœ ์ „์†ก

profile
"๋กœ์ปฌ์—์„  ๋ฌธ์ œ์—†์—ˆ๋Š”๋ฐโ€ฆ?"

0๊ฐœ์˜ ๋Œ“๊ธ€