Wróć do bloga
31 sierpnia 2026Sergei Solod8 min czytania

Mój VPS w FDCServers działał dobrze przy ponad 3 TB ruchu. Potem odczyt z dysku zaczął trwać 18 sekund

Mój VPS w FDCServers początkowo normalnie obsługiwał rzeczywisty ruch i przesłał ponad 3 TB danych. Później zwykłe obciążenie zaczęło wywoływać poważne zastoje wirtualnego dysku: CPU iowait dochodził do 100%, presja I/O w Linuksie niemal do 100%, odczyty do 18,7 sekundy, a flush do ponad 53 sekund.

FDCServersVPSLinuxI/O dyskuDevOps

Nie piszę tego jako negatywnej recenzji FDCServers. Nie próbuję też mówić innym, czy powinni kupować u nich VPS. Miałem jeden serwer, jeden workload i jeden konkretny ciąg problemów. To za mało, by oceniać całą firmę hostingową.

To po prostu jedna z historii z mojej codziennej pracy jako programisty. VPS przez pewien czas działał normalnie i zdążył już przesłać ponad 3 TB ruchu. Potem zwykłe obciążenie zaczęło powodować ogromne opóźnienia. Strony potrafiły odpowiadać bardzo długo albo całkowicie wpadać w timeout. Na początku podejrzewałem standardowe rzeczy: aplikację, Nginx, pamięć, sieć, limity połączeń lub zbyt duże obciążenie. Metryki Linuksa wskazywały jednak gdzie indziej.

VPS nie był wolny od początku

Między incydentami maszyna mogła wyglądać całkowicie zdrowo. W jednym normalnym okresie nie było procesów w D-state, CPU iowait wynosił około 0%, opóźnienie dysku około 2–4 ms, a I/O PSI było niemal zerowe:

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

Gdybym połączył się tylko wtedy i sprawdził top, free -h, df -h oraz systemctl status nginx, prawdopodobnie uznałbym, że VPS jest w porządku. Potem wracał normalny workload i stan maszyny zmieniał się dramatycznie.

Odczyty trwające 18,7 sekundy zmieniły kierunek diagnostyki

Jedna z najmocniejszych próbek iostat wyglądała tak:

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

Te liczby trzeba czytać razem. Zakończone odczyty trwały średnio około 18,7 sekundy. Latency flush wynosiło około 53,6 sekundy. Średnia kolejka I/O przekraczała 128, a użyteczny throughput odczytu wynosił zaledwie 88 KB/s. To nie był po prostu zajęty dysk przesyłający dużą ilość danych. Ścieżka storage spędzała ogromną ilość czasu na oczekiwaniu na zakończenie operacji.

iowait i PSI pokazały presję I/O w całym systemie

W najgorszych okresach vmstat pokazywał 4–9 zablokowanych procesów, CPU iowait około 97–100% i CPU idle 0%. iowait nie znaczy, że aplikacja zużywa całą moc CPU. Oznacza, że użyteczna praca czeka na zakończenie operacji I/O.

Linux Pressure Stall Information pokazał to jeszcze wyraźniej:

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

W późniejszych reprodukcjach full zbliżało się do 98%. Disk utilization mówi mi, że urządzenie jest zajęte; PSI pokazuje, jak mocno workload jest zatrzymywany przez dany zasób. Utrzymująca się presja I/O na poziomie 95–98% nie jest drobną utratą wydajności.

D-state i stosy kernela wskazywały poniżej warstwy aplikacji

Następnie sprawdziłem, które procesy były zablokowane. Jednocześnie widziałem jbd2, systemd-journald, workery Nginx, procesy cache Nginx oraz inne operacje filesystem w D-state. Gdyby zawiesiła się tylko moja aplikacja, badałbym aplikację. Kiedy jednocześnie blokują się Nginx, journal systemowy i journal EXT4, storage staje się najbardziej oczywistą wspólną zależnością.

Ścieżki journalingu EXT4 obejmowały:

wait_on_buffer
jbd2_log_wait_commit
jbd2_journal_commit_transaction

Workery Nginx czekały w zwykłych operacjach odczytu plików:

folio_wait_bit_common
filemap_read
generic_file_read_iter
ext4_file_read_iter
vfs_read
pread64

W pewnym momencie kernel zgłosił, że task Nginx był zablokowany przez ponad 122 sekundy. Nginx nadal mógł być oznaczony jako active, ale nie znaczyło to, że jego workery potrafiły kończyć użyteczną pracę. running i zdrowy to różne stany.

Odpowiedź backendu w 3 ms oddzieliła aplikację od ścieżki filesystem

Najczystsze porównanie było między Nginx a lokalnym backendem aplikacji. Requesty przez Nginx kończyły się HTTP=000 z timeoutem połączenia lub TLS. Bezpośredni request do backendu kończył się w około 3 ms:

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

Dokładny status HTTP nie miał tu znaczenia. Backend przyjął połączenie, przetworzył request i niemal od razu wygenerował odpowiedź. Jednocześnie workery Nginx czekały na odczyty EXT4. To pozwoliło oddzielić normalne wykonywanie aplikacji od zależnej od filesystem ścieżki znajdującej się przed nią.

Problem dało się odtworzyć i potrafił znowu zniknąć

Przed jedną reprodukcją maszyna wyglądała normalnie:

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

Około pół minuty później:

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

Później CPU iowait dochodził do 96–100%, I/O PSI full zbliżało się do 98%, kolejka HTTPS osiągała 512, a testy HTTP nadal kończyły się niepowodzeniem. To było znacznie bardziej użyteczne niż stwierdzenie, że VPS wydaje się wolny.

Po usunięciu workloadu odwrotna zmiana mogła zajść bardzo szybko:

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

Dlatego przerywane problemy storage są trudne do zbadania po fakcie. Provider może sprawdzić serwer po recovery i rzeczywiście zobaczyć normalną latencję. To nadal nie wyjaśnia, co wydarzyło się dziesięć minut wcześniej. Dokładne timestampy UTC stały się więc kluczowe.

Dwie mylące metryki: await=0 i wolne miejsce

W niektórych złych interwałach widziałem r_await = 0, mimo że iowait był wysoki, procesy znajdowały się w D-state, requesty nadal były in-flight, a niemal żadne odczyty się nie kończyły. Statystyki latency bazują na zakończonych operacjach I/O. Jeśli operacja pozostaje zablokowana i nie kończy się w oknie pomiarowym, nie może wejść do średniej latency zakończonych operacji. Zero nie zawsze oznacza więc natychmiastowy dysk.

Sprawdziłem też kwestię zapełnienia dysku. Później filesystem był pełniejszy, niż normalnie bym dopuścił, ale ten sam typ awarii wystąpił wcześniej, gdy root filesystem był zajęty tylko w około 24%. Wtedy dostępne było około 1,2 GiB RAM, inode usage wynosiło około 5%, a interfejs sieciowy nie pokazywał błędów ani dropped packets. Zapełnienie dysku nie mogło wyjaśnić całego incydentu.

Co mogłem udowodnić, a czego nie

Z wnętrza VPS mogłem obserwować aplikację, Linux VFS, EXT4 i wirtualne urządzenie blokowe. Dalej zaczyna się infrastruktura providera: virtualizacja, distributed storage, storage networking, fizyczne urządzenia, scheduling i inne warstwy niewidoczne z guest.

Nie mogę więc uczciwie twierdzić, że zepsuty był konkretny fizyczny SSD, ani wskazać określonego storage node, ścieżki sieciowej czy komponentu virtualizacji jako root cause.

Mogę powiedzieć coś węższego, ale dobrze udokumentowanego: wirtualna ścieżka storage udostępniona mojemu Linux guest wielokrotnie wchodziła w stan, w którym zwykłe operacje filesystem I/O trwały sekundy lub nie kończyły się w rozsądnym czasie. Dowody pochodziły z iostat, PSI, D-state, kernel wait stacks, oczekiwań EXT4/jbd2, filesystem waits Nginx, głębokości kolejki i timingów requestów. To wystarczało do diagnozy inżynierskiej, której potrzebowałem, ale nie do ustalenia fizycznej root cause.

Dlaczego wcześniejsze ponad 3 TB ruchu nie przeczy późniejszym stallom

Początkowo mnie to zastanawiało. Skoro storage miał problemy, jak VPS wcześniej z powodzeniem przesłał kilka terabajtów?

Bo ruch sieciowy nie jest tym samym co fizyczne I/O dysku. Plik może zostać raz odczytany z backing storage, pozostać w Linux page cache, a później być wielokrotnie serwowany z pamięci. Trzy terabajty przesłane przez sieć nie oznaczają więc trzech terabajtów unikalnych fizycznych odczytów.

Warunki infrastruktury również zmieniają się w czasie: stan cache, obciążenie storage, kolejki, host placement i inne workloady mogą się zmieniać. To, że VPS działał normalnie wczoraj, nie gwarantuje identycznego zachowania storage dzisiaj.

W końcu przestałem czekać na głębszą root cause

Zbierałem dokładne timestampy, vmstat, iostat, PSI, snapshoty zablokowanych procesów, kernel stacks, filesystem waits, głębokości kolejek i timingi HTTP. Wysłałem diagnostykę do FDCServers i czekałem na głębsze wyjaśnienie po stronie infrastruktury.

Czekałem długo. W końcu przestałem. Ze swojej strony wiedziałem już wystarczająco dużo, by podjąć decyzję operacyjną: problem był powtarzalny, poważny, widoczny poniżej warstwy aplikacji, a fizyczna przyczyna znajdowała się poza tym, co mogłem zobaczyć z VPS.

Poprosiłem o zwrot pieniędzy i FDCServers go wykonał

Wysłałem FDCServers podsumowanie problemów oraz zebranej diagnostyki, poprosiłem o anulowanie usługi i zwrot pieniędzy. Pieniądze zostały zwrócone.

Ta historia nie kończy się więc długą walką o płatność. Czekałem na ostateczne wyjaśnienie techniczne, zdecydowałem, że nie chcę czekać dłużej, wysłałem swoje dowody i poprosiłem o pieniądze z powrotem. FDCServers je zwrócił.

Co zmieniłem później

Najbardziej użytecznym rezultatem nie było ustalenie, czy jedna firma hostingowa jest dobra czy zła. Zmieniło się to, jak diagnozuję wolne serwery Linux.

Nadal używam top, free -h i df -h, ale teraz znacznie wcześniej sięgam po:

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

Gdy to możliwe, testuję też oddzielnie ścieżki requestów: publiczny request, lokalny Nginx, bezpośredni backend, filesystem i metryki block device. Pytanie nie brzmi już tylko: dlaczego serwer jest wolny? Brzmi: na której warstwie użyteczna praca przestaje się kończyć?

Jeśli to możliwe, zbieram dowody przed rebootem. Restart może przywrócić usługę, ale może też usunąć dokładnie te D-state, PSI, kolejki i latency, dzięki którym przerywany incident da się zdiagnozować.

Na koniec

Nie kupiłem VPS w FDCServers po to, żeby mieć materiał do artykułu o hostingu. Potrzebowałem serwera z dużą przepustowością. Przez jakiś czas dostałem dokładnie to: obsługiwał realny workload i przesłał ponad trzy terabajty.

Później normalny workload zaczął powtarzalnie powodować iowait do 100%, I/O PSI prawie 100%, odczyty do 18,7 sekundy, flush latency ponad 53 sekundy, duże kolejki, Nginx zablokowany na filesystem reads oraz EXT4/jbd2 czekające na I/O.

Nigdy nie dowiedziałem się, który fizyczny lub host-side komponent był przyczyną, i nie muszę udawać, że wiem. Ustaliłem warstwę, na której problem się ujawniał, zebrałem wystarczająco dużo dowodów, by odróżnić go od problemu aplikacji, przestałem czekać na głębszą analizę root cause i poprosiłem o zwrot pieniędzy. FDCServers je zwrócił.

To nie jest werdykt na temat każdego VPS w FDCServers. To dobrze udokumentowane przypomnienie, że usługa może wyglądać na active, proces na running, a maszyna może mimo to spędzać niemal cały użyteczny czas na oczekiwaniu na storage.