Linuksowy scheduler CFS kontroluje sposób, w jaki rdzenie serwera przydzielają swój czas procesom, a tym samym bezpośrednio wpływa na opóźnienia, przepustowość i sprawiedliwość w hostingu serwerów. W tym przewodniku wyjaśniam, jak to działa, jakie są dźwignie dostrajania i przydatne alternatywy, takie jak ULE, BFS i EEVDF. Hosting z serwery internetowe.
Punkty centralne
- Sprawiedliwość oraz vruntime określić, które zadanie otrzyma CPU.
- Cgroups regulować kwoty i cpu.shares dla izolacji klienta.
- Dostrajanie jądra poprzez sched_latency_ns i Ziarnistość.
- Alternatywy takich jak BFS, ULE, EEVDF dla specjalnych Obciążenia.
- PraktykaPokrewieństwo rdzenia, planer we/wy i Testy połączyć.
Jak działa CFS w codziennym hostingu
W przypadku całkowicie sprawiedliwego harmonogramu, wirtualne środowisko uruchomieniowe decyduje, które zadanie ma zostać uruchomione jako następne, co skutkuje uczciwy i przewidywalny Przydział jest tworzony. Każde zadanie otrzymuje czas procesora proporcjonalny do wartości nice, więc niska wartość nice otrzymuje więcej udziałów. W środowiskach hostingowych wiele małych żądań internetowych, zadań cronjobs i kopii zapasowych dzieli procesor między siebie, bez jednego procesu zajmującego wszystko. Interaktywne obciążenia, takie jak żądania NGINX, korzystają z częstych, krótkich wycinków czasu, podczas gdy zadania wsadowe otrzymują dłuższe bloki. Oznacza to, że czasy odpowiedzi pozostają niezawodne dla użytkowników, nawet jeśli wiele witryn przetwarza żądania równolegle.
Używam Cgroups do ograniczania klientów i usług, ponieważ cpu.shares i cpu.max zapewniają przejrzystość. Suma udziałów i twardy Ograniczenia. Domyślna wartość 1024 udziałów dla “normalnych” i 512 dla “mniej ważnych” rozdziela rdzenie w zrozumiały sposób. Na przykład w cpu.max ustawiłem 50ms w okresie 100ms, co efektywnie odpowiada udziałowi 50% CPU. Ta konfiguracja oferuje przewidywalne rezerwy dla hostingu obciążeń o zmiennym obciążeniu. Kompaktowe wyjaśnienie tej zasady można znaleźć na stronie Sprawiedliwa dystrybucja procesora.
Mechanika CFS wyjaśniona w przejrzysty sposób
W swojej istocie CFS zarządza wszystkimi gotowymi do uruchomienia zadaniami w czerwonym/czarnym drzewie, posortowanymi według vruntime i z wydajnym Wybór najmniejszego wirtualnego czasu wykonywania. To zadanie jest uruchamiane jako następne i zwiększa swój wirtualny czas wykonywania proporcjonalnie do zużytego czasu procesora i jest ważone za pomocą wartości nice. Tworzy to płynną równowagę bez twardych kolejek, co zapewnia czyste wyniki, zwłaszcza przy mieszanych obciążeniach. W systemach wielordzeniowych harmonogram przenosi zadania między kolejkami uruchamiania, ale zwraca uwagę na lokalizację pamięci podręcznej poprzez powinowactwo rdzeni. W ten sposób CFS łączy równoważenie obciążenia z jak najmniejszą liczbą kosztownych migracji.
W celu precyzyjnego dostrojenia, parametry takie jak sched_latency_ns i sched_min_granularity_ns ustawiają kurs dla Opóźnienie oraz Przepustowość. Mniejsze wartości opóźnień faworyzują krótkie, interaktywne zadania, podczas gdy większe wartości wzmacniają zadania wsadowe. W testach z narzędziami takimi jak stress-ng i fio sprawdzam wpływ na czasy odpowiedzi i wykorzystanie procesora. Wraz ze wzrostem liczby zadań wzrasta obciążenie administracyjne drzewa, co może objawiać się szczytowymi opóźnieniami. Jednak prawidłowo ustawione kwoty i limity utrzymują te efekty pod kontrolą w środowiskach hostingowych.
Mocne strony CFS w hostingu serwerów
Największa siła tkwi w Sprawiedliwość, równomiernie i zrozumiale Zasoby rozproszone. W przypadku środowisk współdzielonych oznacza to, że żaden klient nie wypiera innych, ponieważ kwoty i udziały jasno określają wagi. Usługi interaktywne otrzymują szybki czas reakcji, podczas gdy kopie zapasowe mogą działać bez pośpiechu. Priorytetyzacja za pomocą ładnych wartości uzupełnia ten obraz i pozostawia mi miejsce na koordynację w zależności od roli usługi. Równoważenie obciążenia na wszystkich rdzeniach pozwala mi dobrze wykorzystać dostępną moc obliczeniową bez poświęcania zbyt wiele miejsca poszczególnym wątkom.
W praktyce siła CFS staje się widoczna, gdy występują szczyty serwera WWW i pojawia się wiele krótkich żądań, ponieważ CFS przydziela częste sloty do tego typu zadań. Czyste grupy C pomagają ustalić twarde górne limity na klienta lub kontener. Pomiary średnich i percentyli pokazują wiarygodne czasy odpowiedzi, co opłaca się w codziennej działalności. Takie podejście jest szczególnie przydatne w przypadku stosów aplikacji z wieloma komponentami. To właśnie tam połączenie przewidywalnej uczciwości i wystarczającej elastyczności osiąga wysokie wyniki.
Ograniczenia i typowe przeszkody
Przy bardzo dużej liczbie jednoczesnych zadań, narzut operacji drzewa wzrasta, co nie ma miejsca w przypadku Wskazówki die Opóźnienie może napędzać. W konfiguracjach hostingu z wieloma bardzo krótkimi żądaniami czasami występują częste zmiany kontekstu. Takie zachowanie “thrashing” zmniejsza wydajność, jeśli wartości ziarnistości są wybrane nieprawidłowo. Mniejsza liczba, ale dłuższych wycinków czasu może pomóc, o ile zachowana jest interaktywność. CFS reaguje wrażliwie na nieprawidłowe limity, dlatego konsekwentnie sprawdzam limity za pomocą testów obciążenia.
Nawet obciążenia przyjazne dla affinity cierpią, jeśli zadania zbyt często przeskakują między rdzeniami. Czysta koncepcja powinowactwa pozwala utrzymać ciepło w pamięci podręcznej i zmniejsza koszty migracji. Lubię również wiązać hałaśliwe zadania wsadowe z ich własnymi rdzeniami, aby żądania internetowe działały cicho na ich rdzeniach. W przypadku usług o krytycznym opóźnieniu warto ustawić niskie wartości i precyzyjnie dostroić opóźnienie. Ostatecznie liczy się to, że pomiary potwierdzają wybrane parametry.
Porównanie alternatyw: ULE, BFS i EEVDF
W przypadku specjalnych obciążeń szukam alternatywnych rozwiązań, aby Opóźnienie lub Skalowanie w różny sposób ustalają priorytety. ULE wykorzystuje prostsze kolejki i uzyskuje wyniki przy mniejszym wysiłku administracyjnym, BFS nadaje priorytet responsywności i wyróżnia się niewielką liczbą zadań, a EEVDF łączy sprawiedliwą dystrybucję z terminami. EEVDF w szczególności obiecuje krótsze czasy oczekiwania dla interaktywnych obciążeń, ponieważ program planujący zwraca większą uwagę na “najwcześniejszy dopuszczalny termin”. W przypadku bardzo dużych pól serwerowych ostatecznie liczy się to, która mieszanka wydajności i możliwości planowania naprawdę wygrywa we własnym stosie. Ustrukturyzowane spojrzenie na mocne i słabe strony oraz obszary zastosowań pomaga w wyborze.
| harmonogram | Złożoność | Mocne strony hostingu | Słabe strony | Odpowiedni dla |
|---|---|---|---|---|
| CFS | Wysoki | Uczciwa dystrybucja, Cgroups | Szczyty opóźnień | Hosting współdzielony, mieszane obciążenia |
| ULE | Niski | Proste wskazówki, niski poziom Obciążenie | Mniejsza izolacja | Maszyny wirtualne, wzorce podobne do HPC |
| BFS | Średni | Interaktywność, Prędkość | Słabe skalowanie | Komputery stacjonarne, małe serwery |
| EEVDF | Średni | Niskie opóźnienia, terminy | Wciąż mało praktyki | Nowoczesne stosy hostingowe |
Strojenie jądra: praktyczne kroki dla CFS
W przypadku CFS często przełączam sched_autogroup_enabled=0, aby żadne niejawne grupy nie zniekształcały obrazu, a Rozkład obciążenia czysty pozostałości. W przypadku sched_latency_ns lubię zaczynać od 20 ms, co sprzyja usługom interaktywnym, i dostosowywać sched_min_granularity_ns, aby okiełznać zmiany kontekstu. Wartości zależą od profilu: wiele krótkich żądań internetowych wymaga innego dostrojenia niż okna kopii zapasowych. Testuję zmiany seryjnie i mierzę percentyle zamiast patrzeć tylko na średnie. Gwarantuje to, że nie tylko średnie wartości wyglądają ładnie, ale także, że długie kolejki się kurczą.
Jeśli chcesz zagłębić się w parametry sysctl, tutaj znajdziesz dobre wprowadzenie: sysctl tuning. Dostrajam również dystrybucję IRQ, CPU governor i profile energetyczne, aby procesor nie przechodził stale w stany ekonomiczne. Używam regulatorów wydajności dla stosów opartych na opóźnieniach, podczas gdy czyste pudełka wsadowe żyją ze zrównoważoną kontrolą. Wyraźnie oddzielam fazę testową od produkcyjnej, aby uniknąć niespodzianek. Po każdym kroku sprawdzam logi i metryki, zanim przejdę dalej.
Rozsądne korzystanie z cgroups i quotas
Za pomocą cpu.shares przypisuję względne Wagi podczas gdy cpu.max jest trudne Granice zestawów. Klient z 512 udziałami otrzymuje o połowę mniej czasu obliczeniowego niż klient z 1024 udziałami, jeśli obaj generują obciążenie w tym samym czasie. Używam cpu.max do czystego ograniczania szczytów, na przykład 50ms w 100ms. W przypadku zadań dedykowanych warto użyć cpuset.cpus, aby usługa korzystała ze stałych rdzeni, a pamięć podręczna pozostawała ciepła. Podsumowując, skutkuje to odporną separacją między klientami a usługami.
Dokumentuję każdą zmianę i porównuję ją z poziomami usług, które chcę osiągnąć. Bez zmierzonych wartości udziały szybko prowadzą do błędnych interpretacji, dlatego zawsze towarzyszę dostosowaniom testami obciążenia. W przypadku kontenerów sugeruję realistyczne kwoty, które mogą poradzić sobie ze szczytami, ale nie spowalniają hosta. Ważne jest, aby mieć przewidywalny budżet błędów, aby wykryć zauważalne szczyty opóźnień. Jeśli zrobisz to konsekwentnie, unikniesz niespodzianek w godzinach szczytu.
Praktyka: Serwer WWW i bazy danych w CFS
Serwery internetowe sterowane zdarzeniami ograniczają przełączanie kontekstu i współgrają z CFS, co skutkuje zauważalnie stałym Czasy reakcji i lepiej Skalowanie generowane. W testach widzę, że NGINX utrzymuje wyższe wskaźniki żądań przy mniejszym jitterze na tym samym sprzęcie. Bazy danych pozytywnie reagują na powinowactwo rdzeni, gdy zadania w tle są trzymane z dala od gorących rdzeni. Pomagają w tym proste zasady: Web na rdzeniach A-B, batch na C-D i DB na E-F. W ten sposób stos utrzymuje potok w czystości, a pamięci podręczne w cieple.
Wielu małych pracowników PHP FPM powoduje zbyt wiele przełączeń z agresywną ziarnistością. Następnie zwiększam minimalny wycinek czasu i sprawdzam, czy czasy odpowiedzi pozostają stabilne. Jednocześnie dławię logi czatu, aby I/O nie stało się hamulcem. CFS stanowi tutaj podstawę, ale szczytową wydajność osiąga się poprzez dostrojenie całego stosu. W ten sposób wszystkie tryby zazębiają się, nie pozbawiając hosta oddechu.
Planowanie we/wy pamięci i procesora: wzajemne oddziaływanie
Harmonogram procesora i harmonogram we/wy wpływają na siebie nawzajem, dlatego zharmonizowana konfiguracja może przynieść zauważalną różnicę. Zalety na stronie Opóźnienie przynosi. W przypadku NVMe zwykle używam Noop lub mq-deadline, podczas gdy na dyskach HDD mq-deadline lepiej obsługuje długie kolejki. Jeśli CPU przydziela czas na czas, ale ścieżka I/O się zacina, ogólny efekt jest zniwelowany. Dlatego sprawdzam harmonogram I/O równolegle z parametrami CFS. Tutaj przedstawiam przegląd Noop, mq-deadline i BFQ: Planer we/wy w porównaniu.
W przypadku hostów baz danych dostosowuję głębokość kolejek i wyprzedzenie odczytu, aby zaplanowane przez CFS sloty nie wygasały z powodu blokowania operacji we/wy. Serwery internetowe z wieloma małymi plikami korzystają z niskich opóźnień w stosie we/wy. W scenariuszach wirtualizacji polegam na spójnych harmonogramach na hoście i gościu, aby uniknąć nieprzewidywalnych wzorców. W ten sposób harmonogram CPU współdziała z podsystemem pamięci masowej. Ostatecznie liczy się spójny łańcuch od żądania do odpowiedzi.
Równoważenie SMP, powinowactwo rdzeni i NUMA
Kieruję wątki do stałych rdzeni, aby Skrytki Koszty ogrzewania i migracji mały pozostają. W przypadku hostów NUMA łączę pamięć i procesor razem, ponieważ zdalny dostęp do pamięci zwiększa opóźnienia. CFS równoważy obciążenie między kolejkami uruchamiania, ale celowe reguły powinowactwa często pozwalają uzyskać więcej. Usługi z częstym dostępem do pamięci podręcznej korzystają ze stabilnych grup rdzeni. Zadania wsadowe mogą się przemieszczać, o ile nie kolidują z gorącymi rdzeniami.
W praktyce ustawiam opcje cpuset.cpus i numactl, a następnie testuję czasy żądań i współczynniki braku procesora. Im mniej niepotrzebnych migracji, tym lepszy czas reakcji. Oceniam również dystrybucję przerwań, aby twarde szczyty IRQ nie zatykały rdzenia. W ten sposób uzyskuję płynne taktowanie ważnych wątków. Ten spokój opłaca się w ogólnej wydajności stosu.
Planowanie grupowe: ładne, ważone i hierarchie
Częstą przeszkodą w hostingu jest Interakcja pomiędzy ładny-Priorytety i Wagi grupy C. CFS najpierw sprawiedliwie rozdziela pomiędzy grupy, a następnie w ramach grupy pomiędzy zadania. Oznacza to, że proces z ładnym -5 może nadal otrzymywać mniej CPU niż inny z ładnym 0, jeśli jego grupa (klient/kontener) ma niższą wagę. Aby uzyskać spójne wyniki, najpierw ustawiłem wartość Wagi grupowe i używać nice tylko do dostrajania w ramach usługi.
W praktyce pracuję z kilkoma wyraźnymi poziomami (np. 512/1024/2048 udziałów dla “niskiego/normalnego/wysokiego”) i dokumentuję, które usługi są uruchomione w której grupie. Pozwala to zachować Sprawiedliwość identyfikowalne w hierarchii. Każdy, kto dużo pracuje z krótkotrwałymi procesami (np. zadaniami CGI/CLI) również czerpie korzyści z oparty na cgroup ponieważ w przeciwnym razie zadania lotne mogłyby w sposób niezamierzony ominąć gorset grupowy. Regularnie korzystam z metryk runtime, aby sprawdzić, czy wewnętrzna alokacja nadal pasuje do profilu obciążenia.
Kontenery i orkiestracja: żądania, limity i dławienie
W środowiskach kontenerowych “żądanie” jest zazwyczaj mapowane na waga względna (udziały/waga), “limit” na Kwota (cpu.max). Interakcja decyduje o DławienieJeśli kwota jest zbyt niska, procesor kontenera jest spowolniony w danym okresie - widoczne w odbiciach opóźnienia p95/p99. Dlatego utrzymuję kwoty w taki sposób, że normalne wybuchy mieszczą się w okresie, a usługi rzadko są mocno dławione. Tam, gdzie to możliwe, używam Burst-reserve (np. cpu.max.burst) do amortyzowania krótkich szczytów bez zniekształceń.
Ważne jest, aby nie ustawiać żądań zbyt nisko: Jeśli wagi są zbyt niskie, usługi interaktywne pozostaną w tyle za szumem wsadowym. Kalibruję żądania na podstawie zmierzonego obciążenia podstawowego i bezpiecznych limitów, tak aby Budżety błędów są utrzymywane w godzinach szczytu. W przypadku węzłów z wieloma dzierżawcami planuję również rdzenie buforujące, aby szczyty obciążenia poszczególnych kontenerów nie wpływały na sąsiadów.
Metody pomiaru i rozwiązywania problemów w kontekście harmonogramu
Nigdy nie oceniam strojenia CFS na ślepo, ale mierzę je w ukierunkowany sposób. Używam do przeglądu:
- Długość kolejki run na procesor (obciążenie a aktywne rdzenie),
- Zmiana kontekstu na sekundę i liczbę wątków,
- Kradzież procesora oraz SoftIRQ-akcje,
- Percentyl czasów reakcji (p50/p95/p99),
- Dystrybucja vruntime lub opóźnienia w harmonogramie.
Jeśli wystąpią szczyty opóźnień, najpierw szukam Dławienie (limit wyczerpany), a następnie po Migracje (pamięć podręczna zimna) i wreszcie po Blokady we/wy (głębokość kolejki, nasycenie pamięci masowej). Patrzę na wzorce wybudzeń: częste krótkie wybudzenia wielu pracowników wskazują na zbyt małą ziarnistość lub gadatliwe I/O. Zwiększony odsetek ksoftirqd na rdzeniu wskazuje na gorące kolejki IRQ - w takim przypadku rozdzielam IRQ i aktywuję RPS/XPS, aby obciążenie sieci było bardziej rozłożone.
Klasy działające w czasie rzeczywistym, funkcja pre-emption i kontrola kleszczy
Oprócz CFS istnieją następujące klasy czasu rzeczywistego SCHED_FIFO/RR. Nadpisują one CFS: nieprawidłowo skonfigurowany wątek RT może dosłownie usunąć powietrze z systemu. Dlatego przypisuję RT-Prio tylko bardzo selektywnie (np. dla audio/telemetrii) i definiuję wyraźne watchdogi. Do hostingu zwykle wystarcza CFS z czystymi obciążeniami.
Do WyłączenieWybór modelu preemption (np. “voluntary” vs. “full/dynamic preempt”) zmienia stosunek opóźnienia do przepustowości. Dla stosów webowych preferuję więcej preemption, dla czystych hostów wsadowych mniej. Optymalizacje ticków (nohz-mode) może zredukować jitter, ale powinno być używane z ostrożnością. Na odizolowanych rdzeniach, czasami łączę nohz_full i Affinity, aby gorące wątki działały w możliwie niezakłócony sposób - ważne jest, aby obciążenia systemowe i IRQ nie migrowały przypadkowo do tych rdzeni.
Wirtualizacja: KVM, vCPU pinning i steal time
W środowisku hypervisor harmonogram hosta określa, kiedy vCPU można uruchomić. Tworzenie overbookingów Czas kradzieży w gościach, co działa jak “niewidzialne opóźnienie”. W przypadku dzierżawców krytycznych pod względem opóźnień przypinam jednostki vCPU do fizycznych rdzeni i utrzymuję umiarkowany overcommit. Oddzielam również wątki emulatora (wątki IO, vhost) od gorących rdzeni gości, aby nie zakłócały się nawzajem.
Unikam podwójnego dławienia: jeśli gość już używa cpu.max, nie ustawiam żadnych dodatkowych twardych kwot na hoście dla tego samego obciążenia. Kontrola częstotliwości pozostaje zadaniem hosta; goście korzystają pośrednio, jeśli administrator hosta skaluje się czysto z rzeczywistym obciążeniem. W przypadku równych opóźnień uważam, że stabilność wykraczająca poza czystą maksymalną częstotliwość jest ważniejsza niż szczytowe GHz na papierze.
AutoNUMA, lokalizacja pamięci i THP
NUMA może przynieść wzrost wydajności lub pułapkę wydajności. AutoNUMA często pomaga, ale może powodować dodatkowy narzut, jeśli istnieje wiele wątków roamingowych. W stosach hostingowych z wyraźnymi granicami usług, przypinam CPU i Pamięć (cpuset.cpus oraz cpuset.mems). Oznacza to, że gorące dane pozostają lokalne, a CFS musi zrekompensować mniejszą liczbę migracji.
Duże strony (THP) obniżają ciśnienie TLB, ale nie pasują do każdego profilu. W przypadku baz danych “madvise” może mieć więcej sensu niż ogólne “zawsze”. Blokowanie błędów stron mocno uderza w interaktywne opóźnienia; dlatego planuję bufory (pamięć podręczną stron, bufor współdzielony) tak, aby sloty CFS były używane produktywnie i nie czekały na zdarzenia we / wy lub MMU. Można to zmierzyć za pomocą wskaźników błędów stron i krzywych braku pamięci podręcznej.
Ścieżka sieciowa: kontrola IRQ, RPS/XPS i polling zajętości
Wiele obciążeń sieciowych jest zdominowanych przez NIC. Dystrybuuję IRQ-kolejki karty sieciowej na kilku rdzeniach i przechowywanie ich afiniczny do wątków roboczych, dzięki czemu wybudzenia pozostają lokalne. RPS/XPS pomaga rozwiązać soft hotspoty, jeśli poszczególne kolejki RX/TX przenoszą zbyt duże obciążenie. Jeśli ksoftirqd staje się wyraźnie gorący, jest to oznaka przepełnienia SoftIRQs - wtedy wyrównuję przepływy i zwiększam parametry budżetu, jeśli to konieczne, bez utraty sprawiedliwości.
Opcjonalne zajęte odpytywanie może mieć sens w bardzo specjalnych konfiguracjach o niskim opóźnieniu, ale kosztuje czas procesora. Rzadko go używam i tylko wtedy, gdy mogę udowodnić za pomocą pomiarów, że p99 spada znacząco bez ogólnego obciążania hosta. Zwykle czyste powinowactwo IRQ, grupy C i ziarnistość CFS zapewniają lepszy stosunek kosztów do korzyści.
Perspektywy: Od CFS do EEVDF i podejścia do przestrzeni użytkownika
EEVDF rozszerza sprawiedliwą dystrybucję, aby uwzględnić terminy, co jest zauważalnie krótszy i bardziej przewidywalny Odpowiedzi obietnice. Zwłaszcza w przypadku interaktywnych celów opóźnień może to mieć ogromne znaczenie. Bacznie obserwuję wersje jądra i testuję EEVDF osobno przed przełączeniem. W tym samym czasie harmonogramowanie przestrzeni użytkownika za pomocą wzorców eBPF nabiera tempa, co może pozwolić na dodatkową kontrolę w zależności od obciążenia. CFS pozostaje istotny dla infrastruktur hostingowych, ale EEVDF szybko ugruntuje swoją pozycję.
Ważna pozostaje jasna ścieżka migracji: testy, wdrożenie na wybranych hostach, a następnie ekspansja. To jedyny sposób na utrzymanie percentyli i wskaźników błędów pod kontrolą. Utrzymuję benchmarki blisko rzeczywistości, w tym fazy burst i powolne backendy. Dopiero potem interweniuję w środowiskach na żywo. W ten sposób można osiągnąć postęp bez przykrych niespodzianek.
Krótkie podsumowanie
Linux Scheduler CFS zapewnia uczciwą dystrybucję, solidne integracje i dobre wyniki. Kontrola poprzez Cgroups. Dzięki odpowiednim parametrom sysctl, czystemu affinity i realistycznym kwotom, utrzymuję niskie opóźnienia i wysoką przepustowość. ULE, BFS lub EEVDF oferują dodatkową dźwignię dla specjalnych wzorców. Mierzę, porównuję i wprowadzam zmiany etapami, aby ograniczyć ryzyko. Dzięki temu hosting jest przewidywalny, a wydajność jest na właściwym miejscu.


