관리 메뉴

나구리의 개발공부기록

모니터링 메트릭 활용, 메트릭 등록(예제 만들기/카운터/@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% -> 심각
  • 처음엔 잘 나누기가 어렵지만 경고와 심각을 잘 나누어서 무분별한 알람이 계속 오는 것을 방지해야 삶에 방해가 되지 않을 수 있다...
  • 거짓 알람은 바로바로 처리해야하는데 과거에 경고나 심각하다고 판단되는 수준으로 알람을 처리해 두었다가 알람을 받아보니 괜찮다고 판단되어 해당 알람을 처리하지않고 방치하고 있으면, 이후에 알람 자체를 무시하다가 진짜 심각한 알람을 놓치게 될 수 있음

'인프런 - 스프링 완전정복 코스 로드맵 > 스프링 부트 - 핵심 원리와 활용' 카테고리의 다른 글

마이크로미터/프로메테우스/그라파나, 마이크로미터 소개, 메트릭 확인하기, 다양한 메트릭, 프로메테우스와 그라파나 소개, 프로메테우스(설치/애플리케이션 설정/ 수집 설정/ 기본 기능/게이지와 카운터), 그라파나(설치 및 연동/대시보드 만들기/공유 대시보드 활용/메트릭을 통한 문제 확인)  (2) 2024.12.08
액츄에이터, 프로덕션 준비 기능이란?, 프로젝트 설정 및 액츄에이터 시작, 엔드 포인트 설정, 다양한 엔드포인트, 헬스 정보, 애플리케이션 정보, 로거, HTTP 요청 응답 기록, 액츄에이터와 보안  (2) 2024.12.06
외부설정과 프로필2, 프로젝트 설정, 외부 설정 사용(Environment/@Value/@ConfigurationProperties(시작/생성자/검증)), YAML, @Profile  (2) 2024.12.06
외부설정과 프로필1, 프로젝트 설정 및 외부 설정이란?, 외부 설정(OS 환경 변수/자바 시스템 속성/커맨드 라인 인수/커맨드 라인 옵션 인수/커맨드 라인 옵션 인수와 스프링 부트/스프링 통합), 설정 데이터(외부 파일/내부 파일 분리/내부 파일 합체), 우선순위(설정 데이터/전체)  (1) 2024.12.05
자동 구성(Auto Configuration), 순수 라이브러리 만들기, 순수 라이브러리 사용하기, 자동 구성 라이브러리 만들기, 자동 구성 라이브러리 사용하기, 자동 구성 이해(스프링 부트의 동작/ImportSelector)  (1) 2024.12.02