...

Hosting procesorów softirq: optymalizacja wydajności serwera i przepustowości sieci

Pokazuję, jak softirq cpu wraz z NAPI, dystrybucją IRQ i projektowaniem kolejek ogranicza lub uwalnia przepustowość sieci w hostingu. Dzięki jasnym punktom pomiarowym, ukierunkowanemu tuningowi i czystym powinowactwom, redukuję Opóźnienia i konsekwentnie zwiększać przepustowość pps na wydajnych serwerach.

Punkty centralne

Te podstawowe idee skutecznie przenoszą pakiety sieciowe przez CPU, jądro i NIC - i ograniczają czas odpowiedzi do minimum. stały niski.

  • Budżet NAPI precyzyjne dostrajanie: Większa liczba pakietów na ankietę zmniejsza koszty ogólne i wygładza Obciążenie procesora.
  • Równoważenie IRQ i powinowactwo: unikaj hotspotów, zwiększ liczbę trafień w pamięci podręcznej, Szczyty opóźnień nacisnąć.
  • Wiele kolejek, RSS/RPS/XPS: równoległość przepływów, utrzymanie wyrównania NUMA, pps podnieść.
  • Odciążenia używać świadomie: GRO/LRO, OSP, ocenić koalescencję, Jitter na widoku.
  • Izolacja i Busy Polling: przewidywalne czasy odpowiedzi na dedykowanych serwerach Rdzenie.

Podstawy: Co dzieje się w jądrze podczas ruchu sieciowego?

Pakiet najpierw ląduje w przerwaniu sprzętowym, po czym jądro przejmuje pracę w SoftIRQs i pętle ankiet NAPI. Upewniam się, że szybka faza HardIRQ pozostaje naprawdę krótka i że faktyczna logika przenosi się do właściwego kontekstu, tak aby czas procesora nie wygasa. Wątki ksoftirqd wkraczają tylko wtedy, gdy bezpośrednie przetwarzanie nie jest możliwe, co szybko prowadzi do kolejek pod ciągłym obciążeniem. To właśnie tam pojawia się czas oczekiwania, co znajduje odzwierciedlenie w zwiększonym TTFB i wahaniach przepustowości. Jeśli chcesz zagłębić się w temat, praktyczną wiedzę na temat przetwarzania IRQ znajdziesz w tym artykule na stronie Obsługa przerwań i wydajność procesora, którego używam do kategoryzacji.

NAPI, SoftIRQs i ksoftirqd: kontrolowanie opóźnień zamiast zarządzania nimi

NAPI redukuje burze przerwań, odbierając kilka pakietów na uruchomienie w ramach określonego budżetu, a tym samym minimalizując czas przerwania. Nad głową obniża. Jeśli budżet jest niewystarczający, paczki piętrzą się, ksoftirqd jest gorący i Opóźnienie wzrasta mierzalnie. W takich sytuacjach systematycznie sprawdzam /proc/softirqs i /proc/net/softnet_stat w celu wizualizacji spadków, time_squeeze lub przepełnionych kolejek. Następnie stopniowo zwiększam net.core.netdev_budget lub net.core.netdev_budget_usecs i równolegle monitoruję obciążenie CPU, rozkład p95/p99 i straty pakietów. Sztuczka polega na tym, aby wykonać wystarczającą ilość pracy na jedną ankietę bez tłumienia interaktywnego wykonywania wątków użytkownika.

Równoważenie IRQ i powinowactwo: unikaj hotspotów, zwiększ liczbę trafień w pamięci podręcznej

Pojedynczy rdzeń ze wszystkimi IRQ NIC staje się wąskim gardłem, ponieważ musi obsługiwać przerwania, miękkie IRQ i wątki aplikacji. IRQ ukierunkowane. Usługa irqbalance pomaga, ale w przypadku wysokich szybkości pps wyraźnie mapuję kolejki RX / TX poprzez powinowactwo do odpowiednich jądra. W systemach NUMA wiążę kolejki z rdzeniami tego samego węzła, aby uniknąć zdalnego dostępu do pamięci. Wątki aplikacji działają na sąsiadujących, ale oddzielnych rdzeniach, co poprawia lokalizację pamięci podręcznej i harmonogramowanie. Ten przewodnik po strategicznej dystrybucji zapewnia dobry przegląd Równoważenie IRQ w centrum danych, którego używam jako odniesienia do dostrajania.

Wiele kolejek, RSS/RPS/XPS: prawidłowe korzystanie z równoległości

Nowoczesne karty NIC są wyposażone w kilka kolejek RX/TX, które mogę kontrolować za pomocą RSS do przepływów, a tym samym osiągnąć rzeczywistą równoległość. Jeśli karta oferuje zbyt mało kolejek, używam RPS/XPS, aby dokonać regulacji po stronie oprogramowania w celu rozsądnej dystrybucji pakietów między przepływami. jądra do wypchnięcia. Czysta dystrybucja hashów jest ważna, aby przepływ zawsze pozostawał na tym samym CPU i nie dochodziło do kosztownych zniekształceń pamięci podręcznej. Jednocześnie utrzymuję ścieżki TX i RX blisko siebie, aby uniknąć blokady i niepotrzebnych dostępów międzywęzłowych. Zwiększa to przepustowość pps bez hamowania przez pojedynczy rdzeń.

Przynależność procesora do przestrzeni użytkownika: kompleksowe myślenie

Planuję ścieżkę danych od NIC-IRQ przez kolejki NAPI do wątków roboczych aplikacji, tak aby pakiety docierały do miejsca docelowego bez zbędnych haczyków i bez zbędnych błędów. Czas reakcji pozostaje stała. Aby to osiągnąć, konsekwentnie oddzielam rdzenie dla przerwań/softIRQ od rdzeni aplikacji i tworzę wyraźne Affinity-zasady. Serwery internetowe, odwrotne proxy i bazy danych otrzymują stałe zestawy procesorów, które są blisko rdzeni IRQ, aby utrzymać krótkie ścieżki. Dodatkowo ustawiłem CPU Governor na wydajność, aby zmiany zegara nie powodowały jittera w p99. To spójne przypisanie sprawia, że zachowanie jest przewidywalne i pomaga w łatwym diagnozowaniu wąskich gardeł.

Odciążenia, GRO/LRO, firewall i eBPF: Oszczędzaj ładunek bez latania na ślepo

Zapisywanie sum kontrolnych, odciążanie OSP i koalescencja czas procesora, Mogą one jednak zmieniać rozmiary pakietów, zachowanie burst i jitter, dlatego mierzę efekty w szczególności. GRO/LRO łączą ramki i zmniejszają obciążenie stosu, ale w przypadku wymagań czasu rzeczywistego decyduję o tym w zależności od sytuacji. Dezaktywacja lub ograniczone użycie. Tabele Conntrack i głębokie łańcuchy nftables/iptables kosztują zegary, więc porządkuję zbędne reguły i upraszczam ścieżki. W razie potrzeby korzystam z eBPF (XDP, tc-BPF), aby podejmować wczesne decyzje w NIC i unikać kosztownych ścieżek. Dobrym punktem wyjścia dla praktyki dostrajania jest ten przegląd Koalescencja przerwań, które biorę pod uwagę w przypadku wrażliwych budżetów opóźnień.

Odpytywanie zajętości i izolacja CPU: Blokowanie czasów odpowiedzi

W przypadku celów o dużych opóźnieniach używam zajętego odpytywania, dzięki czemu gniazda przestrzeni użytkownika odbierają pakiety jeszcze wcześniej i Czas oczekiwania shorten. Zwiększa to obciążenie, ale zapewnia mi bardzo wąskie dystrybucje p99 dla API lub obciążeń handlowych na dedykowanych serwerach. Rdzenie. Ponadto izoluję rdzenie za pomocą isolcpus=, nohz_full= i rcu_nocbs=, tak aby timery, RCU i usługi systemowe działały tylko na procesorach sprzątających. Taka separacja zapobiega zakłóceniom na rdzeniach opóźniających i sprawia, że zachowanie jest powtarzalne. Rezultatem jest jasna mapa drogowa: dedykowane rdzenie, wczesne zbieranie pakietów, zdefiniowane budżety.

Monitorowanie i rozwiązywanie problemów: od objawu do przyczyny

Zaczynam od pps, przepustowości i obciążenia rdzenia, a następnie sprawdzam spadki i aktywność ksoftirqd-wątki w czasie, aby niezawodnie rozpoznawać wzorce. Narzędzia takie jak sar, htop, ss, nload i ethtool pokazują mi, kiedy i gdzie występuje przeciążenie i czy Wskazówki osiągają swoje limity. Rozkłady są ważne zamiast wartości średnich, aby wieczorne szczyty, okna cron lub kampanie nie zostały utracone. Koreluję szczyty TTFB z dystrybucją IRQ, budżetem NAPI i ustawieniami odciążania w celu wprowadzenia ukierunkowanych zmian. Dostosowane powinowactwo IRQ lub nowo dostosowany budżet NAPI często wystarcza, aby zauważalnie zmniejszyć limity czasu.

Parametry strojenia w skrócie

Poniższy przegląd pomaga mi mądrze korzystać ze zmian i jasno przypisywać efekty, zanim wprowadzę trwałe zmiany. wdrożenia plan. Testuję iteracyjnie każde dostosowanie, mierzę rozkłady opóźnień i obserwuję efekty uboczne na CPU i pamięć. Zawsze zmieniam tylko jeden punkt na okno testowe, aby przyczyna i skutek pozostały jasne. Następnie dokumentuję wyniki i ustawiam wartości progowe dla alertów. W ten sposób osiągam powtarzalne ulepszenia bez ryzyka niespodzianek w ruchu produktywnym.

Parametr/Funkcja Efekt w ścieżce danych Kiedy podnieść/aktywować Ryzyko/skutki uboczne
net.core.netdev_budget Więcej pakietów na ankietę NAPI Dla spadków w softnet_stat Dłuższe ankiety wypierają wątki użytkowników
net.core.netdev_budget_usecs Ograniczenie okna czasowego na ankietę Dla jittera spowodowanego dużymi seriami Za małe: więcej zmian kontekstu
RSS/RPS/XPS Dystrybucja przepływów między rdzeniami Dla hotspotów na rdzeniu Nieprawidłowe skróty: zniekształcenia pamięci podręcznej
Powinowactwo IRQ Powiązanie IRQ blisko rdzenia Z NUMA-Missmatch Niewłaściwa alokacja tworzy nowe hotspoty
GRO/LRO/TSO Zmniejsza liczbę pakietów W przypadku wąskiego gardła procesora Jitter, większe serie
Zajęte odpytywanie Wczesny odbiór paczek Dla trudnych celów p99 Większe zużycie procesora

Pierścienie RX/TX i głębokość cue: prawidłowy wymiar buforów

Nawet przy prawidłowo rozłożonych IRQ i odpowiednich budżetach, zbyt małe lub zbyt duże pierścienie NIC mogą obniżyć wydajność. Dlatego też sprawdzam rozmiary pierścieni RX/TX karty i dostosowuję je do docelowych wartości burst i opóźnień. Zbyt małe pierścienie prowadzą do spadków w NIC podczas szczytów ruchu, widocznych jako rx_missed_errors lub fifo_errors w statystykach sterownika. Zbyt duże pierścienie maskują przeciążenia, zwiększają opóźnienia i tworzą długie krawędzie końcowe w p95/p99. Szukam rozwiązania pośredniego: wystarczającego bufora, aby zaabsorbować krótkie impulsy, ale nie tak dużego, że pakiety “starzeją się” w kolejkach.

Ponadto przyglądam się stronie hosta tx_queue_len i używanego Qdisc. Za pomocą sch_fq lub fq_codel mogę wygładzić zachowanie burst i dystrybuować duże pakiety TSO poprzez pacing. Redukuje to mikrowybuchy na porcie przełącznika i wygładza krzywą opóźnienia - co jest ważne w przypadku mieszanych obciążeń, w których małe RPC działają obok dużych wysyłek. Monitoruję statystyki ethtool i koreluję je z softnet_stat, aby rozpoznać, czy przeciążenie występuje w pierścieniu NIC, w backlogu netdev czy w Qdisc.

MTU, ramki jumbo i segmentacja

Die MTU to klasyczna dźwignia, która jest często niedoceniana. Ramki jumbo zmniejszają liczbę pakietów na Gbit/s i zmniejszają obciążenie procesora - ale tylko wtedy, gdy ścieżka naprawdę obsługuje jumbo od końca do końca. Dlatego systematycznie sprawdzam zdalne stacje, przełączniki i tunele. Jak tylko gdzieś nastąpi fragmentacja z powrotem do 1500, istnieje ryzyko problemów z MTU ścieżki, retransmisji i niepotrzebnych Jitter. W centrach danych, w których dominuje komunikacja wschód/zachód, opłacalna jest jednorodna strategia 9k, podczas gdy 1500 jest często bardziej stabilnym wyborem dla obciążeń związanych z Internetem.

Zawsze oceniam MTU w połączeniu z OSP/GSO/GROZbyt agresywne łączenie może prowadzić do dużych impulsów w TX, które wypełniają bufory upstream i generują szczyty opóźnień. Celem jest spójna ścieżka: rozsądna segmentacja w nadajniku, wystarczające mechanizmy stymulacji i GRO, które oszczędzają pracę po stronie odbiornika bez udaremniania wymagań czasu rzeczywistego.

Obciążenia UDP, QUIC i streaming: rozważ szczegóły

Nie cały ruch to TCP. UDP-Profile o dużym obciążeniu (DNS, VoIP, QUIC, telemetria) zachowują się inaczej w RSS/RPS i GRO. Nowoczesne stosy obsługują UDP-GRO/GSO, co może zmniejszyć obciążenie CPU - używam tego selektywnie i mierzę, czy ryzyko zmiany kolejności lub jittera wzrasta. W przypadku obciążeń QUIC/HTTP3 kluczowa jest czysta dystrybucja przepływów: RPS może pomóc, jeśli NIC oferuje zbyt mało kolejek RSS, ale nie może “rozrzucać” żadnych gorących przepływów pamięci podręcznej. Po stronie TX ustawiłem XPS do łączenia ścieżek transmisji i zmniejszania rywalizacji blokad. W praktyce, cicha alokacja rdzenia jest opłacalna, zwłaszcza w przypadku wielu średnich przepływów UDP, gdzie liczy się każde trafienie w pamięci podręcznej.

Wirtualizacja i kontenery: czysta integracja hosta, gościa i vhosta

W środowiskach zwirtualizowanych praca przenosi się między hostem, wątkami vhost i IRQ gościa. Upewniam się, że vhost-net-Wątki otrzymują własne rdzenie i nie kolidują z pracownikami aplikacji. Ich powinowactwa muszą pasować do fizycznych kolejek RX/TX, w przeciwnym razie nastąpi niepotrzebna migracja między procesorami. W gościu sprawdzam kolejki virtio-net, aktywuję multi-queue i ustawiam RSS/RPS analogicznie do bare metal. Gdzie latencja i pps są na pierwszym planie SR-IOV dalsze zmniejszenie kosztów ogólnych - warunkiem wstępnym jest spójna topologia NUMA: VF, vCPU i pamięć należą do tego samego węzła.

W stosie kontenerów sieci nakładkowe, głębokie łańcuchy NAT i złożone topologie CNI powodują dodatkowe przeskoki. W przypadku usług krytycznych pod względem opóźnień preferuję hostNetwork lub sieci szczupłe (macvlan/ipvlan), wyrównuję ścieżki NAT i zachowuję Conntrack tak małe, jak to tylko możliwe. Ważna jest spójna strategia CPU: rdzenie IRQ i NAPI hosta powinny znajdować się w sąsiedztwie rdzeni, na których działają vhost/container workers - to jedyny sposób na utrzymanie krótkiej i przewidywalnej ścieżki danych.

Planowanie, stany C i wątkowanie IRQ

Ponieważ opóźnienie to nie tylko czas obliczeniowy, ale także Czas budzenia Minimalizuję głębokie stany C na rdzeniach opóźniających. Agresywne oszczędzanie energii może kosztować milisekundy przed faktycznym uruchomieniem SoftIRQ. Dlatego polegam na regulatorach wydajności, ograniczam głębokie stany C i utrzymuję spójność turbo, aby skoki częstotliwości były przewidywalne. Równie ważne jest Wątkowanie IRQTam, gdzie sterowniki na to pozwalają, przenoszę pracę do wątków IRQ i ustalam priorytety tak, aby RX uruchamiał się przed dalszą pracą bez całkowitego wypierania userland. Interakcja polityk sched, powinowactw i budżetów jest trudna; testuję krok po kroku, loguję p99 i uważam na interferencję z ksoftirqd, który w przeciwnym razie staje się ukrytym wąskim gardłem.

Szczegółowa obserwacja: punkty śledzenia, liczniki, histogramy

Jeśli metryki pozostają niejasne, wchodzę o jeden poziom głębiej: używam tracepointów jądra wokół netif_receive_skb, napi_poll oraz net_dev_queue, aby wyświetlić czas trwania odpytywania, ilość pakietów i czasy oczekiwania w postaci histogramów. Takie rozkłady pokazują, czy 1 % sondaży trwa zbyt długo lub czy poszczególne kolejki się wyczerpują. Dodatkowo, ethtool-rx/tx-counters, TCP retransmits, busy poll hits i softnet_stat wyraźnie wskazują, gdzie pakiety są tracone. Używam analizy dropów, aby rozpoznać, czy NIC upuszcza (ring full), netdev backlog się zapada (time_squeeze) lub Qdisc/firewall zwalnia. Dopiero gdy te elementy układanki pasują do siebie, dostosowuję pierścienie, budżety lub odciążenia.

Usprawnienie ścieżek zabezpieczeń i filtrowania

Złożone ACL, głębokie łańcuchy nftables/iptables i szerokie tabele conntrack dodają stałe opóźnienia na pakiet. Konsoliduję reguły, pracuję z zestawami/mapami i przenoszę ogólne spadki tak daleko na ścieżce, jak to możliwe - najlepiej jak najwcześniej w NIC (XDP/clsact), jeśli opóźnienie jest krytyczne. Przepływy bezstanowe, telemetria lub znane “bezpieczne” porty mogą być używane w sposób ukierunkowany. bez śledzenia aby wyeliminować potrzebę kosztownego wyszukiwania. Jednocześnie utrzymuję świeżość tabel stanu, dostosowuję rozmiary hashów do szczytów obciążenia i agresywnie porządkuję osierocone wpisy. Celem jest czysta, identyfikowalna ścieżka polityki, która nie jest zauważalna w profilu jako stałe obciążenie.

Typowe antywzorce i jak ich unikać

  • Wszystkie IRQ na jednym rdzeniu: prowadzi do przeciążenia i gorących ksoftirqd. Antidotum: ukierunkowane powinowactwa na wskazówkę, spójne z NUMA.
  • Ślepa maksymalizacja pierścieni/budżetów: ukrywa zatory, zwiększa opóźnienia. Antidotum: zwiększaj stopniowo, mierz rozkłady.
  • Nieprawidłowa konfiguracja haszowania przepływu: Przepływy przeskakują między rdzeniami, cache przestaje działać. Antidotum: stabilne klucze RSS, RPS/XPS tylko z jasnym celem.
  • Wątki aplikacji na tych samych rdzeniach co SoftIRQ: Zakłócenia i jitter. Antidotum: twarda separacja, alokacja sąsiedzka.
  • Nakładki/NAT bez budżetu: dodane do każdego przeskoku. Rozwiązanie: Usprawnienie ścieżek, sieci hostów dla obciążeń z opóźnieniami.
  • Oszczędzanie energii na rdzeniach z opóźnieniami: Głębokie stany C spowalniają reakcję. Antidotum: regulator wydajności, ograniczenie stanu C.
  • Rozładunek bez pomiaru: TSO/GRO może zaostrzyć burst i jitter. Rozwiązanie: Aktywacja specyficznego obciążenia, monitorowanie p99.

Praktyczny hosting: kroki, które działają

Zaczynam od czystej fazy pomiarowej, ustalam wartości bazowe i utrzymuję wszystkie zmiany na niskim poziomie w krótkich oknach czasowych, dzięki czemu mogę Przyczyny można rozdzielić. Następnie włączam irqbalance, sprawdzam automatyczną dystrybucję i, jeśli to konieczne, ustawiam ręczne powinowactwa, aż do momentu, gdy nie będzie już żadnego Hotspoty nie są już widoczne. Następnie konfiguruję Multi-Queue, RSS i - w razie potrzeby - RPS/XPS, zsynchronizowane z NUMA. Wiążę pracowników aplikacji z rdzeniami w sąsiedztwie ich rdzeni IRQ, ale bez bezpośredniej kolizji. Na koniec czyszczę ścieżki firewalla, sprawdzam tabele conntrack i podejmuję świadome decyzje o odciążeniach w oparciu o docelowe opóźnienia.

Przykładowy playbook dla opóźnień p99

Najpierw mierzę p95/p99 poprzez reprezentatywne obciążenie i bezpieczne logi z /proc/softirqs i /proc/net/softnet_stat w celu Krople i time_squeeze są wyraźnie widoczne. Następnie zwiększam netdev_budget lub netdev_budget_usecs krok po kroku i przytrzymuję p99 po każdej zmianie, abym mógł zobaczyć rzeczywistą wartość. Trendy rozpoznać. Równolegle wiążę IRQ z rdzeniami węzła NUMA i przenoszę pracowników aplikacji do odpowiednich sąsiadów. Jeśli p99 nadal skacze, testuję warianty GRO/LRO i profile koalescencji przerwań, każdy z krótką ścieżką pomiarową. Dopiero gdy dystrybucja pozostaje stabilna, przenoszę konfigurację do ról Ansible lub dropinów systemd.

Skrócona wersja dla administratorów

Największą dźwignię osiągam poprzez SoftIRQs, Budżet NAPI, powiązania IRQ i wątki aplikacji jako spójna ścieżka danych. Rozdzielam pracę sieciową między rdzenie, utrzymuję spójne kolejki NUMA i rozsądnie łączę pracowników, tak aby Trasy Krótko mówiąc. Celowo ustawiam odciążenia i mierzę jitter, zamiast ślepo optymalizować przepustowość. W przypadku twardych celów opóźnień polegam na zajętym odpytywaniu i izolacji procesora, podczas gdy procesory sprzątające przechwytują zakłócenia. Jeśli wdrożysz te kroki w zdyscyplinowany sposób, uzyskasz stałą przepustowość, węższe rozkłady opóźnień i środowisko hostingowe, które reaguje przewidywalnie na szczyty obciążenia.

Artykuły bieżące