Spring Boot에서 HikariCP MBean 충돌 문제 해결기

Kim Tae Won·2025년 9월 26일
0
post-thumbnail

최근에 새 프로젝트에 커넥션 풀 모니터링을 위해 hikariCPisRegisterMBeanstrue로 설정 후 실행하다가 다음과 같은 에러를 맞닥뜨렸습니다.

org.springframework.jmx.export.UnableToRegisterMBeanException: Unable to register MBean [HikariDataSource (FdsPostgreSQLHikariCP)] with key 'fdsDataSource'
Caused by: javax.management.InstanceAlreadyExistsException: MXBean already registered with name com.zaxxer.hikari:type=PoolConfig (FdsPostgreSQLHikariCP)

애플리케이션 컨텍스트(ApplicationContext)가 초기화되는 시점에 MBean이 중복 등록되었다는 오류였습니다. 처음 보는 분들에게는 다소 생소할 수 있는 개념인데, 이 문제를 풀면서 정리한 내용을 공유해보려고 합니다.


MBean이란 무엇인가?

  • MBean (Managed Bean)란?
    • JMX(Java Management Extensions) 표준을 통해 JVM 내부 리소스를 관리·모니터링하기 위해 노출되는 객체
    • 예: HikariCP의 커넥션 풀 사이즈, active 커넥션 수, timeout 설정 등.
  • JConsole, VisualVM, Prometheus JMX Exporter 같은 도구를 통해 이 정보를 조회할 수 있음.

즉, 운영 중인 애플리케이션의 상태를 외부에서 관측하거나 일부 속성을 조정하기 위해 등록하는 내부 빈입니다.


Spring Boot Actuator와 MBeanExporter

Spring Boot에서는 spring-boot-actuator를 통해 애플리케이션 상태를 HTTP(/actuator/**)나 JMX(MBeans)로 노출할 수 있습니다.

  • Spring Boot Actuator → 헬스 체크, 메트릭, 환경 변수 등 운영 지표를 엔드포인트로 노출
  • MBeanExporter → 스프링 빈을 자동으로 JMX MBean으로 등록해주는 역할

문제는, 외부 모니터링을 위해 HikariCP 설정 중 isRegisterMbeans를 true로 설정하는 경우에 자체적으로 MBean을 등록한다는 점입니다. isRegisterMbeans=true로 설정되어 있으면 Hikari가 com.zaxxer.hikari:type=PoolConfig,Pool 같은 이름으로 MBean을 등록합니다. 그런데 Spring도 같은 대상을 등록하려고 하면서 충돌이 발생하는 것이 위 에러의 이유였습니다.


문제 상황 요약

  • JVM 옵션에서 -Dspring.jmx.enabled=true로 JMX가 활성화되어 있었음
  • HikariCP 설정에서 isRegisterMbeans=true로 되어 있었음
  • Spring의 MBeanExporter도 동일한 ObjectName으로 등록 시도 → InstanceAlreadyExistsException 발생

해결 방법

해결책은 여러 가지가 있습니다.

  1. Spring 쪽에서 충돌 무시하기

    @EnableMBeanExport(registration = RegistrationPolicy.IGNORE_EXISTING)

    → 이미 같은 이름의 MBean이 있으면 스프링이 새로 등록하지 않고 넘어감.
    → 제가 적용한 방식.

  2. HikariCP의 MBean 등록 끄기

    config.isRegisterMbeans = false

    → Hikari가 직접 등록하지 않고 Spring 쪽만 등록되도록 조정.
    → 이렇게 하면, 모니터링 시스템에서 풀 모니터링이 불가능했음. 아래에서 설명.

  3. Spring JMX 자체 끄기

    spring.jmx.enabled: false

    → Actuator의 JMX 노출을 아예 막고, Hikari 기본 MBean만 사용.


그런데 여기서 의문점이 생길 수 있습니다. 저도 그랬구요!

결국 registerMbeans=false하더라도 MBean은 등록이 되는 것 같은데 왜 메트릭이 안 보일까??

여기서 흥미로운 점이 있었습니다.

  • registerMbeans=false로 하면 외부 모니터링 시스템에서 Hikari 풀 지표가 아예 나타나지 않았습니다.
  • 반대로 registerMbeans=true + @EnableMBeanExport(registration = IGNORE_EXISTING) 조합에서는 지표가 잘 보였습니다.

그 이유는 다음과 같습니다.

  • Spring의 MBeanExporter는 스프링 빈을 MBean으로 등록하는 역할을 할 뿐, Hikari 내부 풀 상태 지표(MXBean)까지 대신 등록하지는 않습니다.
  • 따라서 registerMbeans=false로 꺼버리면, 단순히 DataSource라는 객체만 MBean으로 보일 뿐 우리가 원하는 풀 상태 지표(ActiveConnections, IdleConnections 등) 는 아예 노출되지 않습니다.
  • 반대로 registerMbeans=trueHikariCP가 직접 풀 관련 MBean을 JMX에 등록합니다.
  • IGNORE_EXISTING 덕분에 Spring이 중복 등록을 시도하다가 충돌하지 않고, Hikari가 등록한 MBean이 그대로 유지됩니다.
  • 결과적으로 외부 모니터링 툴에서 Hikari 풀 지표가 정상적으로 보이게 되는 것입니다.

그럼 어떤 방법을 선택해야 할까요?

  • 운영에서 모니터링을 Spring Actuator + Micrometer/Prometheus로 통일한다면 → Hikari MBean 등록을 끄고 (registerMbeans=false), 대신 Micrometer의 Hikari 모듈을 활성화하는 것이 깔끔합니다.
  • 반대로 JMX 기반 모니터링(예: JConsole, Prometheus JMX Exporter)을 쓰고 싶다면 → registerMbeans=true를 유지하고, Spring에는 IGNORE_EXISTING을 줘서 충돌만 피하는 게 현실적인 방법입니다.

저 같은 경우, 사내에서 후자의 JMX 기반 모니터링 툴이 제공되고 사용하고 있기 때문에 registerMbeans=true를 유지하고, Spring에는 IGNORE_EXISTING을 주는 방식으로 문제를 해결했습니다. (물론, 프로메테우스/그라파나 도 사용 중이지만, 해당 용도와 달라 위와 같이 판단했습니다.)

마치며

이번 이슈를 겪으면서, 단순히 “에러 로그 고치기”를 넘어서 Spring Boot Actuator, JMX, MBean, HikariCP의 관계를 이해할 수 있었습니다.

혹시 비슷한 에러(InstanceAlreadyExistsException)를 만난다면,

  • 누가 MBean을 등록하는지(Hikari? Spring?)
  • JMX 노출을 어디까지 쓸 건지(Actuator? 직접 JMX?)
    를 먼저 확인해 보시면 빠르게 원인을 잡을 수 있을 것 같습니다!

오늘도 화이팅입니다~!

profile
꿈이 너무나 큰 평범한 컴공 대딩에서 취업 성공!

0개의 댓글