Przejdź do treści
Lekcja 60 z 117

Kurs › Moduł 6 › Lekcja 60

Swap

⏱ ok. 11 min czytania· moduł 6: Dyski i systemy plików

Po tej lekcji: kursant wie, czym jest przestrzeń wymiany, zakłada swap w pliku i na partycji (mkswap, swapon, swapoff), dopisuje go na stałe do /etc/fstab i świadomie ustawia vm.swappiness.

Wstęp

htop, pasek pamięci czerwony do końca, serwer ledwo dyszy

Piątek, szesnasta. Serwer z bazą danych zaczyna się zachowywać jak komputer babci z 2008 roku. Wszystko działa, ale każda komenda trwa wieczność. Patrzysz w free -h i widzisz: pamięć pełna, a w wierszu Swap zero. Brak zapasu.

Pięć minut później w logach pojawia się wpis, że jądro zabiło proces bazy danych, żeby ratować resztę. Baza leży. Weekend się właśnie skończył, zanim się zaczął.

Dziś zbudujemy ten zapas. Plik wymiany i partycję wymiany. Zobaczysz, jak go włączyć, jak wyłączyć, jak zapisać na stałe i jak powiedzieć jądru, jak chętnie ma z niego korzystać.

Swap, czyli piwnica przy kuchni

Wracamy do kuchni. Proces to kucharz, a pamięć RAM to blat, na którym kucharz trzyma wszystko, czego właśnie używa. Blat jest szybki: sięgasz ręką i masz.

Ale blat ma swoją długość. Kiedy kucharzy przybywa, a każdy rozkłada swoje garnki, miejsce się kończy. I co wtedy? Ktoś bierze rzeczy, których od dawna nikt nie dotykał, i znosi je do piwnicy. Słoik z ogórkami z zeszłego roku. Gofrownicę używaną raz na pół roku. Blat się zwalnia, kuchnia pracuje dalej.

Ta piwnica to swap, po polsku przestrzeń wymiany. Kawałek dysku, na który jądro wynosi z RAM-u to, co jest najdawniej nieużywane.

Tylko że piwnica jest daleko. Jak kucharz nagle potrzebuje ogórków, ktoś musi po nie zejść po schodach. Dysk jest wiele razy wolniejszy niż RAM. Dlatego swap to koło ratunkowe, a nie dodatkowa pamięć. Serwer, który ciągle biega do piwnicy, jest jak restauracja, w której kelner co chwilę znika na dwadzieścia minut. Formalnie działa. Klienci już niekoniecznie.

Ile mam piwnicy?

free -h
swapon --show
cat /proc/swaps
               total        used        free      shared  buff/cache   available
Mem:           7.8Gi       660Mi       7.1Gi        15Mi       335Mi       7.2Gi
Swap:             0B          0B          0B

free -h pokazuje wiersz Swap. Zero znaczy, że nie ma żadnego swapu. swapon --show wypisuje wszystkie włączone obszary wymiany, a jak ich nie ma, nic nie wypisuje. Znowu cisza, tylko tym razem niekoniecznie pochwała.

Na świeżych systemach zwykle coś już jest. [Ubuntu] Instalator serwera lubi zakładać plik /swap.img w głównym systemie plików. [RHEL] Przy automatycznym podziale dysku instalator robi osobną przestrzeń wymiany i dobiera jej rozmiar do ilości RAM-u. W chmurze i w kontenerach często nie ma żadnego swapu, bo dostawca woli, żebyś kupił większą maszynę.

Ile tej piwnicy potrzeba?

Red Hat w dokumentacji podaje taką tabelkę:

RAMZalecany swapSwap, gdy chcesz hibernację
poniżej 2 GB2 × RAM3 × RAM
2–8 GBtyle co RAM2 × RAM
8–64 GBco najmniej 4 GB1,5 × RAM
powyżej 64 GBzależy od obciążenia, co najmniej 4 GBhibernacja niezalecana

Kto pamięta zasadę „swap ma być dwa razy większy niż RAM”, ten pamięta czasy, kiedy RAM-u było 512 MB. Serwer z 256 GB RAM-u i 512 GB swapu to jak dom z garażem na jeden samochód i piwnicą na dwanaście.

Swap w pliku: krok po kroku

Najprostsza droga. Nie trzeba ruszać partycji, wystarczy wolne miejsce w systemie plików.

dd pisze tam, gdzie mu każesz, bez pytania. Jedna literówka w of= i nadpisujesz coś ważnego. Ćwicz na maszynie wirtualnej i czytaj komendę dwa razy przed Enterem.

sudo dd if=/dev/zero of=/swapfile bs=1M count=1024
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
swapon --show
Setting up swapspace version 1, size = 1024 MiB (1073737728 bytes)
no label, UUID=fb4c0d69-41dc-4d07-a03d-5f769ecebcef
NAME      TYPE SIZE USED PRIO
/swapfile file   1G   0B   -2

Po kolei:

  • dd robi plik o rozmiarze 1 GB wypełniony zerami. if to skąd czytać, of to dokąd pisać, bs to wielkość kawałka, count ile kawałków.
  • chmod 600, bo w swapie lądują kawałki pamięci procesów. Hasła, klucze, dane klientów. Plik czytelny dla wszystkich to jak piwnica z drzwiami na oścież i napisem „Zapraszamy”.
  • mkswap zakłada w pliku nagłówek swapu. To jak namalowanie na podłodze piwnicy kratek z numerami półek. Bez tego jądro nie wie, jak tego miejsca używać.
  • swapon włącza swap. Od tej chwili jądro może tam wynosić strony.

Czemu dd, a nie szybsze fallocate? Dokumentacja swapon mówi wprost: najbardziej przenośny sposób to dd z /dev/zero. Plik zrobiony przez fallocate na niektórych systemach plików jądro uzna za dziurawy. [dla zaawansowanych: na ext4 fallocate działa, na XFS od jądra 4.18. Na Btrfs plik wymiany musi mieć atrybut nocow, a nowe mkswap --file potrafi go od razu takim założyć.]

Swap na stałe

swapon działa do restartu. Po restarcie piwnica znowu jest zamknięta na kłódkę. Żeby była otwierana przy starcie, dopisujemy ją do planu biurowca, czyli do /etc/fstab:

echo '/swapfile none swap defaults 0 0' | sudo tee -a /etc/fstab
sudo systemctl daemon-reload
sudo swapon -a
swapon --show

Czym ta linijka różni się od tych z lekcji o montowaniu? Punkt montowania to none, bo swapu nigdzie nie podpinamy jako folderu. Typ to swap. A dwa zera na końcu, bo swapu się nie archiwizuje i nie sprawdza fsck. swapon -a włącza wszystko, co jest w fstab z typem swap, i jest odpowiednikiem mount -a. Jak przejdzie bez błędu, restart też przejdzie.

Swap na partycji

Na serwerach często swap ma własną partycję albo, jak zobaczysz w następnej lekcji, własny wolumin LVM. Po stronie komend różnica jest jedna: zamiast pliku podajesz urządzenie.

mkswap na złej partycji kasuje to, co na niej było. Sprawdź lsblk dwa razy.

Na mojej maszynie ćwiczebnej to dysk /dev/loop0 z partycją założoną w parted, tak jak w lekcji o partycjach:

sudo parted -s /dev/loop0 mklabel gpt mkpart swap linux-swap 1MiB 100%
sudo mkswap -L swapdysk /dev/loop0p1
sudo blkid /dev/loop0p1
sudo swapon -p 10 /dev/loop0p1
swapon --show
Setting up swapspace version 1, size = 199 MiB (208642048 bytes)
LABEL=swapdysk, UUID=92dc621a-ac66-45ea-975a-4da1677c9814
/dev/loop0p1: LABEL="swapdysk" UUID="92dc621a-ac66-45ea-975a-4da1677c9814" TYPE="swap" PARTLABEL="swap" PARTUUID="70d7fe9c-6eba-4144-b5c9-62303b6e14bd"
NAME          TYPE      SIZE USED PRIO
/dev/loop0p1  partition 199M   0B   10
/swapfile     file        1G   0B   -2

-L nadaje etykietę. Najważniejszy jest jednak UUID, bo to on trafia do fstab, dokładnie jak przy systemach plików:

UUID=92dc621a-ac66-45ea-975a-4da1677c9814  none  swap  defaults,pri=10  0 0

A -p 10 i pri=10? To priorytet. Kiedy masz kilka piwnic, jądro najpierw zapełnia tę z wyższym priorytetem. Szybki dysk SSD dostaje wyższy, wolny talerzowy niższy. Obszary z tym samym priorytetem jądro zapełnia na zmianę, trochę jak RAID 0, o którym za dwa lekcje.

Wyłączanie: swapoff

sudo swapoff /swapfile
sudo swapoff -a

swapoff zamyka piwnicę. Ale najpierw wszystko, co w niej leży, musi wrócić na blat. Jeśli w swapie jest 3 GB, a wolnego RAM-u masz 1 GB, swapoff się nie uda, bo nie ma gdzie tego położyć. I może chwilę potrwać, bo wnoszenie po schodach jest wolne.

Po co wyłączać? Żeby zmienić rozmiar pliku, usunąć go albo przenieść. Kolejność zawsze ta sama: swapoff, potem zmiany, potem mkswap i swapon. Kasowanie pliku wymiany, który jest włączony, to jak burzenie piwnicy, kiedy ktoś w niej stoi.

vm.swappiness: jak chętnie schodzić do piwnicy

Jądro samo decyduje, kiedy wynieść rzeczy do piwnicy. Ale można mu powiedzieć, jak chętnie ma to robić. Służy do tego ustawienie vm.swappiness.

Wyobraź sobie pomocnika kuchennego. Przy niskiej wartości to leń: zniesie coś do piwnicy dopiero wtedy, kiedy na blacie naprawdę nie ma miejsca. Przy wysokiej to pedant, który od razu wynosi wszystko, czego nikt nie dotyka.

cat /proc/sys/vm/swappiness
sysctl vm.swappiness
sudo sysctl -w vm.swappiness=10
60
vm.swappiness = 60
vm.swappiness = 10

Domyślnie jest 60. Na nowszych jądrach skala idzie od 0 do 200, a 100 znaczy dla jądra, że wynoszenie do swapu i ponowne czytanie plików z dysku kosztują tyle samo. Spróbuj wpisać 201:

sysctl: setting key "vm.swappiness": Invalid argument

Jądro grzecznie odmawia. Jak bramkarz przy wadze 201 kilo na ringu do 200.

Kiedy zmieniać? Na serwerach baz danych często ustawia się niską wartość, na przykład 10, żeby baza trzymała swoje dane w RAM-ie jak najdłużej. Zero nie znaczy „nigdy nie używaj swapu”, tylko „używaj dopiero w ostateczności”. Nie kręć tym na ślepo: zmień, obserwuj, porównaj.

sysctl -w działa do restartu. Na stałe zapisujesz to w pliku w /etc/sysctl.d/:

echo 'vm.swappiness = 10' | sudo tee /etc/sysctl.d/99-swappiness.conf
sudo sysctl --system

[RHEL] [Ubuntu] Tak samo na obu rodzinach. Plik w sysctl.d przeżyje aktualizacje lepiej niż grzebanie w głównym /etc/sysctl.conf.

Kto siedzi w piwnicy?

vmstat 1 5
grep VmSwap /proc/*/status | sort -k2 -n | tail

W vmstat patrz na kolumny si i so: swap in i swap out, ile na sekundę wnoszone i wynoszone. Pojedyncze ruchy są w porządku. Kiedy obie kolumny przez minuty pokazują duże liczby, serwer ciągle biega po schodach w górę i w dół. To ma nazwę: thrashing, młócenie. Rozwiązanie to nie większy swap, tylko więcej RAM-u albo mniej chętnych do jedzenia procesów.

Druga komenda pokazuje, ile każdy proces ma w swapie. Folder /proc z lekcji o mapie Linuksa znowu się przydaje.

Jak to działa pod spodem

mkswap zapisuje na początku pliku albo partycji nagłówek z wersją, rozmiarem, UUID i etykietą. swapon to wywołanie systemowe o tej samej nazwie. Jądro dostaje plik albo urządzenie i od tej pory zapisuje tam strony pamięci bezpośrednio, z pominięciem systemu plików. Dlatego plik musi mieć wszystkie bloki fizycznie przydzielone. Dziury jądra nie interesują, bo pisze prosto w konkretne miejsca na dysku.

Kiedy RAM się kończy, jądro szuka stron najdawniej używanych. Strony z plików po prostu wyrzuca, strony danych procesów zapisuje do swapu. Kiedy proces znowu sięga po taką stronę, procesor zgłasza błąd strony, jądro przynosi ją z piwnicy, a proces nawet nie wie, że coś się wydarzyło. Po prostu czekał trochę dłużej.

A kiedy skończy się i RAM, i swap? Wtedy wkracza OOM killer, czyli zabójca od braku pamięci. Wybiera proces, który zwolni najwięcej, i wyłącza mu prąd, czyli wysyła SIGKILL. Ślad zostaje w dzienniku jądra: journalctl -k | grep -i oom. Swap nie chroni przed OOM killerem na zawsze. Daje Ci czas, żeby zauważyć problem, zanim zrobi to klient.

Ściąga flag

KomendaFlagaCo robi
mkswap-L NAZWAnadaje etykietę obszarowi wymiany
mkswap-U UUIDustawia konkretny UUID
swapon--showpokazuje włączone obszary wymiany
swapon-awłącza wszystkie obszary swap z /etc/fstab
swapon-p Nustawia priorytet (wyższy używany pierwszy)
swapon-vgada, co robi
swapoffPLIK/URZĄDZENIEwyłącza jeden obszar
swapoff-awyłącza wszystkie obszary
free-hpamięć i swap w czytelnych jednostkach
sysctl-w klucz=wartośćzmienia ustawienie jądra do restartu
sysctl--systemwczytuje wszystkie pliki z /etc/sysctl.d/
vmstat1 5statystyki co sekundę, pięć razy; kolumny si/so

⚠ Typowy błąd

Pierwszy klasyk: plik zrobiony, mkswap zapomniane.

sudo fallocate -l 128M /swapfile2
sudo swapon /swapfile2
swapon: /swapfile2: insecure permissions 0644, 0600 suggested.
swapon: /swapfile2: read swap header failed

Dwa błędy naraz. Pierwszy: uprawnienia. Plik jest czytelny dla wszystkich, a w swapie mogą leżeć hasła. Drugi: „nie umiem przeczytać nagłówka swapu”, bo go po prostu nie ma. Piwnica bez kratek na podłodze. Naprawa: chmod 600, potem mkswap, potem swapon.

Drugi klasyk: ktoś zrobił plik przez truncate, bo było szybciej.

swapon: /swapfile: skipping - it appears to have holes.

truncate robi plik dziurawy: system plików tylko udaje, że ma 1 GB, a naprawdę nie przydzielił żadnego bloku. Jak piwnica narysowana na planie budynku, której nikt nie wykopał. Jądro chce pisać prosto w bloki na dysku, więc odmawia. Naprawa: swapoff, jeśli coś jest włączone, usunąć plik i zrobić go od nowa przez dd.

I trzeci, cichy: swap włączony ręcznie, a linijki w fstab brak. Wszystko działa do pierwszego restartu, a potem serwer znowu nie ma piwnicy. Sprawdzaj zawsze swapon -a po edycji fstab.

✎ Zadanie

Na maszynie wirtualnej:

  1. Sprawdź, czy masz już swap i ile (free -h, swapon --show).
  2. Zrób dodatkowy plik wymiany /swapfile2 o rozmiarze 512 MB, z dobrymi uprawnieniami, włącz go i dopisz na stałe do /etc/fstab.
  3. Sprawdź swapon -a i swapon --show.
  4. Ustaw na stałe vm.swappiness na 20 i sprawdź po restarcie, że zostało.
Pokaż rozwiązanie
free -h
swapon --show
sudo dd if=/dev/zero of=/swapfile2 bs=1M count=512
sudo chmod 600 /swapfile2
sudo mkswap /swapfile2
sudo swapon /swapfile2
echo '/swapfile2 none swap defaults 0 0' | sudo tee -a /etc/fstab
sudo swapon -a
swapon --show
echo 'vm.swappiness = 20' | sudo tee /etc/sysctl.d/99-swappiness.conf
sudo sysctl --system

Po restarcie:

sysctl vm.swappiness
swapon --show
vm.swappiness = 20

I w swapon --show nowy plik obok starego swapu. Zwróć uwagę, że do sysctl.d pisałem tee bez -a: to osobny plik tylko na to jedno ustawienie, więc można go spokojnie nadpisać. A do fstab z -a, bo nadpisanie fstab jedną linijką o swapie to najkrótsza droga do ciekawego poranka. Serwer wstaje, ale bez dysków. Jak kierowca, który dojechał do pracy, tylko autobus zostawił w zajezdni.

Co dalej

Partycja to ściana z cegły: postawisz, to już stoi. A co, jeśli za pół roku zabraknie miejsca? W następnej lekcji LVM, czyli regały na kółkach, które przesuwasz i powiększasz bez przeprowadzki. A na deser szyfrowanie dysku LUKS, żeby skradziony laptop był dla złodzieja tylko cegłą.