LabHub
시작하기
배우기 러닝패스 코스

스토리지 실무 — RAID·스냅숏·iSCSI·fio

"디스크가 느리다" 를 숫자로 바꾼다 — IOPS·지연·큐 깊이와 fio

LabHub 에서 이어서 보기

한 줄 요약

디스크 성능은 숫자 하나가 아니라 세 개다. 초당 몇 번(IOPS), 초당 얼마나(처리량), 한 번에 얼마나 걸리나(지연). 이 셋은 블록 크기와 큐 깊이에 따라 서로 바뀌므로, "몇 IOPS 나와요" 는 조건을 함께 말하지 않으면 아무 뜻이 없다. fio 는 그 조건을 고정해 놓고 재는 도구다.

왜 이게 필요했나

"스토리지가 느려요" 라는 신고는 대부분 증거가 없다. 새 스토리지를 사기 전, LUN 을 옮기기 전, 클라우드 볼륨 등급을 고르기 전에 같은 조건에서 잰 숫자가 있어야 비교가 된다. 그런데 dd if=/dev/zero of=파일 로 잰 숫자는 거의 언제나 틀린다. 쓰기는 페이지 캐시에 먼저 들어가 메모리 속도가 찍히고, 한 번에 하나씩 순차로 쓰므로 데이터베이스가 실제로 하는 작은 무작위 읽기와는 전혀 다른 일을 잰다.

어떻게 동작하나

세 숫자의 관계. 처리량 = IOPS × 블록 크기다. 4KiB 로 10,000 IOPS 면 약 40MiB/s 이고, 1MiB 로 400 IOPS 면 400MiB/s 다. 데이터베이스의 무작위 읽기는 작은 블록이라 IOPS 와 지연이 중요하고, 백업이나 대용량 복사는 큰 블록이라 처리량이 중요하다. 그래서 측정은 "무엇을 흉내 낼 것인가" 부터 정한다.

큐 깊이와 리틀의 법칙. 동시에 날아가 있는 요청 수가 큐 깊이(queue depth)다. 대기열 이론의 리틀의 법칙은 "시스템 안에 머무는 평균 개수 = 처리율 × 평균 머무는 시간" 이다. 스토리지에 대면 동시 요청 수 ≈ IOPS × 평균 지연 이다. 큐 깊이 1 이면 IOPS 는 1 ÷ 지연을 넘을 수 없다. 지연이 0.2ms 면 최대 5,000 IOPS 다. 큐 깊이를 올리면 장치가 여러 요청을 겹쳐 처리해 IOPS 가 오르지만, 요청 하나하나는 줄을 서므로 지연도 오른다. 스토리지 사양서의 큰 IOPS 는 대개 깊은 큐에서 잰 값이고, 한 번에 하나씩 기다리는 애플리케이션은 그 숫자를 절대 보지 못한다.

평균이 아니라 백분위수. 평균 지연이 1ms 여도 100번에 한 번 50ms 가 걸리면, 요청마다 디스크를 여러 번 읽는 서비스는 그 꼬리를 자주 밟는다. 그래서 p99(99번째 백분위수) 지연을 함께 본다.

fio 의 옵션. fio 문서의 옵션 가운데 결과를 좌우하는 것은 몇 개다.

옵션 이 실습의 값
rw 읽기/쓰기, 순차/무작위 randread
bs 블록 크기 4k
ioengine 요청을 내는 방식 libaio (비동기, 큐 깊이가 의미 있음)
iodepth 큐 깊이 132
direct 페이지 캐시 우회 1
runtime·time_based 정해진 시간 동안 계속 10초
output-format 출력 형식 json

direct=1 은 O_DIRECT 로 열어 페이지 캐시를 건너뛴다. 이것을 빼면 두 번째 읽기부터 메모리에서 답이 나와 디스크가 아니라 RAM 을 잰다. ioengine=libaio 가 아닌 동기 방식(psync)이면 iodepth 를 올려도 한 번에 하나씩만 나가므로 큐 깊이 실험이 되지 않는다.

JSON 을 읽는 법. --output-format=json 결과의 jobs[0].read 아래에 iops, 지연 통계 lat_ns(평균 mean), 완료 지연 clat_ns.percentile"99.000000" 키가 있다. 단위가 나노초이므로 마이크로초로 옮길 때 1,000 으로 나눈다. 사람이 읽는 기본 출력을 복사하지 말고 JSON 에서 숫자를 꺼내야 보고서와 재현이 맞는다.

현장에서 만나는 모습

클라우드 블록 볼륨은 대개 IOPS 와 처리량 상한을 등급으로 판다. 그 상한은 충분히 깊은 큐에서만 닿는다. 애플리케이션이 큐 깊이 1 로 동기 쓰기를 하면(예: 트랜잭션마다 fsync 하는 데이터베이스 로그) 비싼 등급을 사도 지연만큼만 나온다. 그때 볼 숫자는 IOPS 가 아니라 지연이다.

새 스토리지 도입 시험에서는 운영 부하를 흉내 낸 조건 두세 개를 정해 두고 늘 같은 명령으로 잰다. 명령과 JSON 결과를 함께 보관하면, 반년 뒤 "예전보다 느려졌다" 는 말에 같은 명령을 다시 돌려 숫자로 답할 수 있다. 측정은 운영 중인 파일시스템 위의 전용 파일에 하고, 원시 장치에 쓰기 측정을 하는 것은 데이터를 지우는 일이므로 빈 장치에서만 한다. 그리고 측정 중에는 iostat -x 를 옆에 띄워 두면 fio 의 숫자와 커널이 본 장치의 숫자가 맞는지 함께 확인할 수 있다.

다음 실습에서 할 것

같은 VM 안에 LIO 타깃을 세워 512MiB LUN 을 내주고, 이니시에이터로 발견·로그인해 새 디스크가 iscsi 로 보이는지 확인한다. ext4 로 포맷해 _netdev 가 붙은 fstab 줄로 마운트하고 자동 로그인을 켠다. 그 위의 전용 파일에 4KiB 무작위 읽기를 큐 깊이 1 과 32 로 재고, JSON 에서 IOPS·p99·평균 지연을 꺼내 리틀의 법칙이 맞는지 확인한다.