node_exporter 가 노출하는 디스크 메트릭은 모두 부팅 이후 누적되는 카운터다. 값을 그대로 그리면 계속 올라가는 직선만 나오므로 반드시 rate() 나 irate() 를 씌워 초당 증가분으로 바꾼다. 장비에 디스크가 여러 개면 device 레이블로 시계열이 갈라지므로, 합계와 평균 중 무엇을 원하는지 먼저 정한다.
| 메트릭 | 의미 |
|---|---|
node_disk_read_bytes_total |
읽은 바이트 누적 |
node_disk_written_bytes_total |
쓴 바이트 누적 |
node_disk_reads_completed_total |
완료된 읽기 요청 수 누적 |
node_disk_writes_completed_total |
완료된 쓰기 요청 수 누적 |
node_disk_read_time_seconds_total |
읽기에 쓴 시간 누적 |
node_disk_write_time_seconds_total |
쓰기에 쓴 시간 누적 |
node_disk_io_time_seconds_total |
I/O 가 진행 중이던 시간 누적 |
node_disk_io_time_weighted_seconds_total |
대기 중인 요청 수로 가중된 I/O 시간 |
노드 전체의 디스크 처리량은 장치별 값을 더하는 것이 맞다. 평균을 내면 디스크를 여러 개 붙일수록 값이 작아 보인다.
sum by (instance) (rate(node_disk_read_bytes_total{device=~"sd.*|nvme.*"}[5m]))
sum by (instance) (rate(node_disk_written_bytes_total{device=~"sd.*|nvme.*"}[5m]))
장치당 평균이 필요할 때만 avg 를 쓴다.
avg by (instance) (rate(node_disk_read_bytes_total{device=~"sd.*|nvme.*"}[5m]))
device 필터를 두는 이유는 루프백 장치(loop*), 램디스크(ram*), 매퍼 장치(dm-*)가 물리 디스크와 중복 집계되기 때문이다. LVM 을 쓰면 dm-* 만 보거나 물리 장치만 보거나 중 하나로 통일한다.
# 초당 요청 수
sum by (instance, device) (rate(node_disk_reads_completed_total[5m]))
# 디스크 사용률 (iostat 의 %util 에 해당, 0~1)
rate(node_disk_io_time_seconds_total[5m])
# 요청당 평균 대기 시간 (iostat 의 await 에 해당, 초)
rate(node_disk_read_time_seconds_total[5m])
/ clamp_min(rate(node_disk_reads_completed_total[5m]), 1)
대기 시간을 구할 때 분모가 0 이 되면 결과가 사라지므로 clamp_min 으로 막는다. node_disk_io_time_seconds_total 의 rate 는 1 에 가까울수록 해당 장치가 쉬는 시간이 없다는 뜻이다. 다만 NVMe 처럼 큐가 깊은 장치에서는 이 값이 1 이어도 여유가 있을 수 있으므로 포화 지표로 단정하지 않는다.
여러 장치를 합치면 device 레이블이 사라진다. 그래프 범례에 노드 이름만 남기고 싶을 때는 집계 뒤 필요한 레이블만 남기고, 반대로 노드 이름을 다른 메트릭에서 끌어오고 싶으면 group_left 로 붙인다.
sum by (instance) (rate(node_disk_written_bytes_total[5m]))
* on (instance) group_left(nodename) node_uname_info
Grafana 패널에서는 Legend 를 {{instance}} / {{device}} 로 두면 시계열마다 알아볼 수 있다.
rate() 의 구간은 스크레이프 주기의 최소 4배를 준다. 15초 주기면 [1m] 이 하한이고 보통 [5m] 을 쓴다. 구간이 너무 짧으면 샘플이 둘 미만이라 결과가 비고, 너무 길면 짧은 스파이크가 뭉개진다. 알림 규칙에는 rate() 를, 순간 변동을 봐야 하는 대시보드에는 irate() 를 쓰는 편이 무난하다.