RED and USE
RED는 service를 request 관점에서 보고, USE는 시스템을 resource 관점에서 본다.
Intro
metric을 늘리는 건 쉽다. exporter 하나만 붙여도 수백 개가 쏟아진다.
어려운 건 그중 무엇을 대시보드 맨 위에 둘지 정하는 일이다.
패널이 40개라고 해보자. 얼마나 도움이 될까?
단순히 메트릭만 늘린다고 능사가 아니다. 패널이 40개인 대시보드는 사실 2-3개인 대시보드와 크게 다를바가 없다.
장애가 나면 사람들은 그 40개를 위에서부터 훑는 대신 평소 눈에 익은 두세 개만 본다.
RED와 USE는 우리가 무엇에 집중할지 정해주는 방법론이다.
Four Golden Signals
2012년 Brendan Gregg가 USE method를 내놨다.
USE는 시스템 성능 문제를 Resource, 자원의 관점에서 훑는 체크리스트다.
2016년 Google SRE book의 “Monitoring Distributed Systems” 장은 Four Golden Signals를 제시했다.
Latency, Traffic, Errors, Saturation
Tom Wilkie가 여기서 Saturation을 덜어내고 이름을 다듬어 RED method로 정리했다.
Rate, Errors, Duration.
| 방법론 | 보는 대상 | 구성 |
|---|---|---|
| USE | resource | Utilization, Saturation, Errors |
| Four Golden Signals | service | Latency, Traffic, Errors, Saturation |
| RED | service | Rate, Errors, Duration |
USE
USE는 모든 resource에 대해 세 칸을 채우는 체크리스트라고 생각하면 된다.
CPU, memory, disk, network interface 같은 하드웨어뿐 아니라 thread pool, connection pool 같은 소프트웨어 resource도 대상이다.
- Utilization: resource가 일하는 데 쓰인 비율
- Saturation: 처리하지 못해 대기 중인 작업의 양
- Errors: 에러 이벤트 수
Utilization이 익숙한 지표라 거기로 눈이 가지만, 실제로 우리가 주목해야할 곳은 Saturation이다.
| 지표 | 값 | 읽히는 것 |
|---|---|---|
| CPU utilization | 60% | 여유 있어 보인다 |
| run queue length | 12 | 실제로는 줄을 서 있다 |
utilization은 보통 1분 평균이다.
100ms 단위로 100%와 0%를 오가는 워크로드도 평균을 내면 60%로 찍힌다. 평균에는 burst가 남지 않는다.
burst는 측정 주기보다 짧은 시간 척도에서 벌어지는 100% 포화 구간을 말한다. 왜 이게 문제가 될까?
1분은 100ms 슬롯 600개다. 그중 360개에서 CPU가 100%였고 240개에서 0%였다면 평균은 정확히 60%로 나온다.
여기서 burst는 360번의 완전 포화 상태를 뜻한다. Utilization 60%를 보면 적당히 잘 일하고 있는 것처럼 보인다. 그러나 포화구간에 도착한 작업은 기다려야한다는 것이 문제다.
이 burst의 흔적은 큐에 남는다. 대기한 작업이 있었다는 사실은 평균을 내도 지워지지 않는다.
그래서 saturation을 의미있는 선행지표로 쓸 수 있다.
resource별로 무엇을 볼지는 대체로 정해져 있다.
| resource | Utilization | Saturation | Errors |
|---|---|---|---|
| CPU | busy 시간 비율 | run queue length, CFS throttle 시간 | — |
| Memory | 사용 중인 용량 | swap, page scan rate | OOM kill |
| Disk | I/O 처리 중이던 시간 | I/O 대기 큐 길이 | I/O error |
| Network | 대역폭 사용률 | drop, retransmit | CRC error |
| Connection pool | 점유 중인 slot | pool wait time | 획득 timeout |
이 표를 시스템에 대고 채워보는 게 USE method의 실제 사용법이다.
그리고 진짜 소득은 채운 칸이 아니라 못 채운 칸에 있다.
“이 resource의 saturation은 무엇으로 재지?”에서 막히면 거기가 블랙박스다.
Two Kinds of Utilization
Utilization이라는 한 단어가 실은 두 가지를 가리킨다. 섞어 쓰면 같은 숫자를 보고 정반대 결론이 나온다.
time-based는 시간의 비율이다.
CPU가 busy였던 시간 비율, disk가 I/O를 처리 중이던 시간 비율이 여기 속한다.
capacity-based는 용량의 비율이다.
disk 사용량, memory 사용량, connection pool 점유 수가 여기 속한다.
100%가 뜻하는 바가 서로 반대다.
| time-based | capacity-based | |
|---|---|---|
| 묻는 것 | 얼마나 오래 바빴나 | 얼마나 찼나 |
| 100%의 뜻 | 쉬는 틈 없이 일하는 중 | 꽉 참 |
| 더 받을 수 있나 | 받을 수도 있다 | 못 받는다 |
| 넘어서면 | 큐에 쌓여 느려진다 | 즉시 실패한다 |
| 곡선 모양 | 비선형 | 선형 |
time-based 100%는 한계가 아니라 포화의 시작 신호다. 병렬 resource라면 특히 그렇다.
NVMe나 RAID array는 iostat의 %util이 100%여도 요청을 더 소화한다. 요청 하나라도 처리 중이면 busy로 세기 때문이다.
현대 storage에서 %util은 그대로 믿을 수 없다.
capacity-based 100%는 벽이다. 그 다음은 성능 저하가 아니라 ENOSPC나 OOM kill이다.
이 구분이 갈리는 지점은 알림 설계다. capacity-based는 선형이라 소진 시각을 예측할 수 있다.
predict_linear(node_filesystem_avail_bytes[6h], 4 * 3600) < 0
“4시간 뒤 disk full”은 의미 있는 알림이다. 반면 CPU가 “3시간 뒤 100%에 도달한다”는 예측은 아무것도 뜻하지 않는다.
time-based에는 70~80% 구간부터 latency가 꺾이는 비선형 구간이 있어서, 절대값보다 saturation을 같이 봐야 한다.
resource마다 어느 쪽이 본질인지도 다르다. CPU는 time-based만 있다. 쌓아두는 물건이 아니니 capacity 개념 자체가 없다. Memory는 반대로 capacity-based가 본질이다. Disk는 둘 다 있고 서로 무관하다. 용량이 텅 비어 있어도 I/O는 포화될 수 있다. I/O가 한가해도 용량이 차면 쓰기가 실패한다.
Kubernetes에서 “CPU limit은 걸지 말고 memory limit은 걸어라”라고들 하는 것도 이 차이 때문이다. CPU는 time-based라 초과분이 CFS throttling, 즉 대기로 흡수된다. 느려질 뿐 죽지는 않는다. Memory는 capacity-based라 초과가 곧 OOM kill이다. 완충 구간이 없다.
RED
RED는 request를 받아 처리하는 모든 service에 대해 세 가지를 본다.
- Rate: 초당 요청 수
- Errors: 실패한 요청 수 또는 비율
- Duration: 요청 처리 시간의 분포
Rate가 왜 이 목록에 있는지는 자주 지나친다. error 5건은 그 자체로 아무 뜻이 없다. 초당 10 요청 중 5건이면 장애고, 초당 10만 요청 중 5건이면 노이즈다. Errors와 Duration을 해석하려면 Rate가 분모로 있어야 한다.
서비스가 몇 개든 대시보드 모양이 같아서 USE에 비해 단순한 편이다.
USE는 resource 종류마다 봐야 할 지표가 다르지만 RED는 결제 서비스든 인증 서비스든 세 패널이 똑같다.
Percentiles and Histograms
RED의 Duration은 세 지표 중 유일하게 분포다.
평균 응답시간은 거의 항상 오해를 부른다. 대부분의 요청이 빠르고 소수가 아주 느린 long tail 분포에서, 평균은 어느 쪽도 대표하지 못한다.
p50, p90, p99, p99.9를 본다. 그리고 그 숫자를 사람 수로 환산해봐야 한다. 초당 1만 요청이면 p99 초과는 초당 100명이다. 하루면 864만 번의 나쁜 경험이다.
quantile은 평균낼 수 없다. instance 10개의 p99를 평균한 값은 전체 p99가 아니다. 수학적으로 무의미한 연산이다.
그래서 Prometheus에서는 summary 대신 histogram을 쓰라고 한다. summary는 quantile을 client 쪽에서 미리 계산해 노출하기 때문에 여러 instance를 가로질러 합칠 수 없다. histogram은 bucket별 카운터를 노출하고, bucket은 그냥 더하면 된다.
histogram_quantile(
0.99,
sum by (le) (rate(http_request_duration_seconds_bucket[5m]))
)
sum by (le)로 모든 instance의 bucket을 합친 다음 quantile을 계산한다. 순서가 반대면 안 된다.
실패는 latency를 좋아 보이게 만든다. circuit breaker가 열려 요청이 1ms 만에 실패하면 p99가 급격히 개선된다. Duration 그래프만 보면 성능이 나아진 것처럼 보인다.
그래서 Duration은 성공한 요청만 따로 보거나, 최소한 status code로 나눠 봐야 한다. Errors와 Duration은 같은 화면에 둔다.
Symptom and Cause
“같은 장애를 RED는 service 쪽에서, USE는 resource 쪽에서 본다”를 그림으로 표현해보자.
RED는 증상, USE는 원인이다.
RED는 사용자가 실제로 겪는 것이고, USE는 증상이 발생한 이유이다.
그래서 CPU utilization 90%에는 알림을 걸지 않는다. 사용자가 멀쩡한데 CPU가 90%면 그건 장애가 아니라 자원을 잘 쓰고 있는 상태다. 반대로 CPU가 30%인데 p99가 5초면 장애다.
utilization 알림은 사람을 깨우지만 대체로 아무 일도 아니고, 그게 반복되면 알림 자체를 무시하게 된다.
순서는 항상 RED에서 이상을 감지하고, USE로 내려가 원인을 찾아야 한다.
References
- Brendan Gregg — The USE Method
https://www.brendangregg.com/usemethod.html - Tom Wilkie — The RED Method
https://grafana.com/blog/2018/08/02/the-red-method-how-to-instrument-your-services/ - Google SRE Book — Monitoring Distributed Systems
https://sre.google/sre-book/monitoring-distributed-systems/ - Prometheus — Histograms and Summaries
https://prometheus.io/docs/practices/histograms/ - Brendan Gregg — Thinking Methodically about Performance (ACM Queue)
https://queue.acm.org/detail.cfm?id=2413037