[Grafana Tempo] API ๊ธฐ๋ณธ

์ด๋ฒˆ์—” ๊ทธ๋ƒฅ ๋Œ€์‹œ๋ณด๋“œ๋ž‘ sdk๋กœ ๋Œ€์ถฉ ๋ผ์šฐ๋Š”๊ฑฐ ๋ง๊ณ , ์ง์ ‘ HTTP API ์ด๋ณด๋ฉด์„œ ๊ตฌ์กฐ๋ฅผ ํŒŒ์•…ํ•ด๋ณด์ž.
https://grafana.com/docs/tempo/latest/api_docs/

๊ตฌ์„ฑ์€ docker compose ๊ธฐ๋ฐ˜์ด๋‹ค.
์ด๊ฒŒ tempo.yml์ด๊ณ 

stream_over_http_enabled: true
server:
  http_listen_port: 3200
  log_level: info

query_frontend:
  search:
    duration_slo: 5s
    throughput_bytes_slo: 1.073741824e+09
    metadata_slo:
      duration_slo: 5s
      throughput_bytes_slo: 1.073741824e+09
  trace_by_id:
    duration_slo: 5s
  metrics:
    max_duration: 120h
    query_backend_after: 5m
    duration_slo: 5s
    throughput_bytes_slo: 1.073741824e+09

distributor:
  receivers:
    otlp:
      protocols:
        http:
          endpoint: "0.0.0.0:4318"
        grpc:
          endpoint: "0.0.0.0:4317"

ingester:
  max_block_duration: 5m # cut the headblock when this much time passes. this is being set for demo purposes and should probably be left alone normally

compactor:
  compaction:
    block_retention: 168h

metrics_generator:
  registry:
    external_labels:
      source: tempo
      cluster: docker-compose
  storage:
    path: /var/tempo/generator/wal
  traces_storage:
    path: /var/tempo/generator/traces

storage:
  trace:
    backend: local # backend configuration to use
    wal:
      path: /var/tempo/wal # where to store the wal locally
    local:
      path: /var/tempo/blocks

overrides:
  defaults:
    metrics_generator:
      processors: [service-graphs, span-metrics, local-blocks] # enables metrics generator
      generate_native_histograms: both

์ด๊ฒŒ docker-compose.yml์ด๋‹ค.

services:
  tempo:
    image: grafana/tempo:2.7.1
    ports:
      - "3200:3200"
    command: ["-config.file=/etc/tempo.yaml"]
    volumes:
      - tempo_data:/var/tempo
      - ./tempo.yaml:/etc/tempo.yaml

volumes:
  tempo_data:
    driver: local

์‹คํ–‰ํ•œ๋‹ค.
3200 ํฌํŠธ๋กœ Tempo ์„œ๋ฒ„๋ฅผ, 4318 ํฌํŠธ๋กœ open telemetry ํ˜ธํ™˜ ์„œ๋ฒ„๋ฅผ ์—ด์—ˆ๋‹ค.




์ƒํƒœ ํ™•์ธ

๊ธฐ๋ณธ์ ์œผ๋กœ Tempo์— ์ง์ ‘ ์ฝ๊ธฐ๋ฅผ ์˜๋Š” ๊ฒƒ์€ 3200 ํฌํŠธ๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.

curl -X GET http://localhost:3200/status

๊ทธ๋Ÿผ ์ด๋ ‡๊ฒŒ ์ƒํƒœ ์ •๋ณด๋‚˜ api ๋ชฉ๋ก๊ฐ™์€๊ฒŒ ๋œฌ๋‹ค.




trace ์‚ฝ์ž…ํ•˜๊ธฐ

๋ฐ์ดํ„ฐ๋ฅผ ์ง‘์–ด๋„ฃ์„ ๋•Œ๋Š” oltp ํ˜ธํ™˜ ์—”๋“œํฌ์ธํŠธ๋ฅผ ํ†ตํ•ด ์ฐ”๋Ÿฌ์•ผ ํ•œ๋‹ค.
์ด๋ ‡๊ฒŒ ๋„ฃ์œผ๋ฉด ๋œ๋‹ค.

curl -X POST http://localhost:4318/v1/traces \
  -H "Content-Type: application/json" \
  -d '{
    "resourceSpans": [{
      "resource": {
        "attributes": [{
          "key": "service.name",
          "value": { "stringValue": "test-service" }
        }]
      },
      "scopeSpans": [{
        "spans": [{
          "traceId": "5b8aa5a2d2c872e8321cf37308d69df2",
          "spanId": "5fb397be34d25d8b",
          "name": "test-span",
          "kind": 1,
          "startTimeUnixNano": "1644238522000000000",
          "endTimeUnixNano": "1644238522000000000"
        }]
      }]
    }]
  }'

์ด๋ ‡๊ฒŒ ๋œจ๋ฉด ์ž˜ ๋œ ๊ฒƒ์ด๋‹ค.
scopeSpans์— ์žˆ๋Š”๊ฒŒ ๊ธฐ๋ณธ์ ์ธ span ํ•„๋“œ ๊ฐ’๋“ค์ด๋‹ค.
kind๊ฐ€ ์„œ๋ฒ„์ธ์ง€ consumer์ธ์ง€ ๊ตฌ๋ถ„ํ•˜๋Š” enum ์ •์ˆ˜๊ฐ’์ด๊ณ , resource.attribute๊ฐ€ ๋ถ€๊ฐ€์ ์ธ ํƒœ๊ทธ๊ฐ’๋“ค์ด๋‹ค.




๋‹จ์ˆœ ์กฐํšŒ

traceID ๊ธฐ๋ฐ˜์˜ ์ƒ์„ธ ์กฐํšŒ๋Š” ๊ทธ๋ƒฅ trace ID๋ฅผ path parameter๋กœ ์ฑ„์›Œ์„œ ์˜๋ฉด ๋œ๋‹ค.

curl -X GET http://localhost:3200/api/traces/5b8aa5a2d2c872e8321cf37308d69df2

search API๋ฅผ ์“ฐ๋ฉด ํƒœ๊ทธ๊ฐ’๋“ค์„ ๊ธฐ๋ฐ˜์œผ๋กœ ํ•„ํ„ฐํ•ด์„œ ๊ฐ€์ ธ์˜ฌ ์ˆ˜๋„ ์žˆ๋‹ค.

curl -X GET "http://localhost:3200/api/search?tags=service.name%3Dtest-service"

grafana์—์„œ๋„ explorer ํƒญ์—์„œ ์ด search ๊ธฐ๋Šฅ์„ ์‹œ๊ฐํ™”๋œ ํ˜•ํƒœ๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

๊ทผ๋ฐ ์ด๋Ÿฐ ๋ฐฉ์‹์€ ์ข€ ๋ถˆํŽธํ•˜๊ธฐ๋„ ํ•˜๊ณ , ๋ณดํ†ต traceQL์ด๋ผ๋Š” ํ™•์žฅ ๊ตฌ๋ฌธ์„ ํ†ตํ•ด์„œ Grafana์—์„œ ์ง์ ‘ ์˜๋Š” ํ˜•ํƒœ๋กœ ์ž์ฃผ ์“ด๋‹ค.

q๋ผ๋Š” ํŒŒ๋ผ๋ฏธํ„ฐ์— TraceQL ํ‘œํ˜„์‹์„ ๋‹ด์•„์„œ ์˜๋ฉด ๊ฑฐ๊ธฐ ๋งž์ถฐ์„œ ๋ฟŒ๋ ค์ค€๋‹ค.

TraceQL์˜ ๋ฌธ๋ฒ•๊ณผ ์‚ฌ์šฉ๋ฒ•์€ ๋‹ค์Œ ํฌ์ŠคํŠธ์—์„œ ๋‹ค๋ค„๋ณธ๋‹ค.



์ฐธ์กฐ
https://grafana.com/docs/tempo/latest/api_docs/
https://grafana.com/docs/tempo/latest/api_docs/pushing-spans-with-http/