본문 바로가기
카테고리 없음

[Spring Boot] 액추에이터 실시간 모니터링(Actuator)

열심히 개발한 스프링 부트(Spring Boot) 서버를 무사히 라이브(Live) 환경에 배포했습니다. 그런데 배포 후 3일째 되는 날, 유저 한 명이 "홈페이지가 엄청 느려요!"라고 게시판에 불평을 남겼습니다. 이 말을 들은 개발자는 등골이 오싹해집니다. "서버 메모리가 다 차서 느려진 건가? 아니면 DB 커넥션이 부족한가? 혹시 특정 API가 유독 늦게 응답하는 건가?" 원인을 찾기 위해 리눅스 서버에 SSH로 접속해서 top 명령어를 치거나, 끝도 없이 올라가는 로그 텍스트를 눈알이 빠지도록 쳐다보는 것은 원시 시대의 방식입니다. 서버 스스로가 자신의 건강 상태(Health), 메모리, CPU 사용량, DB 연결 상태, 최근 100개의 HTTP 요청 내역 등을 완벽한 JSON 데이터로 뱉어내 주면 얼마나 좋을까요? 이 꿈 같은 모니터링 기술을 단 '의존성 추가 한 줄'로 끝내버리는 괴물 같은 라이브러리가 바로 Spring Boot Actuator (액추에이터)입니다.

🩺 1. Actuator 의존성 추가와 Health Check의 기적

Actuator를 도입하는 방법은 허탈할 정도로 쉽습니다. build.gradle에 라이브러리 한 줄을 추가하고 서버를 재시작하기만 하면 끝입니다.

// build.gradle
implementation 'org.springframework.boot:spring-boot-starter-actuator'

이제 서버를 띄운 뒤 브라우저에서 http://localhost:8080/actuator/health 주소로 접속해 봅시다. 화면에 {"status":"UP"}이라는 JSON이 뜰 것입니다. 이것이 그 유명한 '헬스 체크(Health Check)'입니다. 실무에서 AWS 로드밸런서(ALB)나 쿠버네티스(K8s)는 10초에 한 번씩 이 주소를 찔러보고 UP이 돌아오면 "아, 이 서버 살아있구나!"라고 판단하여 트래픽을 넣어주고, 만약 DOWN이나 500 에러가 뜨면 "서버 죽었다!"라고 판단해 즉시 트래픽을 차단하고 서버를 재시작시켜 버립니다.

 

2. 판도라의 상자 열기: 엔드포인트(Endpoint) 노출 설정

초기 상태에서는 보안을 위해 /health 정도만 열려 있습니다. 서버의 모든 민감한 정보를 보기 위해 application.yml에서 판도라의 상자를 열어야 합니다.

management:
  endpoints:
    web:
      exposure:
        include: "*" #  실무 주의: 모든 엔드포인트 노출! (실서버에선 Spring Security로 이 주소들을 꽉 막아야 합니다
  endpoint:
    health:
      show-details: always # health 정보에 DB, Redis, 디스크 용량 상태까지 아주 상세하게 다 보여달라는 옵션

설정을 마치고 /actuator에 접속해보면 엄청난 주소(Endpoint) 목록이 쏟아집니다. 대표적인 실무 활용처는 다음과 같습니다.

  • /actuator/metrics: JVM 메모리, CPU 사용량, DB 커넥션 풀(HikariCP) 사용량 등 수백 가지의 수치형 지표(Metrics)를 보여줍니다.
  • /actuator/env: 현재 서버에 띄워져 있는 모든 환경 변수와 application.yml 속성값을 싹 다 보여줍니다. (DB 비밀번호는 ******로 마스킹 처리됨)
  • /actuator/httpexchanges: (구 httptrace) 최근 서버로 들어왔던 100개의 HTTP 요청과 응답 내역(시간, 파라미터 등)을 그대로 보여줍니다. 에러 추적에 최고입니다.
  • /actuator/loggers: (실무 최고의 마법) 서버를 재시작하지 않고도, API 호출 한 번으로 특정 패키지의 로깅 레벨을 INFO에서 DEBUG로 실시간 변경할 수 있습니다! 장애가 났을 때만 잠깐 디버그 모드를 켜서 쿼리를 볼 수 있습니다.

 

3. 모니터링의 최종 진화: Prometheus와 Grafana

Actuator가 뱉어내는 데이터는 시커먼 JSON 텍스트입니다. 이걸 사람이 하루 종일 쳐다보고 있을 수는 없습니다. 실무에서는 "미크로미터(Micrometer) 레지스트리" 의존성을 하나 더 추가하여, JSON 데이터를 프로메테우스(Prometheus)라는 수집기가 이해할 수 있는 포맷으로 변환해 줍니다.

프로메테우스는 10초마다 /actuator/prometheus 주소로 찾아와서 지표들을 싹 쓸어 담아(Scraping) 자신의 DB에 저장합니다. 그리고 최종적으로 프론트엔드 시각화 도구인 그라파나(Grafana)가 이 DB를 읽어서 "현재 활성화된 DB 커넥션 수", "JVM 힙 메모리 사용량", "초당 HTTP 요청 수(TPS)" 등을 스타크래프트 상황판처럼 화려하고 직관적인 그래프 대시보드로 그려냅니다. 특정 수치가 임계점을 넘으면 슬랙(Slack)으로 비상 알람을 쏴주기도 합니다.

 

4. 마무리 및 주요 발전 방향

지금까지 서버의 블랙박스를 열어젖혀 현재 건강 상태와 메모리, 트래픽을 완벽하게 엑스레이 찍듯 들여다보게 해주는 운영 필수 무기, Spring Boot Actuator와 헬스 체크 시스템에 대해 아주 치밀하게 다루어 보았습니다. 이제 여러분은 더 이상 에러가 터졌을 때 눈을 감고 로그를 뒤지는 장님 코끼리 만지기 식의 운영을 하지 않아도 됩니다.

모니터링을 달고 나니 마음이 든든합니다. 하지만 회사가 커져서 MSA(마이크로서비스 아키텍처)를 도입하기로 했습니다. 결제 서버, 회원 서버, 상품 서버 등 스프링 부트 서버가 무려 50개로 쪼개졌습니다. 그런데 만약 "데이터베이스 비밀번호"가 변경되면 어떻게 될까요? 50개의 레포지토리에 들어가서 application.yml을 일일이 수정하고 50번을 재빌드해서 다시 배포해야 합니다. 이런 지옥 같은 설정 파편화를 막기 위해, "모든 서버의 application.yml 파일을 깃허브(Github) 한 곳에 몰아넣고, 서버들이 부팅될 때 알아서 설정을 다운로드 받게 하자!"는 기적의 인프라가 필요해졌습니다. 본 설명에서는 "설정 파일의 완벽한 중앙 집중화 통제!" Spring Cloud Config를 활용한 MSA 외부 설정 관리 완벽 가이드에 대해 아주 심도 있게 살펴보겠습니다.


회사가 거대해지면 하나의 거대한 스프링 부트(Spring Boot) 애플리케이션(Monolithic)은 한계를 맞이합니다. 결제팀은 결제 로직만, 회원팀은 회원 로직만 따로 떼어내어 각각 50개의 독립적인 서버로 쪼개는 마이크로서비스 아키텍처(MSA, Microservice Architecture)의 시대가 열립니다. 그런데 이 MSA 환경에서 인프라 엔지니어를 가장 고통스럽게 만드는 것은 코드가 아니라 다름 아닌 application.yml 설정 파일입니다. 공통으로 사용하는 'Redis 서버 IP'가 바뀌거나 '결제 연동 API Key'를 변경해야 할 때, 50개의 프로젝트 레포지토리를 열어서 일일이 yml을 수정하고, 50번의 도커(Docker) 이미지를 다시 굽고, 50대의 서버를 재시작해야 하는 끔찍한 '설정 파편화의 지옥'이 펼쳐집니다. 이 지옥을 끝내기 위해 등장한 구원자가 바로 Spring Cloud Config입니다.

Spring Cloud Config Server 구축과 실무 아키텍처에 대하여 다뤄보겠습니다.

1. Config Server의 탄생: 코드와 설정의 분리

12-Factor App이라는 최신 클라우드 네이티브 방법론의 제3원칙은 "설정(Config)을 코드(Code)에서 완벽히 분리하라"입니다. 소스코드 안에 DB 비밀번호나 API 키가 하드코딩(또는 로컬 yml에 종속)되어 있으면 보안상 위험할 뿐만 아니라 유연성이 떨어집니다.

Spring Cloud Config 아키텍처는 크게 3가지 구성 요소로 이루어집니다.

  1. Github Repository (저장소): 모든 서비스의 yml 파일들(예: payment-prod.yml, member-dev.yml)을 오직 이 깃허브 레포지토리 한 곳에만 몰아넣고 관리합니다. 변경 이력(History) 추적도 완벽하게 지원됩니다.
  2. Config Server (중앙 서버): 깃허브와 항상 연결되어 있는 특별한 스프링 부트 서버입니다. 클라이언트들의 요청이 오면 깃허브에서 yml을 읽어서 던져주는 다리(Bridge) 역할을 합니다.
  3. Config Client (우리의 서비스들): 결제 서버, 주문 서버 등입니다. 이 녀석들은 부팅될 때 자신의 application.yml 대신, Config Server 주소로 찾아가서 "나 'payment' 서버고 'prod' 환경인데 내 설정값 좀 줘!"라고 부탁합니다.

2. Config Server 초간단 구축하기

Config Server를 띄우는 것은 놀랍도록 쉽습니다. Spring Initializr에서 Config Server 의존성을 추가하고 프로젝트를 생성합니다.

@EnableConfigServer //  이 어노테이션 하나로 평범한 스프링 부트가 중앙 설정 서버로 진화합니다!
@SpringBootApplication
public class ConfigServerApplication {
    public static void main(String[] args) {
        SpringApplication.run(ConfigServerApplication.class, args);
    }
}

그리고 application.yml에 깃허브 저장소 주소만 꽂아주면 끝납니다.

server:
  port: 8888 # Config Server의 국룰 포트
spring:
  cloud:
    config:
      server:
        git:
          uri: https://github.com/my-company/my-config-repo.git # yml 파일들이 모여있는 깃허브 주소!

3. Config Client 연동과 대마법 (동적 갱신)

이제 결제(Payment) 서버를 띄워볼 차례입니다. 이 클라이언트는 구동될 때 가장 먼저 Config Server(8888 포트)를 찾아가야 합니다.

# 클라이언트의 application.yml
spring:
  application:
    name: payment-service
  profiles:
    active: prod
  config:
    import: "optional:configserver:http://localhost:8888" #  나 부팅될 때 일단 저 서버로 가서 설정값 좀 다운로드 받아와 줘!

결제 서버를 실행하면, 로그에 Fetching config from server at : http://localhost:8888 이라는 문구가 찍히며 깃허브에 있던 payment-service-prod.yml의 DB 주소와 설정값들을 기가 막히게 싹 긁어와서 자신의 설정으로 세팅한 뒤 톰캣(Tomcat)을 띄우게 됩니다.

가장 놀라운 기능: @RefreshScope (재시작 없는 동적 갱신)
클라이언트 코드에 @RefreshScope를 달아두면, 깃허브에서 값을 수정하고 클라이언트의 /actuator/refresh API를 딱 한 번 찌르는 순간! 서버를 재시작하지(Reboot) 않고도 메모리에 올라간 변수값이 실시간으로 싹 바뀌어버립니다. 이벤트 할인율을 10%에서 20%로 무중단으로 실시간 변경할 수 있는 엄청난 무기입니다.


4. 마무리 및 주요 발전 방향

지금까지 수십 개의 마이크로서비스 설정값을 깃허브 한 곳에 모아 중앙 집중 관리하고, 서버 다운타임(Downtime) 없이 실시간으로 설정값을 갱신(Refresh)해 버리는 웅장한 아키텍처, Spring Cloud Config에 대해 다루어 보았습니다. 이제 인프라 엔지니어의 퇴근 시간이 3시간은 훌쩍 앞당겨졌습니다.

 

서버 설정은 깔끔해졌는데, MSA 구조의 가장 큰 고통인 '서버 간의 통신(Communication)' 문제가 터졌습니다. 결제 서버가 결제를 완료하면 주문 서버에게 "재고 깎아!"라고 API(HTTP)를 날리는데, 하필 그 순간 주문 서버가 죽어있으면 어떻게 될까요? 결제도 실패하고 다 터져버립니다(강한 결합). 이 치명적인 의존성을 끊어내고, "결제 서버는 메시지를 편지함에 던져놓고 제 갈 길 가고, 주문 서버는 살아나면 언제든 편지를 꺼내 읽어라!"라는 진정한 비동기 이벤트 주도 아키텍처(EDA)가 필요합니다.

다음 설명에서는 "마이크로서비스 생태계의 절대적인 대동맥!" Spring Boot와 Apache Kafka(아파치 카프카) 메시지 큐(Message Queue) 연동의 모든 것에 대해 아주 심도 있게 살펴보겠습니다.