Skip to content

Metric Design

pp edited this page Jul 30, 2024 · 14 revisions
Test Monitoring 관련으로 메트릭 디자인하면서 진행했던 내용 공유 

🔎 Metrics

  • 시스템의 성능과 상태를 모니터링하기 위해 수집되는 다양한 유형의 데이터. 이러한 데이터는 수치 형식으로 표현되며, 시스템의 다양한 측면에 대한 정량적 정보를 제공한다.
  • 이런 정보를 수집, 저장, 쿼리 및 알람을 통해 모니터링할 수 있게 해주는 것이 Prometheus 등의 도구.

💊 USE / RED

USE/RED는 메트릭 디자인 방법론의 종류. 
USE는 물리적 하드웨어(서버) 위주의 메트릭 디자인 방법을,
RED는 어플리케이션 레벨의 메트릭 디자인 방법을 제시함.  

📒 USE

For every resource, check utilization, saturation, and errors.
- 모든 자원에 대해서 점유율, 포화율, 오류를 확인하자. 
  • 주로 물리적 자원에 대한 메트릭을 수집하는데 사용되는 기준이라고 이해함.
  • Resource(자원) : 물리적 서버의 모든 기능 구성 요소
  • Utilization(점유율) : 리소스가 서비스에 대하여 바쁘게 사용되는 평균 시간
    -> 점유율이 높다 = 리소스가 처리되지않고 밀려있을 가능성(bottle neck 등)이 있다.
  • Saturation(포화율) : 리소스가 처리하지 못한 여분의 일(extra work)의 정도. 리소스가 처리할 수 있는 한계를 넘어 처리하지 못한 작업의 정도.
  • Errors(오류) : 에러 이벤트의 횟수
  • 세 지표를 사용해서 낮은 레벨의(서버의 물리적 자원 - 네트워크) 모니터링이 가능하다.
    CPU 점유율은 낮은데 포화율이 높다? -> 스레드나 프로세스 분배가 잘못되어 있어서 하나의 CPU 코어에 일이 몰려있다. -> 스레드 풀을 사용해서 하나의 스레드에 몰린 작업을 분배하는 등의 조치를 취할 수 있음.
Pros and cons
  • Pros : 'it solves about 80% of server issues with 5% of the effort', 'it can be applied to systems other than servers'. USE 방법론을 적용하여 디자인한 메트릭은 서버 뿐만 아니라 여러 시스템에 적용 가능하고, 적은 노력을 들여 높은 효율의 성능 개선을 이뤄낼 수 있다.
  • Cons : 다양한 정보를 얻을 수 있지만, MSA 구조에서는 신뢰하기가 힘듬(추상적이기 때문). 문제가 발생하는 것은 캐치 할 수 있지만 low level 메트릭(인프라 레벨의 메트릭)이어서 어느 어플리케이션의 어느 서비스에서 문제가 발생하는지 알 수 없음.

brendangregg

📕 RED

“The USE Method doesn’t really apply to services; it applies to hardware, network disks, things like this,”
“We really wanted a microservices-oriented monitoring philosophy, so we came up with the RED Method.”
“모든 리소스에 대해 요청률(Rate), 오류율(Error), 지속시간(Duration)을 모니터링 하는 것을 강조”
  • USE 방법론이 low level의 메트릭 수집을 강조했다면, RED 방법론은 Application 레벨의 메트릭을 강조
  • Resource : Application 의 Service 를 의미한다고 봐도 됨
  • Rate : 처리율. 어플리케이션이 처리하는 요청의 속도. ex) 초당 HTTP request 수.
  • Error : 오류 수. 요청이 실패하는 비율.ex) HTTP 500 error 비율
  • Duration : 처리 시간. 요청이 처리되는 데 걸리는 시간. ex) HTTP request 시간.
  • RED 방법론은 SLA(Service Level Agreement) 등의 파악에 사용될 수 있음.

The RED Method

📝 Metrics Design

USE/RED 방법론을 참고해서 사용할 시스템에 적합한 메트릭 선정하기

1. 메트릭 디자인 시 고민해본 점

어떤 메트릭을 수집해야 할 건지?

1) 기본적으로 시스템 내의 모든 아키텍쳐는 모니터링 되어야 한다.

  • 기본적으로 해당 아키텍쳐가 제공해주는 메트릭 외에도 아키텍쳐와 application 이 통신하는 부분에서 메트릭 수집하기 (요청 횟수, 요청 시간, 에러 횟수 체크)

2) 서비스 path 별로 메트릭 수집이 가능해야 한다.(어느 서비스 지점에서 문제가 발생하는지 파악하기 위하여)

  • tag를 사용하여 path 별 메트릭을 구분하여 수집한다. (같은 이름을 가지더라도 태그로 메트릭이 구분된다)

3) 전체 횟수와 메소드 요출 횟수 둘 다 수집이 필요하다.

  • 실제 서비스 상태에서 어느 메소드에 트래픽이 어느 정도 몰리는지를 파악하려면 필요하지 않을까?
  • 특정 트래픽이 몰리는 메소드에는 추가적인 아키텍쳐를 붙여서 성능 개선을 해볼 수도 있을 것 같음.

4) http request time 과는 별개로 메소드의 응답 속도도 필요할 것

5) USE 보다는 RED 에 중점을 두고 메트릭을 디자인하는 것이 좋지 않을까?

  • 인프라의 정보를 전해주는 USE 관련 메트릭들은 기본 actuator metrics 등에서 제공해준다.
  • 반면 서비스 레벨의 정보를 전달해주는 메트릭은 별도로 제공해주지 않음 (기껏해야 http request 와 관련된 메트릭들 정도인데, 이것도 요청 전체를 알려줄 뿐이지 각 서비스 레벨에서는 모니터링이 불가능
  • RED 방법론을 사용해서 사용자가 사용 시, 각각의 서비스 별로 요청 횟수(Rate), 에러 횟수 및 발생 지점(Error), 처리 시간(스프링 내부의 코드 문제인지? 연결된 아키텍쳐의 문제인지?)(Duration)을 생각해보고 디자인 하기.

2. 모니터링 대상 시스템

image image

  • Backend App (Spring Actuator)
  • Frontend App (Node Exporter)
  • Nginx (Nginx Exporter)
  • Opensearch (Opensearch Exporter)
  • MySQL (MySQL Exporter)
  • Redis (Redis Exporter)
  • Node: Springboot, Vue.js 등의 동작하는 WorkerNode 모니터링 (Node Exporter)

3. Spring Actuator 기본 제공 메트릭

{
    "names": [
    	# 애플리케이션이 실행되고 Ready 상태가 되기까지 걸린 시간
        "application.ready.time",
        # 애플리케이션이 시작되기 시작한 시간
        "application.started.time",
        # 디스크의 여유 공간
        "disk.free",
        # 디스크의 총 용량
        "disk.total",
        # 현재 활성화된 executor 작업의 수
        "executor.active",
        # 완료된 executor 작업의 수
        "executor.completed",
        # executor 풀의 핵심 크기
        "executor.pool.core",
        # executor 풀의 최대 크기
        "executor.pool.max",
        # executor 풀의 현재 크기
        "executor.pool.size",
        # executor 풀의 큐에 대기 중인 작업의 수
        "executor.queue.remaining",
        # 현재 executor 풀에 대기 중인 작업의 수
        "executor.queued",
        # 총 HTTP 서버 요청의 수
        "http.server.requests",
        # 현재 처리 중인 HTTP 서버 요청의 수
        "http.server.requests.active",
        # JVM 버퍼의 개수.
        "jvm.buffer.count",
        # 사용 중인 JVM 버퍼 메모리 양.
        "jvm.buffer.memory.used",
        # JVM 버퍼의 총 용량.
        "jvm.buffer.total.capacity",
        # 로드된 JVM 클래스의 수
        "jvm.classes.loaded",
        # 언로드된 JVM 클래스의 수
        "jvm.classes.unloaded",
        # JVM에서의 클래스 컴파일 시간
        "jvm.compilation.time",
        # GC(Garbage Collection) 중에 살아있는 객체의 크기
        "jvm.gc.live.data.size",
        # GC 중에 수집될 수 있는 최대 데이터 크기
        "jvm.gc.max.data.size",
        # GC로 할당된 메모리 양
        "jvm.gc.memory.allocated",
        # GC로 프로모션된 메모리 양
        "jvm.gc.memory.promoted",
        # GC 오버헤드.
        "jvm.gc.overhead",
        # GC 일시 정지 시간.
        "jvm.gc.pause",
        # JVM에 대한 일반적인 정보를 포함하는 문자열
        "jvm.info",
        # JVM이 할당한 메모리 중에 현재 커밋된(확보된) 메모리 양
        "jvm.memory.committed",
        # JVM이 최대로 사용할 수 있는 메모리 양
        "jvm.memory.max",
        # 가비지 컬렉션 후의 JVM 메모리 사용률
        "jvm.memory.usage.after.gc",
        # JVM이 현재 사용 중인 메모리 양
        "jvm.memory.used",
        # 현재 실행 중인 데몬(백그라운드) 스레드의 수
        "jvm.threads.daemon",
        # 현재 활성화된 스레드의 수
        "jvm.threads.live",
        # 애플리케이션이 실행되는 동안 최대로 활성화된 스레드의 수
        "jvm.threads.peak",
        # 애플리케이션이 실행되면서 시작된 총 스레드의 수
        "jvm.threads.started",
        # 현재 스레드의 상태를 나타내는 메트릭
        "jvm.threads.states",
        # Logback 로깅 이벤트의 총 수
        #애플리케이션에서 로깅이 발생할 때마다 이 값이 증가
        "logback.events",
        # 현재 프로세스의 CPU 사용률
        "process.cpu.usage",
        # 프로세스가 시작된 시간
        "process.start.time",
        # 프로세스가 실행된 시간
        "process.uptime",
        # 시스템에서 사용 가능한 CPU 코어의 수
        "system.cpu.count",
        # 시스템 전체 CPU 사용률
        "system.cpu.usage",
        # 현재 활성화된 Tomcat 세션의 수
        "tomcat.sessions.active.current",
        # Tomcat 세션의 최대 활성화 수
        "tomcat.sessions.active.max",
        # 유효한 최대 Tomcat 세션 수
        "tomcat.sessions.alive.max",
        # 애플리케이션에서 생성된 총 Tomcat 세션 수
        "tomcat.sessions.created",
        # 만료된 Tomcat 세션의 총 수
        "tomcat.sessions.expired",
        # 거부된 Tomcat 세션의 총 수
        "tomcat.sessions.rejected"
    ]
}

4. 디자인한 메트릭

  • 1차
1. 전체적으로 확인할 메트릭 목록 체크
2. USE / RED 각각 Error 메트릭 수집을 어떻게 해야 할지 확인
*Spring
USE
U
(disk.total - disk.free) / disk.total ( (전체 용량 - 여유 용량) / 전체 용량)
system.cpu.usage (전체 시스템 cpu 사용률)
process.cpu.usage (프로세스 cpu 사용률) 
(jvm.memory.max - jvm.memory.used) / jvm.memory.max (jvm 메모리 사용률)
(executor.pool.size - executor.queue.remaining) / executor.pool.size (현재 풀 사용량)

S
(executor.pool.size - executor.queued) / executor.pool.size ( 스레드 풀의 포화 상태 )
executor.queued (스레드 풀의 대기열에 현재 대기 중인 작업의 수) 
jvm.gc.overhead

E
??기본 제공 merics중에서 에러 카운트 할만한 메트릭이 뭐가 있는지 

RED
R
http.server.requests (총 HTTP 요청 수) 
http.server.requests.active ( 현재 처리 중인 HTTP 요청 수 )
-> 1분 단위로 끊어서 (총 HTTP 요청 - 현재 처리 중인 HTTP 요청) / 총 HTTP 요청 하면 
요청이 얼마나 남아있는지를 알 수 있을 듯

??

D
??


Opensearch
USE
U
CPU_Utilization (CPU 사용률) 
Disk_Utilization (디스크 사용률) 
Heap_Used / Heap_Maxed (메모리 사용률)
IO_ReadThroughput (지난 5초간 디스크에서 읽어온 데이터 양)

S
(ThreadPool_TotalThreads - ThreadPool_ActiveThreads) / ThreadPool_TotalThreads
(스레드 풀 잔여량) 

E
Paging_MajfltRate (초당 발생한 주요한 오류 수)
Paging_MinfltRate (초당 발생한 마이너 오류 수)

RED
R
HTTP_TotalRequest 
HTTP_RequestDocs
Disk_ServiceRate

E
ThreadPool_RejectedReqs(거부된  executions 수)

D
Disk_WaitTime( 지난 5초간 디스크 r/w 평균 응답시간)
GC_Collection_Time
  • 2차
1. opensearch를 제외한 springboot 메트릭으로 범위 축소 (필요한 경우 타 아키텍쳐 메트릭 수집도 추가)
2. 사용자 정의 메트릭이 적절하게 적용되고 있는지 확인 필요
3. 추가적인 메트릭이 어느 게 있을 수 있을지 확인 (향후 Redis 연결 시 Redis 응답 시간 / 접속 에러 관련 메트릭 추가 등)

search.request.count(controller)

  • 해당 어댑터(컨트롤러)로 들어온 전체 요청 수 확인

  • 핵심 비즈니스 로직 파악

  • 출력 예시

# HELP search_request_count_total  
# TYPE search_request_count_total counter
search_request_count_total{application="webtoon-search",class="search-webtoon-controller",exception="IllegalArgumentException",method="searchWebtoon",result="failure"} 4.0
search_request_count_total{application="webtoon-search",class="search-webtoon-controller",exception="none",method="searchWebtoon",result="success"} 3.0

search.request.duration(controller)

  • 웹툰 검색 메소드의 response 까지의 반환 시간 확인

  • handler method ~ opensearch 까지의 전체적인 응답 시간 파악

  • 여기서 문제가 없는데 전체 응답 시간이 길어지는 거면 톰캣 ~ Nginx ~ LB ~ Client 아키텍쳐의 문제를 의심해볼 수 있을 것

  • 출력 예시

# HELP search_request_duration_seconds duration until search webtoon list
# TYPE search_request_duration_seconds summary
search_request_duration_seconds_count{application="webtoon-search",class="com.samsamohoh.webtoonsearch.adapter.web.SearchWebtoonController",endpoint="/webtoons/search",exception="IllegalArgumentException",method="searchWebtoon"} 4
search_request_duration_seconds_sum{application="webtoon-search",class="com.samsamohoh.webtoonsearch.adapter.web.SearchWebtoonController",endpoint="/webtoons/search",exception="IllegalArgumentException",method="searchWebtoon"} 7.155E-4
search_request_duration_seconds_count{application="webtoon-search",class="com.samsamohoh.webtoonsearch.adapter.web.SearchWebtoonController",endpoint="/webtoons/search",exception="none",method="searchWebtoon"} 3
search_request_duration_seconds_sum{application="webtoon-search",class="com.samsamohoh.webtoonsearch.adapter.web.SearchWebtoonController",endpoint="/webtoons/search",exception="none",method="searchWebtoon"} 0.1211729

search.condition.null.count(service)

  • controller로부터 받아오는 검색어 객체가 null 또는 빈 값을 가지고 있는지 확인

  • 프론트 단에서 1차적으로 처리하지 못한 케이스가 있을 경우 에러 반환 후 카운팅

  • 의미 없는 메트릭일 가능성도 있음 (카운팅으로 어떤 효과를 얻을 수 있을지?)

  • 출력 예시

# HELP search_condition_null_count_total title is null
# TYPE search_condition_null_count_total counter
search_condition_null_count_total{application="webtoon-search",class="search-webtoon-service",endpoint="/webtoons/search",method="search-webtoons"} 4.0

search.opensearch.reply.duration(adapter)

  • adapter ~ opensearch 간의 응답 시간 확인

  • 출력 예시

# HELP search_opensearch_reply_duration_seconds duration until opensearch reply
# TYPE search_opensearch_reply_duration_seconds summary
search_opensearch_reply_duration_seconds_count{application="webtoon-search",class="com.samsamohoh.webtoonsearch.adapter.searchengine.SearchEngineAdapter",endpoint="/webtoons/search",exception="none",method="loadWebtoons"} 3
search_opensearch_reply_duration_seconds_sum{application="webtoon-search",class="com.samsamohoh.webtoonsearch.adapter.searchengine.SearchEngineAdapter",endpoint="/webtoons/search",exception="none",method="loadWebtoons"} 0.1199463
# HELP search_opensearch_reply_duration_seconds_max duration until opensearch reply
# TYPE search_opensearch_reply_duration_seconds_max gauge
search_opensearch_reply_duration_seconds_max{application="webtoon-search",class="com.samsamohoh.webtoonsearch.adapter.searchengine.SearchEngineAdapter",endpoint="/webtoons/search",exception="none",method="loadWebtoons"} 0.0971084

opensearch.connection.fail.count(adapter)

  • opensearch의 응답 실패 발생시 확인

  • opensearch 응답 실패가 얼마나 자주 있었는지 카운팅

  • 횟수가 많으면 spring ~ opensearch 간의 네트워크 문제 또는 opensearch 자체의 문제 의심가능.

  • 출력 예시

# HELP opensearch_connection_fail_count_total metrics for opensearch connecting failure
# TYPE opensearch_connection_fail_count_total counter
opensearch_connection_fail_count_total{application="webtoon-search",class="search-engine-adapter",endpoint="/webtoons/search",method="load-webtoons"} 2.0