프로덕션 서버가 살아있는지 어떻게 알 수 있는가 — Actuator와 모니터링

프로덕션 서버가 응답하지 않는다. 메모리가 부족한가? DB 연결이 끊겼는가? 스레드가 고갈됐는가? 로그를 봐도 원인이 안 보인다. Spring Boot Actuator가 켜져 있었다면 — GET /actuator/health로 헬스 상태를, GET /actuator/metrics로 메모리/스레드/DB 풀 상태를 즉시 확인할 수 있었다.

이 글은 Actuator의 엔드포인트, 헬스 체크, 메트릭, 그리고 Prometheus + Grafana 통합을 풀어간다. (Spring Boot Reference - Actuator)

Actuator란 — 운영 도구 모음

spring-boot-starter-actuator를 추가하면 프로덕션 준비 기능(헬스 체크, 메트릭, 환경 정보, 스레드 덤프 등)이 자동으로 활성화된다.

# Spring Boot 4 / Java 25 — application.yml
management:
  endpoints:
    web:
      exposure:
        include: health,info,metrics,prometheus   # 노출할 엔드포인트
  endpoint:
    health:
      show-details: always   # 헬스 상세 정보 표시
엔드포인트 URL 용도
health /actuator/health 헬스 상태 (UP/DOWN)
info /actuator/info 애플리케이션 정보 (버전 등)
metrics /actuator/metrics JVM/애플리케이션 메트릭
prometheus /actuator/prometheus Prometheus 형식 메트릭
env /actuator/env 환경 변수/프로퍼티
loggers /actuator/loggers 로그 레벨 조회/변경
threaddump /actuator/threaddump 스레드 덤프
heapdump /actuator/heapdump 힙 덤프 (다운로드)

보안: Actuator 엔드포인트는 민감 정보를 노출할 수 있다. 프로덕션에서는 management.endpoints.web.exposure.include로 노출 범위를 최소화하고, Spring Security로 보호한다.

비유: Actuator는 자동차의 계기판이다. 속도계, 온도계, 연료 게이지, 엔진 경고등 — 운전자가 직접 엔진을 열어보지 않아도 핵심 상태를 한눈에 볼 수 있다. /actuator/health는 "엔진 정상?"을, /actuator/metrics는 "현재 연료 양은?"을, /actuator/threaddump는 "엔진 내부 상태 점검"을 제공한다.

내부 메커니즘: Actuator는 어떻게 DB 연결 상태를 아는가? DataSourceHealthIndicator가 주기적으로 SELECT 1 쿼리를 실행해 본다. 쿼리가 성공하면 "UP", 실패하면 "DOWN". 디스크 공간은 DiskSpaceHealthIndicatorFile.getUsableSpace()로 확인한다. 모든 HealthIndicator가 모여 HealthContributor 체인을 구성하고, 하나라도 DOWN이면 전체 상태가 DOWN이 된다. Kubernetes가 /actuator/health를 주기적으로 호출하여 파드의 헬스를 판단한다.

헬스 체크 — health 엔드포인트

# Spring Boot 4 — 헬스 체크
curl http://localhost:8080/actuator/health
{
  "status": "UP",
  "components": {
    "db": {
      "status": "UP",
      "details": { "database": "PostgreSQL", "validationQuery": "SELECT 1" }
    },
    "diskSpace": {
      "status": "UP",
      "details": { "total": 50000000000, "free": 20000000000 }
    },
    "ping": { "status": "UP" }
  }
}

헬스 체크는 DB 연결, 디스크 공간, Redis 연결 등을 자동 검사한다. 컴포넌트 중 하나라도 DOWN이면 전체 상태가 DOWN이 된다. Kubernetes Liveness/Readiness Probe와 결합하여 자동 복구할 수 있다. (Spring Boot - Health

커스텀 헬스 인디케이터

// Spring Boot 4 / Java 25 — 커스텀 헬스 체크
import org.springframework.boot.actuate.health.*;

@Component
public class ExternalApiHealthIndicator implements HealthIndicator {

    @Override
    public Health health() {
        try {
            // 외부 API 연결 확인
            checkExternalApi();
            return Health.up().withDetail("externalApi", "접근 가능").build();
        } catch (Exception e) {
            return Health.down().withDetail("error", e.getMessage()).build();
        }
    }
}

메트릭 — metrics 엔드포인트

# 사용 가능한 메트릭 목록
curl http://localhost:8080/actuator/metrics
{
  "names": [
    "jvm.memory.used", "jvm.threads.live", "process.cpu.usage",
    "http.server.requests", "hikaricp.connections.active",
    "hikaricp.connections.pending"
  ]
}
# 특정 메트릭 상세 조회
curl http://localhost:8080/actuator/metrics/jvm.memory.used
{
  "name": "jvm.memory.used",
  "measurements": [{ "statistic": "VALUE", "value": 123456789 }],
  "availableTags": [{ "tag": "area", "values": ["heap", "nonheap"] }]
}

핵심 메트릭

메트릭 의미 주의 임계값
jvm.memory.used JVM 힙 사용량 힙의 80% 초과 시 경고
hikaricp.connections.active DB 연결 풀 활성 연결 풀 크기의 80% 초과 시 경고
hikaricp.connections.pending 대기 중인 연결 요청 0 초과 시 풀 확장 검토
http.server.requests HTTP 요청 지연/카운트 p99 > 1초 시 경고
process.cpu.usage 프로세스 CPU 사용률 80% 초과 시 경고
jvm.threads.live 활성 스레드 수 급증 시 스레드 누수 의심

Prometheus + Grafana 통합

# application.yml — Prometheus 메트릭 활성화
management:
  endpoints:
    web:
      exposure:
        include: health,prometheus
  metrics:
    tags:
      application: my-service   # 모든 메트릭에 애플리케이션 태그 추가
# Prometheus 설정 (prometheus.yml)
scrape_configs:
  - job_name: 'spring-boot-app'
    metrics_path: '/actuator/prometheus'
    static_configs:
      - targets: ['app:8080']

Prometheus가 주기적으로 /actuator/prometheus를 스크랩하여 메트릭을 수집한다. Grafana에서 Prometheus를 데이터 소스로 연결하여 대시보드를 만든다. (Spring Boot - Prometheus

커스텀 메트릭 — MeterRegistry

// Spring Boot 4 / Java 25 — 커스텀 메트릭
import io.micrometer.core.instrument.*;

@Service
public class OrderService {
    private final Counter orderCounter;
    private final Timer orderTimer;

    public OrderService(MeterRegistry registry) {
        this.orderCounter = Counter.builder("orders.created")
            .description("생성된 주문 수")
            .tag("type", "online")
            .register(registry);

        this.orderTimer = Timer.builder("orders.processing.time")
            .description("주문 처리 시간")
            .register(registry);
    }

    public void placeOrder(Order order) {
        Timer.Sample sample = Timer.start();
        try {
            // 주문 처리
            orderCounter.increment();
        } finally {
            sample.stop(orderTimer);
        }
    }
}

Micrometer는 Spring Boot의 메트릭 추상화 계층이다 — Prometheus, Datadog, CloudWatch 등 다양한 백엔드로 메트릭을 내보낼 수 있다. "메트릭 API를 한 번 작성하면 모니터링 시스템을 바꿔도 코드가 그대로" 유지된다. (Micrometer Reference)

Spring Boot 4.0 — 구조화된 로깅과 관측성

Spring Boot 4.0은 구조화된 로깅(ECS, GELF)과 분산 추적(OpenTelemetry) 통합을 강화했다:

# Spring Boot 4 — 분산 추적 + 구조화된 로깅
logging:
  structured:
    format:
      console: ecs   # JSON 형식 로그

management:
  tracing:
    sampling:
      probability: 1.0   # 100% 샘플링 (개발용)

구조화된 로깅 + 메트릭 + 분산 추적(Tracing)의 세 가지 관측성(Observability) 기둥이 Spring Boot 4.0에서 통합 지원된다. ELK(로그) + Prometheus(메트릭) + Jaeger/Zipkin(추적) 조합으로 마이크로서비스 환경을 완전히 관측할 수 있다.

실습 — 헬스 체크 시뮬레이션

// Spring Boot 4 / Java 25 — HealthCheckDemo.java
import java.util.*;

public class HealthCheckDemo {
    record HealthStatus(String component, String status, String detail) {}

    public static void main(String[] args) {
        List<HealthStatus> checks = List.of(
            new HealthStatus("db", "UP", "PostgreSQL 연결 정상"),
            new HealthStatus("disk", "UP", "여유 공간 20GB"),
            new HealthStatus("redis", "DOWN", "연결 타임아웃"),
            new HealthStatus("ping", "UP", null)
        );

        boolean allUp = checks.stream().allMatch(c -> c.status().equals("UP"));
        String overall = allUp ? "UP" : "DOWN";

        System.out.println("전체 상태: " + overall);
        checks.forEach(c ->
            System.out.println("  " + c.component() + ": " + c.status()
                + (c.detail() != null ? " (" + c.detail() + ")" : "")));
    }
}
java HealthCheckDemo.java
전체 상태: DOWN
  db: UP (PostgreSQL 연결 정상)
  disk: UP (여유 공간 20GB)
  redis: DOWN (연결 타임아웃)
  ping: UP

확인할 것: 하나의 컴포넌트(Redis)가 DOWN이면 전체 상태가 DOWN이 된다. Kubernetes가 이 엔드포인트를 주기적으로 체크하여 unhealthy 판정 시 파드를 재시작한다.

관리 포트 분리 — 보안 강화

# Spring Boot 4 / Java 25 — Actuator를 별도 포트로
management:
  server:
    port: 9090   # 애플리케이션(8080)과 다른 포트
  endpoints:
    web:
      exposure:
        include: "*"

관리 엔드포인트를 별도 포트(9090)로 분리하면, 외부 인터넷에 노출되는 애플리케이션 포트(8080)와 내부 관리 포트를 분리할 수 있다. K8s에서 관리 포트만 ClusterIP로 제한하여 보안을 강화한다.

요약 — 이 글의 결론

  • Actuator는 운영 도구(헬스 체크, 메트릭, 환경 정보)를 자동 제공한다. spring-boot-starter-actuator 추가만으로 활성화된다.
  • /actuator/health로 헬스 상태를 확인한다. DB, 디스크, Redis 등 컴포넌트별 상태를 자동 검사. Kubernetes와 결합하여 자동 복구.
  • /actuator/metrics로 JVM, DB 풀, HTTP 지연 등을 모니터링한다. 핵심 메트릭: 힙 사용량, DB 연결 풀 활성 수, HTTP p99 지연.
  • Prometheus + Grafana로 메트릭 대시보드를 구축한다. /actuator/prometheus 엔드포인트로 Prometheus가 스크랩. Micrometer로 메트릭을 백엔드 독립적으로 작성.
  • Spring Boot 4.0은 구조화된 로깅(ECS/GELF)과 분산 추적(OpenTelemetry)을 통합 지원한다. 관측성의 3축(로그, 메트릭, 추적)이 Spring Boot 하나로 통합.

생각해 볼 문제

  1. /actuator/env가 비밀번호를 마스킹하는 방식은? 실제 값을 보려면 어떻게 하는가?
  2. Micrometer의 Timer@Timed 어노테이션의 차이는? 각각 언제 쓰는가?
  3. Kubernetes Liveness Probe와 Readiness Probe를 /actuator/health에 연결할 때의 베스트 프랙티스는?
  4. Spring Boot 4.0의 구조화된 로깅에서 trace ID/span ID가 자동으로 로그에 포함되는 원리는?
  5. Actuator 엔드포인트를 별도 관리 포트(예: 9090)로 분리하는 이유는?
  6. Micrometer의 Counter, Timer, Gauge 각각의 용도와 사용 시나리오를 설명하라.

참고

분산 추적(Tracing) — OpenTelemetry 통합

Spring Boot 4.0은 OpenTelemetry 기반 분산 추적을 통합 지원한다. 마이크로서비스 간 요청 흐름을 trace ID로 추적할 수 있다.
7. Spring Boot 4.0과 이전 버전(Spring Boot 3.x)의 가장 큰 차이점은 무엇인가? 마이그레이션 시 주의할 점은?
8. 이 글에서 다룬 개념을 실제 프로젝트에 적용할 때 가장 먼저 고려해야 할 것은?

'Develop Artifacts > Spring Boot' 카테고리의 다른 글

SpringBoot - 10. deployment  (0) 2026.07.12
SpringBoot - 09. config profile  (0) 2026.07.12
SpringBoot - 07. security  (0) 2026.07.12
SpringBoot - 06. testing  (0) 2026.07.12
SpringBoot - 05. spring-data-jpa  (0) 2026.07.12