스프링 부트 공식 레퍼런스를 한글로 번역한 문서입니다.
전체 목차는 여기에 있습니다.
목차
- 8.2.1. Enabling Endpoints
- 8.2.2. Exposing Endpoints
- 8.2.3. Securing HTTP Endpoints
- 8.2.4. Configuring Endpoints
- 8.2.5. Hypermedia for Actuator Web Endpoints
- 8.2.6. CORS Support
- 8.2.7. Implementing Custom Endpoints
- 8.2.8. Health Information
- 8.2.9. Kubernetes Probes
- 8.2.10. Application Information
8.2. Endpoints
액추에이터 엔드포인트를 사용하면 쉽게 애플리케이션을 모니터링하고 상호 작용할 수 있다. 스프링 부트는 여러 가지 엔드포인트를 내장하고 있으며, 자체 엔드포인트도 추가할 수 있다. 예를 들어 health
엔드포인트는 기본적인 애플리케이션의 상태 정보를 제공한다.
각 엔드포인트들은 개별적으로 활성화하거나 비활성화할 수 있으며, HTTP나 JMX를 통해 노출(원격에서도 접근 가능)할 수 있다. 엔드포인트는 활성화와 노출을 동시에 해줬을 때 사용 가능한 것으로 간주한다. 이렇게 사용이 가능할 때에만 내장 엔드포인트를 자동 설정한다. 대부분의 애플리케이션은 HTTP를 통한 노출을 선택하고 있으며, 엔드포인트의 ID에 /actuator
를 프리픽스로 달아서 URL에 매핑한다. 예를 들어 health
엔드포인트는 기본적으로 /actuator/health
에 매핑된다.
액추에이터의 엔드포인트와 요청, 응답 포맷을 자세히 알고 싶다면 별도 API 문서를 참고해라 (HTML, PDF).
아래 엔드포인트들은 사용하는 기술에 상관 없이 지원한다:
ID | Description |
---|---|
auditevents |
현재 애플리케이션에 대한 감사audit 이벤트들의 정보를 노출한다. AuditEventRepository 빈이 있어야 한다. |
beans |
애플리케이션에 있는 전체 스프링 빈들을 보여준다. |
caches |
사용 가능한 캐시들을 노출한다. |
conditions |
설정과 자동 설정 클래스들에서 평가한 조건들과 함께, 그 조건이 매칭되거나 매칭되지 않은 이유를 보여준다. |
configprops |
모든 @ConfigurationProperties 에 있는 정보들을 모아서 보여준다. |
env |
스프링의 ConfigurableEnvironment 에 있는 프로퍼티들을 노출한다. |
flyway |
적용시킨 모든 Flyway 데이터베이스 마이그레이션들을 보여준다. Flyway 빈이 하나 이상 필요하다. |
health |
애플리케이션의 상태 정보를 보여준다. |
httptrace |
HTTP trace 정보를 보여준다 (기본적으로는 HTTP request-response exchange를 마지막 100개까지 표기). HttpTraceRepository 빈이 있어야 한다. |
info |
애플리케이션 정보를 보여주며, 애플리케이션 정보는 임의로 추가할 수 있다. |
integrationgraph |
Spring Integration graph를 보여준다. spring-integration-core 의존성이 필요하다. |
loggers |
애플리케이션에 있는 로거 설정을 조회하고 변경한다. |
liquibase |
적용시킨 모든 Liquibase 데이터베이스 마이그레이션들을 보여준다. Liquibase 빈이 하나 이상 필요하다. |
metrics |
현재 애플리케이션의 ‘메트릭’ 정보를 보여준다. |
mappings |
모든 @RequestMapping path 정보를 모아서 보여준다. |
quartz |
Quartz 스케줄러 job들에 관한 정보를 보여준다. |
scheduledtasks |
애플리케이션에 있는 스케줄링된 테스크들을 보여준다. |
sessions |
스프링 세션 기반 세션 저장소에서 사용자 세션을 검색하고 삭제할 수 있다. 스프링 세션을 사용하는 서블릿 웹 애플리케이션이 필요하다. |
shutdown |
애플리케이션을 graceful하게 종료시킬 수 있다. 기본적으로는 비활성화한다. |
startup |
ApplicationStartup 으로 수집한 startup 단계 데이터를 보여준다. SpringApplication 에 BufferingApplicationStartup 을 설정해줘야 한다. |
threaddump |
스레드 덤프를 수행한다. |
웹 애플리케이션에선 (스프링 MVC, 스프링 웹플럭스, Jersey) 아래와 같은 엔드포인트들도 이용할 수 있다:
ID | Description |
---|---|
heapdump |
hprof 힙 덤프 파일을 반환한다. HotSpot JVM이 필요하다. |
jolokia |
HTTP를 통해 JMX 빈을 노출한다 (클래스패스에 Jolokia가 있을 땐 웹플럭스에선 사용할 수 없다). jolokia-core 의존성이 필요하다. |
logfile |
로그 파일에 있는 내용들을 반환한다 (logging.file.name 이나 logging.file.path 프로퍼티를 설정해줬다면). HTTP Range 헤더를 사용해서 로그 파일에 있는 내용 일부만 조회할 수도 있다. |
prometheus |
메트릭을 프로메테우스 서버에서 스크랩할 수 있는 포맷으로 노출한다. micrometer-registry-prometheus 의존성이 필요하다. |
8.2.1. Enabling Endpoints
기본적으로 shutdown
을 제외한 모든 엔드포인트를 활성화한다. 활성화할 엔드포인트를 설정해주려면 management.endpoint.<id>.enabled
프로퍼티를 사용해라. 아래 예시에선 shutdown
엔드포인트를 활성화하고 있다:
management.endpoint.shutdown.enabled=true
management:
endpoint:
shutdown:
enabled: true
활성화를 기본으로 두기보단 옵트인 방식으로 엔드포인트를 활성화하고 싶다면 management.endpoints.enabled-by-default
프로퍼티를 false
로 설정하고, 개별 엔드포인트 enabled
프로퍼티를 통해 원하는 엔드포인트를 다시 활성화해라. 아래 예시에선 info
엔드포인트를 활성화하고, 그외 다른 엔드포인트는 전부 비활성화한다:
management.endpoints.enabled-by-default=false
management.endpoint.info.enabled=true
management:
endpoints:
enabled-by-default: false
endpoint:
info:
enabled: true
비활성화한 엔드포인트는 애플리케이션 컨텍스트에서 완전히 제거된다. 엔드포인트를 노출할 기술만 따로 변경하고 싶으면
include
와exclude
프로퍼티를 사용해라.
8.2.2. Exposing Endpoints
엔드포인트에는 민감한 정보가 들어있을 수 있기 때문에, 엔드포인트들은 언제 노출해줄지를 신중하게 생각해야 한다. 다음은 내장 엔드포인트별로 디폴트 노출 정책을 정리한 테이블이다:
ID | JMX | Web |
---|---|---|
auditevents |
Yes | No |
beans |
Yes | No |
caches |
Yes | No |
conditions |
Yes | No |
configprops |
Yes | No |
env |
Yes | No |
flyway |
Yes | No |
health |
Yes | Yes |
heapdump |
N/A | No |
httptrace |
Yes | No |
info |
Yes | No |
integrationgraph |
Yes | No |
jolokia |
N/A | No |
logfile |
N/A | No |
loggers |
Yes | No |
liquibase |
Yes | No |
metrics |
Yes | No |
mappings |
Yes | No |
prometheus |
N/A | No |
quartz |
Yes | No |
scheduledtasks |
Yes | No |
sessions |
Yes | No |
shutdown |
Yes | No |
startup |
Yes | No |
threaddump |
Yes | No |
노출할 엔드포인트를 변경하려면 아래에 있는 해당 기술 전용 include
, exclude
프로퍼티를 사용해라:
Property | Default |
---|---|
management.endpoints.jmx.exposure.exclude |
|
management.endpoints.jmx.exposure.include |
* |
management.endpoints.web.exposure.exclude |
|
management.endpoints.web.exposure.include |
health |
include
프로퍼티에는 노출할 엔드포인트들의 ID를 나열한다. exclude
프로퍼티로는 노출하면 안 되는 엔드포인트들의 ID를 나열한다. exclude
프로퍼티는 include
프로퍼티보다 우선순위가 높다. include
, exclude
프로퍼티 모두 엔드포인트 ID들로 설정하면 된다.
예를 들어서 JMX를 통한 엔드포인트 노출을 전부 중단하고 health
와 info
엔드포인트만 노출하려면, 아래 프로퍼티를 사용해라:
management.endpoints.jmx.exposure.include=health,info
management:
endpoints:
jmx:
exposure:
include: "health,info"
모든 엔드포인트들을 선택할 때는 *
을 사용할 수 있다. 예를 들어 env
, beans
엔드포인트를 제외한 모든 항목을 HTTP로 노출해주려면 아래 프로퍼티를 사용해라:
management.endpoints.web.exposure.include=*
management.endpoints.web.exposure.exclude=env,beans
management:
endpoints:
web:
exposure:
include: "*"
exclude: "env,beans"
YAML에선
*
을 특별한 의미로 사용하기 때문에, 모든 엔드포인트를 포함(또는 제외)시키려면 따옴표를 추가해야 한다.
애플리케이션을 외부로 노출한다면 무조건 엔드포인트들을 보호해주는 게 좋다.
엔드포인트들을 노출할 때 자체 전략을 구현하고 싶으면
EndpointFilter
빈을 등록하면 된다.
8.2.3. Securing HTTP Endpoints
HTTP 엔드포인트는 민감한 정보를 가지고 있는 다른 URL들과 똑같이 보호해야 한다. 스프링 시큐리티가 있을 땐 기본적으로 스프링 시큐리티의 content-negotiation 전략을 통해 엔드포인트를 보호한다. HTTP 엔드포인트의 보안 설정을 커스텀하려면, 예를 들어 특정 role을 가진 사용자만 접근을 허용하고 싶다면, 스프링 부트가 제공하는 간편한 RequestMatcher
객체들을 스프링 시큐리티와 함께 사용하면 된다.
일반적인 스프링 시큐리티 설정은 아래 예시와 비슷할 거다:
@Configuration(proxyBeanMethods = false)
public class MySecurityConfiguration {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http.requestMatcher(EndpointRequest.toAnyEndpoint())
.authorizeRequests((requests) -> requests.anyRequest().hasRole("ENDPOINT_ADMIN"));
http.httpBasic();
return http.build();
}
}
위 예시에선 EndpointRequest.toAnyEndpoint()
를 사용해 모든 엔드포인트에 대한 요청을 매칭하고, ENDPOINT_ADMIN
role을 가지고 있는지 확인한다. EndpointRequest
에 있는 다른 matcher 메소드들도 활용해도 된다. 자세한 내용은 API 문서를 참고해라 (HTML, PDF).
애플리케이션 앞에 방화벽이 있다면 인증 없이도 모든 액추에이터 엔드포인트에 접근할 수 있기를 바랄 수도 있다. 이럴 땐 management.endpoints.web.exposure.include
프로퍼티를 다음과 같이 변경하면 된다:
management.endpoints.web.exposure.include=*
management:
endpoints:
web:
exposure:
include: "*"
스프링 시큐리티를 사용하고 있다면, 아래 예제처럼 엔드포인트에 인증 없이도 접근할 수 있도록 허용해줄 커스텀 시큐리티 설정을 추가로 넣어줘야 한다:
@Configuration(proxyBeanMethods = false)
public class MySecurityConfiguration {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http.requestMatcher(EndpointRequest.toAnyEndpoint())
.authorizeRequests((requests) -> requests.anyRequest().permitAll());
return http.build();
}
}
위에 있는 두 예제에선 액추에이터 엔드포인트에만 설정을 적용하고 있다. 스프링 부트의 시큐리티 설정은
SecurityFilterChain
빈이 있을 땐 전혀 적용되지 않기 때문에,SecurityFilterChain
빈을 하나 더 설정해서 애플리케이션의 다른 부분에 적용할 규칙들을 추가해야 한다.
8.2.4. Configuring Endpoints
엔드포인트에선 파라미터를 받지 않는 읽기 작업에 대한 응답을 자동으로 캐시한다. 엔드포인트가 응답을 캐시하고 있을 시간을 설정하려면 cache.time-to-live
프로퍼티를 사용해라. 아래 예시에선 beans
엔드포인트 캐시의 TTLtime-to-live을 10초로 설정하고 있다.
management.endpoint.beans.cache.time-to-live=10s
management:
endpoint:
beans:
cache:
time-to-live: "10s"
management.endpoint.<name>
프리픽스로 설정 중인 엔드포인트를 고유하게 식별할 수 있다.
8.2.5. Hypermedia for Actuator Web Endpoints
모든 엔드포인트들의 링크를 가지고 있는 “디스커버리 페이지”가 하나 추가된다. “디스커버리 페이지”는 기본적으로 /actuator
에서 이용할 수 있다.
이 “디스커버리 페이지”를 비활성화하려면 애플리케이션 프로퍼티에 아래 프로퍼티를 추가해라:
management.endpoints.web.discovery.enabled=false
management:
endpoints:
web:
discovery:
enabled: false
커스텀 management 컨텍스트 경로를 설정하면 “디스커버리 페이지”는 자동으로 /actuator
에서 management 컨텍스트의 루트로 이동한다. 예를 들어 management 컨텍스트 경로가 /management
일 땐 디스커버리 페이지는 /management
에서 이용할 수 있다. management 컨텍스트 경로를 /
로 설정하면 다른 매핑 경로와 충돌하지 않도록 디스커버리 페이지를 비활성화한다.
8.2.6. CORS Support
CORSCross-origin resource sharing는 W3C 사양 중 하나로, 어떤 종류의 cross-domain 요청을 승인할지를 유연하게 지정할 수 있다. 스프링 MVC나 스프링 웹플럭스를 사용하고 있다면, 액추에이터의 웹 엔드포인트에서도 이런 시나리오를 지원하도록 설정할 수 있다.
CORS 지원은 기본적으로 비활성화돼 있으며, management.endpoints.web.cors.allowed-origins
프로퍼티를 설정했을 때만 활성화된다. 아래 설정에선 example.com
도메인에서 GET
, POST
호출을 허용한다:
management.endpoints.web.cors.allowed-origins=https://example.com
management.endpoints.web.cors.allowed-methods=GET,POST
management:
endpoints:
web:
cors:
allowed-origins: "https://example.com"
allowed-methods: "GET,POST"
지원하는 전체 옵션들은 CorsEndpointProperties를 확인해봐라.
8.2.7. Implementing Custom Endpoints
@Endpoint
어노테이션을 선언한 @Bean
을 추가하면 @ReadOperation
이나 @WriteOperation
, @DeleteOperation
어노테이션이 달린 메소드들은 모두 자동으로 JMX를 통해 자동으로 노출되며, 웹 애플리케이션에선 HTTP를 통해서도 노출된다. HTTP를 통한 엔드포인트 노출은 Jersey, 스프링 MVC, 스프링 웹플럭스를 통해 할 수 있다. Jersey와 스프링 MVC가 둘 다 있을 때는 스프링 MVC를 사용한다.
아래 예제에선 커스텀 객체를 반환하는 읽기 작업read operation을 노출한다:
@ReadOperation
public CustomData getData() {
return new CustomData("test", 5);
}
@JmxEndpoint
나 @WebEndpoint
를 사용해서 기술 전용 엔드포인트를 작성할 수도 있다. 이 엔드포인트들은 해당 기술로만 제한된다. 예를 들어 @WebEndpoint
는 HTTP를 통해서만 노출되며, JMX로는 노출되지 않는다.
@EndpointWebExtension
과 @EndpointJmxExtension
을 사용하면 기술 전용 익스텐션을 작성할 수 있다. 이 어노테이션으론 기존 엔드포인트에 기술 전용 작업을 추가할 수 있다.
마지막으로 웹 프레임워크 전용 기능에 접근해야 한다면 서블릿이나 스프링 @Controller
, @RestController
엔드포인트를 구현할 수 있다. 대신 이때는 JMX로는 노출해줄 수 없으며, 다른 웹 프레임워크에선 사용할 수 없다.
Receiving Input
엔드포인트의 오퍼레이션은 파라미터를 통해 입력을 받는다. 웹을 통해 노출하면 이런 파라미터 값은 URL의 쿼리 파라미터와 JSON 요청 body에서 가져온다. JMX를 통해 노출하면 파라미터는 MBean operation의 파라미터에 매핑된다. 기본적으로 파라미터는 필수다. @javax.annotation.Nullable
이나 @org.springframework.lang.Nullable
어노테이션을 사용하면 파라미터를 생략 가능하게 만들 수 있다.
JSON 요청 body에 있는 모든 프로퍼티는 엔드포인트의 파라미터에 매핑할 수 있다. 아래 JSON이 요청 body라고 생각해보자:
{
"name": "test",
"counter": 42
}
이 프로퍼티들을 사용해서 다음 예제와 같이 String name
과 int counter
파라미터를 받는 쓰기 작업write operation을 호출할 수 있다:
@WriteOperation
public void updateData(String name, int counter) {
// injects "test" and 42
}
엔드포인트는 기술에 상관 없이 사용할 수 있기 때문에, 메소드 시그니처에는 간단한 타입만 지정할 수 있다. 특히
name
과counter
프로퍼티를 정의하는CustomData
타입을 단일 파라미터로는 선언할 수 없다.
입력을 오퍼레이션 메소드의 파라미터에 매핑하려면 엔드포인트를 구현하는 자바 코드는
-parameters
로 컴파일해야 하며, 코틀린 코드는-java-parameters
로 컴파일해야 한다. 스프링 부트의 그래들 플러그인을 사용하거나, 메이븐과spring-boot-starter-parent
를 사용하고 있다면 자동으로 추가된다.
Input Type Conversion
엔드포인트 오퍼레이션 메소드에 전달된 파라미터는 필요한 경우 자동으로 타입을 변환한다. JMX나 HTTP 요청을 통해 받은 입력은 오퍼레이션 메소드를 호출하기 전에, @EndpointConverter
로 지정한 Converter
나 GenericConverter
빈과 ApplicationConversionService
인스턴스를 사용해서 필요한 타입으로 변환한다.
Custom Web Endpoints
@Endpoint
, @WebEndpoint
, @EndpointWebExtension
에 있는 작업은 자동으로 Jersey나 스프링 MVC, 스프링 웹플럭스를 사용해서 HTTP로 노출한다. Jersey와 스프링 MVC가 둘 다 있을 땐 스프링 MVC를 사용한다.
Web Endpoint Request Predicates
웹으로 노출한 엔드포인트에 있는 모든 작업에는 request predicate가 자동으로 생성된다.
Path
predicate path는 엔드포인트의 ID와 웹에 노출한 엔드포인트의 base path로 결정한다. 디폴트 base path는 /actuator
다. 예를 들어 ID가 sessions
인 엔드포인트에선 /actuator/sessions
를 predicate path로 사용한다.
오퍼레이션 메소드에 있는 파라미터 하나 이상에 @Selector
를 선언하면 path를 좀 더 커스텀할 수 있다. 이런 파라미터는 path predicate에 path 변수로 추가된다. 변수 값은 엔드포인트 작업을 호출할 때 오퍼레이션 메소드에 전달한다. 나머지 path 요소를 모두 잡아내려면 마지막 파라미터에 @Selector(match=ALL_REMAINING)
을 추가하고 String[]
으로 변환할 수 있는 타입으로 만들면 된다.
HTTP method
predicate의 HTTP 메소드는 아래 테이블에 정리해 둔대로 오퍼레이션 타입에 따라 결정된다:
Operation | HTTP method |
---|---|
@ReadOperation |
GET |
@WriteOperation |
POST |
@DeleteOperation |
DELETE |
Consumes
요청 body를 사용하는 @WriteOperation
(HTTP POST
)에선 predicate의 consumes 항목은 application/vnd.spring-boot.actuator.v2+json, application/json
이다. 다른 오퍼레이션에선 모두 consumes 항목은 빈 값이다.
Produces
predicate의 produces 항목은 @DeleteOperation
, @ReadOperation
, @WriteOperation
어노테이션의 produces
속성으로 결정할 수 있다. 이 속성은 생략할 수 있다. 사용하지 않으면 produces 항목을 자동 결정한다.
오퍼레이션 메소드 반환 타입이 void
나 Void
면 produces를 비워둔다. 오퍼레이션 메소드가 org.springframework.core.io.Resource
를 반환한다면 produces 항목은 application/octet-stream
이 된다. 그외 다른 오퍼레이션은 모두 application/vnd.spring-boot.actuator.v2+json, application/json
을 사용한다.
Web Endpoint Response Status
엔드포인트 작업에서 반환하는 디폴트 상태 코드는 오퍼레이션 타입(읽기, 쓰기, 삭제)과 오퍼레이션이 반환하는 내용(있다면)에 따라 다르다.
@ReadOperation
에선 값을 반환하면 200(OK)을 돌려준다. 값을 반환하지 않으면 404 (Not Found)로 응답한다.
@WriteOperation
이나 @DeleteOperation
이 값을 반환하면 응답 상태는 200(OK)이 된다. 값을 반환하지 않으면 204 (No Content)로 응답한다.
오퍼레이션을 필수 파라미터 없이 실행하거나, 필수 타입으로 변환할 수 없는 파라미터로 호출하면, 오퍼레이션 메소드를 호출하지 않으며 응답 상태는 400(Bad Request)이 된다.
Web Endpoint Range Requests
HTTP range request를 사용하면 HTTP 리소스의 일부를 요청할 수 있다. 스프링 MVC나 스프링 웹플럭스를 사용할 때는 org.springframework.core.io.Resource
를 반환하는 작업은 자동으로 range request가 지원된다.
Jersey를 사용할 때는 range request를 지원하지 않는다.
Web Endpoint Security
웹 엔드포인트나 웹 전용 엔드포인트 익스텐션의 작업에선 현재 java.security.Principal
이나 org.springframework.boot.actuate.endpoint.SecurityContext
를 메소드 파라미터로 받을 수 있다. 전자는 보통 @Nullable
과 함께 사용해서 사용자의 인증 여부에 따라 다른 동작을 수행한다. 후자는 보통 isUserInRole(String)
메소드를 사용해서 권한을 검사authorization check하는 데 활용한다.
Servlet Endpoints
@ServletEndpoint
어노테이션을 달고 동시에 Supplier<EndpointServlet>
을 구현하는 클래스를 만들면 Servlet
을 엔드포인트로 노출할 수 있다. 서블릿 엔드포인트는 서블릿 컨테이너와 좀 더 깊게 통합할 수 있게 해주지만, 그대신 이식성을 포기해야 한다. 서블릿 엔드포인트는 기존 Servlet
을 엔드포인트로 노출하는 용도다. 엔드포인트를 새로 만들 때는 가능하면 @Endpoint
와 @WebEndpoint
어노테이션을 사용하는 게 좋다.
Controller Endpoints
@ControllerEndpoint
와 @RestControllerEndpoint
는 스프링 MVC나 스프링 웹플럭스로만 노출할 엔드포인트를 구현할 때 사용할 수 있다. 메소드는 @RequestMapping
, @GetMapping
같은 스프링 MVC와 스프링 웹플럭스의 표준 어노테이션을 사용해 매핑하며, 이 path 앞에 엔드포인트 ID를 붙인다. 컨트롤러 엔드포인트는 스프링의 웹 프레임워크와 좀 더 깊게 통합할 수 있게 해주지만, 그대신 이식성을 포기해야 한다. 가능하면 @Endpoint
와 @WebEndpoint
어노테이션을 사용하는 게 좋다.
8.2.8. Health Information
실행 중인 애플리케이션의 상태는 상태 정보health information를 통해 확인할 수 있다. 모니터링 소프트웨어에선 보통 프로덕션 시스템이 다운되면 누군가에게 알림을 전송할 수 있도록 상태 정보를 활용한다. health
엔드포인트로 노출하는 정보는 management.endpoint.health.show-details
, management.endpoint.health.show-components
프로퍼티에 따라 달라진다. 이 프로퍼티는 아래 있는 값들 중 하나로 설정할 수 있다:
Name | Description |
---|---|
never |
상세 정보는 절대 보여주지 않는다. |
when-authorized |
인증된 사용자에게만 상세 정보를 보여준다. 권한을 부여할 role은 management.endpoint.health.roles 로 설정할 수 있다. |
always |
모든 사용자에게 상세 정보를 보여준다. |
디폴트 값은 never
다. 엔드포인트의 role을 하나 이상 가진 사용자는 권한이 있는 것으로 간주한다. 엔드포인트에 설정한 role이 없을 땐 (디폴트) 인증된 모든 사용자가 권한이 있는 것으로 간주한다. 이 role은 management.endpoint.health.roles
프로퍼티로 설정할 수 있다.
보안을 적용한 애플리케이션에서
always
를 사용하고 싶다면, 시큐리티 설정에선 반드시 인증된 사용자와 인증되지 않은 사용자 모두에게 health 엔드포인트 접근을 허용해야 한다.
상태는 HealthContributorRegistry
에 있는 정보로 수집한다. (기본적으로는 ApplicationContext
에 정의한 모든 HealthContributor
인스턴스). 스프링 부트에서는 여러 가지 HealthContributor
들을 자동 설정해주며, 직접 구현할 수도 있다.
HealthContributor
는 HealthIndicator
일 수도 있고 CompositeHealthContributor
일 수도 있다. HealthIndicator
는 Status
를 포함한 실제 상태 정보를 제공한다. CompositeHealthContributor
는 다른 HealthContributor
들을 가지고 있는 composite이다. contributor들을 함께 모아서 전반적인 시스템 상태를 나타내는 트리 구조를 형성한다.
기본적으로 시스템의 최종 상태는 각 HealthIndicator
의 상태를 정해진 순서대로 정렬하는 StatusAggregator
로 만든다. 정렬한 리스트에서 가장 앞에 있는 상태를 전반적인 상태 정보로 사용한다. HealthIndicator
가 StatusAggregator
가 알지 못하는 상태를 반환하면 UNKNOWN
상태를 반환한다.
HealthContributorRegistry
를 사용하면 런타임에 health indicator를 등록하거나 제외시킬 수 있다.
Auto-configured HealthIndicators
스프링 부트는 아래 있는 HealthIndicator
들을 필요할 때 자동으로 설정해준다. management.health.key.enabled
를 아래 테이블에 나와있는 key
를 사용해 설정하면 원하는 indicator를 활성화/비활성화할 수도 있다:
Key | Name | Description |
---|---|---|
cassandra |
CassandraDriverHealthIndicator |
Cassandra 데이터베이스 상태를 검사한다. |
couchbase |
CouchbaseHealthIndicator |
Couchbase 클러스터 상태를 검사한다. |
db |
DataSourceHealthIndicator |
DataSource 에서 커넥션을 획득할 수 있는 지 검사한다. |
diskspace |
DiskSpaceHealthIndicator |
남아 있는 디스크 공간을 검사한다. |
elasticsearch |
ElasticsearchRestHealthIndicator |
Elasticsearch 클러스터 상태를 검사한다. |
hazelcast |
HazelcastHealthIndicator |
Hazelcast 서버 상태를 검사한다. |
influxdb |
InfluxDbHealthIndicator |
InfluxDB 서버 상태를 검사한다. |
jms |
JmsHealthIndicator |
JMS 브로커 상태를 검사한다. |
ldap |
LdapHealthIndicator |
LDAP 서버 상태를 검사한다. |
mail |
MailHealthIndicator |
mail 서버 상태를 검사한다. |
mongo |
MongoHealthIndicator |
Mongo 데이터베이스 상태를 검사한다. |
neo4j |
Neo4jHealthIndicator |
Neo4j 데이터베이스 상태를 검사한다. |
ping |
PingHealthIndicator |
항상 UP 으로 응답한다. |
rabbit |
RabbitHealthIndicator |
Rabbit 서버 상태를 검사한다. |
redis |
RedisHealthIndicator |
Redis 서버 상태를 검사한다. |
solr |
SolrHealthIndicator |
Solr 서버 상태를 검사한다. |
management.health.defaults.enabled
프로퍼티를 사용하면 전부 비활성화할 수 있다.
그 밖에 다른 HealthIndicator
도 있지만, 기본으로 활성화하진 않는다:
Key | Name | Description |
---|---|---|
livenessstate |
LivenessStateHealthIndicator |
애플리케이션의 “Liveness” 가용성 상태를 노출한다. |
readinessstate |
ReadinessStateHealthIndicator |
애플리케이션의 “Readiness” 가용성 상태를 노출한다. |
Writing Custom HealthIndicators
상태 정보를 커스텀하려면 HealthIndicator
인터페이스를 구현한 스프링 빈을 등록하면 된다. health()
메소드를 구현해서 Health
응답을 반환해야 한다. Health
응답은 status를 가지고 있어야 하며, 표기하고 싶은 다른 세부정보도 함께 넣어도 된다. 다음은 HealthIndicator
의 샘플 구현체다:
@Component
public class MyHealthIndicator implements HealthIndicator {
@Override
public Health health() {
int errorCode = check();
if (errorCode != 0) {
return Health.down().withDetail("Error Code", errorCode).build();
}
return Health.up().build();
}
private int check() {
// perform some specific health check
return ...
}
}
HealthIndicator
빈의 이름에서 뒤의HealthIndicator
(있다면)를 제외한 게HealthIndicator
의 식별자다. 위 예시에 있는 상태 정보는my
라는 엔트리로 사용할 수 있다.
Health
는 스프링 부트에서 미리 정의해둔 Status
타입 외에도 시스템 상태를 나타내는 새로운 커스텀 Status
를 반환할 수도 있다. 이땐 StatusAggregator
인터페이스의 커스텀 구현체를 함께 제공해야 하며, 아니면 management.endpoint.health.status.order
설정 프로퍼티로 디폴트 구현체를 설정해야 한다.
예를 들어 code값이 FATAL
인 Status
를 새로 만들어 HealthIndicator
구현체 중 하나에서 사용하고 있다고 해보자. Status
를 심각한 순서대로 정렬해주려면 애플리케이션 프로퍼티에 아래 프로퍼티를 추가해라:
management.endpoint.health.status.order=fatal,down,out-of-service,unknown,up
management:
endpoint:
health:
status:
order: "fatal,down,out-of-service,unknown,up"
응답에 있는 HTTP 상태 코드는 전반적인 상태를 반영해서 만든다. 기본적으로 OUT_OF_SERVICE
와 DOWN
은 503에 매핑된다. UP
과 그외 매핑되지 않은 상태는 200에 매핑한다. HTTP를 통해 상태 엔드포인트에 접근한다면 상태 매핑을 커스텀하고 싶을 수도 있다. 커스텀 매핑을 설정하게되면 DOWN
과 OUT_OF_SERVICE
의 디폴트 매핑은 비활성화된다. 디폴트 매핑을 그대로 유지하려면 커스텀 매핑과 함께 명시해줘야 한다. 예를 들어 아래 프로퍼티는 FATAL
을 503(service unavailable)으로 매핑하면서 동시에 DOWN
과 OUT_OF_SERVICE
의 디폴트 매핑을 보존하고 있다:
management.endpoint.health.status.http-mapping.down=503
management.endpoint.health.status.http-mapping.fatal=503
management.endpoint.health.status.http-mapping.out-of-service=503
management:
endpoint:
health:
status:
http-mapping:
down: 503
fatal: 503
out-of-service: 503
다른 세세한 로직이 필요하다면 자체
HttpCodeStatusMapper
을 정의해도 된다.
다음은 내장된 Status
에 대한 디폴트 매핑을 정리한 테이블이다:
Status | Mapping |
---|---|
DOWN |
SERVICE_UNAVAILABLE (503 ) |
OUT_OF_SERVICE |
SERVICE_UNAVAILABLE (503 ) |
UP |
기본적으로는 매핑되지 않아서 HTTP 상태는 200 이 된다 |
UNKNOWN |
기본적으로는 매핑되지 않아서 HTTP 상태는 200 이 된다 |
Reactive Health Indicators
스프링 웹플럭스를 사용하는 등의 리액티브 애플리케이션에선 ReactiveHealthContributor
가 논블로킹으로 애플리케이션 상태를 가져오는 역할을 담당한다. 기존 HealthContributor
와 마찬가지로 상태 정보는 ReactiveHealthContributorRegistry
에 있는 정보로 수집한다 (기본적으로는 ApplicationContext
에 정의한 모든 HealthContributor
, ReactiveHealthContributor
인스턴스). 리액티브 API를 사용하지 않는 일반 HealthContributor
는 elastic 스케줄러에서 실행한다.
리액티브 애플리케이션에서 런타임에 health indicator를 등록하고 제외시키려면
ReactiveHealthContributorRegistry
를 사용해야 한다. 일반HealthContributor
를 등록해야 한다면ReactiveHealthContributor#adapt
를 사용해 래핑해야 한다.
리액티브 API로 커스텀 상태 정보를 제공하려면 ReactiveHealthIndicator
인터페이스를 구현한 스프링 빈을 등록하면 된다. 다음은 ReactiveHealthIndicator
의 샘플 구현체다:
@Component
public class MyReactiveHealthIndicator implements ReactiveHealthIndicator {
@Override
public Mono<Health> health() {
return doHealthCheck().onErrorResume((exception) ->
Mono.just(new Health.Builder().down(exception).build()));
}
private Mono<Health> doHealthCheck() {
// perform some specific health check
return ...
}
}
에러를 자동으로 처리하고 싶으면
AbstractReactiveHealthIndicator
를 상속받는 것도 좋다.
Auto-configured ReactiveHealthIndicators
스프링 부트는 아래 있는 ReactiveHealthIndicator
들을 필요할 때 자동으로 설정해준다:
Key | Name | Description |
---|---|---|
cassandra |
CassandraDriverReactiveHealthIndicator |
Cassandra 데이터베이스 서버 상태를 검사한다. |
couchbase |
CouchbaseReactiveHealthIndicator |
Couchbase 클러스터 상태를 검사한다. |
elasticsearch |
ElasticsearchReactiveHealthIndicator |
Elasticsearch 클러스터 상태를 검사한다. |
mongo |
MongoReactiveHealthIndicator |
Mongo 데이터베이스 서버 상태를 검사한다. |
neo4j |
Neo4jReactiveHealthIndicator |
Neo4j 데이터베이스 서버 상태를 검사한다. |
redis |
RedisReactiveHealthIndicator |
Redis 서버 상태를 검사한다. |
일반 indicator는 필요 시엔 리액티브 indicator로 대체된다. 또한 명시적으로 처리하지 않은
HealthIndicator
는 자동으로 래핑된다.
Health Groups
health indicator를 다양한 목적으로 활용할 수 있는 그룹으로 묶으면 유용할 때가 많다.
health indicator 그룹을 생성하려면 management.endpoint.health.group.<name>
프로퍼티를 사용해 include
나 exclude
에 health indicator ID들을 지정하면 된다. 예를 들어, 데이터베이스 indicator만 포함하는 그룹을 생성하려면 아래와 같이 정의하면 된다:
management.endpoint.health.group.custom.include=db
management:
endpoint:
health:
group:
custom:
include: "db"
이제 localhost:8080/actuator/health/custom
에 접속하면 결과를 확인해볼 수 있다.
마찬가지로, 그룹에서 데이터베이스 indicator를 제외하고, 다른 indicator는 전부 포함시키려면 아래와 같이 정의하면 된다:
management.endpoint.health.group.custom.exclude=db
management:
endpoint:
health:
group:
custom:
exclude: "db"
그룹은 기본적으론 시스템 health와 동일하게 StatusAggregator
와 HttpCodeStatusMapper
설정을 상속하지만, 그룹별로도 따로 정의할 수 있다. 필요하다면 show-details
와 roles
프로퍼티도 재정의할 수 있다:
management.endpoint.health.group.custom.show-details=when-authorized
management.endpoint.health.group.custom.roles=admin
management.endpoint.health.group.custom.status.order=fatal,up
management.endpoint.health.group.custom.status.http-mapping.fatal=500
management.endpoint.health.group.custom.status.http-mapping.out-of-service=500
management:
endpoint:
health:
group:
custom:
show-details: "when-authorized"
roles: "admin"
status:
order: "fatal,up"
http-mapping:
fatal: 500
out-of-service: 500
그룹과 함께 사용할 커스텀
StatusAggregator
나HttpCodeStatusMapper
빈을 등록해야 한다면@Qualifier("groupname")
을 사용하면 된다.
DataSource Health
DataSource
health indicator는 표준 데이터소스와 라우팅 데이터소스 빈의 상태를 모두 보여준다. 라우팅 데이터소스의 상태에는 각 타겟 데이터소스의 상태가 들어있다. health 엔드포인트의 응답에선 라우팅 키로 라우팅 데이터소스의 각 타겟들의 이름을 지정한다. indicator에서 라우팅 데이터소스를 출력하지 않도록 하려면 management.health.db.ignore-routing-data-sources
를 true
로 설정해라.
8.2.9. Kubernetes Probes
쿠버네티스에 배포된 애플리케이션은 컨테이너 프로브를 통해 내부 상태에 대한 정보를 제공할 수 있다. 사용하는 쿠버네티스 설정에 따라 kubelet이 이 프로브를 호출하고 결과에 따라 대응해줄 거다.
스프링 부트는 특별한 설정 없이도 애플리케이션 가용성 상태를 관리해준다. 액추에이터는 쿠버네티스 환경에 배포되면 ApplicationAvailability
인터페이스에서 모은 “Liveness”와 “Readiness” 정보를 전용 Health Indicator, LivenessStateHealthIndicator
와 ReadinessStateHealthIndicator
에 활용한다. 이 indicator들은 글로벌 health 엔드포인트("/actuator/health"
)에 노출된다. 더불어 Health 그룹, "/actuator/health/liveness"
와 "/actuator/health/readiness"
를 통해서도 별도 HTTP 프로브로 노출된다.
이제 이 엔드포인트 정보를 활용해서 쿠버네티스 인프라를 구성할 수 있다:
livenessProbe:
httpGet:
path: /actuator/health/liveness
port: <actuator-port>
failureThreshold: ...
periodSeconds: ...
readinessProbe:
httpGet:
path: /actuator/health/readiness
port: <actuator-port>
failureThreshold: ...
periodSeconds: ...
<actuator-port>
는 액추에이터 엔드포인트에 접근할 수 있는 포트로 설정해야 한다. 엔드포인트 포트는 메인 웹 서버의 포트일 수도 있고,"management.server.port"
프로퍼티를 설정했다면 별도로 사용하는 management 포트일 수도 있다.
이 health 그룹은 애플리케이션이 쿠버네티스 환경에서 실행 중일 때만 자동으로 활성화된다. 설정 프로퍼티 management.endpoint.health.probes.enabled
를 사용하면 모든 환경에서 활성화할 수 있다.
애플리케이션을 기동하는 시간이 liveness period로 설정한 시간보다 길다면, 쿠버네티스에선 가능한 솔루션으로
"startupProbe"
를 언급하고 있다. 모든 기동 태스크를 완료할 때까지"readinessProbe"
가 실패할 거기 때문에"startupProbe"
가 반드시 필요한 건 아니다. 애플리케이션 라이프사이클 동안 프로브가 어떻게 동작하는지도 함께 참고해라.
액추에이터 엔드포인트를 별도 management 컨텍스트에 배포하게 되면, 엔드포인트에선 메인 애플리케이션과 동일한 웹 인프라(포트, 커넥션 풀, 프레임워크 컴포넌트들)를 사용하지 않는다는 점에 유의해라. 이땐 메인 애플리케이션이 제대로 동작하지 않을 때도 (예를 들어 커넥션을 새로 수락할 수 없을 때) 프로브 검사에 성공할 수도 있다.
Checking External State with Kubernetes Probes
액추에이터는 “liveness”와 “readiness” 프로브를 Health 그룹으로 구성한다. 덕분에 여기서는 Health 그룹 기능을 전부 이용할 수 있다. 예를 들어 다른 Health Indicator를 추가로 설정할 수 있다:
management.endpoint.health.group.readiness.include=readinessState,customCheck
management:
endpoint:
health:
group:
readiness:
include: "readinessState,customCheck"
기본적으로 스프링 부트는 이 그룹들에 다른 Health Indicator는 추가하지 않는다.
“liveness” 프로브는 외부 시스템의 상태에 의존하면 안 된다. 쿠버네티스는 애플리케이션의 liveness 상태가 깨지면 애플리케이션 인스턴스를 재시작하는 식으로 문제를 해결하려고한다. 즉, 외부 시스템(데이터베이스, 웹 API, 외부 캐시)이 실패하면 쿠버네티스는 모든 인스턴스를 재시작해서 연이은 실패를 트리거할 수 있다.
“readiness” 프로브에선, 외부 시스템까지 체크할 지는 애플리케이션 개발자가 신중하게 결정해야 한다. 다시 말해 스프링 부트에선 readiness 프로브에 별다른 상태 체크를 추가하지 않는다. 쿠버네티스는 애플리케이션 인스턴스의 readiness 상태가 아직 준비되지 않았을 땐 해당 인스턴스로 트래픽을 라우팅하지 않는다. 일부 외부 시스템은 애플리케이션 인스턴스들이 공유하고 있지 않을 수도 있는데, 이럴 땐 readiness 프로브에 포함시키는 게 자연스럽다. 또다른 외부 시스템은 애플리케이션에 반드시 필요한 건 아닐 수도 있다 (서킷 브레이커나 폴백이 있을 수 있다). 이럴 땐 분명히 포함시키지 않는게 맞다. 안타깝지만 모든 애플리케이션 인스턴스가 공유하는 외부 시스템은 굉장히 흔하며, 직접 판단해서 결정을 내려야 한다. readiness 프로브에 포함시켜 외부 서비스가 다운되면 애플리케이션이 서비스에서 제외되길 기대하거나, 아니면 그대로 놔두고 호출부에서 서킷 브레이커 등을 활용하는 식으로 상위 스택으로 실패 처리를 넘겨라.
type=ClusterIP
나NodePort
를 사용하는 쿠버네티스 서비스는 애플리케이션 인스턴스들이 전부 준비되지 않았을 땐 들어오는 커넥션을 허용하지 않는다. 커넥션 자체가 없기 때문에 HTTP 에러 응답(503 등)도 없다.type=LoadBalancer
를 사용하는 서비스는 제공업체에 따라 커넥션을 수락할 수도 있고 수락하지 않을 수도 있다. 인그레스를 가지고 있는 서비스도 구현체에 따라 다르게 응답한다. 인그레스 서비스 자체에서 다운스트림에서 발생하는 “connection refused”를 처리하는 방법을 결정해야 할 거다. 로드 밸런서와 인그레스 모두 HTTP 503이 발생할 확률이 높다.
게다가 애플리케이션이 쿠버네티스 오토스케일링을 사용하고 있다면, 오토스케일러 설정에 따라 로드 밸런서에서 애플리케이션이 제거됐을 때의 대응이 다를 수도 있다.
Application Lifecycle and Probe States
쿠버네티스의 프로브를 이용할 땐 애플리케이션 라이프 사이클과의 일관성에 주목해야 한다. 메모리 상에 존재하며 애플리케이션의 내부 상태를 나타내는 AvailabilityState
와, 이 state를 노출하는 실제 프로브에는 상당한 차이가 존재한다. 애플리케이션 라이프사이클 단계에 따라 프로브를 사용하지 못할 때도 있다.
스프링 부트는 기동과 종료 중에 애플리케이션 이벤트를 발행하며, 프로브에선 이 이벤트를 받아listen AvailabilityState
정보를 노출할 수 있다.
다음은 각 단계마다의 AvailabilityState
와 HTTP 커넥터 상태를 담고있는 테이블이다:
스프링 부트 애플리케이션을 기동할 때:
Startup phase | LivenessState | ReadinessState | HTTP server | Notes |
---|---|---|---|---|
Starting | BROKEN |
REFUSING_TRAFFIC |
기동 전 | 쿠버네티스는 “liveness” 프로브를 확인하고 시간이 너무 오래 걸리면 애플리케이션을 재시작한다. |
Started | CORRECT |
REFUSING_TRAFFIC |
요청 거부 | 애플리케이션 컨텍스트를 리프레시한다. 애플리케이션은 기동 태스크를 수행하며 아직 트래픽을 받진 않는다. |
Ready | CORRECT |
ACCEPTING_TRAFFIC |
요청 수락 | 기동 태스크들을 완료한다. 애플리케이션은 트래픽을 받기 시작한다. |
스프링 부트 애플리케이션을 종료할 때:
Shutdown phase | Liveness State | Readiness State | HTTP server | Notes |
---|---|---|---|---|
Running | CORRECT |
ACCEPTING_TRAFFIC |
요청 수락 | 종료를 요청했다. |
Graceful shutdown | CORRECT |
REFUSING_TRAFFIC |
새 요청은 거부 | graceful shutdown을 활성화했다면 진행 중인 요청을 처리한다. |
Shutdown complete | N/A | N/A | 서버 종료 | 애플리케이션 컨텍스트를 종료하고 애플래케이션을 종료시킨다. |
쿠버네티스 배포에 관한 자세한 내용은 쿠버네티스 컨테이너 라이프 사이클 섹션을 확인해봐라.
8.2.10. Application Information
애플리케이션 정보로 노출하는 정보들은 ApplicationContext
에 정의한 모든 InfoContributor
빈에서 수집해온다. 스프링 부트에서는 여러 가지 InfoContributor
들을 자동 설정해주며, 직접 구현할 수도 있다.
Auto-configured InfoContributors
스프링 부트는 아래 있는 InfoContributor
들을 필요할 때 자동으로 설정해준다:
Name | Description |
---|---|
EnvironmentInfoContributor |
Environment 에서 info 아래에 있는 모든 키들을 노출한다. |
GitInfoContributor |
git.properties 파일이 있을 때 git 정보를 노출한다. |
BuildInfoContributor |
META-INF/build-info.properties 파일이 있을 때 빌드 정보를 노출한다. |
management.info.defaults.enabled
프로퍼티를 사용하면 전부 비활성화할 수 있다.
Custom Application Information
스프링 프로퍼티 info.*
를 설정하면 info
엔드포인트로 노출할 데이터를 커스텀할 수 있다. info
키 아래에 설정한 모든 Environment
프로퍼티를 자동으로 노출해준다. 예를 들어 application.properties
파일에 다음과 같은 설정을 추가할 수 있다:
info.app.encoding=UTF-8
info.app.java.source=11
info.app.java.target=11
info:
app:
encoding: "UTF-8"
java:
source: "11"
target: "11"
info 프로퍼티를 직접 하드코딩하는 대신 빌드 시점에 확장할 수도 있다.
메이븐을 사용한다면 위 예제는 아래처럼 작성해도 된다:
properties yamlinfo.app.encoding=@project.build.sourceEncoding@ info.app.java.source=@java.version@ info.app.java.target=@java.version@
info: app: encoding: "@project.build.sourceEncoding@" java: source: "@java.version@" target: "@java.version@"
Git Commit Information
info
엔드포인트에서 또 한가지 유용한 기능은 프로젝트를 빌드할 때 git
소스 코드 레포지토리의 상태 정보를 게시하는 기능이다. GitProperties
빈을 사용할 수 있으면 이런 프로퍼티도 info
엔드포인트로 노출할 수 있다.
클래스패스 루트에
git.properties
파일이 있다면GitProperties
빈은 자동으로 설정된다. 자세한 내용은 “깃 정보 생성하기“를 참고해라.
기본적으로 엔드포인트는 git.branch
, git.commit.id
, git.commit.time
프로퍼티(있으면)를 노출한다. 이런 프로퍼티를 엔드포인트 응답에서 제외하고 싶으면 git.properties
파일에서 제거해야 한다. git 정보를 전부 (즉, git.properties
에 있는 전체 내용) 표기하려면 management.info.git.mode
프로퍼티를 사용해라:
management.info.git.mode=full
management:
info:
git:
mode: "full"
info
엔드포인트에서 git 커밋 정보를 완전히 제외시키려면 아래 예시처럼 management.info.git.enabled
프로퍼티를 false
로 설정해라:
management.info.git.enabled=false
management:
info:
git:
enabled: false
Build Information
BuildProperties
빈이 등록돼 있으면 info
엔드포인트로 빌드에 관한 정보도 게시할 수 있다. 클래스패스에 META-INF/build-info.properties
파일이 있을 때 게시된다.
메이븐이나 그래들 플러그인으로 이 파일을 생성할 수 있다. 자세한 내용은 “빌드 정보 생성하기“를 참고해라.
Writing Custom InfoContributors
커스텀 애플리케이션 정보를 제공하려면 InfoContributor
인터페이스를 구현한 스프링 빈을 등록하면 된다.
다음 예제에선 단일 값을 가지고 있는 example
엔트리를 제공한다:
@Component
public class MyInfoContributor implements InfoContributor {
@Override
public void contribute(Info.Builder builder) {
builder.withDetail("example", Collections.singletonMap("key", "value"));
}
}
info
엔드포인트에 접근하면 응답에 아래와 같은 엔트리가 추가된 걸 확인할 수 있을 거다:
{
"example": {
"key" : "value"
}
}
Next :Monitoring and Management over HTTP
액추에이터 path, 포트, 주소, ssl 커스텀하기
전체 목차는 여기에 있습니다.