- Spring Boot Actuator Metrics 데이터 활용
이 글은 Spring Boot Actuator Metrics 데이터를 Storage 에 저장하고 시각화하는 방법에 대해서 검토한 글이다. 참고로 스프링 부트 2.X 이상 버전에 해당하는 글이다.
목차
1. Spring Boot Actuator
2. InfluxDB & Grafana
3. ElasticSearch & Kibana
3. MongoDB & MQ
스프링 부트 Actuator 는 애플리케이션을 모니터링하고 관리할 수 있는 데이터를 제공해준다. 스프링 부트에서 Actuator 디펜던시를 설정하면 애플리케이션 모니터링 및 관리할 수 있는 정보를 제공받을 수 있다.
dependencies { compile("org.springframework.boot:spring-boot-starter-actuator") }
이 글은 Spring Boot Actuator 에 대해서 상세하게 다루지 않는다. 이 글은, Spring Boot Actuator Metrics 데이터를 스토리지에 저장하고 활용하는 것에 대한 글이다. 상세한 정보를 확인하고 싶다면 스프
링 공식 레퍼런스를 참고하길 바란다.
https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#production-ready
참고로, Spring Boot Actuator 1.X , 2.X 버전 차이가 있다. 가장 큰 차이는 2.X버전 에서는 마이크로미터 데이터를 지원한다. Baeldung 의 글을 참고하자.
https://www.baeldung.com/spring-boot-actuator
https://www.baeldung.com/micrometer
https://spring.io/blog/2018/03/16/micrometer-spring-boot-2-s-new-application-metrics-collector
https://spring.io/blog/2018/03/16/micrometer-spring-boot-2-s-new-application-metrics-collector
https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#production-ready-metrics
Spring Boot Actuator에서 기본으로 제공하는 데이터 외에 커스텀하게 데이터를 추가할 수 있다. 아래와 같이 코드를 작성하면, parameter.empty.count 라는 신규의 Counter 타입의 데이터를 생성한다. 그리고, increment 메서드를 실행하면 데이터가 +1 된다.
private final MeterRegistry registry;
private Counter parameterEmptyCounter;
//생략
this.parameterEmptyCounter = registry.counter("parameter.empty.count");
//생략
parameterEmptyCounter.increment();
필자가 커스텀하게 생성한 parameter.empty.count 가 생성된 것이 보인다.
그리고, increment() 메서드를 실행할 때마다 값이 증가할 것이다. actuator/metrics/parameter.empty.count 를 실행하면 아래와 같이 조회할 있다.
참고로 value 가 10 인 이유는, 필자가 임의로 increment 를 10번 실행했기 때문이다.
간단하게 Spring Boot Actuator 에 대해서 알아보았다. 기본적으로 Actuator 데이터는 비휘발성 메모리 기반 데이터이다. 즉, 임베디드 톰캣 서버를 재시작하면 데이터는 모두 날라갈 것이다. 실서비스에서 사용하기 위해서는 Actuator 의 데이터를 외부 스토리지에 저장해서 모니터링 할 수 있는 환경이 필요하다. 2장~4장에서는 다양한 방법으로 Actuator 데이터를 스토리지에 저장 및 모니터링 환경을 구축해보도록 하겠다. 참고로 이런 작업은 필자도 처음이다. 실무에 도입하겠다는 생각도 없다. 그냥 아무생각 없이 해보는 글이다. 혹시라도 실무적용 사례가 있다면 피드백을 댓글로 남겨주길 바란다.
Spring Boot Actuator 2.X 에서는 MicroMeter Metrics 데이터를 제공한다. 또한, MicroMeter 에 대한 AutoConfiguration을 제공한다.
필자는 이중에서 Influx DB 를 테스트 하겠다. 이유는 딱히 없다. InfluxDB가 제일 많이 사용하는 것 같다. 추가로 InfluxDB에 저장한 데이터를, 시각화하여 보여주기 위해서 Grafana를 연동하였다.
https://computingforgeeks.com/install-grafana-and-influxdb-on-centos-7/
필자는 CentOS7 에 설치하였다. 필자처럼 따라하지 말고, 그냥 도커 이미지로 설치하길 바란다.
influx --version 으로 버전을 확인할 수 있다.
데이터베이스를 생성하자.
>CREATE DATABASE autuator
>use actuator
상세한 설치 방법은 생략한다. 초기 비밀번호는 admin/admin 이다. 데이터소스를 생성해야 하는데, 방금 설치한 InfluxDB 를 연동하자.
데이터 소스를 생성하면 아래와 같이 New dashboard 가 활성화 된다.
http://docs.grafana.org/guides/getting_started/
스프링부트 Property 설정을 한다. 간단하다.
management:
metrics:
export:
influx:
enabled: true
db: actuator01
uri: http://influxDBurl:8086
# user-name:
# password:
connect-timeout: 1s
read-timeout: 10s
auto-create-db: true
step: 1m
num-threads: 2
consistency: one
compressed: true
batch-size: 10000
정상적으로 연결이 되었다면, 아래와 같이 successfully sent xx metrics to influx 라는 메시지가 출력된다.
Graph
Singlestat
Table
Text
Heatmap
Alert List
Dashboard list
Row
Plugin list
필자는 Grafana 사용이 처음이고, 자세히 볼 시간이 없다. 대략적으로 간단하게만 설정했다.
간단하게 이것저것 빠르게 검토한 결과, 사용하기 쉽고 Spring Boot Actuator Metrix 데이터를 충분히 활용할 수 있을 것 같다. 한두시간 정도 빠르게 검토한 내용이라서 판단하기 조금 애매하지만, 실서비스에서 사용하기에도 괜찮을 듯 하다. 추후에 시간이 많을 때 더 상세하게 검토를 할 예정이다.
Spring Boot Actuator 2.X & InfluxDB & Grafana 조합이 생각보다 괜찮은 느낌적인 느낌이다.
2장에서 살펴본 influxDB 는 마이크로미터 데이터를 호환하기 때문에 아주 심플하고 간결하게 연동이 가능했다. ElasticSearch 에 저장하는 방법에 대해서는 따로 검토가 필요하다. 꼭 해보고 싶었지만, 시간 부족으로 이부분은 생략한다.
4장에서는 커스텀하게 몽고DB 에 저장을 해보겠다. 아주 커스텀한 방법이라서 손이 많이 간다. 그래서 이 방법이 완벽하게 좋다고 생각하지는 않는다. 개인적으로는 2장에서 검토한 InfluxDB 연동이 더 쉽다. 그럼에도 불구하고 커스텀하게 운영해 보고 싶다면 MongoDB 도 좋은 선택지가 될 것이다. 추가로 MQ를 연동하여 시스템 간의 디펜던시를 줄일 수 있도록 구성해봤다.
참고로 이 작업은 어디에서 듣도보지도 못한 신선한 연동이다. 허접한 필자의 개인적인 생각이라는 점을 참고하길 바란다. 그러니, 쌩뚱맞은 MongoDB연동 은 따라하진 말자. 물론 이 글을 여기까지 읽은 사람이 없겠지만, 혹시라도 읽은 사람이 있다면 감사하다.
InfluxDB 와 같은 시계열 DB 는 MicroMeter 데이터를 저장하기에 아주 적합하다. 시계열 DB가 아니지만 MongoDB 역시 좋은 선택지가 될 것이다. NoSQL 이고, DocumentDB라서 Metrics데이터를 저장하기 좋다. 또한, 스케일아웃하기 좋고, 대중적으로 많이 사용한다는 점 등 다양한 장점이 있다. 물론 단점도 많을 것이다. 이 글은, 좋고 나쁨을 다루지는 않겠다. 참고로 개인적인 생각으로는 실무에서는 MongoDB 에 Actuator 데이터를 저장하는 것은 적합하지 않은 것 같다는 필자의 의견이다. 그럼에도 불구하고 재미삼아, 쉬는 날 집에서 띵까띵까 놀면서 공부할 겸 그냥 한번 해본다.
시스템 구성도는 아래와 같다.
클라이언트 마이크로서비스 : 스프링 부트 환경으로 각자 Actuator Metrics를 메시지 브로커에 전송한다.
메시지 브로커 : 클라이언트 마이크로서비스로 부터 전달받은 Metrics 를 콜렉터에 전달한다.
콜렉터 : 메시지 브로커의 특정 큐 를 리스닝 하고 있다가, Metrics 데이터를 수신 받는다. 수신받은 데이터를 NoSQL에 저장한다.
콜렉터와 각각의 마이크로서비스는 스프링 부트 2.X 디펜던시로 구성된다. MQ 브로커는 필자가 주로 사용하는 RabbitMQ 를 사용하였다. 참고로 카프카를 연동하는 것도 좋다. 개인적으로는 Log 데이터 전달은 카프카가 조금 더 장점이 많다는 생각이다. 반대로 시스템 통합의 브로커 로는 무조건 RabbitMQ가 좋다. 참고로 RabbitMQ 설치 등 관련 인프라에 대한 내용은 이 글에서는 생략한다.
필자의 기억이 맞다면 부트 1.X에서는 Actuator 디펜던시만 추가하면 왠만한 조회가 되는 것으로 알고 있지만, 부트 2.X 에서는 그렇지 않다. 필요한 데이터에 한해서, 조회 가능하도록 설정을 따로 해줘야 한다.
management.security.enabled=false
management.health.diskspace.enabled=true
management.endpoint.httptrace.enabled=true
management.endpoints.beans.enabled=false
management.endpoints.web.exposure.include=*
//등등.. 상세 설정 생략
스프링 부트에서 io.micrometer.core.instrument.MeterRegistry 를 주입하여, MeterRegistry 빈에서 Metrics 데이터를 조회할 수 있다.
private final MeterRegistry registry;
//생략
for (Meter meter : registry.getMeters()) {
result.put(meter.getId().getName(), meter.measure());
}
//무식하게 반복문 돌려서...
registry.getMeters() : 모든 Metrics 데이터 조회
Meter.getId() : 기본 정보 조회(Id, 이름 등)
Meter.measure() : 저장 데이터 조회
필자는 RabbitMQ 를 메시지 브로커로 연동할 것이고, Spring Cloud Stream 를 활용할 것이다. 기본적인 스프링 클라우드 스트림 디펜던시를 추가한다. 그리고 프로퍼티 설정을 아래와 같이 한다. 똑같이 할 필요는 없다. 알아서 시스템 환경에 맞게 잘 하면 된다. 잘...
spring.cloud.stream.bindings.SINK-HEALTH.destination=apm
spring.cloud.stream.bindings.SINK-HEALTH.group=health
spring.cloud.stream.rabbit.bindings.SINK-HEALTH.consumer.bindingRoutingKey=system.*
spring.cloud.stream.rabbit.bindings.SINK-HEALTH.consumer.exchangeType=topic
spring.cloud.stream.rabbit.bindings.SINK-HEALTH.consumer.durableSubscription=true
spring.cloud.stream.rabbit.bindings.output.producer.routingKeyExpression='system.public'
spring.cloud.stream.bindings.SOURCE-HEALTH.destination=apm
spring.cloud.stream.rabbit.bindings.SOURCE-HEALTH.producer.routingKeyExpression='system.public'
spring.rabbitmq.host=
spring.rabbitmq.username=
spring.rabbitmq.password=
간단하게 요약하자면, RabbitMQ 의 Exchange 는 apm 이라는 이름으로 정했다. Exchange 타입은 토픽 타입이다. 영속성 설정을 true 로 하였는데, 클라이언트에서 커넥션이 끊겨도 QUeue 는 살아 있을 것이다. 채널 이름은 SINK-HEALTH 로 정하였다. RabbitMQ 프로퍼티 설정에 대한 내용은 상세하게 다루지 않겠다. 필자도 아직까지 잘 모르겠다.
설명 생략
@Component
@RequiredArgsConstructor
@EnableBinding(CustomSource.class)
public class MessagePublishComponent {
@Autowired
private CustomSource customSource;
public void sendMessageByte(String info) {
customSource.output().send(MessageBuilder.withPayload(SerializationUtils.serialize(info)).build());
}
}
콜렉터도 마찬가지로 스프링 부트 및 스크링 클라우드를 연동하여 간단하게 발로 코딩해서 구현했다.
@EnableBinding(ActuatorListener.Sink.class)
@RequiredArgsConstructor
public class ActuatorListener {
private final CustomMetricsUseCase customMetricsUseCase;
@StreamListener("sink-merge")
public void subscribe2(Message<?> message) {
ObjectMapper objectMapper = new ObjectMapper();
try {
CustomMetrics customMetrics = objectMapper.readValue((String)SerializationUtils.deserialize((byte[])message.getPayload()), CustomMetrics.class);
customMetricsUseCase.save(customMetrics);
} catch (IOException e) {
e.printStackTrace();
}
}
public interface Sink {
@Input("sink-merge")
SubscribableChannel HealthListener();
}
}
필자가 여기서 좀 해매고 있는 점은, 역직렬화 구문이다. 직렬화 없이 메시지 전송이 가능할 것 같은데 잘 모르겠다. 필자가 해보고 싶은 로직은 public void subscribe2(Metrics pojo) 형태로 MetricsPojo 데이터 포맷에 맞게 데이터를 주고 받을 수 있을 것 같은데... 아직은 잘 안되고 있다. 필자가 잘 못해서.. 일단 대충 Message<?> 로 데이터 전달 받아서 테스트 하였다. 와일드 카드 남용은 좋지 않다.
MongoDB 에 저장하기 위해서, Spring Data MongoDB 디펜던시를 사용하였다. 일단, 데이터 매핑을 위해서 커스텀한 Metrics 클래스를 정의하였다.
@Document(collection = "metrics")
@JsonIgnoreProperties(ignoreUnknown = true)
@Data
public class CustomMetrics {
@Id
private String id;
@DateTimeFormat(iso = ISO.DATE_TIME)
@JsonProperty("createdate")
private Date createdDate;
@JsonProperty("application.name")
private Object applicationName;
@JsonProperty("jvm.memory.max")
private Object jvmMemMax;
@JsonProperty("http.server.requests")
private Object httpServerRequests;
@JsonProperty("jvm.gc.memory.promoted")
private Object jvmGcMemoryPromoted;
//생략
추가로 기본적인 MongoRepository 를 만들었따.
public interface CustomMetricsRepository extends MongoRepository<CustomMetrics, Integer> {
}
Metrics 데이터를 MongoDB에 저장하는 것까지 성공하였다. 아주 간단하다. MongoDB에 저장하는 것은 아주 큰 의미는 없어서 대충 마무리 한다.
기타 추가의견을 정리한다.
spring Boot Admin 오픈소스를 활용하면 모니터링 관리를 쉽게 할 수 있다. 해당 오픈소스는 Pivotal 정식 프로젝트는 아니다. 필자가 잠시 해본 결과 Spring Boot Actuator 뿐만 아니라, Spring Cloud Eureka, Ribbon 등 스프링 클라우드 라이브러리를 적극적으로 활용한 것으로 추측된다. 괜찮은 오픈소스이다. 하지만, 실무에서 사용할 수 있는지는 섬세하게 검토가 필요하다.
https://github.com/codecentric/spring-boot-admin
Spring Boot Actuator Metrics 데이터를 외부 스토리지에 저장하는 것에 대해서 검토하였다. 추후에 기회가 된다면 실무에 적용할 기회가 생기면 그 때 다시 글을 남기겠다. 참고로, 당장 적용은 어렵겠다는 생각이다. 실무에서 이미 APM 모니터링 툴을 사용중이라면, 굳이 Actuator 디펜던시로 다 바꿀 필요가 없어 보인다.
https://www.slideshare.net/mobile/makingx/spring-boot-actuator-20-micrometer