...

softirq cpu hosting: optimera serverprestanda och nätverksgenomströmning

Jag visar hur softirq cpu tillsammans med NAPI, IRQ-distribution och ködesign begränsar eller frigör nätverksgenomströmningen i hosting. Med tydliga mätpunkter, målinriktad tuning och rena affiniteter minskar jag Fördröjningar och konsekvent öka pps-genomströmningen på produktiva servrar.

Centrala punkter

Dessa kärnidéer transporterar nätverkspaket effektivt via CPU, kernel och NIC - och håller svarstiderna till ett minimum. konstant låg.

  • NAPI:s budget finjustering: Fler paket per omröstning minskar omkostnaderna och jämnar ut CPU-belastning.
  • IRQ-balansering och affinitet: undvik hotspots, öka antalet träffar i cacheminnet, Fördröjningstoppar trycka.
  • Flera köer, RSS/RPS/XPS: parallellisera flöden, upprätthålla NUMA-anpassning, pps höja.
  • Avlastning använda medvetet: GRO/LRO, TSO, utvärdera sammanslagning, Jitter i sikte.
  • Isolering och Busy Polling: Förutsägbara svarstider på dedikerade Kärnor.

Grunderna: Vad som händer i kärnan under nätverkstrafik

Ett paket landar först i ett hårdvaruinterrupt, varefter kärnan tar över arbetet i SoftIRQs och NAPI poll-loopar. Jag ser till att den snabba HardIRQ-fasen förblir riktigt kort och att den faktiska logiken flyttas till rätt sammanhang så att CPU-tid inte försvinner. Ksoftirqd-trådarna träder bara in om direktbearbetning inte är möjlig, vilket snabbt leder till köer under kontinuerlig belastning. Det är precis här som väntetiden uppstår, vilket återspeglas i ökad TTFB och fluktuerande genomströmning. Om du vill fördjupa dig kan du hitta praktisk kunskap om IRQ-bearbetning i den här artikeln om Interrupthantering och CPU-prestanda, som jag använder för kategoriseringen.

NAPI, SoftIRQs och ksoftirqd: kontrollera fördröjning i stället för att hantera den

NAPI minskar avbrottsstormarna genom att plocka upp flera paket per körning inom en definierad budget och därmed minimera avbrottstiden. Overhead sänker. Om budgeten är otillräcklig staplas paketen på varandra, ksoftirqd går varm och Fördröjning ökar mätbart. I sådana situationer kontrollerar jag systematiskt /proc/softirqs och /proc/net/softnet_stat för att visualisera droppar, time_squeeze eller överfyllda köer. Sedan ökar jag gradvis net.core.netdev_budget eller net.core.netdev_budget_usecs och övervakar CPU-belastning, p95/p99-distribution och paketförluster parallellt. Tricket är att få tillräckligt med arbete gjort per poll utan att tränga ut den interaktiva exekveringen av användarlandstrådar.

IRQ-balansering och -affinitet: undvik hotspots, öka antalet träffar i cacheminnet

En enda kärna med alla NIC IRQ:er blir en flaskhals eftersom den måste hantera avbrott, mjuka IRQ:er och apptrådar; jag distribuerar därför IRQ:er riktade. Tjänsten irqbalance hjälper till, men för höga pps-hastigheter mappar jag uttryckligen RX/TX-köer via affinitet till lämpliga kärnor. På NUMA-system binder jag köer till kärnor i samma nod för att undvika fjärrminnesåtkomst. Applikationstrådar körs på närliggande men separata kärnor, vilket förbättrar cachelokaliteten och schemaläggningen. Den här guiden till strategisk distribution ger en bra översikt över de IRQ-balansering i datacentret, som jag använder som referens för att finjustera.

Multi-queue, RSS/RPS/XPS: Använd parallellisering på rätt sätt

Moderna nätverkskort levereras med flera RX/TX-köer, som jag kan styra via RSS till flöden och på så sätt uppnå verklig parallellism. Om kortet erbjuder för få köer använder jag RPS/XPS för att göra justeringar på mjukvarusidan för att fördela paketen på ett vettigt sätt över flödena. kärnor att trycka på. Ren hashdistribution är viktigt så att ett flöde alltid finns kvar på samma CPU och inga dyra cache-förvrängningar uppstår. Samtidigt håller jag TX- och RX-vägarna nära varandra för att undvika låskonflikter och onödiga accesser mellan noderna. Detta ökar pps-genomströmningen utan att en enda kärna drar i bromsen.

CPU-affinitet ända in i användarutrymmet: end-to-end-tänkande

Jag planerar datavägen från NIC-IRQ via NAPI-köer till appens arbetstrådar så att paketen når sin destination utan onödiga krokar och Svarstid förblir konstant. För att uppnå detta separerar jag konsekvent kärnor för avbrott/softIRQ från app-kärnor och skapar tydliga Affinitet-regler. Webbservrar, omvända proxyer och databaser får fasta CPU-uppsättningar som ligger nära IRQ-kärnorna för att hålla vägarna korta. Dessutom ställer jag in CPU-guvernören på prestanda så att klockförändringar inte driver jitter till p99. Den här konsekventa tilldelningen gör beteendet förutsägbart och hjälper till att diagnostisera flaskhalsar på ett enkelt sätt.

Avlastning, GRO/LRO, brandvägg och eBPF: Spara belastning utan att flyga i blindo

Spara avlastning av checksumma, TSO och koalescens CPU-tid, De kan dock ändra paketstorlekar, burstbeteende och jitter, vilket är anledningen till att jag mäter effekterna specifikt. GRO/LRO buntar ihop ramar och minskar belastningen på stacken, men för realtidskrav beslutar jag situationsbaserat om Avaktivering eller begränsad användning. Conntrack-tabeller och djupa nftables/iptables-kedjor kostar tid, så jag rensar upp bland överflödiga regler och förenklar sökvägarna. Om det behövs använder jag eBPF (XDP, tc-BPF) för att fatta tidiga beslut vid NIC:en och undvika kostsamma vägar. En bra utgångspunkt för att finjustera praxis är denna översikt över Sammanslagning av avbrott, vilket jag tar hänsyn till för känsliga latensbudgetar.

Busy polling och CPU-isolering: Låsning av svarstider

För hårda latensmål använder jag busy polling så att userspace-sockets plockar upp paket ännu tidigare och Väntetider förkorta. Detta ökar belastningen, men ger mig mycket smala p99-distributioner för API eller handelsarbetsbelastningar på dedikerade Kärnor. Dessutom isolerar jag kärnor med isolcpus=, nohz_full= och rcu_nocbs= så att timers, RCU och systemtjänster bara körs på hushålls-CPU:er. Den här separationen förhindrar störningar på latensprocessorerna och gör beteendet reproducerbart. Resultatet är en tydlig färdplan: dedikerade kärnor, tidig paketinsamling, definierade budgetar.

Övervakning och felsökning: från symptom till orsak

Jag börjar med pps, genomströmning och kärnbelastning, sedan kontrollerar jag droppar och aktiviteten hos ksoftirqd-trådar över tid för att på ett tillförlitligt sätt känna igen mönster. Verktyg som sar, htop, ss, nload och ethtool visar mig när och var överbelastning uppstår och om Ledtrådar når sina gränser. Fördelningar är viktiga i stället för medelvärden så att kvällstoppar, cron-fönster eller kampanjer inte går förlorade. Jag korrelerar TTFB-toppar med IRQ-distribution, NAPI-budget och offload-inställningar för att kunna göra riktade justeringar. En justerad IRQ-affinitet eller en ny skräddarsydd NAPI-budget är ofta tillräckligt för att märkbart minska timeouts.

Tuningparametrar i en överblick

Följande översikt hjälper mig att använda ändringar på ett klokt sätt och att tydligt ange effekterna innan jag gör permanenta ändringar. Rollouts planera. Jag testar varje justering iterativt, mäter latensfördelningar och observerar bieffekter på CPU och minne. Jag ändrar alltid bara en punkt per testfönster så att orsak och verkan förblir tydliga. Sedan dokumenterar jag resultaten och fastställer tröskelvärden för varningar. På så sätt uppnår jag reproducerbara förbättringar utan att riskera överraskningar i den produktiva trafiken.

Parameter/Funktion Effekt i datavägen När ska man samla in pengar/aktivera Risker/biverkningar
net.core.netdev_budget Fler paket per NAPI-undersökning För droppar i softnet_stat Längre omröstningar tränger undan användartrådar
net.core.netdev_budget_usecs Begränsa tidsfönstret per omröstning För jitter på grund av stora bursts För liten: fler kontextförändringar
RSS/RPS/XPS Fördela flöden över kärnor För hotspots på en kärna Felaktiga hashes: cache-förvrängningar
IRQ-affinitet Bind IRQ:er nära kärnan Med NUMA-Missmatch Felallokering skapar nya hotspots
GRO/LRO/TSO Minskar antalet förpackningar För CPU-flaskhals Jitter, större bursts
Upptagen polling Tidig paketinsamling För tuffa p99-mål Mer CPU-förbrukning

RX/TX-ringar och cue-djup: dimensionera buffertar korrekt

Även med korrekt fördelade IRQ:er och lämpliga budgetar kan NIC-ringar som är för små eller för stora försämra prestandan. Jag kontrollerar därför kortets RX/TX-ringstorlekar och anpassar dem till burstkaraktären och latensmålen. För små ringar leder till avbrott i NIC:en under trafiktoppar, vilket syns som rx_missed_errors eller fifo_errors i drivrutinens statistik. Ringar som är för stora döljer överbelastning, ökar latensen och skapar långa trailing edges i p95/p99. Jag letar efter en medelväg: tillräckligt med buffert för att absorbera korta utbrott, men inte så mycket att paketen “åldras” i köerna.

Dessutom tittar jag på värdsidan tx_kö_len och den Qdisc som används. Med sch_fq eller fq_codel kan jag jämna ut burstbeteendet och fördela stora TSO-paket via pacing. Detta minskar mikroburst på switchporten och gör latensförloppet jämnare - viktigt för blandade arbetsbelastningar där små RPC:er körs parallellt med stora uppladdningar. Jag övervakar ethtool-statistiken och korrelerar den med softnet_stat för att se om överbelastningen uppstår i NIC-ringen, i netdev-backloggen eller i Qdisc.

MTU, jumboramar och segmentering

Die MTU är en klassisk hävstång som ofta underskattas. Jumboramar minskar antalet paket per Gbit/s och minskar belastningen på processorn - men bara om vägen verkligen är jumbokapabel från början till slut. Jag validerar därför systematiskt fjärrstationerna, switcharna och tunnlarna. Så snart det sker en fragmentering tillbaka till 1500 någonstans finns det risk för MTU-problem på vägen, återsändningar och onödiga Jitter. I datacenter med dominerande öst-västlig kommunikation lönar det sig att använda en homogen 9k-strategi, medan 1500 ofta är det stabilare valet för arbetsbelastningar som vetter mot Internet.

Jag utvärderar alltid MTU i samband med TSO/GSO/GROAlltför aggressiv buntning kan leda till stora bursts i TX som fyller uppströms buffertar och genererar latensökningar. Målet är en konsekvent väg: förnuftig segmentering i sändaren, tillräckliga pacing-mekanismer och GRO som sparar arbete på mottagarsidan utan att motverka realtidskraven.

UDP, QUIC och strömmande arbetsbelastningar: tänk på detaljerna

All trafik är inte TCP. UDP-tunga profiler (DNS, VoIP, QUIC, telemetri) beter sig annorlunda i RSS/RPS och GRO. Moderna stackar stöder UDP-GRO/GSO, vilket kan minska belastningen på processorn - jag använder detta selektivt och mäter om omordningsrisker eller jitter ökar. För QUIC/HTTP3-belastningar är ren flödesfördelning avgörande: RPS kan hjälpa till om NIC:en erbjuder för få RSS-köer, men får inte “kasta runt” några heta cache-flöden. På TX-sidan ställer jag in XPS för att samla överföringsvägar och minska låskonflikter. I praktiken lönar det sig med en tyst, kärnaffin allokering, särskilt med många medelstora UDP-flöden där varje cache-träff räknas.

Virtualisering och containrar: ren integration av host, guest och vhost

I virtualiserade miljöer skiftar arbetet mellan IRQ:er för host, vhost-trådar och guest. Jag ser till att vhost-nät-trådar får sina egna kärnor och kolliderar inte med app-arbetare. Deras affiniteter måste matcha de fysiska RX/TX-köerna, annars blir det onödig migration mellan olika CPU:er. I gästen kontrollerar jag virtio-net-köer, aktiverar multi-queue och sätter upp RSS/RPS analogt med bare metal. Där latency och pps är i förgrunden SR-IOV ytterligare minska overheadkostnader - förutsättningen är en konsekvent NUMA-topologi: VF, vCPU och minne hör hemma på samma nod.

I containerstacken orsakar överlagrade nätverk, djupa NAT-kedjor och komplexa CNI-topologier ytterligare hopp. För latenskritiska tjänster föredrar jag hostNetwork eller magra nätverk (macvlan/ipvlan), utjämnar NAT-vägar och håller Conntrack så liten som möjligt. En konsekvent CPU-strategi är viktig: IRQ- och NAPI-kärnor i värden bör placeras i närheten av de kärnor som vhost/container-arbetare körs på - detta är det enda sättet att hålla datavägen kort och förutsägbar.

Schemaläggning, C-status och IRQ-threading

Eftersom latens inte bara är beräkningstid utan också Tid för uppvaknande Jag minimerar djupa C-status på latens-kärnorna. En aggressiv powersave kan kosta millisekunder innan en SoftIRQ faktiskt körs. Därför förlitar jag mig på prestandastyrningen, begränsar djupa C-states och håller turbon konsekvent för att göra frekvenshopp förutsägbara. Lika viktigt är IRQ-trådningDär drivrutinerna tillåter det flyttar jag arbetet till IRQ-trådar och prioriterar så att RX startar före nedströmsarbete utan att helt förskjuta användarland. Samspelet mellan schemapolicyer, affiniteter och budgetar är knepigt; jag testar steg för steg, loggar p99 och ser upp för störningar med ksoftirqd, som annars blir en hemlig flaskhals.

Observation på djupet: tracepoints, counters, histos

Om mätvärdena förblir vaga går jag en nivå djupare: jag använder kernel tracepoints runt netif_mottagning_skb, napi_poll och net_dev_queue, för att visa polltider, paketvolymer och väntetider som histogram. Sådana fördelningar visar om 1 % av undersökningarna tar för lång tid eller om enskilda köer håller på att ta slut. Dessutom kan ethtool-rx/tx-räknare, TCP retransmits, busy poll hits och softnet_stat visar tydligt var paket går förlorade. Jag använder drop-analys för att se om NIC:en släpper paket (ringen full), om netdev-backloggen kollapsar (time_squeeze) eller om Qdisc/firewall saktar ned. Det är först när dessa pusselbitar passar ihop som jag justerar ringar, budgetar eller avlastningar.

Effektivisera säkerhets- och filtreringsvägarna

Komplexa ACL:er, djupa nftables/iptables-kedjor och breda conntrack-tabeller ger konstant latens per paket. Jag konsoliderar regler, arbetar med set/maps och flyttar generiska drops så långt fram i vägen som möjligt - helst så tidigt som möjligt vid NIC:en (XDP/clsact) om latensen är kritisk. Statlösa flöden, telemetri eller kända “säkra” portar kan användas på ett målinriktat sätt. utan spårning för att eliminera behovet av kostsamma uppslagningar. Samtidigt håller jag statstabellerna uppdaterade, anpassar hashstorlekar till belastningstoppar och rensar aggressivt bort föräldralösa poster. Målet är en ren, spårbar policyväg som inte märks i profilen som en permanent belastning.

Typiska anti-mönster och hur jag undviker dem

  • Alla IRQ:er på en kärna: leder till överbelastning och heta ksoftirqd. Motgift: riktade affiniteter per cue, NUMA-koherent.
  • Blind maximering av ringar/budgets: döljer trängsel, ökar latenstiderna. Motgift: öka stegvis, mät distributioner.
  • Felaktig konfiguration av flödeshastighet: Flödena hoppar mellan kärnorna, cacheminnet försvinner. Motgift: stabila RSS-nycklar, RPS/XPS endast med ett tydligt mål.
  • App-trådar på samma kärnor som SoftIRQs: Interferens och jitter. Motgift: hård separation, granntilldelning.
  • Overlays/NAT utan budget: läggs till varje hopp. Åtgärd: Strömlinjeforma vägar, värdnätverk för latenta arbetsbelastningar.
  • Energibesparing på latenskärnor: Djupa C-tillstånd saktar ner reaktionen. Motgift: prestandareglering, begränsning av C-tillstånd.
  • Avlastning utan mätning: TSO/GRO kan förvärra bursts och jitter. Åtgärd: Aktivera arbetsbelastningsspecifik, övervaka p99.

Praktisk hosting: steg som fungerar

Jag börjar med en ren mätfas, sätter baslinjer och håller alla förändringar små i korta tidsfönster så att jag kan Orsaker kan separeras. Jag aktiverar sedan irqbalance, kontrollerar den automatiska fördelningen och ställer vid behov in manuella affiniteter tills ingen Hotspots är inte längre synliga. Sedan konfigurerar jag Multi-Queue, RSS och - om det behövs - RPS/XPS, synkroniserat med NUMA. Jag binder app-arbetarna till kärnor i närheten av deras IRQ-kärnor, men utan direkt kollision. Slutligen rensar jag brandväggsvägar, kontrollerar conntrack-tabeller och fattar medvetna beslut om avlastning baserat på latensmål.

Exempel på spelbok för p99-latenstider

Först mäter jag p95/p99 via representativ belastning och säkra loggar från /proc/softirqs och /proc/net/softnet_stat för att Droppar och time_squeeze är tydligt synliga. Sedan ökar jag netdev_budget eller netdev_budget_usecs steg för steg och håller p99 efter varje ändring så att jag kan se verkliga Trender känner igen. Parallellt binder jag IRQ:er till kärnor i en NUMA-nod och flyttar apparbetare till lämpliga grannar. Om p99 fortsätter att hoppa testar jag GRO/LRO-varianter och avbryter sammanfogningsprofiler, var och en med en kort mätväg. Först när distributionen förblir stabil överför jag konfigurationen till Ansible-roller eller systemd-dropins.

Kort version för administratörer

Jag uppnår störst hävstångseffekt genom att SoftIRQs, NAPI-budget, IRQ-affiniteter och apptrådar som en sammanhängande dataväg. Jag fördelar nätverksarbete över kärnor, håller NUMA-köer sammanhängande och kopplar ihop arbetare på ett förnuftigt sätt så att Rutter håll dig kort. Jag ställer in avlastningar medvetet och mäter jitter i stället för att blint optimera för genomströmning. För hårda latensmål förlitar jag mig på upptagen polling och CPU-isolering, medan hushålls-CPU:er fångar upp störningar. Om du implementerar dessa steg på ett disciplinerat sätt får du konstant genomströmning, smalare latensfördelningar och en hostingmiljö som reagerar förutsägbart på belastningstoppar.

Aktuella artiklar