블로그로 돌아가기
2026년 8월 31일Sergei Solod10 분 읽기

제 FDCServers VPS는 3 TB가 넘는 트래픽까지 멀쩡했습니다. 그런데 디스크 읽기에 18초가 걸리기 시작했습니다

제 FDCServers VPS는 처음에는 실제 트래픽을 정상적으로 처리하며 3 TB가 넘는 데이터를 전송했습니다. 이후 평범한 workload에서도 심각한 가상 디스크 stall이 발생해 CPU iowait가 100%, Linux I/O pressure가 거의 100%, read latency가 18.7초, flush latency가 53초 이상까지 올라갔습니다.

FDCServersVPSLinux디스크 I/ODevOps

이 글은 FDCServers에 대한 부정적인 리뷰를 쓰기 위한 것이 아닙니다. 다른 사람에게 이 회사의 VPS를 사라거나 사지 말라고 말하려는 목적도 없습니다. 제가 경험한 것은 한 대의 서버, 하나의 workload, 한 번의 문제 흐름입니다. 이것만으로 호스팅 회사 전체를 평가할 수는 없습니다.

개발자로 일하다 겪은 평범하지 않은 하루의 기록에 가깝습니다. VPS는 한동안 정상적으로 동작했고 이미 3 TB가 넘는 트래픽을 전송한 상태였습니다. 그런데 어느 순간 평소와 같은 workload에서 극심한 지연이 발생하기 시작했습니다. 페이지 응답에 매우 오래 걸리거나 완전히 timeout되는 경우도 있었습니다. 처음에는 application, Nginx, memory, network, connection limit, 과도한 load 같은 흔한 원인을 의심했습니다. 하지만 Linux metrics는 다른 방향을 가리켰습니다.

VPS가 처음부터 느렸던 것은 아닙니다

장애 사이에는 서버가 완전히 정상으로 보이는 구간도 있었습니다. 한 정상 구간에서는 D-state process가 하나도 없었고, CPU iowait는 약 0%, disk latency는 약 2–4 ms, I/O PSI도 거의 0이었습니다.

D-state processes: 0
CPU iowait:        ~0%
disk latency:      ~2–4 ms
I/O PSI some:      0.04
I/O PSI full:      0.04

제가 그 순간에만 접속해 top, free -h, df -h, systemctl status nginx를 확인했다면 VPS는 정상이라고 결론 내렸을 겁니다. 하지만 일반 workload가 돌아오자 상태가 급격하게 변했습니다.

18.7초짜리 read가 조사 방향을 바꿨습니다

가장 강력했던 iostat sample 중 하나는 다음과 같습니다.

r_await = 18744 ms
f_await = 53561 ms
aqu-sz  = 128.54
util    = 100.10%
read    = 88 KB/s

각 숫자를 따로 보는 것이 아니라 함께 봐야 합니다. 완료된 read의 평균 latency는 약 18.7초, flush latency는 약 53.6초였습니다. 평균 I/O queue는 128을 넘었는데 실제 read throughput은 88 KB/s에 불과했습니다. 많은 데이터를 빠르게 처리하느라 disk가 바쁜 상황이 아니었습니다. Storage path가 operation 완료를 기다리는 데 엄청난 시간을 쓰고 있었습니다.

iowait와 PSI는 시스템 전체의 I/O pressure를 보여줬습니다

가장 심한 구간에서 vmstat는 blocked process 4–9개, CPU iowait 97–100%, CPU idle 0%를 보여줬습니다. iowait는 application이 CPU 전체를 사용한다는 뜻이 아닙니다. 실제로 해야 할 작업이 outstanding I/O가 끝나기를 기다리고 있다는 뜻입니다.

Linux Pressure Stall Information을 보니 더 명확했습니다.

I/O PSI some avg10 = 99.14
I/O PSI full avg10 = 95.55

다른 재현에서는 full이 약 98%까지 올라갔습니다. Disk utilization은 device가 얼마나 바쁜지 알려주지만, PSI는 해당 resource 때문에 workload가 실제로 얼마나 stall되는지 보여줍니다. 95–98% 수준의 I/O pressure가 지속되는 것은 작은 performance 저하가 아닙니다.

D-state와 kernel stack은 application 아래쪽을 가리켰습니다

어떤 process들이 막혀 있는지도 확인했습니다. 같은 시점에 jbd2, systemd-journald, Nginx worker, Nginx cache process, 그 밖의 filesystem 작업들이 D-state에 있었습니다. 제 application만 멈췄다면 application을 조사했을 겁니다. Nginx, system journal, EXT4 journal이 한꺼번에 막힌다면 공통 의존성인 storage가 훨씬 중요한 단서가 됩니다.

EXT4 journaling path에는 다음 함수들이 있었습니다.

wait_on_buffer
jbd2_log_wait_commit
jbd2_journal_commit_transaction

Nginx worker는 평범한 file read 안에서 기다리고 있었습니다.

folio_wait_bit_common
filemap_read
generic_file_read_iter
ext4_file_read_iter
vfs_read
pread64

한때 kernel은 Nginx task 하나가 122초 이상 blocked됐다고 기록했습니다. 그런데도 Nginx는 active로 표시될 수 있었습니다. Process가 존재한다는 것과 worker가 실제 작업을 완료할 수 있다는 것은 다른 문제입니다. running과 healthy는 같은 상태가 아닙니다.

3 ms의 backend 응답으로 application과 filesystem path를 분리했습니다

가장 깔끔한 비교는 Nginx와 local application backend였습니다. Nginx를 거친 request는 connection 또는 TLS timeout과 함께 HTTP=000을 반환했습니다. 반면 backend에 직접 보낸 request는 약 3 ms 만에 끝났습니다.

connect = 0.000423 s
TTFB    = 0.003063 s
total   = 0.003139 s

이 테스트에서 정확한 HTTP status는 중요하지 않았습니다. Backend는 connection을 받고 request를 처리한 뒤 거의 즉시 response를 만들었습니다. 동시에 Nginx worker는 EXT4 read를 기다리고 있었습니다. 이 테스트로 application 자체의 실행과 그 앞에 있는 filesystem 의존 경로를 분리할 수 있었습니다.

문제는 재현할 수 있었고 다시 사라지기도 했습니다

한 번의 재현 직전에는 상태가 정상적이었습니다.

HTTP:          200
D-state:       0
CPU iowait:    3%
r_await:       ~1.18 ms
I/O PSI full:  ~2.95%

약 30초 뒤에는 다음과 같이 바뀌었습니다.

D-state:       4
CPU iowait:    91%
CPU idle:      0%
I/O PSI some:  86.11%
I/O PSI full:  78.02%
r_await:       236.50 ms
HTTP:          000

이후 CPU iowait는 96–100%, I/O PSI full은 약 98%, HTTPS queue는 512까지 올라갔고 HTTP check는 계속 실패했습니다. VPS가 느리다고 말하는 것보다 훨씬 의미 있는 evidence였습니다.

workload를 제거하면 반대 방향의 변화도 빠르게 일어났습니다.

D-state:      0
CPU iowait:   6%
r_await:      ~0.98 ms
queue depth:  ~0.07
HTTP:         200

그래서 intermittent storage problem은 장애가 지나간 뒤 확인하기 어렵습니다. Recovery 후 provider가 서버를 보면 실제로 정상 latency가 보일 수 있습니다. 그렇다고 10분 전에 있었던 현상이 설명되지는 않습니다. 정확한 UTC timestamp를 남기는 것이 중요해졌습니다.

오해하기 쉬운 두 가지 metric: await=0와 free disk space

일부 심각한 구간에서는 r_await = 0이 보였지만 iowait는 높았고, process는 D-state, request는 in-flight, 완료되는 read는 거의 없었습니다. Latency statistics는 완료된 I/O를 기준으로 합니다. Sampling interval 안에 operation이 끝나지 않으면 완료된 operation의 latency 평균에 들어가지 못합니다. 따라서 0이 항상 즉시 처리되는 disk를 의미하지는 않습니다.

Disk fullness도 검토했습니다. 나중에는 filesystem이 제가 평소 허용하는 것보다 많이 찼지만, 같은 종류의 failure는 root filesystem 사용량이 약 24%였을 때 이미 나타났습니다. 당시 사용 가능한 RAM은 약 1.2 GiB, inode usage는 약 5%였고 network interface에는 error나 dropped packet이 없었습니다. 따라서 disk fullness만으로 전체 incident를 설명할 수 없었습니다.

제가 증명할 수 있었던 것과 없었던 것

VPS 안에서는 application, Linux VFS, EXT4, virtual block device까지 볼 수 있었습니다. 그 뒤에는 provider의 infrastructure가 있습니다. Virtualization, distributed storage, storage network, physical device, scheduling 등 guest에서 직접 볼 수 없는 영역입니다.

그래서 특정 physical SSD가 고장 났다거나 특정 storage node, network path, virtualization component가 root cause였다고 정직하게 단정할 수는 없습니다.

대신 다음 정도는 충분히 근거를 갖고 말할 수 있습니다. 제 Linux guest에 제공된 virtual storage path가 반복적으로, 일반 filesystem I/O에 수초가 걸리거나 합리적인 시간 안에 완료되지 않는 상태에 들어갔습니다. 근거는 iostat, PSI, D-state, kernel wait stack, EXT4/jbd2 wait, Nginx filesystem wait, queue depth, request timing이었습니다. 제가 필요한 engineering diagnosis에는 충분했지만 physical root cause를 특정하기에는 부족했습니다.

이전의 3 TB 이상 트래픽이 이후 stall과 모순되지 않는 이유

처음에는 이 부분이 혼란스러웠습니다. Storage가 문제였다면 왜 그 전에 몇 TB를 정상적으로 전송할 수 있었을까요?

Network traffic과 physical disk I/O는 같은 것이 아니기 때문입니다. File 하나를 backing storage에서 한 번 읽고 Linux page cache에 남겨 둔 뒤, memory에서 여러 번 제공할 수 있습니다. 따라서 network로 3 TB를 전송했다는 사실이 3 TB의 unique physical disk read를 의미하지는 않습니다.

Infrastructure 조건도 시간에 따라 바뀝니다. Cache state, storage load, queueing, host placement, 다른 workload가 모두 변할 수 있습니다. 어제 VPS가 정상이었다는 사실만으로 오늘도 storage가 똑같이 동작한다고 보장할 수 없습니다.

결국 더 깊은 root cause를 기다리는 것을 그만뒀습니다

정확한 timestamp, vmstat, iostat, PSI, blocked-process snapshot, kernel stack, filesystem wait, queue depth, HTTP timing을 수집했습니다. Diagnostics를 FDCServers에 보내고 infrastructure level의 더 깊은 설명을 기다렸습니다.

오랫동안 기다렸습니다. 결국 더는 기다리지 않기로 했습니다. 운영 결정을 내리는 데 필요한 정보는 이미 충분했습니다. 문제는 재현 가능하고 심각했으며 application layer 아래에서 보였고, 물리적인 원인은 제 VPS의 visibility 밖에 있었습니다.

환불을 요청했고 FDCServers는 돈을 돌려줬습니다

문제와 수집한 diagnostics를 정리해 FDCServers에 보내고 service 취소와 refund를 요청했습니다. 돈은 환불됐습니다.

따라서 이 이야기는 환불을 둘러싼 긴 싸움으로 끝나지 않습니다. 최종적인 기술 설명을 기다렸고, 더 이상 기다리지 않기로 한 뒤 제가 가진 evidence를 보내 돈을 돌려달라고 요청했습니다. FDCServers는 환불했습니다.

이후 제가 바꾼 것

가장 유용한 결과는 특정 hosting company가 좋다거나 나쁘다고 판단한 것이 아닙니다. 느린 Linux server를 debug하는 방식이 바뀌었습니다.

top, free -h, df -h는 여전히 사용하지만 이제는 훨씬 일찍 다음 항목도 확인합니다.

date -u
uptime
cat /proc/pressure/io
cat /proc/pressure/memory
cat /proc/pressure/cpu
vmstat 1 10
iostat -x 1 10
ps -eo state,pid,ppid,etime,wchan:50,comm,args | awk 'NR==1 || $1 ~ /^D/'
ss -lntp
journalctl -k --since '30 min ago' --no-pager

가능하면 request path도 따로 테스트합니다. Public request, local Nginx, direct backend, filesystem, block-device metrics를 각각 봅니다. 이제 질문은 단순히 서버가 왜 느린가가 아닙니다. 어느 layer에서 실제 작업이 완료되지 않기 시작하는가?를 묻습니다.

또 가능하면 reboot 전에 evidence를 수집합니다. Reboot은 service를 복구할 수 있지만 intermittent incident를 진단하게 해 주는 정확한 D-state, PSI, queue, latency까지 지워버릴 수 있기 때문입니다.

마지막으로

호스팅 글을 쓸 소재가 필요해서 FDCServers VPS를 산 것이 아닙니다. 많은 bandwidth가 있는 server가 필요했습니다. 한동안은 정확히 원하는 대로였습니다. 실제 workload를 처리했고 3 TB가 넘는 데이터를 전송했습니다.

그러다가 normal workload가 반복적으로 iowait 최대 100%, I/O PSI 거의 100%, read 최대 18.7초, flush latency 53초 이상, 큰 queue, filesystem read에서 blocked된 Nginx, I/O를 기다리는 EXT4/jbd2를 만들어내기 시작했습니다.

어떤 physical 또는 host-side component가 원인이었는지는 끝내 알지 못했고, 안다고 가장할 필요도 없습니다. Failure가 나타나는 layer를 확인했고 application problem과 구분할 만큼 evidence를 모았고, 더 깊은 root-cause explanation을 기다리는 일을 멈춘 뒤 환불을 요청했습니다. FDCServers는 돈을 돌려줬습니다.

이것은 모든 FDCServers VPS에 대한 verdict가 아닙니다. Service가 active, process가 running으로 보이면서도 machine이 유효한 시간의 거의 전부를 storage 기다림에 쓸 수 있다는 사실을 보여 주는 하나의 잘 기록된 사례입니다.