limity zasobów cgroups v2 z systemd
11 min czytania - 3 czerwca 2026

Ustawianie limitów CPU, pamięci i I/O za pomocą cgroups v2 i systemd. Praktyczna konfiguracja dla hostów Linux z wieloma dzierżawcami, z monitorowaniem PSI i izolacją wycinków.
Limity zasobów cgroups v2 w systemd
cgroups v2 to ujednolicona struktura kontroli zasobów jądra systemu Linux. Zastępuje ono fragmentaryczną hierarchię v1 pojedynczym drzewem, które spójnie obsługuje procesor, pamięć i operacje wejścia/wyjścia oraz stanowi podstawę izolacji kontenerów w Dockerze, Kubernetesie i systemd. Ten post opisuje, jak włączyć cgroups v2, ustawić limity za pomocą systemd i zastosować to w rzeczywistych scenariuszach hostingu wielodostępnego.
Włączanie cgroups v2
Nowoczesne dystrybucje mają domyślnie włączone cgroups v2: Ubuntu 21.10+, Debian 11+, Fedora 31+ oraz RHEL/Rocky 9+. Starsze systemy mogą działać w hierarchii hybrydowej lub nadal domyślnie korzystać z wersji v1. Sprawdź za pomocą:
stat -fc %T /sys/fs/cgroup/
Wynik polecenia cgroup2fs potwierdza, że wersja v2 jest aktywna. tmpfs zazwyczaj oznacza wersję v1.
Aby przełączyć system hybrydowy na czystą wersję v2, edytuj /etc/default/grub i dodaj następujący wpis do GRUB_CMDLINE_LINUX_DEFAULT:
systemd.unified_cgroup_hierarchy=1 cgroup_no_v1=all
Następnie wygeneruj ponownie GRUB i uruchom ponownie system:
sudo update-grub
sudo reboot
W środowisku produkcyjnym uruchom jądro 5.2 lub nowsze, aby uzyskać zamrażarkę cgroup dla v2 oraz systemd 244+ dla pełnej cpuset delegacji. W systemach Rocky Linux 8 i RHEL 8 może być również konieczne wyraźne włączenie rozliczania poprzez dodanie tych wierszy do /etc/systemd/system.conf:
DefaultCPUAccounting=yes
DefaultMemoryAccounting=yes
DefaultIOAccounting=yes
Przeładuj za pomocą sudo systemctl daemon-reexec. Po ponownym uruchomieniu sprawdź, które kontrolery są dostępne:
cat /sys/fs/cgroup/cgroup.controllers
Powinieneś zobaczyć cpu, memory, ioi pids wykaz. Kontrolery te nie są domyślnie włączone dla podrzędnych grup cgroup. Aby je aktywować, zapisz w pliku kontrolnym poddrzewa root:
echo "+cpu +memory +io" | sudo tee /sys/fs/cgroup/cgroup.subtree_control
Aby uzyskać szczegółowy przegląd tego, czym wersja v2 różni się wewnętrznie od wersji v1, najlepszym źródłem informacji jest wykład Michaela Kerriska wygłoszony podczas NDC TechTown:
Jak systemd organizuje cgroups
systemd tworzy cgroup dla każdej uruchamianej usługi, nazywając ją zgodnie z nazwą jednostki. nginx.service pobiera /sys/fs/cgroup/system.slice/nginx.service/, a każdy uruchamiany przez nią proces działa w ramach tej grupy cgroup. Trzy typy jednostek odpowiadają bezpośrednio hierarchii:
| Typ jednostki | Rola | Opis |
|---|---|---|
.slice | Węzeł wewnętrzny | Grupuje powiązane usługi i definiuje wspólne limity |
.service | Węzeł końcowy | Zarządza procesami uruchomionymi przez systemd |
.scope | Węzeł liścia | Śledzi procesy uruchomione zewnętrznie (ładunki kontenerów, sesje logowania) |
Cztery domyślne segmenty są dostarczane w standardzie: -.slice (root), system.slice, user.slicei machine.slice. Każde ograniczenie zastosowane do segmentu automatycznie dotyczy każdej usługi w nim zawartej.
Jedna zasada w wersji 2, o której warto pamiętać: procesy mogą istnieć tylko w węzłach liściowych. Grupa cgroup z podgrupami cgroup nie może bezpośrednio hostować procesów, dlatego systemd nigdy nie umieszcza usług w pniu segmentu.
Zawsze należy ustawiać ograniczenia za pośrednictwem systemd, a nie zapisując je /sys/fs/cgroup/ . Ręczne zapisy nie są zachowywane po ponownym uruchomieniu i kolidują z wyłączną własnością hierarchii przez systemd. Użyj systemctl set-property do jednorazowych zmian i wtyczek jednostek (systemctl edit nginx.service) do zmian stałych.
Ograniczenia procesora
cgroups v2 oferuje dwie opcje kontroli procesora: twardy limit (cpu.max, udostępniony jako CPUQuota w systemd) oraz proporcjonalną wagę (cpu.weight / CPUWeight).
CPUQuota jest to absolutny pułap. CPUQuota=50% pozwala na wykorzystanie połowy rdzenia; CPUQuota=200% pozwala na wykorzystanie czasu odpowiadającego dwóm pełnym rdzeniom. Usługa jest ograniczana, jeśli próbuje przekroczyć ten limit, niezależnie od tego, jak bardzo reszta procesora jest niewykorzystana.
CPUWeight ma znaczenie tylko w przypadku rywalizacji o zasoby. Zakres wynosi od 1 do 10 000, domyślnie 100. Trzy usługi o wagach 150, 100 i 50 otrzymują w przybliżeniu 50%, 33% i 17% czasu procesora, gdy wszystkie chcą go w tym samym czasie. Gdy procesor jest w innym przypadku bezczynny, wagi niczego nie ograniczają.
W przypadku obciążeń wrażliwych na opóźnienia, przypisuj procesy do konkretnych rdzeni za pomocą AllowedCPUs=. Zmniejsza to liczbę przełączania kontekstu i utrzymuje pamięć podręczną każdego rdzenia w stanie aktywnym:
[Service]
CPUQuota=200%
CPUWeight=150
AllowedCPUs=0-3
Użyj twardego limitu, gdy potrzebujesz przewidywalnych kosztów (rozliczenia wielodostępne, izolacja od hałaśliwych sąsiadów). Użyj wag, gdy chcesz maksymalnie wykorzystać sprzęt i potrzebujesz ustalenia kolejności priorytetów tylko podczas szczytów obciążenia.
Ograniczenia pamięci w cgroups v2
Pamięć ma dwa poziomy: memory.high (miękki, ograniczający przepustowość) oraz memory.max (twardy, OOM). Aby uzyskać informacje na temat pamięci wymiany, odzyskiwania stron i mechanizmu OOM killer jądra, zapoznaj się z naszym dodatkowym wpisem na temat zarządzania pamięcią w systemie Linux.
Ustaw memory.high około 10 do 20% poniżej memory.max. Jądro zaczyna odzyskiwać strony i dławić alokacje po memory.high , co zazwyczaj pozwala na przywrócenie wydajności obciążenia, zanim uruchomi się mechanizm OOM. Jeśli zużycie osiągnie memory.max, jądro zabija procesy w cgroup.
Typowa konfiguracja:
[Service]
MemoryHigh=400M
MemoryMax=512M
MemorySwapMax=0
MemorySwapMax=0 wyłącza swap dla tej cgroup. Warto to zrobić w przypadku obciążeń wrażliwych na opóźnienia (bazy danych, strumieniowanie w czasie rzeczywistym), gdzie operacje wejścia/wyjścia na swapie mogłyby znacznie zwiększyć opóźnienie ogona.
W przypadku pul pracowników, gdzie pozostawienie osieroconych procesów siostrzanych mogłoby uszkodzić stan współdzielony, należy wpisać 1 do pliku memory.oom.group . Gdy jeden proces zostanie zabity z powodu braku pamięci (OOM), jądro zabija jednocześnie wszystkie procesy w cgroup.
Sprawdź memory.events , aby sprawdzić, jak często usługa była ograniczana lub zabijana przez OOM:
cat /sys/fs/cgroup/system.slice/nginx.service/memory.events
Pole high i oom_kill wskazują, czy limity są ustawione prawidłowo. Trwałe wartości niezerowe oznaczają, że obciążenie wymaga większego zapasu mocy obliczeniowej.
Ograniczenia we/wy
Kontroler I/O ma tę samą konstrukcję dwumodową: bezwzględne ograniczenia poprzez io.max oraz proporcjonalne współdzielenie poprzez io.weight.
Limity dotyczą poszczególnych urządzeń blokowych, identyfikowanych przez numery major:minor. Znajdź je za pomocą lsblk -o NAME,MAJ:MIN. Typowa konfiguracja systemd:
[Service]
IOReadBandwidthMax=/dev/sda 50M
IOWriteBandwidthMax=/dev/sda 30M
IOReadIOPSMax=/dev/sda 1000
IOWriteIOPSMax=/dev/sda 500
io.weight działa tak jak cpu.weight: zakres od 1 do 10 000, domyślnie 100. Przypisanie wartości 500 do usługi dla klientów i 50 do nocnej kopii zapasowej zapobiega przeciążeniu dysku przez kopię zapasową w godzinach szczytu, ale pozwala jej wykorzystać pełną przepustowość, gdy niczego innego nie potrzeba.
Limity I/O mają zastosowanie tylko wtedy, gdy są skierowane do właściwego urządzenia. Jądro śledzi operacje I/O według urządzeń blokowych, więc limit na /dev/sda nie ma wpływu na operacje wejścia/wyjścia kierowane do /dev/nvme0n1. Na hostach z wieloma dyskami należy ustawić limity dla poszczególnych urządzeń.
Izolacja wielodostępna z wykorzystaniem segmentów
W przypadku środowisk współdzielonych należy zdefiniować segment dla każdego dzierżawcy. Utworzenie /etc/systemd/system/tenant-a.slice:
[Slice]
CPUQuota=200%
CPUWeight=150
MemoryHigh=3584M
MemoryMax=4096M
MemorySwapMax=0
IOReadBandwidthMax=/dev/sda 200M
TasksMax=512
TasksMax=512 ogranicza całkowitą liczbę procesów i wątków, co zapobiega sytuacji, w której fork bomb u jednego dzierżawcy powoduje awarię hosta. Umieść usługi dzierżawcy w tym segmencie (poprzez Slice=tenant-a.slice w ich plikach jednostkowych), a automatycznie odziedziczą one wszystkie ustawienia.
Ten wzorzec sprawdza się również przy oddzielaniu hałaśliwych zadań w tle od usług widocznych dla użytkownika. Umieść kopie zapasowe, rotację logów i zadania wsadowe w background.slice o niskim CPUWeight i io.weight . Otrzymują one pełne zasoby, gdy system jest w stanie bezczynności, i ustępują miejsca, gdy pojawia się ruch produkcyjny.
W przypadku środowisk uruchomieniowych kontenerów, takich jak Docker i Podman, dodaj Delegate=yes do ich plików jednostek systemd. Pozwala to im zarządzać własnymi podgrupami cgroups bez uprawnień roota, a limity ustawione na nadrzędnym segmencie nadal mają zastosowanie do wszystkich elementów poniżej.
Monitorowanie za pomocą systemd-cgtop i PSI
Aby uzyskać podgląd na żywo w stylu top dotyczący procesora, pamięci i operacji wejścia/wyjścia dla poszczególnych grup cgroup, uruchom:
systemd-cgtop
Aby uzyskać statyczną hierarchię i informacje o tym, gdzie znajdują się poszczególne procesy, użyj systemd-cgls.
Najbardziej przydatną funkcją wersji 2 do monitorowania produkcji jest Pressure Stall Information (PSI). PSI podaje procent czasu, w którym zadania w cgroup były wstrzymane w oczekiwaniu na zasób, co jest widoczne w trzech plikach dla każdej cgroup:
cat /sys/fs/cgroup/tenant-a.slice/cpu.pressure
cat /sys/fs/cgroup/tenant-a.slice/memory.pressure
cat /sys/fs/cgroup/tenant-a.slice/io.pressure
Procesor o 100% wykorzystaniu i 0% presji działa prawidłowo. Każde zadanie, które potrzebuje procesora, otrzymuje go. Ten sam procesor o 80% wykorzystaniu, ale 30% presji oznacza, że zadania czekają w kolejce na czas działania. Należy monitorować PSI, a nie wykorzystanie: wykrywa ono konflikty, które wskaźniki wykorzystania całkowicie pomijają.
Dostosuj limity na żywo bez konieczności ponownego uruchamiania czegokolwiek:
sudo systemctl set-property tenant-a.slice MemoryMax=6144M
Zmiana obowiązuje natychmiast i jest zachowywana po ponownym uruchomieniu. W połączeniu z alertami opartymi na PSI pozwala to reagować na zmiany obciążenia, zanim przerodzą się one w zabijanie procesów z powodu braku pamięci (OOM) lub niekontrolowane opóźnienia.
Jeśli obsługujesz obciążenia wielodostępne o dużej gęstości i potrzebujesz hosta z wystarczającą rezerwą mocy, aby płynnie stosować te zasady, nasze serwery dedykowane są stworzone właśnie do tego.
Dlaczego ważne jest posiadanie wydajnego i niezmierzonego serwera VPS?
Niezmierzony VPS zapewnia zryczałtowaną przepustowość przy stałej prędkości portu. Czym różnią się one od planów taryfowych, kiedy się opłacają i co należy sprawdzić przed zakupem.
7 min czytania - 9 maja 2025
Zarządzanie pamięcią w systemie Linux: Swap, OOM Killer i Cgroups
12 min czytania - 31 maja 2026

Masz pytania lub potrzebujesz niestandardowego rozwiązania?
Elastyczne opcje
Globalny zasięg
Natychmiastowe wdrożenie
Elastyczne opcje
Globalny zasięg
Natychmiastowe wdrożenie