spring boot/actuator in spring boot

spring boot 3.x + actuator 파헤치기. 5. about health endpoint

Hello World Study 2023. 3. 25. 21:00

 

https://youtu.be/vGCN34hGBUQ

 

actuator 에서 기본 제공하는 endpoint 중 모니터링 시스템과 자주 연동될 endpoint 로 health endpoint 가 있습니다.

 

이름에서 알수 있듯이 application의 health 정보를 제공합니다.

 

default health endpoint

application.yml 에 아래 설정만  한 상태에서 spring boot 를 구동해봅시다.

management:
  endpoints:
    web:
      exposure:
        include: "*"

이후 웹브라우저에서 health endpoint 로 접근해봅시다.

위와 같이 application이 구동중이라는 의미의 status: UP  이라는 json을 리턴합니다. 고작 이 정보만 있지는 않겠죠?

아래처럼 application.yml의  management.endpoint.health 를 입력해보고 그 하위에 어떤 구성필드가 있는지 확인해봅시다.

show-components , show-details 

눈에 띄는게 show-components 와 show-details 필드입니다. 우선 show-components: ALWAYS 로 설정 후 spring boot 재구동해봅시다.

management:
  endpoints:
    web:
      exposure:
        include: "*"
  endpoint:
    health:
      show-components: ALWAYS

해당 config를 철자까지 다 외워서 입력하지 말고, IDE 의 자동완성을 이용해서 쉽게 설정하는걸 추천합니다.

show-components 설정을 했더니 아래처럼 components 필드에 disk 와 ping 의 health 상태가 추가되었습니다. 

show-components 대신에 show-details 설정을 아래와 같이 해보겠습니다.

management:
  endpoints:
    web:
      exposure:
        include: "*"
  endpoint:
    health:
      show-details: ALWAYS

다시 health endpoint 를 확인해보면 아래처럼 components 내의 diskSpace 항목에 details 항목이 추가되었고 total, free 등의 상세 정보가 더 보입니다. 즉 show-details 는 details 필드에 상세 정보를 보여주는 설정입니다.

기본 제공해주는 health 정보들 

spring boot는 auto configuration에 의해 아래와 같이 많은 health 정보들을 기본 제공해주고 있습니다. (너무 많아서 화면 캡쳐는 앞부분만 했습니다. )

그런데 위 예제에서는 diskspace 와 ping 밖에 안보여지는 이유는 cassandra, couchbase 등을 사용하지 않으니 health 정보를 보여줄게 없기 때문입니다.

https://docs.spring.io/spring-boot/docs/current/reference/html/actuator.html#actuator.endpoints.health.auto-configured-health-indicators

기본 제공되는 health 정보가 활성화 되기 위한 조건

DB health 정보가 보이기 위해서는 DB를 사용해야 합니다. 그래서 간단히 아래처럼 Database 설정을 해보겠습니다.

pom.xml 에 아래처럼 h2 DB 및 JPA 의존성을 넣어줍니다. 

DB관련 의존성만 넣으면 되는것이니 mysql, oracle 의존성을 넣어도 되며, JPA 대신 mybatis 를 넣어도 됩니다. 

<dependency>
	<groupId>com.h2database</groupId>
	<artifactId>h2</artifactId>
	<scope>runtime</scope>
</dependency>

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

이걸로 끝입니다. spring boot auto configuration에 의해 h2 DB와 연결됩니다. 즉 application 이 DB와 연결이 되었습니다.

이제 다시 health endpoint 를 확인해보면 아래처럼 db 라는 필드가 보이고 상세정보에 H2 DB 라는 정보가 보입니다.

만약 redis, mongo 등의 다른 Database 를 추가로 연결해서 사용한다면 redis, mongo 등의 health 정보도 함께 보이게 됩니다.

 

about HealthIndicator , custom HealthIndicator 만들기

아래와 같이 spring boot 에서는 기본 제공해주는 health 정보들이 많이 있습니다.

아래 파란색 부분은 health 정보 제공하는 실제 클래스명입니다.

https://docs.spring.io/spring-boot/docs/current/reference/html/actuator.html#actuator.endpoints.health.auto-configured-health-indicators

아래처럼 기본 제공되는 클래스중 하나인 DataSourceHealthIndicator 의 내부구조를 살펴보겠습니다.

상속을 하고 있어 다소 복잡해보이나 결국 HealthIndicator 라는 인터페이스를 구현한 클래스입니다.

HealthIndicator 인터페이스의 중요한 부분만 발췌하면 아래와 같습니다.

health() 라는 메서드를 각자 구현해야 하고, getHealth()  메서드는 details 정보를 응답에 포함할지 여부만 제공하는 default 메서드 입니다.

public interface HealthIndicator extends HealthContributor {
	
	default Health getHealth(boolean includeDetails) {
		Health health = health();
		return includeDetails ? health : health.withoutDetails();  <-- details 정보 포함여부 기능 제공
	}

	Health health();  <-- 여길 각 클래스에서 구현해야함.
}

아래처럼 나만의 custom health indicator 를 구현해봤습니다.

학습용도이니 단순히 현재 시간을 기준으로 up ,down 을 판단하고 그에 맞게 detail 정보를 다르게 설정해서 리턴하도록 만들었습니다.

실무에서는 application이 정상 구동되기 위해 필요로 하는(=의존하는) 서비스와 잘 연결되어 있는지를 구현하고 UP, DOWN 을 리턴하도록 하면 됩니다.

 

withDetail() 메서드에 key, value를 넣어주면, 응답 json 의 detail 필드에 key, value 가 들어가는 것만 기억해주세요. Health 클래스가 상당히 직관적으로 만들어져 있어서 별도 주석이 없어도 의미를 파악할 수 있어 보입니다.

 

@Component
public class MyCustomHealthIndicator implements HealthIndicator {
    @Override
    public Health health() {
        boolean status = getStatus();
        if ( status ) {
            Health upHealth = Health.up()
                    .withDetail("key1", "value1")
                    .withDetail("key2", "value2")
                    .build();
            return upHealth;
        }
        Health downHealth = Health.down()
                .withDetail("key3", "value3")
                .withDetail("key4", "value4")
                .build();
        return downHealth;
    }

    boolean getStatus() {
        // 현재시각이 짝수,홀수인지에 따라 up, down을 판단하는 것으로 대체
        if ( System.currentTimeMillis() % 2 == 0 )
            return true;
        return false;
    }
}

이제 spring boot 재구동한 후 health endpoint 를 확인해봅시다.

아래처럼 myCustom 이라는 필드가 추가되었으며, 랜덤하게 up, down 으로 상태가 바뀝니다.

최상단의 status 는 모든 components 의 status가 모두 UP 일때만 UP 으로 표시됩니다.

그래서 myCustom 이 종종 DOWN  으로 표시될때, 최상단의 status 도 DOWN 으로 표시되게 됩니다.

클래스명을 MyCustomHealthIndicator 라고 만들었기에 HealthIndicator 를 제외한 앞쪽 부분이 json에 표시된다고 공식가이드에 적혀있습니다.

The identifier for a given HealthIndicator  is the name of the bean without the HealthIndicator  suffix, if it exists. In the preceding example, the health information is available in an entry named my.

< 출처: https://docs.spring.io/spring-boot/docs/current/reference/html/actuator.html#actuator.endpoints.health.writing-custom-health-indicators >

 

 

MyCustomHealthIndicator 클래스에 @Component 를 붙여놓았기에 무조건 bean 으로 등록이 됩니다. 만약 특정 상황일때만 bean 으로 등록하고 싶다면 어떻게 해야할까요? 

위에서 살펴봤듯이 db, redis, mongo 등의 healthIndicator 들은 application이 해당 서비스를 실제로 사용할때만 json 에 health 정보가 보였습니다. 어떻게 이런 동작이 가능한 걸까요?

 

spring boot 에서 어떻게 구현했는지 코드를 확인하면 될듯 합니다.

위에서 살펴봤던 DataSourceHealthIndicator 클래스를 통해 spring boot 구현 방법을 알아보겠습니다.

 

우리가 알고 싶은건 DataSourceHealthIndicator 클래스에 @Component 어노테이션이 없는데 어떻게 bean 이 되었는가? 입니다.

아래처럼 DataSourceHealthIndicator  의 생성자에 디버그 포인트를 모두 걸어주고 디버그 모드로 구동을 해줍니다. spring boot 구동시점에 DataSourceHealthIndicator  bean이 생성되며, 좌측 하단의 call stack 을 통해 어떤 클래스에서 DataSourceHealthIndicator 의 생성자를 호출했는지 알수 있습니다.

 

auto configuration에 대해 잘 알고 있는 분이라면 미리 짐작할 수 있었을듯 한데

spring-boot-actuator-autoconfigure 의 DataSourceHealthContributorAutoConfiguration 클래스에서 생성자를 호출해주고 있었습니다. 무조건 생성자를 호출해주는게 아닌 아래와 같이 클래스 위쪽에 @ConditionalOnXXX 부분을 통해 특정 클래스파일이 있어야 하고 특정 bean이 존재해야 하며, db 라는 health indicator 가 enable 되어 있어야 하는 조건이 설정된걸 알 수있습니다. 

@AutoConfiguration(after = DataSourceAutoConfiguration.class)
@ConditionalOnClass({ JdbcTemplate.class, AbstractRoutingDataSource.class })
@ConditionalOnBean(DataSource.class)
@ConditionalOnEnabledHealthIndicator("db")
@EnableConfigurationProperties(DataSourceHealthIndicatorProperties.class)
public class DataSourceHealthContributorAutoConfiguration implements InitializingBean {
  (생략)
}

위와 같이 AutoConfiguration 관련 클래스에서 컨디션을 체크한 후 모두 만족되면 그때 HealthIndicator 관련 클래스들을 bean으로 등록해주는 겁니다.

 

@ConditionalOnXXX 어노테이션이 익숙하지 않은 분들은 아래 공식 가이드를 참고해주세요.

https://docs.spring.io/spring-boot/docs/current/reference/html/features.html#features.developing-auto-configuration.condition-annotations.class-conditions

 

status 종류

이제까지는 status: UP 혹은 status: DOWN 만 다루어 봤습니다.

그외에는 아래처럼 몇가지 상태 값이 더 있으며 각 상태별로 http response status 값도 알 수 있습니다.상태값을 추가하거나 http response status 값을 굳이(!) 변경하고 싶다면 아래 그림의 링크에 자세히 나와있으니 참고하세요. 개인적인 의견으로는 특별한 이유가 없다면 http response status 는 default 값을 써야 서로가 편하지 않을까 싶습니다. 공부할것도 많은데 사내에서만 적용되는 코딩 규칙이 너무 많아지면 힘드니까요.

https://docs.spring.io/spring-boot/docs/current/reference/html/actuator.html#actuator.endpoints.health.writing-custom-health-indicators

 

실제 코드에서도 아래처럼 Health. 을 누르면 up, down, outOfService, unknown 이 나오기에 위 상태값을 굳이 외울 이유는 없어보입니다.

 

이번 포스팅에서 사용된 소스코드는 아래 링크에 있습니다.

https://github.com/ChunGeun-Yu/spring-actuator-study/tree/healthEndpoint

 

 

본 포스팅은 health endpoint 공식 가이드를 기반으로 작성되었습니다. 좀 더 확실하게 알고 싶다면 아래 링크를 정독하면 됩니다.  https://docs.spring.io/spring-boot/docs/current/reference/html/actuator.html#actuator.endpoints.health


다음 포스팅에서는 info endpoint에 대해 알아보겠습니다. 내용이 어렵지 않습니다. 정주행 합시다~