Jeg viser, hvordan softirq cpu sammen med NAPI, IRQ-distribution og kø-design begrænser eller frigør netværksgennemstrømningen i hosting. Med klare målepunkter, målrettet tuning og rene affiniteter reducerer jeg Forsinkelser og konsekvent øge pps-gennemstrømningen på produktive servere.
Centrale punkter
Disse kerneideer transporterer netværkspakker effektivt via CPU, kerne og NIC - og holder svartiderne på et minimum. konstant lav.
- NAPI-budget finjustering: Flere pakker pr. afstemning reducerer overhead og udjævner CPU-belastning.
- IRQ-balancering og affinitet: undgå hotspots, øg antallet af cache-hits, Toppen af ventetiden tryk.
- Multi-kø, RSS/RPS/XPS: paralleliserer flows, opretholder NUMA-tilpasning, pps rejser sig.
- Aflæsning Brug bevidst: GRO/LRO, TSO, evaluere sammensmeltning, Jitter i udsigt.
- Isolering og Busy Polling: Forudsigelige svartider på dedikerede Kerner.
Grundlæggende: Hvad sker der i kernen under netværkstrafik?
En pakke lander først i et hardware-interrupt, hvorefter kernen overtager arbejdet i SoftIRQs og NAPI poll-loops. Jeg sørger for, at den hurtige HardIRQ-fase forbliver meget kort, og at den faktiske logik flyttes til den rigtige kontekst, så CPU-tid ikke går i stå. Ksoftirqd-trådene træder kun til, hvis direkte behandling ikke er mulig, hvilket hurtigt fører til køer under kontinuerlig belastning. Det er netop her, der opstår ventetid, hvilket afspejles i øget TTFB og svingende gennemstrømning. Hvis du vil dykke dybere ned, kan du finde praktisk viden om IRQ-behandling i denne artikel om Interrupt-håndtering og CPU-ydelse, som jeg bruger til kategoriseringen.
NAPI, SoftIRQs og ksoftirqd: Kontrol af latenstid i stedet for styring af den
NAPI reducerer afbrydelsesstorme ved at hente flere pakker pr. kørsel inden for et defineret budget og dermed minimere afbrydelsestiden. Overhead sænker. Hvis budgettet er utilstrækkeligt, hober pakkerne sig op, ksoftirqd bliver varm og Forsinkelse stiger målbart. I sådanne situationer tjekker jeg systematisk /proc/softirqs og /proc/net/softnet_stat for at visualisere drops, time_squeeze eller overfyldte køer. Derefter øger jeg gradvist net.core.netdev_budget eller net.core.netdev_budget_usecs og overvåger CPU-belastning, p95/p99-fordeling og pakketab parallelt. Tricket er at få udført nok arbejde pr. poll uden at fortrænge den interaktive udførelse af userland-tråde.
IRQ-balancering og -affinitet: undgå hotspots, øg cache-hits
En enkelt kerne med alle NIC-IRQ'er bliver en flaskehals, fordi den skal betjene afbrydelser, bløde IRQ'er og app-tråde; jeg distribuerer derfor IRQ'er målrettet. Irqbalance-tjenesten hjælper, men til høje pps-hastigheder kortlægger jeg eksplicit RX/TX-køer via affinitet til passende Kerner. På NUMA-systemer binder jeg køer til kerner i samme node for at undgå fjernadgang til hukommelsen. Applikationstrådene kører på nærliggende, men separate kerner, hvilket forbedrer cache-lokaliteten og planlægningsmulighederne. Denne guide til strategisk distribution giver et godt overblik over IRQ-balancering i datacentret, som jeg bruger som reference til finjustering.
Multi-queue, RSS/RPS/XPS: brug af parallelisering korrekt
Moderne NIC'er kommer med flere RX/TX-køer, som jeg kan styre via RSS til flows og dermed opnå reel parallelitet. Hvis kortet har for få køer, bruger jeg RPS/XPS til at foretage justeringer på softwaresiden for at fordele pakkerne fornuftigt på tværs af flows. Kerner til at skubbe. Ren hash-distribution er vigtig, så et flow altid forbliver på den samme CPU, og der ikke opstår dyre cache-forvridninger. Samtidig holder jeg TX- og RX-stierne tæt sammen for at undgå lock contention og unødvendige adgange på tværs af noder. Det øger pps-gennemstrømningen, uden at en enkelt kerne trækker i bremsen.
CPU-affinitet lige ind i brugerrummet: end-to-end-tænkning
Jeg planlægger datastien fra NIC-IRQ via NAPI-køer til appens arbejdstråde, så pakkerne når frem til deres destination uden unødvendige hooks, og Svartid forbliver konstant. For at opnå dette adskiller jeg konsekvent kerner til afbrydelser/softIRQ'er fra app-kerner og opretter klare Affinitet-regler. Webservere, reverse proxies og databaser får faste CPU-sæt, der ligger tæt på IRQ-kernerne for at holde stierne korte. Derudover sætter jeg CPU-guvernøren til performance, så clock-ændringer ikke skubber jitter ind i p99. Denne konsekvente tildeling gør adfærden forudsigelig og hjælper med at diagnosticere flaskehalse rent.
Offloads, GRO/LRO, firewall og eBPF: Spar på belastningen uden at flyve i blinde
Gem checksum offload, TSO og coalescing CPU-tid, De kan dog ændre pakkestørrelser, burst-adfærd og jitter, hvilket er grunden til, at jeg måler effekterne specifikt. GRO/LRO bundter frames og reducerer belastningen på stakken, men til realtidskrav beslutter jeg på situationsbasis om Deaktivering eller begrænset brug. Conntrack-tabeller og dybe nftables/iptables-kæder koster tid, så jeg rydder op i overflødige regler og forenkler stierne. Hvis det er nødvendigt, bruger jeg eBPF (XDP, tc-BPF) til at træffe tidlige beslutninger på NIC'en og undgå dyre stier. Et godt udgangspunkt for at finjustere praksis er denne oversigt over Sammenlægning af afbrydelser, hvilket jeg tager højde for i forbindelse med følsomme latency-budgetter.
Busy polling og CPU-isolering: Fastlåsning af svartider
Til mål med hård latenstid bruger jeg busy polling, så userspace-sockets henter pakker endnu tidligere og Ventetider forkortes. Det øger belastningen, men giver mig meget smalle p99-distributioner til API- eller handelsarbejdsbelastninger på dedikerede Kerner. Derudover isolerer jeg kerner med isolcpus=, nohz_full= og rcu_nocbs=, så timere, RCU og systemtjenester kun kører på husholdnings-CPU'er. Denne adskillelse forhindrer interferens på latency-kernerne og gør adfærden reproducerbar. Resultatet er en klar køreplan: dedikerede kerner, tidlig pakkeindsamling, definerede budgetter.
Overvågning og fejlfinding: fra symptom til årsag
Jeg starter med pps, throughput og core load, tjekker derefter drops og aktiviteten i ksoftirqd-tråde over tid for at kunne genkende mønstre. Værktøjer som sar, htop, ss, nload og ethtool viser mig, hvornår og hvor der opstår overbelastning, og om Stikord når deres grænser. Fordelinger er vigtige i stedet for gennemsnitsværdier, så aftentoppe, cron-vinduer eller kampagner ikke går tabt. Jeg korrelerer TTFB-peaks med IRQ-distribution, NAPI-budget og offload-indstillinger for at kunne foretage målrettede justeringer. En justeret IRQ-affinitet eller et nyt skræddersyet NAPI-budget er ofte nok til at reducere timeouts mærkbart.
Et overblik over indstillingsparametrene
Følgende oversigt hjælper mig med at bruge ændringer med omtanke og tildele effekter tydeligt, før jeg foretager permanente ændringer. udrulninger plan. Jeg tester hver justering iterativt, måler ventetidsfordelinger og observerer bivirkninger på CPU og hukommelse. Jeg ændrer altid kun ét punkt pr. testvindue, så årsag og virkning forbliver tydelige. Derefter dokumenterer jeg resultaterne og sætter tærskelværdier for alarmer. På den måde opnår jeg reproducerbare forbedringer uden at risikere overraskelser i den produktive trafik.
| Parameter/Funktion | Effekt i datastien | Hvornår skal man rejse/aktivere | Risici/bivirkninger |
|---|---|---|---|
| net.core.netdev_budget | Flere pakker per NAPI-undersøgelse | For dråber i softnet_stat | Længere afstemninger fortrænger brugertråde |
| net.core.netdev_budget_usecs | Begræns tidsvinduet pr. afstemning | For jitter på grund af store bursts | For lille: flere kontekstændringer |
| RSS/RPS/XPS | Fordel flows på tværs af kerner | For hotspots på en kerne | Forkerte hashes: cache-forvrængninger |
| IRQ-affinitet | Bind IRQ'er tæt på kernen | Med NUMA-Missmatch | Fejlallokering skaber nye hotspots |
| GRO/LRO/TSO | Reducerer antallet af pakker | Til CPU-flaskehals | Jitter, større bursts |
| Optaget polling | Tidlig afhentning af pakker | Til hårde p99-mål | Mere CPU-forbrug |
RX/TX-ringe og cue-dybde: korrekt dimensionering af buffere
Selv med korrekt fordelte IRQ'er og passende budgetter kan NIC-ringe, der er for små eller for store, nedsætte ydelsen. Jeg tjekker derfor kortets RX/TX-ringstørrelser og tilpasser dem til burst-karakteren og latency-målene. For små ringe fører til udfald i NIC'en under trafikspidser, hvilket ses som rx_missed_errors eller fifo_errors i driverstatistikken. Ringe, der er for store, skjuler overbelastning, øger ventetiden og skaber lange trailing edges i p95/p99. Jeg leder efter en mellemvej: nok buffer til at absorbere korte udbrud, men ikke så meget, at pakkerne “ældes” i køerne.
Derudover ser jeg på host-side tx_kø_len og den anvendte Qdisc. Med sch_fq eller fq_codel kan jeg udjævne burst-adfærd og distribuere store TSO-pakker via pacing. Det reducerer microbursts ved switchporten og gør latency-kurven mere jævn - vigtigt for blandede arbejdsbelastninger, hvor små RPC'er kører sammen med store uploads. Jeg overvåger ethtool-statistikker og sammenholder dem med softnet_stat for at finde ud af, om overbelastningen opstår i NIC-ringen, i netdev-backloggen eller i Qdisc.
MTU, jumbo frames og segmentering
Die MTU er en klassisk løftestang, som ofte undervurderes. Jumbo frames reducerer antallet af pakker pr. Gbit/s og reducerer belastningen på CPU'en - men kun hvis stien virkelig er end-to-end jumbo-kompatibel. Jeg validerer derfor systematisk de eksterne stationer, switche og tunneler. Så snart der er fragmentering tilbage til 1500 et eller andet sted, er der risiko for MTU-problemer på stien, retransmissioner og unødvendige Jitter. I datacentre med dominerende øst/vest-kommunikation kan en homogen 9k-strategi betale sig, mens 1500 ofte er det mere stabile valg til internetvendte arbejdsbelastninger.
Jeg vurderer altid MTU'en i forbindelse med TSO/GSO/GROAlt for aggressiv bundtning kan føre til store bursts i TX, der fylder upstream-buffere og genererer latenstidstoppe. Målet er en konsekvent vej: fornuftig segmentering ved senderen, tilstrækkelige pacing-mekanismer og GRO, der sparer arbejde på modtagersiden uden at modarbejde realtidskrav.
UDP, QUIC og streaming-arbejdsbelastninger: overvej detaljerne
Ikke al trafik er TCP. UDP-tunge profiler (DNS, VoIP, QUIC, telemetri) opfører sig forskelligt i RSS/RPS og GRO. Moderne stakke understøtter UDP-GRO/GSO, som kan reducere belastningen på CPU'en - jeg bruger det selektivt og måler, om risikoen for omorganisering eller jitter stiger. For QUIC/HTTP3-belastninger er ren flowfordeling afgørende: RPS kan hjælpe, hvis NIC'en tilbyder for få RSS-køer, men må ikke “kaste rundt” med varme cache-strømme. På TX-siden indstiller jeg XPS for at samle transmissionsstier og reducere låsekonflikter. I praksis betaler en stille, kerneaffin allokering sig, især med mange mellemstore UDP-strømme, hvor hvert cache-hit tæller.
Virtualisering og containere: ren integration af host, guest og vhost
I virtualiserede miljøer skifter arbejdet mellem host, vhost-tråde og gæste-IRQ'er. Jeg sørger for, at vhost-net-tråde får deres egne kerner og kolliderer ikke med app-arbejdere. Deres affiniteter skal matche de fysiske RX/TX-køer, ellers vil der være unødvendig migration på tværs af CPU'er. I gæsten tjekker jeg virtio-net køer, aktiverer multi-queue og opsætter RSS/RPS analogt til bare metal. Hvor latency og pps er i forgrunden SR-IOV reducere overhead yderligere - forudsætningen er en konsistent NUMA-topologi: VF, vCPU og hukommelse hører til på samme node.
I containerstakken forårsager overlay-netværk, dybe NAT-kæder og komplekse CNI-topologier ekstra hop. Til latency-kritiske tjenester foretrækker jeg hostNetwork eller magre netværk (macvlan/ipvlan), udligner NAT-stier og holder Conntrack så lille som muligt. En konsekvent CPU-strategi er vigtig: IRQ- og NAPI-kerner på værten skal være placeret i nærheden af de kerner, som vhost/container-arbejdere kører på - det er den eneste måde at holde datastien kort og forudsigelig på.
Planlægning, C-states og IRQ-threading
Fordi ventetid ikke kun er beregningstid, men også Tidspunkt for opvågning Jeg minimerer dybe C-states på latency-kernerne. En aggressiv powersave kan koste millisekunder, før en SoftIRQ rent faktisk kører. Jeg sætter derfor min lid til performance governors, begrænser dybe C-states og holder turboen konsistent for at gøre frekvensspring forudsigelige. Lige så vigtigt er det IRQ-trådningHvor driverne tillader det, flytter jeg arbejdet til IRQ-tråde og prioriterer, så RX starter før downstream-arbejde uden helt at fortrænge userland. Samspillet mellem skemapolitikker, affiniteter og budgetter er vanskeligt; jeg tester trin for trin, logger p99 og holder øje med interferens med ksoftirqd, som ellers bliver en hemmelig flaskehals.
Observation i dybden: tracepoints, tællere, histos
Hvis målingerne forbliver vage, går jeg et niveau dybere: Jeg bruger kernel tracepoints omkring netif_receive_skb, napi_poll og net_dev_queue, for at se poll-varigheder, pakkemængder og ventetider som histogrammer. Sådanne fordelinger viser, om 1 % af afstemningerne tager for lang tid, eller om individuelle køer er ved at løbe tør. Derudover kan ethtool-rx/tx-tællere, TCP retransmits, busy poll hits og softnet_stat giver en klar indikation af, hvor pakker går tabt. Jeg bruger drop-analyse til at se, om NIC'en dropper (ringen er fuld), netdev-backloggen kollapser (time_squeeze), eller Qdisc/firewall'en bliver langsommere. Først når disse brikker i puslespillet passer sammen, justerer jeg ringe, budgetter eller offloads.
Strømlin sikkerhed og filtreringsveje
Komplekse ACL'er, dybe nftables/iptables-kæder og brede conntrack-tabeller tilføjer konstant latenstid pr. pakke. Jeg konsoliderer regler, arbejder med sets/maps og flytter generiske drops så langt frem i stien som muligt - ideelt set så tidligt som muligt ved NIC'en (XDP/clsact), hvis latency er kritisk. Statsløse flows, telemetri eller kendte “sikre” porte kan bruges på en målrettet måde. uden sporing for at eliminere behovet for dyre opslag. Samtidig holder jeg tilstandstabellerne friske, justerer hashstørrelser til belastningstoppe og rydder aggressivt op i forældreløse poster. Målet er en ren, sporbar policy-sti, som ikke kan ses i profilen som en permanent belastning.
Typiske anti-mønstre og hvordan jeg undgår dem
- Alle IRQ'er på én kerne: fører til overbelastning og hot ksoftirqd. Modgift: målrettede affiniteter pr. cue, NUMA-kohærent.
- Blind maksimering af ringe/budgetter: Skjuler overbelastning, øger forsinkelseshalerne. Modgift: øg gradvist, mål fordelinger.
- Forkert konfiguration af flow-hashing: Strømmene springer mellem kernerne, cachen går i stå. Modgift: stabile RSS-nøgler, RPS/XPS kun med et klart mål.
- App-tråde på de samme kerner som SoftIRQs: Interferens og jitter. Modgift: hård adskillelse, nabotildeling.
- Overlays/NAT uden budget: tilføjet til hvert hop. Afhjælpning: Strømlin stier, værtsnetværk til latente arbejdsbelastninger.
- Strømbesparelse på latency-kerner: Dybe C-tilstande bremser reaktionen. Modgift: præstationsregulator, begrænsning af C-tilstand.
- Aflæsning uden måling: TSO/GRO kan forværre bursts og jitter. Afhjælpning: Aktivér arbejdsbelastningsspecifik, overvåg p99.
Praktisk hosting: trin, der virker
Jeg starter med en ren målefase, sætter baseline og holder alle ændringer små i korte tidsvinduer, så jeg kan Årsager kan adskilles. Derefter aktiverer jeg irqbalance, kontrollerer den automatiske fordeling og indstiller om nødvendigt manuelle affiniteter, indtil ingen Hotspots er ikke længere synlige. Derefter sætter jeg Multi-Queue, RSS og - om nødvendigt - RPS/XPS op, synkroniseret med NUMA. Jeg binder app-arbejderne til kerner i nærheden af deres IRQ-kerner, men uden direkte kollision. Til sidst renser jeg firewall-stier, tjekker conntrack-tabeller og træffer bevidste beslutninger om offloads baseret på latency-mål.
Eksempel på playbook til p99-latenstider
Først måler jeg p95/p99 via repræsentativ belastning og sikre logfiler fra /proc/softirqs og /proc/net/softnet_stat for at Dråber og time_squeeze er tydeligt synlige. Derefter øger jeg netdev_budget eller netdev_budget_usecs trin for trin og holder p99 nede efter hver ændring, så jeg kan se ægte Tendenser genkende. Parallelt hermed binder jeg IRQ'er til kerner i en NUMA-node og flytter app-arbejdere til passende naboer. Hvis p99 fortsætter med at hoppe, tester jeg GRO/LRO-varianter og interrupt coalescing-profiler, hver med en kort målesti. Først når distributionen forbliver stabil, overfører jeg konfigurationen til Ansible-roller eller systemd-dropins.
Kort version til administratorer
Jeg opnår den største indflydelse ved at SoftIRQs, NAPI-budget, IRQ-affiniteter og app-tråde som en sammenhængende datasti. Jeg fordeler netværksarbejde på tværs af kerner, holder NUMA-køer sammenhængende og forbinder arbejdere fornuftigt, så Ruter Gør det kort. Jeg indstiller offloads bevidst og måler jitter i stedet for blindt at optimere til throughput. Til hårde latency-mål bruger jeg busy polling og CPU-isolering, mens housekeeping-CPU'er opfanger interferens. Hvis du implementerer disse trin på en disciplineret måde, får du konstant throughput, smallere latency-distributioner og et hostingmiljø, der reagerer forudsigeligt på belastningstoppe.


