일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- 자바의 정석 기초편 ch3
- 스프링 mvc1 - 스프링 mvc
- @Aspect
- 스프링 mvc2 - 타임리프
- 스프링 mvc1 - 서블릿
- 자바의 정석 기초편 ch5
- 타임리프 - 기본기능
- 2024 정보처리기사 시나공 필기
- 자바의 정석 기초편 ch9
- 스프링 mvc2 - 로그인 처리
- jpa - 객체지향 쿼리 언어
- 자바의 정석 기초편 ch1
- 자바의 정석 기초편 ch6
- 자바의 정석 기초편 ch2
- 2024 정보처리기사 수제비 실기
- 자바의 정석 기초편 ch11
- 스프링 입문(무료)
- 스프링 db2 - 데이터 접근 기술
- 자바의 정석 기초편 ch7
- jpa 활용2 - api 개발 고급
- 자바의 정석 기초편 ch4
- 자바의 정석 기초편 ch14
- 자바의 정석 기초편 ch13
- 코드로 시작하는 자바 첫걸음
- 스프링 db1 - 스프링과 문제 해결
- 자바의 정석 기초편 ch8
- 스프링 고급 - 스프링 aop
- 자바의 정석 기초편 ch12
- 게시글 목록 api
- 스프링 mvc2 - 검증
- Today
- Total
나구리의 개발공부기록
모니터링 메트릭 활용, 메트릭 등록(예제 만들기/카운터/@Counted/Timer/@timed/게이지), 실무 모니터링 환경 구성 팁 본문
모니터링 메트릭 활용, 메트릭 등록(예제 만들기/카운터/@Counted/Timer/@timed/게이지), 실무 모니터링 환경 구성 팁
소소한나구리 2024. 12. 9. 15:33출처 : 인프런 - 스프링 부트 - 핵심 원리와 활용(유료) / 김영한님
유료 강의이므로 정리에 초점을 두고 코드는 일부만 인용
1. 메트릭 등록 - 예제 만들기
1) 예제 만들기
(1) 비즈니스 메트릭
- CPU 사용량, 메모리 사용량, 톰캣 쓰레드, DB 커넥션 풀과 같이 공통으로 사용되는 기술 메트릭들은 이미 등록이 되어있으므로 이를 사용하여 대시보드를 구성하고 모니터링 하면됨
- 여기서 더 나아가서 주문수, 취소수, 재고 수량과같은 메트릭 처럼 비즈니스에 관련된 메트릭을 모니터링 하고 싶을 때에는 공통으로 만들 수 있는 부분은 아님
- 이런 메트릭들은 시스템을 운영하는데 상당히 도움이 되는데, 취소수가 갑자기 급증하거나 재고 수량이 임계치 이상으로 쌓이거나 하는 부분들은 기술적인 메트릭으로 확인할 수 없는 비즈니스 문제를 빠르게 파악하는데 도움을 줌
- 예를 들어 택배회사에 문제가 생겨서 고객들이 많이 기다리다가 지쳐서 취소수가 증가해도 CPU, 메모리 사용량 같은 시스템 메트릭에는 아무런 문제가 발생하지 않지만 비즈니스 메트릭이 있다면 이런 문제를 빠르게 인지할 수 있음
- 비즈니스에 관한 부분은 각 비즈니스 마다 구현이 다르며 따라서 비즈니스 메트릭은 직접 등록하고 확인해야 함
(2) 메트릭 예제
- 주문수, 취소수: 상품 주문시 주문수가 증가하고 상품을 취소해도 주문 수는 유지하지만 취소수를 증가
- 재고 수량: 상품 주문 시에는 재고 수량 감소, 상품 취소 시에는 재고 수량이 증가, 재고 물량이 들어오면 재고 수량이 증가
- 주문수, 취소수는 계속 증가하므로 카운터를 활용
- 재고 수량은 증가하거나 감소하므로 게이지를 사용
(3) OrderService - 인터페이스
- order 패키지를 생성 후 작성
- 주문, 취소, 재고 수량을 확인할 수 있는 주문 서비스 인터페이스
- AtomicInteger: 멀티 쓰레드 상황에서 값을 안전하게 처리할 수 있는 Integer
package hello.order;
public interface OrderService {
void order();
void cancel();
AtomicInteger getStock();
}
(4) OrderServiceV0
- v0 패키지를 추가후 작성
- new AtomicInteger(100): 초기값을 100으로 설정, 재고 수량이 100부터 시작한다고 가정
- incrementAndGet(): 값을 1증가
- decrementAndGet(): 값을 1감소
package hello.order.v0;
@Slf4j
public class OrderServiceV0 implements OrderService {
private AtomicInteger stock = new AtomicInteger(100);
@Override
public void order() {
log.info("주문");
stock.decrementAndGet(); // 값이 1감소
}
@Override
public void cancel() {
log.info("취소");
stock.incrementAndGet(); // 값이 1증가
}
@Override
public AtomicInteger getStock() {
return stock;
}
}
(5) OrderConfigV0
- OrderServiceV0 수동 빈 등록
package hello.order.v0;
@Configuration
public class OrderConfigV0 {
@Bean
OrderService orderService() {
return new OrderServiceV0();
}
}
(6) OrderController
- 주문, 취소, 재고 수량을 확인하는 컨트롤러
- 주문, 취소는 POST로 해야하지만 예제의 단순함을 위해서 GET을 사용함
package hello.controller;
@Slf4j
@RestController
public class OrderController {
private final OrderService orderService;
public OrderController(OrderService orderService) {
this.orderService = orderService;
}
// order(), cancel()은 POST로 해야하지만 예제의 단순함을 위하여 GET을 사용했음
@GetMapping("/order")
public String order() {
log.info("order");
orderService.order();
return "order";
}
@GetMapping("/cancel")
public String cancel() {
log.info("cancel");
orderService.cancel();
return "cancel";
}
@GetMapping("/stock")
public int stock() {
log.info("stock");
return orderService.getStock().get();
}
}
(7) ActuatorApplication 수정 및 실행
- 설정을 계속 바꿔가며 실습하기위해 @Import와 컴포넌트 스캔 위치를 hello.controller로 지정하는 옵션을 추가
- 해당 옵션을 설정하지 않으면 모든 설정이 컴포넌트 스캔 대상이 되어 모두 스프링 빈으로 등록됨
- 애플리케이션을 실행 후 매핑된 url로 접속해보면 모두 정상 동작하는 것을 확인할 수 있음
@Import(OrderConfigV0.class)
@SpringBootApplication(scanBasePackages = "hello.controller")
public class ActuatorApplication {
// ... 기존 코드 생략
}
2. 메트릭 등록 - 카운터
1) 카운터
(1) MeterRegistry
- 마이크로미터 기능을 제공하는 핵심 컴포넌트로 이를 활용하여 주문수(주문 요청 수), 취소수(취소 요청 수)를 대상으로 카운터 메트릭을 등록
- 스프링을 통해서 주입 받아서 사용하고 이곳을 통해서 카운터, 게이지 등을 등록할 수 있음
(2) Counter(카운터)
- 설명
- 단조롭게 증가하는 단일 누적 측정항목
- 즉, 단일 값(변수가 하나)이고 보통 하나씩 증가하며 누적이므로 전체 값을 포함(total)함
- 프로메테우스에서는 일반적으로 카운터의 이름 마지막에 _total을 붙여서 my_order_total과 같이 표현함
- 값을 증가하거나 0으로 초기화 하는 것만 가능
- 마이크로미터에서 값을 감소하는 기능도 지원하지만 목적에 맞지 않음
- 예) HTTP 요청수
(3) OrderServiceV1
- v1패키지를 생성 후 작성
- Counter.builder(name): name에 메트릭 이름을 지정하여 카운터를 생성
- tag: 프로메테우스에서 필터할 수 있는 레이블로 사용할 수 있음
- 주문과 취소는 메트릭 이름은 같고 tag를 통해서 구분됨
- register(registry): 만든 카운터를 MeterRegistry에 등록, 이렇게 등록해야 실제 동작함
- increment(): 카운터의 값을 하나 증가
package hello.order.v1;
@Slf4j
public class OrderServiceV1 implements OrderService {
private final MeterRegistry register;
private AtomicInteger stock = new AtomicInteger(100);
public OrderServiceV1(MeterRegistry meterRegistry) {
this.register = meterRegistry;
}
@Override
public void order() {
log.info("주문");
stock.decrementAndGet(); // 값이 1감소
Counter counter = Counter.builder("my.order") // 카운터 이름
.tag("class", this.getClass().getName()) // 태그
.tag("method", "order")
.description("order")
.register(register);
counter.increment(); // 메트릭 값이 1이 증가
}
@Override
public void cancel() {
log.info("취소");
stock.incrementAndGet(); // 값이 1증가
Counter counter = Counter.builder("my.order") // 카운터 이름은 같음
.tag("class", this.getClass().getName())
.tag("method", "cancel") // 그러나 메서드 이름으로 구분이 가능함
.description("cancel")
.register(register);
counter.increment();
}
@Override
public AtomicInteger getStock() {
return stock;
}
}
(3) OrderConfigV1
- OrderServiceV1을 스프링 빈으로 등록
- MeterRegistry가 필요함
package hello.order.v1;
@Configuration
public class OrderConfigV1 {
@Bean
OrderService orderService(MeterRegistry registry) {
return new OrderServiceV1(registry);
}
}
(4) ActuatorApplication - 수정
- @Import를 OrderConfigV1으로 변경
(5) 액츄에이터 메트릭 확인
- 애플리케이션 실행 후 /order, /cancel 에 접속해보면 주문과 취소가 정상적으로 동작함
- 주문과 취소를 한번씩 실행 한 후에 localhost:8080/actuator/metrics/my.order로 접속해보면 등록한 my.order가 있는 것을 확인할 수 있음
- 메트릭이 method로 구분 되어 /my.order?tag=method:cancel 처럼 각 메트릭만 확인하는 것도 정상적으로 동작함
(6) 프로메테우스 포맷 메트릭 확인
- 프로메테우스를 실행 후 localhost:8080/actuator/prometheus로 접속하여 확인해보면 메트릭 이름이 my.order에서 my_order_total로 변경된 것을 확인할 수 있음
- 프로메테우스는 .을 _로 변경함
- 카운터는 마지막에 _total을 붙임(프로메테우스의 관례)
- method라는 tag(필터)를 기준으로 데이터가 분류되어있음
2) 그라파나 등록
- 이전에 만들어 둔 hello 대시보드에 주문수, 취소수 그래프를 추가
(1) Panel options
- Title: 주문수
(2) PromQL
- increase(my_order_total{method="order"}[1m]), Legend: {{method}}: 주문 수 카운터 등록
- increase(my_order_total{method="cancel"}[1m]), Legend: {{method}}: 취소 수 카운터 등록
**참고
- 카운터는 계속 증가하기 때문에 특정 시간에 얼마나 증가했는지 확인하려면 increase(), rate()같은 함수와 함께 사용하는 것이 좋음
3. 메트릭 등록 - @Counted
1) @Counted
(1) OrderServiceV1의 단점
- OrderServiceV1의 가장 큰 담점은 메트릭을 관리하는 로직이 핵심 비즈니스 개발 로직에 포함되어있어 유지보수 시 어려움이 생긴다는 것인데, 이부분을 스프링 AOP로 해결할 수 있음
- 직접 필요한 AOP를 만들어서 적용해도 되지만 마이크로미터는 이런 상황에 맞추어 필요한 AOp 구성요소를 이미 다 만들어 두었음
(2) OrderServiceV2
- v2패키지 생성 후 작성
- @Counted 애노테이션을 측정을 원하는 메서드에 적용 후 메트릭 이름을 지정하면 됨
- 이렇게 사용하면 tag에 method를 기준으로 분류해서 적용함
package hello.order.v2;
@Slf4j
public class OrderServiceV2 implements OrderService {
private AtomicInteger stock = new AtomicInteger(100);
@Counted("my.order")
@Override
public void order() {
log.info("주문");
stock.decrementAndGet();
}
@Counted("my.order")
@Override
public void cancel() {
log.info("취소");
stock.incrementAndGet();
}
@Override
public AtomicInteger getStock() {
return stock;
}
}
(3) OrderConfigV2
- OrderServiceV2와 CountedAspect를 스프링 빈으로 등록
- CountedAspect를 등록하면 @Counted를 인지해서 Counter를 사용하는 AOP를 적용됨
- CountedAspect를 빈으로 등록하지 않으면 @Counted관련 AOP가 동작하지 않음
package hello.order.v2;
@Configuration
public class OrderConfigV2 {
@Bean
public OrderService orderService() {
return new OrderServiceV2();
}
@Bean
public CountedAspect countedAspect(MeterRegistry registry) {
return new CountedAspect(registry);
}
}
(4) ActuatorApplication - 수정 및 실행
- @Import를 OrderConfigV2로 변경 후 애플리케이션을 실행
- /order와 /cancel에 접속하여 메트릭을 증가시킨 후 액츄에이터 메트릭과, 프로메테우스 포맷 메트릭을 확인해보면 정상적으로 모두 등록되있는 것을 확인할 수 있음
- 메트릭 이름과 tag가 기존과 같기 때문에 기존에 만들어둔 그라파나 대시보드에서도 확인할 수 있음
4. 메트릭 등록 - Timer
1) Timer
(1) 시간 측정 도구
- 시간을 측정하는데 사용되는 특별한 메트릭 측정 도구이며 카운터와 유사하지만 Timer를 사용하면 실행 시간도 함께 측정할 수 있음
- seconds_count: 누적 실행 수 - 카운터
- seconds_sum: 실행 시간의 합 - sum
- seconds_max: 실행 최대 시간(가장 오래걸린 실행 시간) - 게이지
- Timer는 위와 같은 내용을 한번에 측정해주며 내부에 타임 윈도우라는 개념이 있어서 1-3분 마다 최대 실행 시간이 다시 계산됨
(2) OrderServiceV3
- v3 패키지 생성 후 작성
- Timer.builder(name): name에 메트릭이름을 지정하여 타이머를 생성
- tag, register(registry): 카운터와 기능 동일
- timer.record(): 타이머를 사용, 그 안에 시간을 측정할 내용을 함수로 포함하면 됨
- 걸리는 시간을 확인하기 위해 주문은 0.5초, 취소는 0.2초 대기하도록 하고 추가로 가장 오래 걸린시간을 확인하기 위해서 sleep()메서드에 최대 0.2초를 랜덤하게 추가하도록 구성함
- 모두 0.5초, 0.2초로 같으면 가장 오래걸린 시간을 확인할 수 없기 때문임
package hello.order.v3;
@Slf4j
public class OrderServiceV3 implements OrderService {
private final MeterRegistry registry;
private AtomicInteger stock = new AtomicInteger(100);
public OrderServiceV3(MeterRegistry registry) {
this.registry = registry;
}
@Override
public void order() {
Timer timer = Timer.builder("my.order")
.tag("class", this.getClass().getName())
.tag("method", "order")
.description("order")
.register(registry);
timer.record(() -> {
log.info("주문");
stock.decrementAndGet();
sleep(500);
});
}
@Override
public void cancel() {
Timer timer = Timer.builder("my.order")
.tag("class", this.getClass().getName())
.tag("method", "cancel")
.description("cancel")
.register(registry);
timer.record(() -> {
log.info("취소");
stock.incrementAndGet();
sleep(200);
});
}
@Override
public AtomicInteger getStock() {
return stock;
}
private void sleep(int millis) {
try {
Thread.sleep(millis + new Random().nextInt(200));
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
(3) OrderConfigV3
- MeterRegistry를 인수로 하여 OrderServiceV3를 스프링 빈으로 등록
package hello.order.v3;
@Configuration
public class OrderConfigV3 {
@Bean
OrderService orderService(MeterRegistry registry) {
return new OrderServiceV3(registry);
}
}
(4) ActuatorApplication - 수정 및 실행
- @Import를 OrderConfigV3로 수정 후 동일하게 /order, /cancel을 여러번 호출하여 액츄에이터 메트릭과 프로메테우스 포맷 메트릭을 확인해보면 정상적으로 등록된 것을 확인할 수 있음
- 액츄에이터 메트릭을 확인해보면 COUNT(누적 실행 수)뿐만 아니라 TOTAL_TIME(실행 시간의 합), MAX(최대 실행 시간)까지 3가지 측정 항목이 생겨있음
- 프로메테우스 포맷 메트릭에서도 seconds_count, seconds_sum, seconds_max가 각 메서드별로 생성되어있는 것을 확인할 수 있으며 해당 값으로 최대실행시간 / 누적 실행 수로 계산하여 평균 실행 시간도 구할 수 있음
2) 그라파나 등록 - 주문수 V3
- 메트릭이 변경되었기 때문에 쿼리를 수정해야함
(1) 패널 옵션
- Title: 주문수 v3
(2) PromQL
- increase(my_order_seconds_count{method="order"}[1m]), 범례는 동일
- increase(my_order_seconds_count{method="cancel"}[1m]), 범례는 동일
3) 그라파나 등록 - 최대 실행시간
(1) 패널 옵션
- Title: 최대 실행시간
(2) PromQL
- my_order_seconds_max, Legend: {{method}}
4) 그라파나 등록 - 평균 실행시간
(1) 패널 옵션
- Title: 평균 실행 시간(1분당)
(2) PromQL
- increase(my_order_seconds_sum[1m]) / increase(my_order_seconds_count[1m]), Legend: {{method}}
** 참고
- 실습 버전인 Grafana v11.3.1 버전에서는 increase()함수 사용 시 Range Vector(시간 범위)를 입력해 주지 않으면 bad_data: invalid parameter "query": 1:10: parse error: expected type range vector in call to function "increase", got instant vector 에러가 발생하므로 시간값을 꼭 입력해 주어야 함
- 강의에서 진행하는 버전은 시간값이 없어도 진행이 되었으나 어차피 유의미한 데이터를 뽑으려면 시간 범위를 입력해 주는 것이 좋음
5. 메트릭 등록 - @Timed
1) @Timed
- 카운터와 마찬가지로 Timer도 @Timed 애노테이션을 통해 AOP를 적용할 수 있음
(1) OrderServiceV4
- v4 패키지를 생성 후 작성
- @Timed("my.order"): 클래스나 메서드에 적용할 수 있으며, 클래스에 적용하면 해당 타입의 모든 public 메서드에 타이머가 적용됨
- 클래스 레벨에 적용했으므로 getStock()에도 타이머가 적용됨
- value = "my.order"이지만 자바 문법상 value는 생략 가능함
package hello.order.v4;
@Timed("my.order")
@Slf4j
public class OrderServiceV4 implements OrderService {
private AtomicInteger stock = new AtomicInteger(100);
@Override
public void order() {
log.info("주문");
stock.decrementAndGet();
sleep(500);
}
@Override
public void cancel() {
log.info("취소");
stock.incrementAndGet();
sleep(200);
}
@Override
public AtomicInteger getStock() {
return stock;
}
private void sleep(int millis) {
try {
Thread.sleep(millis + new Random().nextInt(200));
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
(2) OrderConfigV4
- TimedAspect를 스프링 빈으로 등록해야 @Timed에 AOP가 적용됨
package hello.order.v4;
@Configuration
public class OrderConfigV4 {
@Bean
OrderService orderService() {
return new OrderServiceV4();
}
@Bean
public TimedAspect timedAspect (MeterRegistry registry) {
return new TimedAspect(registry);
}
}
(3) ActuatorApplication - 수정 및 실행
- @Import를 OrderConfigV4로 변경 후 실행 후 /order, /cancel로 요청을 보내보면 액츄에이터 메트릭과 프로메테우스 메트릭에서기존과 동일한 메트릭 값들을 확인할 수 있으며 exception이라는 tag가 추가된 것도 확인할 수 있음
- 기존에 등록한 그라파나 대시보드에서도 값들을 확인할 수 있음
6. 메트릭 등록 - 게이지
1) 게이지 등록
(1) Gauge(게이지)
- 설명
- 임의로 오르내릴 수 있는 단일 숫자 값을 나타내는 메트릭
- 값의 현재 상태를 보는데 사용
- 값이 증가하거나 감소할 수 있음
- 예) 차량의 속도, CPU 사용량, 메모리 사용량
- 여러가지 고민 요소가 더 있지만 값이 감소할 수 있는가를 고민해보면 카운터와 게이지를 구분할 때 도움이 됨
(2) StockConfigV1
- order하위에 gauge 패키지를 생성 후 작성
- Gauge.builder()를 통해 my.stock이라는 이름으로 게이지를 등록
- 카운터는 메트릭의 값이 증가되거나 감소하는 처리가 필요하지만 게이지는 요청이 왔을 때 현재 상태 그대로를 반환하면됨
- 게이지를 만들 때 함수를 전달하여 외부에서 메트릭을 확인할 때 마다 호출되고 이 함수의 반환 값이 게이지의 값임
- .register()로 메트릭을 등록
package hello.order.gauge;
@Configuration
public class StockConfigV1 {
@Bean
public MyStockMetric myStockMetric(OrderService orderService, MeterRegistry registry) {
return new MyStockMetric(orderService, registry);
}
@Slf4j
static class MyStockMetric {
private OrderService orderService;
private MeterRegistry meterRegistry;
public MyStockMetric(OrderService orderService, MeterRegistry registry) {
this.orderService = orderService;
this.meterRegistry = registry;
}
@PostConstruct
public void init() {
Gauge.builder("my.stock", orderService, service -> {
log.info("stock gauge call");
return service.getStock().get();
}).register(meterRegistry);
}
}
}
(2) ActuatorApplication 수정
- OrderConfigV4와 StockConfigV1을 함께 등록하도록 변경
@Import({OrderConfigV4.class, StockConfigV1.class})
@SpringBootApplication(scanBasePackages = "hello.controller")
public class ActuatorApplication {
// 기존 코드 생략
}
(3) 실행 결과
- 애플리케이션을 실행하면 stock gauge call 로그가 주기적으로 남는 것을 확인할 수 있는데 프로메테우스가 이 경로를 1초에 한번씩 호출하도록 설정되어있기 때문임
- 즉 게이지를 확인하는 함수는 외부에서 메트릭을 확인할 때 호출되고 프로메테우스가 localhost:8080/actuator/prometheus 경로를 통해 주기적으로 메트릭을 확인함
- 프로메테우스를 종료해보면 해당 함수가 호출되지 않는 것을 확인할 수 있으며 메트릭 확인 경로를 직접 호출하면 해당 함수가 호출됨
- 카운터와 다르게 게이지를 무언가를 누적할 필요도 없고 딱 현재 시점의 메트릭 값을 보여주면 되기 때문에 측정 시점에 현재 값을 반환함
- 액츄에이터 메트릭과 프로메테우스 메트릭을 확인해보면 현재 값을 그대로 보여주기만 하기 때문에 단순함
2) 그라파나 등록 - 재고
(1) 패널 옵션
- Title: 재고
(2) PromQL
- my_stock, Legend: {{__name__}}
3) 게이지 단순하게 등록
(1) StockConfigV2
- MeterBinder 타입을 바로 반환하면 @Bean을 등록하는코드 한번으로 게이지를 기존보다 간편하게 등록할 수 있음
package hello.order.gauge;
@Slf4j
@Configuration
public class StockConfigV2 {
@Bean
public MeterBinder stockSize(OrderService orderService) {
return registry -> Gauge.builder("my.stock", orderService, service -> {
log.info("stock gauge call");
return service.getStock().get();
}).register(registry);
}
}
(2) ActuatorApplication 수정 및 실행
- @Import의 StockConfig를 V2로 변경하여 실행해보면 V1과 마찬가지로 모든 요청의 메트릭이 정상적으로 반영되어 최종적으로 그라파나에도 그래프로 출력되는 것을 확인할 수 있음
3) 정리
(1) Micrometer 사용법 이해
- 메트릭은 100% 정확한 숫자를 보는데 사용하는 것이 아니기 때문에 약간의 오차를 감안하고 실시간으로 대략의 데이터를 보는 목적으로 사용함
- 마이크로미터 핵심 기능: Counter, Gauge, Timer, Tags
(2) MeterRegistry
- 마이크로미터 기능을 제공하는 핵심 컴포넌트이며 스프링을 통해 주입받아서 사용하고 카운터, 게이지 등을 등록함
(3) Counter
- 단조롭게 증가하는 단일 누적 측정 항목
- 단일 값이며 보통 하나씩 증가함
- 누적이므로 전체 값을 포함하며 프로메테우스에서는 관례상 이름 마지막에 _total을 붙임
(4) Gauge
- 임의로 오르내릴 수 있는 단일 숫자 값
- 값의 현재 상태를 보는데 사용하며 값이 증가하거나 감소할 수 있음
(5) Timer
- 시간을 측정하는데 사용
- 카운터와 유사하며 실행 시간도 함께 측정할 수 있음
(6) Tag, 레이블
- Tag를 사용하면 데이터를 나누어서 확인할 수 있음
- Tag는 카디널리티가 낮으면서 그룹화 할 수 있는 단위에 사용해야하며 카디널리티가 높으면 안됨
- 카디널리티가 낮은 예) 성별, 주문 상태 결제 수단[신용카드, 현금] 등등
- 카디널리티가 높은 예) 주문번호, PK 같은 종류 등
7. 실무 모니터링 환경 구성 팁
1) 모니터링 3단계
- 대시보드
- 애플리케이션 추적 - 핀포인트
- 로그
(1) 대시보드
- 전체를 한눈에 볼 수 있는 가장 높은 뷰 즉, 전체를 조망할 수 있는 모니터링 체제
- 제품: 마이크로미터, 프로메테우스, 그라파나 등등
- 모니터링 대상: 시스템 메트릭(CPU, 메모리 등), 애플리케이션 메트릭(톰캣 쓰레드 풀, DB 커넥션 풀 애플리케이션 호출 수 등)비즈니스 메트릭(주문수, 취소수 등)
(2) 애플리케이션 추적
- 주로 각각의 HTTP 요청을 추적하고 일부는 마이크로서비스 환경에서 분산 추적
- 제품: 핀포인트(오픈소스, 네이버 제작), 스카우트(오픈소스), 와탭(상용), 제니퍼(상용)
- 핀포인트 깃허브
- 위 제품 중에서도 핀포인트를 사용하는 것을 추천한다고 하며 장애가 났을때 상당히 많은 부분을 핀포인트로 문제를 찾았다고 함
- 대시보드와 핀포인트를 동시에 사용하는 것을 권장하지만 만약 상황이 안되서 하나만 써야한다면 핀포인트만 쓰는 것을 추천한다고 함
- 제일 먼저 할일이 핀포인트를 깔아야 한다고 할 정도로 웬만한 상용 툴보다 좋다고함
(3) 로그
- 가장 자세한 추적, 원하는데로 커스텀 가능함
- 같은 HTTP 요청을 묶어서 확인할 수 있는 방법이 중요함(UUID같은 것으로 동일한 요청들을 구분)
- 위의 방법을 자동으로 적용해주는 MDC를 검색후 적용 하는 것을 권장함
- 파일로 직접 로그를 남기는 경우: 일반 로그와 에러 로그는 파일을 구분해서 남긴 후 에러 로그만 확인해서 문제를 바로 정리할 수 있도록 해야함
- 클라우드에 로그를 저장하는 경우: 검색이 잘 되도록 구분해야함
(4) 모니터링 정리
- 각각의 용도가 다름
- 관찰을 할 때는 전체를 보고 점점 좁게 관찰해야함
- 마이크로 서비스 분산 모니터링도 가능하고 대용량 트래픽에 대응되고 설치도 쉬운 핀포인트는 꼭 써보는 것을 권장한다고 함
(5) 알람
- 모니터링 툴에서 일정 이상 수치가 넘어가면 알람을 받을 수 있도록 슬랙(메신저), 문자 등을 연동
- 알람은 경고, 심각 2가지 종류로 꼭 구분해서 관리해야함
- 경고는 하루 1번 정도 사람이 직접 확인해도 되는 수준
- 심각은 즉시 확인할 수 있도록 슬랙(권장)이나 문자, 전화 등을 연동해야 함
- 예시:
- 디스크 사용량 70% -> 경고
- 디스크 사용량 80% -> 심각 - 처음엔 잘 나누기가 어렵지만 경고와 심각을 잘 나누어서 무분별한 알람이 계속 오는 것을 방지해야 삶에 방해가 되지 않을 수 있다...
- 거짓 알람은 바로바로 처리해야하는데 과거에 경고나 심각하다고 판단되는 수준으로 알람을 처리해 두었다가 알람을 받아보니 괜찮다고 판단되어 해당 알람을 처리하지않고 방치하고 있으면, 이후에 알람 자체를 무시하다가 진짜 심각한 알람을 놓치게 될 수 있음