Observability — Actuator, Micrometer, OpenTelemetry
Observability — Actuator, Micrometer, OpenTelemetry
요약: Actuator와 OpenTelemetry로 헬스·메트릭·트레이스를 붙여, 느린 지점과 장애 지점을 관측한다.
1. 왜 관측인가
Observability(관측 가능성) 는 로그·메트릭·트레이스만으로 “왜 느리고 어디서 깨졌는지”를 답할 수 있는 상태다.
| 기둥 | 질문 |
|---|---|
| Logs | 무슨 일이 있었나 |
| Metrics | 얼마나, 얼마나 자주 |
| Traces | 이 요청의 시간이 어디에 쓰였나 |
Boot 3는 Micrometer Observation API로 메트릭과 트레이스를 같은 계측에서 뽑는다. Micrometer는 벤더에 치우치지 않는 메트릭 파사드다.
2. Actuator
한 줄 정의: Actuator는 헬스·메트릭·정보 같은 운영 엔드포인트를 HTTP로 연다.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
management:
endpoints:
web:
exposure:
include: health,info,metrics,prometheus
endpoint:
health:
show-details: when_authorized
probes:
enabled: true
server:
port: 8081
| Path | 용도 |
|---|---|
/actuator/health |
생존·준비 요약 |
/actuator/health/liveness |
k8s liveness |
/actuator/health/readiness |
k8s readiness |
/actuator/prometheus |
Prometheus 스크레이프 |
/actuator/info |
빌드·앱 정보 |
운영에서는 별도 포트와 인증으로 막는다. include: '*'는 힙덤프까지 노출하기 쉽다.
3. Health Indicator
외부 결제망처럼 앱이 떠 있어도 트래픽을 받으면 안 되는 의존성은 readiness에 넣을지 팀 정책으로 정한다. 헬스마다 외부 ping을 치면 헬스 자체가 장애가 된다.
@Component
public class PaymentGatewayHealthIndicator implements HealthIndicator {
private final PaymentClient client;
public PaymentGatewayHealthIndicator(PaymentClient client) {
this.client = client;
}
@Override
public Health health() {
try {
client.ping();
return Health.up().withDetail("payment", "reachable").build();
} catch (Exception e) {
return Health.down(e).build();
}
}
}
4. 메트릭과 Observation
Prometheus 레지스트리를 붙이면 /actuator/prometheus로 나간다.
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
</dependency>
HTTP 서버·RestClient·DataSource는 Boot가 기본 계측한다. 비즈니스 카운터는 직접 심는다.
@Service
public class CheckoutService {
private final Counter checkoutCounter;
private final Timer checkoutTimer;
public CheckoutService(MeterRegistry registry) {
this.checkoutCounter = registry.counter("checkout.completed");
this.checkoutTimer = registry.timer("checkout.latency");
}
public void checkout(Order order) {
checkoutTimer.record(() -> {
// 유스케이스
checkoutCounter.increment();
});
}
}
Observation은 메트릭과 트레이스를 한 구간으로 묶는다. 카디널리티가 높은 값(userId, 주문번호)을 태그로 넣으면 시계열이 폭발한다.
Observation.createNotStarted("order.create", observationRegistry)
.lowCardinalityKeyValue("type", "online")
.observe(() -> orderService.create(request));
5. OpenTelemetry
한 줄 정의: OpenTelemetry(OTel) 는 트레이스·메트릭·로그를 보내는 공개 표준이다. Micrometer Tracing이 OTel 브리지로 스팬을 내보내고, Collector가 Jaeger·Tempo 등으로 넘긴다.
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-tracing-bridge-otel</artifactId>
</dependency>
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-exporter-otlp</artifactId>
</dependency>
management:
tracing:
sampling:
probability: 1.0
otlp:
tracing:
endpoint: http://localhost:4318/v1/traces
logging:
pattern:
level: "%5p [${spring.application.name:},%X{traceId:-},%X{spanId:-}]"
운영 샘플링은 0.05~0.2부터 본다. 전 요청 트레이스는 비용이 크다. 로그에 traceId가 있으면 티켓·로그·트레이스를 같은 요청으로 잇는다.
| 환경 | probability |
|---|---|
| local | 1.0 |
| staging | 0.5~1.0 |
| prod | 0.05~0.2 |
6. 구조화 로그
운영 로그는 JSON이 검색·알람에 유리하다. Logback encoder 또는 Log4j2 JSON layout을 쓴다.
남길 것: timestamp, level, service, env, traceId, spanId, 내부 userId, 제한된 스택.
남기지 말 것: 비밀번호, 토큰, 카드번호, 주민번호, 개인정보 원문.
7. 알람 최소선
http.server.requestsp99 급증hikaricp.connections.pending이 지속- 5xx 비율
- readiness 실패
- JVM GC·힙, Tomcat 큐
대시보드는 RED(Rate, Errors, Duration)와 의존성 지연, 그리고 order.created 같은 비즈니스 카운터를 같이 본다.
8. 흔한 실수
| 실수 | 대안 |
|---|---|
Actuator * 노출 |
화이트리스트 + 별도 포트 |
| userId를 메트릭 태그 | low cardinality만 |
| prod 샘플링 1.0 | 트래픽에 맞게 낮춤 |
| 로그에 토큰·PII | 마스킹 |
| 기술 메트릭만 | 비즈니스 카운터 |
연습
- Actuator + Prometheus registry로
/actuator/prometheus를 확인한다. order.created카운터를 추가한다.- 로그 패턴에
traceId를 넣는다. - (선택) OTel Collector + Jaeger로 스팬을 본다.
댓글
0아직 댓글이 없습니다. 첫 댓글을 남겨보세요.