W tym przewodniku po cgroups Hosting Pokażę ci konkretnie, jak izolować zasoby serwera za pomocą grup kontrolnych Linuksa, aby „hałaśliwi sąsiedzi“ nie spowalniali żadnych usług. Dowiesz się, jak ograniczać, ustalać priorytety i niezawodnie monitorować procesor, pamięć RAM, blokowe wejścia/wyjścia i sieć dla każdej witryny, kontenera lub użytkownika - w praktyczny i możliwy do wdrożenia sposób.
Punkty centralne
Poniższe kluczowe aspekty przeprowadzą Cię przez najważniejsze decyzje i kroki.
- IzolacjaCzyste oddzielanie procesów i oswajanie sąsiadów
- KontrolaOgraniczenie CPU, RAM, I/O i urządzeń w ukierunkowany sposób
- PriorytetWażenie i ochrona usług premium
- PrzejrzystośćPomiar obciążenia, korzystanie z alarmów i trendów
- AktualizacjaOd v1 do v2 dla przejrzystego zarządzania
Jak grupy cgroup odłączają zasoby serwera
Grupy kontrolne organizują procesy w grupy i łączą te grupy ze sterownikami zasobów, co pozwala mi na Zasoby na grupę. Na serwerze współdzielonym zapobiega to sytuacji, w której pojedyncza witryna pochłania czas procesora lub wypełnia pamięć po brzegi. W tym celu tworzę hierarchie, w których grupy nadrzędne określają górne limity, które są dziedziczone przez grupy podrzędne. Dzięki temu rozkład obciążenia jest spójny, a wąskie gardła pozostają na uboczu. Używam tego w szczególności do zauważalnego złagodzenia problemu „hałaśliwego sąsiada“, ponieważ silne skoki przebiegają w odizolowanych ścieżkach.
Kontroler i system plików: narzędzia pracy
Praktyczna praca zaczyna się w systemie plików cgroup pod /sys/fs/cgroup, gdzie tworzę grupy i ustawiam limity, tj. konkretne System sterowania zajmują się. Używam kontrolerów takich jak cpu, memory, blkio, cpuset i devices, aby przydzielać wycinki czasu, pokrywać RAM, spowalniać I/O, przypinać rdzenie lub blokować urządzenia. Łączę te bloki konstrukcyjne w zależności od aplikacji: na przykład aplikacje wymagające dużej ilości pamięci z twardymi limitami pamięci RAM, zadania kompilacji z wagą procesora i bazy danych z przepustowością we / wy. Jasny schemat nazewnictwa jest ważny, aby móc później szybko znaleźć grupy. Dzięki temu administracja jest łatwa w zarządzaniu, a zmiany nie są tracone z oczu.
| Podsystem | Cel |
|---|---|
| cpu / cpuacct | Przydzielanie czasu procesora, Wagi i ustawić kwoty |
| pamięć | Ograniczenie pamięci RAM, unikanie zabójstw OOM |
| blkio | Blok przepustnicy we/wy, kontrola szybkości odczytu/zapisu |
| zestaw procesorów | Przypisywanie rdzeni CPU i węzłów NUMA |
| urządzenia | Zezwalanie lub blokowanie dostępu do urządzenia |
| net_cls / net_prio | Oznaczanie i nadawanie priorytetów klasom sieci |
cgroups v1 vs v2 w hostingu
Starsza wersja v1 rozdziela kontrolery na kilka drzewek, co szybko okazało się mylące w dużych konfiguracjach, więc zdecydowałem się użyć Konwersja do v2. cgroups v2 łączy wszystko w przejrzyste drzewo, a tym samym upraszcza administrację, debugowanie i dziedziczenie. Ponadto cpu.max, cpu.weight i memory.max w wersji v2 zapewniają spójny zestaw dźwigni, które dobrze ze sobą współpracują. Orkiestratory kontenerów również wolą używać wersji v2, ponieważ semantyka jest bardziej znormalizowana. W przypadku środowisk hostingowych z wieloma klientami, wersja v2 jest zatem szczuplejszym i bardziej niezawodnym wyborem.
Precyzyjna kontrola w cgroups v2: io, pamięć, pids i PSI
W wersji 2 aktywuję kontrolery jawnie dla każdego poddrzewa, dzięki czemu zyskuję większą kontrolę nad dziedziczeniem. Tylko wtedy, gdy zezwalam na nie w węźle nadrzędnym, mogę ich używać w grupach podrzędnych - zapobiega to niekontrolowanemu wzrostowi i zapewnia czyste zasady.
# Aktywacja kontrolera w węźle głównym dla dzieci
echo "+cpu +io +memory +pids" > /sys/fs/cgroup/cgroup.subtree_control
# Utwórz podgrupę i użyj jej jako obszaru roboczego
mkdir /sys/fs/cgroup/prod-web
Dla I/O, używam kontrolera io w v2 (zamiast blkio). Ustawiam limity przepustowości lub IOPS na urządzenie za pomocą specyfikacji major:minor. Zapewnia to, że dzienniki, indeksy lub kopie zapasowe nie zapychają dysku.
Urządzenie # dla /dev/sda (8:0) ograniczenie do 50 MB/s odczyt, 20 MB/s zapis
echo "8:0 rbps=50M wbps=20M" > /sys/fs/cgroup/prod-web/io.max
# Przydziel wagi relatywnie (zamiast twardego limitu, 100-10000, domyślnie 100)
echo 500 > /sys/fs/cgroup/prod-web/io.weight
Ustawiam pamięć w dwóch etapach: memory.high spowalnia wszystko wcześnie, memory.max jest twardym ograniczeniem. Dezaktywuję również swap dla krytycznych usług, aby opóźnienia nie eksplodowały.
# Miękki hamulec (miękki limit) i twarda pokrywa
echo $((400*1024*1024)) > /sys/fs/cgroup/prod-web/memory.high
echo $((512*1024*1024)) > /sys/fs/cgroup/prod-web/memory.max
# zakaz wymiany (0) lub ograniczenie (np. 128 MB)
echo 0 > /sys/fs/cgroup/prod-web/memory.swap.max
Używam kontrolera pids, aby zapobiegać burzom forków i utrzymywać górny limit procesów i wątków na klienta - jest to skuteczna ochrona przed błędną konfiguracją i niewłaściwym użyciem w środowiskach współdzielonych.
# Maksymalnie 512 procesów/wątków w grupie
echo 512 > /sys/fs/cgroup/prod-web/pids.max
Do diagnostyki używam cgroup.events i PSI (Pressure Stall Information) dla każdej grupy. PSI pokazuje mi, czy CPU, pamięć lub I/O są regularnie na niskim poziomie i powodują czasy oczekiwania. Jest to bardziej wartościowe niż czyste wykorzystanie, ponieważ sprawia, że wąskie gardła są widoczne, zanim zauważą je użytkownicy.
# Odczyt zdarzeń i ciśnień
cat /sys/fs/cgroup/prod-web/cgroup.events
cat /sys/fs/cgroup/prod-web/cpu.pressure
cat /sys/fs/cgroup/prod-web/memory.pressure
cat /sys/fs/cgroup/prod-web/io.pressure
W sytuacjach OOM łączę zabójstwa specjalnie z memory.oom.group, aby powiązane procesy (np. worker + helper) były kończone konsekwentnie, zamiast wprowadzać system w stan częściowego paraliżu. W przypadku okien konserwacyjnych zamrażam grupy na krótko za pomocą cgroup.freeze, a następnie ponownie je odmrażam - przydatne przy migracjach baz danych lub rolloutach atomowych.
Zachowanie # OOM w całej grupie
echo 1 > /sys/fs/cgroup/prod-web/memory.oom.group
# Wstrzymywanie/wznawianie grupy
echo 1 > /sys/fs/cgroup/prod-web/cgroup.freeze
echo 0 > /sys/fs/cgroup/prod-web/cgroup.freeze
Krok po kroku: Ustawianie limitów w systemie Linux
Na serwerach z aktualnym jądrem aktywuję cgroup v2, a następnie tworzę grupy z odpowiednimi górnymi limitami, co pozwala mi na Kontrola bezpośrednio w systemie. Poniższe polecenia pokazują minimalistyczny przykład z limitami CPU i RAM. Wybieram limity zachowawczo, monitoruję obciążenie i dostosowuję je w iteracjach. W ten sposób utrzymuję czasy reakcji na niskim poziomie bez niepotrzebnego skracania użytecznych faz burst. Implementacja pozostaje blisko jądra i działa niezależnie od oprogramowania panelu.
# mount cgroup v2 (jeśli nie automatycznie przez systemd)
mount -t cgroup2 none /sys/fs/cgroup
Utwórz grupę #
mkdir /sys/fs/cgroup/prod-web
Przypisz proces # (przykład: PID 1234) do grupy
echo 1234 > /sys/fs/cgroup/prod-web/cgroup.procs
Przydział procesora #: 100 ms z 200 ms => 50%
echo "100000 200000" > /sys/fs/cgroup/prod-web/cpu.max
Twardy limit pamięci RAM #: 512 MB
echo $((512*1024*1024)) > /sys/fs/cgroup/prod-web/memory.max
Integracja z Systemd i trwałe warstwy
W codziennym życiu pozwalam systemd zarządzać drzewem cgroup. Więc limity pozostają trwały i są udokumentowane w przejrzysty sposób dla każdej usługi. Pracuję z plasterkami (system.slice, user.slice, machine.slice) i definiuję własne hierarchie dla klientów lub ról. Używam plików drop-in do ustawiania wag i limitów bez konieczności ręcznego pisania w /sys/fs/cgroup.
# Przykład: utwórz własny slice dla usług sieciowych
cat > /etc/systemd/system/web.slice < /etc/systemd/system/php-fpm.service.d/10-slice.conf <<'EOF'
[Usługa]
Slice=web.slice
EOF
systemctl daemon-reload
systemctl restart php-fpm.service
W przypadku testów ad-hoc uruchamiam procesy w zakresach bez pisania plików jednostkowych. Nadaje się to do symulowania szczytów obciążenia lub testowania limitów przez krótki czas.
# Krótkie ustawienie zasobów i uruchomienie procesu pod kontrolą
systemd-run --scope -p CPUWeight=200 -p MemoryMax=512M \
-p IOReadBandwidthMax=/dev/sda:50M -p IOWriteBandwidthMax=/dev/sda:20M \
stress-ng --vm 1 --vm-bytes 300M --cpu 1
Kontenerowe środowiska uruchomieniowe zarządzają własnymi plasterkami (machine.slice) w ramach systemd. Delegowanie jest tutaj ważne: pozwalam usłudze środowiska uruchomieniowego zarządzać poddrzewem (delegować), aby strąki/kontenery były czysto odizolowane. Oznacza to, że polityki hosta i kontenera nie nakładają się na siebie.
Bezpieczne korzystanie z limitów pojemników
W środowiskach kontenerowych, cgroups działają jak niewidzialne barierki, które ustawiam za pomocą parametrów runtime i w ten sposób Pojemnik do sprawiedliwych limitów. Docker mapuje na przykład opcje -cpus, -memory i -blkio bezpośrednio na cgroups, podczas gdy Kubernetes tłumaczy żądania i limity na parametry v2. Spójność jest kluczowa: limity muszą odpowiadać rzeczywistemu obciążeniu, aby strąki i kontenery nie dławiły się niepotrzebnie lub nie powodowały błędów OOM. Utrzymuję bardziej restrykcyjne limity dla usług produkcyjnych, podczas gdy zadania kompilacji lub testowania otrzymują większy budżet tylko w razie potrzeby. Zapewnia to przewidywalność wdrożeń i zapobiega ich przeciąganiu.
Unikaj hostingu współdzielonego i hałaśliwych sąsiadów
W środowiskach współdzielonych ustawiam limity na poziomie pakietu lub subskrypcji, dzięki czemu mogę Sprawiedliwość między klientami. Panele takie jak Plesk ułatwiają to dzięki menedżerowi Cgroups, który przydziela CPU, RAM i I/O na subskrypcję i wyświetla je wizualnie. Aktywuję również powiadomienia, aby natychmiast rozpoznawać i reagować na szczyty obciążenia. Jeśli chcesz szczegółowo porównać Tenant-Isolation, możesz rzucić okiem na Izolacja najemcy rzut. Oznacza to, że wszystkie witryny pozostają responsywne, nawet jeśli poszczególni klienci generują czasami większy ruch.
Ustawianie właściwych limitów: cgroups vs. ulimits
cgroups ogranicza rzeczywiste użycie, podczas gdy ulimits to głównie limity na proces lub twarde limity powłoki, dlatego używam Kombinacje konkretnie. Dla CPU, RAM i I/O ustawiam czyste cgroups, dla otwartych plików lub procesów dodatkowo kontroluję poprzez ulimit. Zapobiega to wąskim gardłom deskryptorów plików i nadal utrzymuje ogólne wykorzystanie w ryzach. Jeśli chcesz odświeżyć limity związane z systemem, możesz znaleźć dobry przegląd na stronie Ograniczenia w hostingu. Oba poziomy razem zapewniają najdokładniejsze śruby regulacyjne dla sprawiedliwej separacji klientów.
Monitorowanie i ostrzeganie
Bez pomiarów podejmuję decyzje na ślepo, więc stale uruchamiam metryki i ustawiam Progi dla alarmów. Narzędzia takie jak systemd-cgtop, ps, pidstat czy Prometheus-Exporter pokazują mi na żywo, która grupa aktualnie korzysta z zasobów. W panelach łączę grupy z pulpitami nawigacyjnymi, które oznaczają wartości progowe i wizualizują trendy. Alerty e-mail lub czat informują mnie, gdy grupy przekraczają limity lub często się dławią. Pozwala mi to wcześnie zidentyfikować wąskie gardła i dostosować limity, rozszerzyć sprzęt lub zoptymalizować ścieżki kodu.
Szczegółowy monitoring: kluczowe dane, PSI i istotne alarmy
Monitoruję nie tylko „wykorzystanie“, ale także „ciśnienie“. W cgroups v2 odczytuję cpu.stat (w tym throttled_us), memory.current, memory.high, memory.events, io.stat i pliki presji. Używam ich do tworzenia alarmów, które reagują na niedobory zasobów na wczesnym etapie, nie będąc irytującymi podczas krótkich szczytów.
- CPU: Alert, jeśli throttled_us wzrasta na stałe, a opóźnienia pogarszają się w tym samym czasie. Wtedy zwiększam CPUWeight lub poluzowuję cpu.max.
- Pamięć: memory.current blisko memory.high jest sygnałem ostrzegawczym. Jeśli memory.events często zgłasza „high“, zwiększam wartość high lub optymalizuję cache.
- I/O: io.stat pokazuje rbps/wbps i czasy oczekiwania. Poprawiam stały throttling poprzez io.weight lub dedykowane urządzenia.
- PSI: Utrzymująca się „pewna“/„pełna“ presja jest wskaźnikiem, że obciążenia regularnie oczekują na zasoby. Używam tego do planowania wydajności.
Najlepsze praktyki dotyczące czystej konfiguracji
Zawsze zaczynam od konserwatywnych wartości, ponieważ pokrywy, które są zbyt ostre w godzinach szczytu Wydajność koszty. Następnie obciążam usługę testami porównawczymi, takimi jak ab, siege lub wrk, aby stopniowo zwiększać limity. W przypadku hostów z wieloma aplikacjami układam grupy w plasterki, aby ważne usługi były traktowane priorytetowo bez pozbawiania innych wszystkiego. Ustawiam limity I/O tak, aby krótkie szczyty były przepuszczane, ale dłuższe fazy są spowalniane. Regularne przeglądy zapobiegają dezaktualizacji limitów przy zmieniających się profilach obciążenia.
Migracja z v1 do v2: oto jak postępuję
Planuję zmianę jak zwykłą aktualizację infrastruktury. Najpierw sprawdzam, czy jądro i systemd v2 są domyślnie aktywowane. W razie potrzeby zaczynam od odpowiednich parametrów rozruchowych i sprawdzam, czy ujednolicone drzewo jest aktywne. Następnie testuję wszystkie integracje (panele, agentów, kopie zapasowe, środowisko uruchomieniowe kontenera) w środowisku przejściowym.
- Wykrywanie: mount | grep cgroup2 lub systemd-cgls pokazuje mi, czy v2 jest uruchomiony.
- Parametry rozruchu: W razie potrzeby ustawiam cgroup_no_v1=all lub ujednolicam opcje tak, aby aktywny był tylko v2.
- Mapowanie kontrolera: blkio staje się io; zastępuję niektóre funkcje v1 (net_cls/prio) kontrolą ruchu za pomocą klasyfikatorów cgroup lub BPF.
- Migracja zasad: Użyj wag zamiast sztywnych kwot, wprowadź memory.high, ogranicz swap osobno.
- Dostosowywanie monitorowania: Przenoszenie nowych ścieżek i pól (cgroup.events, cpu.stat) do pulpitów nawigacyjnych.
Dodaj izolację procesów
cgroups rozwiązują kwestię zasobów, ale dla dostępu do systemu dodatkowo oddzielam Przestrzenie nazw i widoki plików. Chroot, CageFS, przestrzenie nazw i więzienia zamykają ścieżki, obiekty jądra i urządzenia, aby klienci nie mogli się do siebie dotrzeć. Ta warstwa ochrony jest przydatnym dodatkiem do limitów, ponieważ zmniejsza promień uszkodzeń i powierzchnię ataku. Zwięzły przegląd najważniejszych wariantów można znaleźć tutaj: Izolacja procesów. W połączeniu z cgroups, tworzona jest czysta separacja klientów dla konfiguracji hostingu o dowolnym rozmiarze.
Praktyczne scenariusze i dostrajanie
Podczas szczytów ruchu w konfiguracjach CMS, daję procesorowi trochę krótkoterminowego oddechu, ale mocno trzymam pamięć RAM, dzięki czemu mogę Bezpieczeństwo przed awariami OOM. W przypadku sklepów intensywnie przetwarzających dane reguluję blkio, aby indeksowanie nie spowalniało wszystkich innych procesów odczytu. Przypinam procesy analityczne lub robocze do kilku rdzeni za pomocą cpuset, dzięki czemu pracownicy sieciowi mogą odpowiadać bez zakłóceń na innych rdzeniach. Przenoszę zadania w tle do grup o niższym obciążeniu procesora, aby żądania frontendu pozostały płynne. W przypadku klientów dedykowanych pozwalam memory.min zabezpieczyć niewielką gwarantowaną bazę pamięci RAM dla aplikacji premium.
Rozwiązywanie problemów i typowe przeszkody
Niektóre wzorce błędów powtarzają się. Zwracam uwagę na następujące punkty, aby zaoszczędzić czas podczas rozwiązywania problemów:
- Zbyt wysokie limity CPU: Stałe dławienie zwiększa opóźnienia. Lepiej pracować z cpu.weight i cpu.max tylko jako pasem bezpieczeństwa.
- Presja pamięci bez OOM: memory.high skutecznie ogranicza, ale jeśli pamięć podręczna stron jest zbyt mocno ograniczona, opóźnienia we/wy wzrastają. Precyzyjne dostrajanie i selektywne przycinanie pamięci podręcznej.
- Efekty wymiany: Zbyt duża ilość swapu powoduje spowolnienie systemu. Z tego powodu krytyczne usługi powinny działać z memory.swap.max=0, ale cały system powinien być zabezpieczony małym buforem.
- Zapomniane poddrzewa: Bez wpisu w cgroup.subtree_control, limity dzieci nie mają zastosowania. Zawsze najpierw należy aktywować kontroler w węźle nadrzędnym.
- Nieprawidłowa grupa: Procesy czasami trafiają do niewłaściwego wycinka. Sprawdzam za pomocą systemd-cgls i poprawiam opcje jednostki usługi (Slice=, Delegate=).
- pids.max za niski: Daemon z wieloma wątkami roboczymi ulega cichej awarii. Wybierz większy bufor i śledź w monitorowaniu.
- Limity we/wy na urządzenie: W przypadku RAID/LVM należy użyć prawidłowych wartości major:minor lub ustawić limity na widoczne urządzenia blokowe, z których faktycznie korzysta obciążenie.
- Priorytetyzacja sieci: net_cls/prio są starszą wersją v1. W wersji v2 polegam na kontroli ruchu za pomocą klasyfikatorów cgroup lub BPF.
Role, profile i modele sprawiedliwości
Lubię pracować z przejrzystymi profilami usług, które przechowuję jako szablony i wdrażam automatycznie:
- Premium (Gold): Wysoka waga CPU i I/O, memory.min dla gwarantowanej bazy, twardy limit memory.max z wystarczającą rezerwą.
- Standard (srebrny): Średnia waga, umiarkowana waga io.weight, memory.high nieco poniżej wartości szczytowej, aby uniknąć rozrastania się pamięci podręcznej.
- Tło (brąz): Niskie obciążenie CPU/I/O, ścisłe cpu.max dla oddzielenia interaktywnych obciążeń.
Rezerwuję również rdzenie i pamięć RAM dla hosta i centralnej infrastruktury (monitorowanie, rejestrowanie, tworzenie kopii zapasowych). Zapobiega to połykaniu przez klientów kosztów ogólnych systemu. W przypadku hostów NUMA używam cpuset, aby zachować pamięć lokalną dla używanych rdzeni - zmniejsza to szczyty opóźnień dla usług wymagających dużej ilości pamięci.
Krótkie podsumowanie
Dzięki cgroups ustawiam wyraźne szyny ochronne dla CPU, RAM, I/O i sieci, co pozwala mi na Sprawiedliwość między usługami i amortyzować wąskie gardła. Ustandaryzowana architektura cgroups v2 ułatwia mi planowanie, obsługę i rozwiązywanie problemów w porównaniu do v1. W kontenerach, hostingu współdzielonym i środowiskach mieszanych mogę kontrolować „hałaśliwych sąsiadów“ i chronić krytyczne obciążenia. Monitorowanie i przydatne alarmy dają mi wczesne sygnały, gdy limity przestają odpowiadać profilom obciążenia. Jeśli połączysz cgroups z izolacją procesów, ulimits i czystym strojeniem, możesz zbudować niezawodną platformę hostingową, która działa konsekwentnie i traktuje klientów sprawiedliwie.


