IT
OpenTelemetry + Tempo를 이용한 Kubernetes 분산 추적
OpenTelemetry + Tempo를 이용한 Kubernetes 분산 추적
FastAPI → Redis → PostgreSQL 요청 흐름을 Trace ID 하나로 추적하는 실전 Kubernetes Observability
Kubernetes 환경에서 Prometheus와 Loki를 구축하면 시스템 상태와 로그를 상당히 편리하게 확인할 수 있다.
하지만 한 가지 문제가 남는다.
예를 들어 사용자가 다음 API를 호출했다고 생각해보자.
POST /api/orders
요청이 다음과 같은 구조로 처리된다.
사용자
|
v
Nginx / Ingress
|
v
FastAPI
|
+------> Redis
|
+------> PostgreSQL
그런데 사용자가 500 오류를 받았다.
Prometheus에서는:
HTTP 500 증가
를 확인할 수 있고,
Loki에서는:
Database connection timeout
이라는 로그를 찾을 수 있다.
하지만 다음 질문에는 바로 답하기 어렵다.
이 요청이 정확히 어떤 Pod를 거쳤는가?
Redis 요청에는 몇 ms가 걸렸는가?
PostgreSQL 쿼리에 몇 ms가 걸렸는가?
전체 요청 시간 중 어디에서 가장 많은 시간이 발생했는가?
Nginx → FastAPI → Redis → PostgreSQL의 호출 관계는 어떻게 되는가?
이 문제를 해결하는 기술이 **Distributed Tracing(분산 추적)**이다.
이번 실습에서는 다음 구조를 구축한다.
Client
|
v
Ingress/Nginx
|
v
FastAPI
/ \
/ \
v v
Redis PostgreSQL
\ /
\ /
\ /
v v
OpenTelemetry
|
v
OpenTelemetry
Collector
|
v
Tempo
|
v
Grafana
1. Distributed Tracing이란?
Distributed Tracing은 하나의 요청이 여러 시스템을 거쳐 처리되는 과정을 추적하는 기술이다.
예를 들어 사용자가 다음 API를 호출한다고 하자.
GET /api/users/100
실제 내부에서는 다음과 같은 작업이 수행될 수 있다.
HTTP Request
|
v
FastAPI
|
+---- Redis GET
|
+---- PostgreSQL SELECT
|
v
Response
이때 전체 요청을 하나의 Trace라고 한다.
Trace 내부에는 여러 개의 Span이 존재한다.
Trace
|
+-- Span: HTTP Request
|
+-- Span: Redis GET
|
+-- Span: PostgreSQL SELECT
|
+-- Span: Response
즉:
Trace = 하나의 요청 전체
Span = 요청을 처리하는 각각의 작업
이라고 이해하면 쉽다.
2. Trace와 Span의 관계
분산 추적의 기본 개념은 반드시 이해해야 한다.
예를 들어 요청 하나가 다음과 같이 처리되었다고 하자.
Trace ID
abc123
그리고 내부 작업이:
FastAPI HTTP
Redis GET
PostgreSQL SELECT
로 구성된다.
그러면:
Trace: abc123
|
+-- Span A: FastAPI HTTP
|
+-- Span B: Redis GET
|
+-- Span C: PostgreSQL SELECT
형태가 된다.
Span에는 다음과 같은 정보가 들어갈 수 있다.
Span ID
Trace ID
Service Name
Operation
Start Time
End Time
Duration
Status
Attributes
예:
Trace ID: abc123
Span: PostgreSQL SELECT
Duration: 83ms
Status: OK
이 정보를 이용하면 어느 구간에서 시간이 오래 걸렸는지 확인할 수 있다.
3. OpenTelemetry란?
OpenTelemetry는 애플리케이션의 Metrics, Logs, Traces와 같은 Telemetry 데이터를 수집하고 전달하기 위한 오픈 표준 및 프로젝트다.
이번 실습에서는 특히 Trace에 집중한다.
Application
|
v
OpenTelemetry SDK
|
v
OpenTelemetry Collector
|
v
Tempo
OpenTelemetry의 장점은 특정 백엔드에 종속되지 않는다는 것이다.
예를 들어:
OpenTelemetry
|
+---- Tempo
+---- Jaeger
+---- 다른 Trace Backend
와 같이 구성할 수 있다.
따라서 애플리케이션에 OpenTelemetry를 적용해 놓으면 나중에 Trace 저장 시스템을 변경하기도 상대적으로 편하다.
4. Tempo란?
Tempo는 Grafana 생태계의 Distributed Tracing Backend다.
기존에 구축한 시스템과 비교하면 이해하기 쉽다.
역할
기술
Metrics
Prometheus
Logs
Loki
Traces
Tempo
Visualization
Grafana
Alerts
Alertmanager
Telemetry Collection
OpenTelemetry Collector / Alloy
전체 구조는 다음과 같다.
Observability
|
+--------------+--------------+
| | |
v v v
Metrics Logs Traces
| | |
Prometheus Loki Tempo
| | |
+--------------+--------------+
|
v
Grafana
이제 Kubernetes 환경에서 세 가지 데이터를 함께 볼 수 있게 된다.
5. 왜 로그만으로 부족한가?
예를 들어 Loki에서 다음 로그를 발견했다고 하자.
Database query timeout
로그만 보면 PostgreSQL 문제가 의심된다.
하지만 실제 요청을 Trace로 보면:
HTTP Request 500ms
|
+-- Redis GET 10ms
|
+-- PostgreSQL 450ms
|
+-- Response 40ms
PostgreSQL에서 대부분의 시간이 소요되고 있다는 사실을 바로 확인할 수 있다.
반대로:
HTTP Request 500ms
|
+-- Redis GET 470ms
|
+-- PostgreSQL 20ms
라면 Redis가 병목이다.
따라서 Distributed Tracing은 성능 병목 구간을 찾는 데 매우 유용하다.
6. 전체 Kubernetes 구조
이번 실습에서는 다음 환경을 기준으로 한다.
Kubernetes Cluster
namespace: tax-prod
Internet
|
v
Ingress/Nginx
|
v
FastAPI
/ \
/ \
v v
Redis PostgreSQL
Observability
FastAPI
|
v
OpenTelemetry SDK
|
v
OTel Collector
|
v
Tempo
|
v
Grafana
그리고 기존 로그 시스템은:
FastAPI/Nginx
|
v
Grafana Alloy
|
v
Loki
Metrics는:
FastAPI/Node/Pod
|
v
Prometheus
|
v
Grafana
로 구성한다.
7. Tempo 설치
먼저 Grafana Helm Repository를 등록한다.
helm repo add grafana https://grafana.github.io/helm-charts
Repository를 업데이트한다.
helm repo update
Tracing용 Namespace를 만든다.
kubectl create namespace tracing
환경에 맞는 Tempo values 파일을 작성한다.
vi tempo-values.yaml
학습 환경에서는 단일 인스턴스 형태로 시작할 수 있다.
예:
deploymentMode: SingleBinary
tempo:
reportingEnabled: false
resources:
requests:
cpu: 100m
memory: 256Mi
limits:
cpu: 500m
memory: 1Gi
persistence:
enabled: true
size: 20Gi
설치:
helm upgrade --install tempo grafana/tempo \
-n tracing \
-f tempo-values.yaml
상태 확인:
kubectl get pods -n tracing
kubectl get svc -n tracing
정상적으로 설치되었다면 Tempo Pod가 Running 상태인지 확인한다.
8. 운영 환경에서는 Storage가 중요하다
Trace 데이터도 계속 증가한다.
예를 들어:
1초에 100 requests
|
v
Trace 데이터 지속 증가
따라서 운영 환경에서는 Storage를 고려해야 한다.
개념적으로:
OpenTelemetry
|
v
Tempo
|
v
Object Storage
|
+-- S3
+-- MinIO
+-- Cloud Storage
개발 환경에서는 Persistent Volume으로 시작할 수 있지만 운영 환경에서는 Object Storage 기반 설계를 검토하는 것이 좋다.
9. OpenTelemetry Collector란?
OpenTelemetry Collector는 Telemetry 데이터를 중간에서 수집하고 전달하는 역할을 한다.
구조는 다음과 같다.
FastAPI
|
| OTLP
v
OpenTelemetry Collector
|
| OTLP
v
Tempo
Collector를 사용하는 이유는 애플리케이션과 Backend를 직접 연결하지 않아도 되기 때문이다.
예를 들어:
Application
|
v
Collector
|
+---- Tempo
+---- 다른 Trace Backend
형태로 확장할 수 있다.
Collector는 다음과 같은 기능을 수행할 수 있다.
Receive
Process
Filter
Batch
Export
10. OpenTelemetry Collector 설치
Collector도 Kubernetes 환경에서 Helm으로 구성할 수 있다.
OpenTelemetry Helm Repository를 등록한다.
helm repo add open-telemetry \
https://open-telemetry.github.io/opentelemetry-helm-charts
업데이트:
helm repo update
Collector용 Namespace:
kubectl create namespace observability
Collector values 파일:
vi otel-values.yaml
예제:
mode: deployment
image:
repository: otel/opentelemetry-collector-k8s
config:
receivers:
otlp:
protocols:
grpc:
http:
processors:
batch:
exporters:
otlp:
endpoint: tempo.tracing.svc.cluster.local:4317
tls:
insecure: true
service:
pipelines:
traces:
receivers:
- otlp
processors:
- batch
exporters:
- otlp
설치:
helm upgrade --install otel-collector \
open-telemetry/opentelemetry-collector \
-n observability \
-f otel-values.yaml
확인:
kubectl get pods -n observability
11. OTLP란?
OpenTelemetry에서 중요한 개념이 **OTLP(OpenTelemetry Protocol)**다.
Application에서 Collector로:
FastAPI
|
| OTLP
v
Collector
Collector에서 Tempo로:
Collector
|
| OTLP
v
Tempo
형태로 데이터를 전달한다.
OTLP는 gRPC와 HTTP 방식으로 사용할 수 있다.
대표적인 포트는:
4317 = OTLP gRPC
4318 = OTLP HTTP
따라서 Kubernetes Service를 구성할 때 이 포트를 확인해야 한다.
12. FastAPI에 OpenTelemetry 적용
이제 실제 애플리케이션에 OpenTelemetry를 적용한다.
Python 패키지를 설치한다.
pip install \
opentelemetry-api \
opentelemetry-sdk \
opentelemetry-exporter-otlp \
opentelemetry-instrumentation-fastapi
FastAPI 자동 계측을 사용할 수 있다.
예:
from fastapi import FastAPI
from opentelemetry.instrumentation.fastapi import FastAPIInstrumentor
app = FastAPI()
FastAPIInstrumentor.instrument_app(app)
이렇게 하면 FastAPI 요청에 대한 Trace를 자동으로 생성할 수 있다.
13. OTLP Exporter 설정
Collector로 Trace를 보내도록 설정한다.
예:
from opentelemetry import trace
from opentelemetry.sdk.resources import Resource
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
resource = Resource.create({
"service.name": "fastapi-backend"
})
provider = TracerProvider(
resource=resource
)
exporter = OTLPSpanExporter(
endpoint="otel-collector.observability.svc.cluster.local:4317",
insecure=True
)
provider.add_span_processor(
BatchSpanProcessor(exporter)
)
trace.set_tracer_provider(provider)
그리고 FastAPI instrumentation:
from opentelemetry.instrumentation.fastapi import FastAPIInstrumentor
FastAPIInstrumentor.instrument_app(app)
구조는 다음과 같다.
FastAPI
|
v
TracerProvider
|
v
OTLP Exporter
|
v
Collector
|
v
Tempo
14. Kubernetes 환경에서는 환경변수로 관리
운영 환경에서는 Collector 주소를 코드에 직접 넣는 것보다 환경변수를 사용하는 것이 좋다.
예:
env:
- name: OTEL_SERVICE_NAME
value: "fastapi-backend"
- name: OTEL_EXPORTER_OTLP_ENDPOINT
value: "http://otel-collector.observability.svc.cluster.local:4317"
- name: OTEL_EXPORTER_OTLP_INSECURE
value: "true"
그러면 환경별로 쉽게 변경할 수 있다.
개발:
otel-collector-dev
운영:
otel-collector-prod
와 같이 분리할 수 있다.
15. Redis Trace 계측
FastAPI에서 Redis를 사용하는 경우 Redis 작업도 Trace에 포함시키면 좋다.
OpenTelemetry에서는 Redis와 같은 라이브러리에 대한 instrumentation을 사용할 수 있다.
개념적으로:
FastAPI Span
|
+-- Redis GET Span
|
+-- Redis SET Span
이렇게 된다.
예:
Trace ID: abc123
FastAPI GET /users
|
+-- Redis GET user:100
|
+-- PostgreSQL SELECT
이제 Redis에서 지연이 발생하는지 쉽게 확인할 수 있다.
16. PostgreSQL Trace 계측
Database도 마찬가지다.
PostgreSQL을 사용하는 Python 애플리케이션에 Database instrumentation을 적용하면 SQL 관련 Span을 확인할 수 있다.
구조:
FastAPI
|
+-- Redis
|
+-- PostgreSQL
|
+-- SELECT
+-- INSERT
+-- UPDATE
예를 들어 PostgreSQL SELECT가:
Duration = 850ms
라고 나타난다면 Database query가 병목일 가능성이 높다.
17. 중요한 점 — SQL 정보와 보안
Database Trace에는 SQL 관련 정보가 포함될 수 있다.
따라서 운영 환경에서는 개인정보나 비밀번호 등의 민감한 데이터가 Trace에 들어가지 않도록 주의해야 한다.
좋지 않은 예:
SELECT * FROM users
WHERE password='123456'
Trace에 민감한 정보가 그대로 남으면 안 된다.
따라서 다음을 고려한다.
민감 데이터 제거
SQL parameter masking
Trace retention 설정
접근 권한 제한
18. 직접 Span 생성하기
자동 instrumentation만으로 부족한 경우 직접 Span을 만들 수 있다.
예:
from opentelemetry import trace
tracer = trace.get_tracer(__name__)
그리고:
with tracer.start_as_current_span("calculate-tax"):
result = calculate_tax()
이렇게 하면 다음과 같은 Span이 생성된다.
FastAPI Request
|
+-- calculate-tax
|
+-- DB Query
복잡한 비즈니스 로직을 추적할 때 매우 유용하다.
19. Trace ID란?
Distributed Tracing에서 가장 중요한 정보 중 하나가 Trace ID다.
예:
Trace ID:
4bf92f3577b34da6a3ce929d0e0e4736
하나의 요청에 동일한 Trace ID가 연결된다.
Nginx
|
| Trace ID = ABC
v
FastAPI
|
| Trace ID = ABC
+------> Redis
|
| Trace ID = ABC
+------> PostgreSQL
따라서 로그에도 Trace ID를 기록하면 강력한 장애 분석이 가능해진다.
20. Logs와 Trace 연결
앞에서 구축한 Loki와 결합하면 더욱 강력해진다.
예:
Trace ID
ABC123
FastAPI 로그:
ERROR Database timeout
trace_id=ABC123
Grafana에서 해당 Trace를 클릭하면:
Trace
|
+-- FastAPI
|
+-- PostgreSQL
그리고 관련 로그를 바로 확인할 수 있다.
개념적으로:
Trace ID
|
+--------+--------+
| |
v v
Tempo Loki
| |
+--------+--------+
|
v
Grafana
이것이 **Logs와 Traces의 상관관계(Correlation)**다.
21. Metrics와 Trace까지 연결
이제 Prometheus까지 연결한다.
Prometheus
|
| Metrics
v
Grafana
|
+------ Loki
|
+------ Tempo
예를 들어 Dashboard에서:
HTTP Error Rate
|
v
500 증가
|
v
해당 시간대 Trace 확인
|
v
PostgreSQL Span 확인
|
v
Query 1.2초
|
v
관련 Loki 로그 확인
|
v
Database timeout
이런 분석이 가능해진다.
22. Grafana에 Tempo 연결
Grafana에서:
Connections
↓
Data Sources
↓
Add data source
↓
Tempo
를 선택한다.
Tempo Service 주소를 입력한다.
예:
http://tempo.tracing.svc.cluster.local:3200
환경에 따라 Service 이름과 포트는 실제 설치 결과를 확인해야 한다.
kubectl get svc -n tracing
정상 연결을 확인한다.
23. Grafana Explore에서 Trace 확인
Grafana:
Explore
↓
Tempo
를 선택한다.
Trace ID를 이용해 검색할 수 있다.
예:
Trace ID
4bf92f3577b34da6a3ce929d0e0e4736
결과는 다음과 같은 구조로 나타날 수 있다.
Trace
|
+-- GET /api/users
|
+-- Redis GET
|
+-- PostgreSQL SELECT
각 Span을 클릭하면:
Duration
Attributes
Status
Service
Operation
등을 확인할 수 있다.
24. Trace Waterfall 이해하기
Distributed Tracing에서 가장 중요한 화면 중 하나가 Waterfall이다.
예:
0ms 1000ms
FastAPI |-----------------------------|
|
Redis |----|
|
PostgreSQL | |---------------|
|
Response |------------------------------|
이 화면을 보면 어느 작업이 오래 걸렸는지 바로 알 수 있다.
예를 들어:
FastAPI 1000ms
Redis 20ms
PostgreSQL 850ms
라면 PostgreSQL이 병목이다.
반면:
FastAPI 1000ms
Redis 900ms
PostgreSQL 30ms
라면 Redis가 병목이다.
25. 장애 상황 분석 실습
이제 실제 장애를 만들어보자.
API:
GET /api/users/100
를 호출한다.
정상 상태:
FastAPI 100ms
Redis 10ms
PostgreSQL 30ms
그런데 PostgreSQL에 의도적으로 느린 Query를 발생시킨다.
결과:
FastAPI 1200ms
Redis 10ms
PostgreSQL 1100ms
Grafana Tempo에서 다음처럼 확인할 수 있다.
GET /api/users/100
|
+-- Redis GET 10ms
|
+-- PostgreSQL SELECT 1100ms
이것만으로도 Database가 병목이라는 것을 확인할 수 있다.
26. Loki와 함께 장애 분석
Tempo에서 느린 PostgreSQL Span을 찾았다.
Trace ID:
ABC123
이제 Loki에서 Trace ID를 검색한다.
{app="backend"} |= "ABC123"
결과:
Database connection timeout
그리고 PostgreSQL 로그:
{app="postgres"} |= "timeout"
결과:
connection pool exhausted
최종적으로:
HTTP 500
|
v
FastAPI
|
v
PostgreSQL Span 1100ms
|
v
Loki
|
v
connection pool exhausted
라는 장애 분석이 가능해진다.
27. 장애 테스트 — Redis 지연
이번에는 Redis를 느리게 만들어본다.
FastAPI
|
v
Redis
|
X
느린 응답
Trace:
FastAPI 1000ms
Redis 950ms
PostgreSQL 20ms
Waterfall에서 Redis Span이 길게 나타난다.
이런 방식으로 성능 병목을 빠르게 찾을 수 있다.
28. 장애 테스트 — PostgreSQL 장애
PostgreSQL Pod를 삭제한다.
kubectl delete pod <postgres-pod> -n tax-prod
FastAPI에서 DB API를 호출한다.
Trace에서는:
FastAPI
|
+-- PostgreSQL
|
+-- ERROR
+-- Timeout
가 나타날 수 있다.
동시에 Loki에서는:
Database connection refused
를 확인할 수 있다.
Kubernetes에서는:
kubectl get pods -n tax-prod
로 PostgreSQL Pod 상태를 확인한다.
이렇게 세 가지 정보를 연결한다.
Tempo
↓
어디에서 실패했는가?
Loki
↓
무슨 오류가 발생했는가?
Kubernetes
↓
Pod/서비스 상태는 어떠한가?
29. Kubernetes에서 Trace Context 전달
Distributed Tracing에서 매우 중요한 개념이 Trace Context Propagation이다.
요청이:
Client
↓
Nginx
↓
FastAPI
↓
Redis
↓
PostgreSQL
로 이동할 때 Trace Context가 전달되어야 한다.
대표적으로 W3C Trace Context가 사용된다.
HTTP Header 예:
traceparent:
00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01
이 정보를 기반으로 하나의 요청을 하나의 Trace로 연결할 수 있다.
30. Trace ID를 로그에 남기는 이유
운영 환경에서 다음과 같은 로그가 있다고 하자.
ERROR Database timeout
이것만으로는 어떤 요청인지 알기 어렵다.
하지만:
ERROR Database timeout
trace_id=ABC123
라면 바로 Tempo에서 찾을 수 있다.
따라서 이상적인 구조는:
Application Log
|
+-- timestamp
+-- level
+-- service
+-- trace_id
+-- span_id
+-- message
이다.
31. Grafana에서 완성되는 Observability
이제 지금까지 구축한 모든 시스템을 연결해보자.
Kubernetes
|
+----------------+----------------+
| | |
v v v
Metrics Logs Traces
| | |
Prometheus Loki Tempo
| | |
+----------------+----------------+
|
v
Grafana
|
+-------------+-------------+
| | |
v v v
Dashboard Logs Traces
Alertmanager까지 연결하면:
Prometheus
|
v
Alertmanager
|
+-- Email
+-- Slack
+-- Webhook
구조가 된다.
32. 실제 장애 대응 흐름
운영 중 다음 알림이 발생했다고 가정하자.
CRITICAL
FastAPI HTTP 500 rate > 5%
운영자는 다음 순서로 확인한다.
1단계 — Metrics
Prometheus에서:
HTTP 500 증가
확인.
2단계 — Logs
Loki에서:
{app="backend"} |= "ERROR"
검색.
결과:
Database timeout
3단계 — Trace
Tempo에서 해당 요청 Trace를 확인.
FastAPI
|
+-- Redis 10ms
|
+-- PostgreSQL 2.8s
4단계 — Kubernetes
kubectl get pods -n tax-prod
PostgreSQL 상태 확인.
5단계 — Database
PostgreSQL 로그에서:
connection pool exhausted
확인.
이제 단순히 "FastAPI 500 오류"가 아니라:
PostgreSQL Connection Pool 부족
이라는 근본 원인까지 좁혀갈 수 있다.
33. Metrics + Logs + Traces의 역할
세 가지 기술의 역할을 명확히 구분하면 이해하기 쉽다.
데이터
질문
Metrics
문제가 발생하고 있는가?
Logs
무슨 오류가 발생했는가?
Traces
요청이 어디에서 느려지거나 실패했는가?
예:
Metrics
HTTP 500 증가
↓
Logs
Database timeout
↓
Trace
PostgreSQL 2.8초
↓
Kubernetes
PostgreSQL Pod 상태 확인
↓
Root Cause
Connection Pool 문제
이것이 현대적인 Observability의 기본적인 장애 분석 방법이다.
34. Production 환경에서 주의할 점
Distributed Tracing을 운영 환경에 적용할 때는 몇 가지를 반드시 고려해야 한다.
34.1 모든 요청을 무조건 저장하지 않기
트래픽이 매우 많다면 모든 Trace를 저장하면 데이터 양이 증가한다.
따라서 Sampling을 사용할 수 있다.
예:
전체 요청 = 100%
Trace 저장 = 10%
또는:
정상 요청 = 일부 Sampling
오류 요청 = 높은 우선순위
와 같은 전략을 고려할 수 있다.
35. Trace Sampling
Sampling은 Trace 데이터의 양을 조절하는 방법이다.
예를 들어 초당:
10,000 requests
가 발생한다면 모든 Trace를 저장하는 것은 부담이 될 수 있다.
따라서:
10,000 requests
|
v
Sampling
|
v
1,000 traces
처럼 줄일 수 있다.
다만 장애 분석을 위해 오류 Trace는 최대한 보존하는 전략을 고려할 수 있다.
36. Trace 데이터 보존 기간
Trace도 로그와 마찬가지로 보존 기간이 필요하다.
예:
Hot Trace
7일
Archive
30일
Delete
90일
실제 보존 기간은 시스템 규모와 장애 분석 요구사항에 따라 결정한다.
37. Resource 제한
OpenTelemetry Collector도 Kubernetes Pod이므로 Resource 제한을 설정해야 한다.
예:
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 512Mi
Trace가 증가하면 Collector 자체의 CPU와 Memory 사용량도 증가할 수 있다.
따라서 Prometheus에서 Collector를 모니터링하는 것이 좋다.
Collector CPU
Collector Memory
Dropped Spans
Export Errors
Queue Size
등을 확인해야 한다.
38. Collector 장애도 모니터링해야 한다
다음 상황을 생각해보자.
FastAPI
|
v
Collector
X
|
v
Tempo
Collector가 장애가 발생하면 Trace가 유실될 수 있다.
따라서:
Application
|
v
Collector
|
v
Tempo
각 구간을 모니터링해야 한다.
특히 운영 환경에서는:
Collector replicas
Queue
Retry
Batch
Resource
등을 고려해야 한다.
39. 개발/운영 환경 분리
앞서 Kubernetes 애플리케이션에서 개발과 운영 환경을 분리했듯이 Observability 환경도 분리할 수 있다.
예:
Namespace
|
+-- tax-dev
|
+-- tax-prod
|
+-- observability
|
+-- tracing
|
+-- logging
또는:
dev
└── Trace
prod
└── Trace
환경을 구분할 수 있다.
Trace에 다음 정보를 넣는 것도 좋다.
deployment.environment=production
그러면 Grafana에서 개발/운영 데이터를 구분할 수 있다.
40. 최종 Kubernetes Observability Architecture
지금까지 작성한 Prometheus, Loki, Tempo를 모두 연결하면 다음과 같은 구조가 된다.
Internet
|
v
Ingress / Nginx
|
v
FastAPI
/ | \
/ | \
v v v
Redis PostgreSQL Other Service
Kubernetes Observability
|
+----------------+----------------+
| | |
v v v
Metrics Logs Traces
| | |
Prometheus Loki Tempo
| | |
+----------------+----------------+
|
v
Grafana
|
v
Unified Observability
|
+----------+----------+
| | |
v v v
Dashboard Logs Trace
|
v
Root Cause
여기에:
Prometheus
|
v
Alertmanager
|
+-- Email
+-- Slack
+-- Webhook
까지 추가하면 운영 환경에서 필요한 기본적인 Observability 구성이 완성된다.
41. 시스템 엔지니어가 반드시 기억해야 할 구조
전체 내용을 가장 간단하게 정리하면 다음과 같다.
Prometheus
|
+-- CPU
+-- Memory
+-- Request
+-- Error Rate
+-- Latency
Loki
|
+-- Application Log
+-- Nginx Log
+-- Kubernetes Log
+-- Error Log
Tempo
|
+-- Request Trace
+-- Service Span
+-- Redis Span
+-- Database Span
Grafana
|
+-- Metrics
+-- Logs
+-- Traces
그리고 장애가 발생하면:
1. Metrics
"문제가 있는가?"
2. Logs
"무슨 오류인가?"
3. Trace
"어디에서 문제가 발생했는가?"
4. Kubernetes
"Pod/Service/Node 상태는 정상인가?"
5. Root Cause
"왜 문제가 발생했는가?"
순서로 접근하면 된다.
42. 최종 정리
Kubernetes 환경에서 애플리케이션이 복잡해질수록 단순한 kubectl logs만으로는 장애 원인을 찾기 어려워진다.
특히 다음과 같은 구조에서는 더욱 그렇다.
Ingress
↓
Nginx
↓
FastAPI
↓
Redis
↓
PostgreSQL
하나의 사용자 요청이 여러 서비스를 통과하기 때문이다.
이때 Distributed Tracing을 사용하면:
사용자 요청
|
v
Trace ID
|
+-- Nginx
|
+-- FastAPI
|
+-- Redis
|
+-- PostgreSQL
전체 요청 흐름을 하나의 Trace로 확인할 수 있다.
이번 실습에서 구축한 핵심 구조는 다음과 같다.
FastAPI
|
v
OpenTelemetry SDK
|
v
OpenTelemetry Collector
|
v
Tempo
|
v
Grafana
그리고 기존 시스템과 결합하면:
Kubernetes
|
+-----------+-----------+
| | |
v v v
Metrics Logs Traces
| | |
Prometheus Loki Tempo
| | |
+-----------+-----------+
|
v
Grafana
|
v
장애 원인 분석
이제 Kubernetes Observability의 핵심 세 가지인 Metrics + Logs + Traces가 모두 연결되었다.
가장 중요한 것은 각각의 기술을 별도로 사용하는 것이 아니라 서로 연결하는 것이다.
Prometheus
"500 오류가 증가했다"
↓
Loki
"Database timeout 로그가 발생했다"
↓
Tempo
"PostgreSQL Span에서 2.8초가 소요되었다"
↓
Kubernetes
"PostgreSQL Pod와 연결 상태를 확인한다"
↓
Root Cause
"Database Connection Pool 부족"
이렇게 Metrics → Logs → Traces → Kubernetes → Root Cause로 연결해서 장애를 분석할 수 있다면 Kubernetes 운영에 필요한 Observability의 핵심 개념을 제대로 이해한 것이다.
댓글
아직 댓글이 없습니다.
로그인 후 댓글을 남길 수 있습니다.