...

Receive Side Scaling vid 10 och 25 Gbit/s: Prestandajustering för moderna Linux-servernätverk

Receive Side Scaling fördelar nätverkstrafiken på ett målinriktat sätt över flera kärnor via 10- och 25-Gbit/s-länkar, så att Linux-servrar kan hantera höga genomströmningshastigheter med låg latens. Jag visar på ett praktiskt sätt hur jag aktiverar RSS, som Ledtrådar på Cores mapp och på så sätt undviker flaskhalsar vid avbrott och cacheträffar.

Centrala punkter

Jag sammanfattar kortfattat de viktigaste punkterna så att du snabbt kan planera nästa steg.

  • Lastfördelning: Paketen fördelas över flera köer till flera kärnor.
  • Cache-plats: Ett flöde förblir konsekvent i samma kö.
  • Hashning: 4-tupel-hash fördelar flöden jämnt över köerna.
  • affinitet: Målriktad IRQ-mappning minskar latensen.
  • Skalning: Från 10/25 Gbit/s garanterar RSS hög genomströmning.

Dessa punkter hänger samman och utgör grunden för Prestanda om verkliga arbetsbelastningar. Jag prioriterar först att fastställa rätt antal köer, sedan CPU-Affinitet. Därefter kontrollerar jag hash-parametrar och finjusteringar.

Vad Receive Side Scaling gör

RSS delar upp paketmottagningen i flera Mottagningsköer, som jag tilldelar specifika CPU-kärnor för att ingen enskild kärna ska bli en flaskhals. Detta minskar kraftiga interrupt-toppar och jämnar ut bearbetningen via SoftIRQ:er, vilket minskar latenstoppar och ökar genomströmningen. Varje kö utlöser egna avbrott, som jag kopplar fast till specifika kärnor för att hålla datavägarna konsistenta. Denna konsistens främjar Cache-Lokalitet, eftersom ett flöde alltid möter samma kärna. Just detta samspel bidrar direkt till mätbar effektivitet vid höga PPS-hastigheter.

Så fungerar RSS rent tekniskt

NIC skapar utifrån käll- och mål-IP samt käll- och målport en Hash och använder den som index för Indirection-tabellen, som pekar på köer. På detta sätt hamnar paket från ett flöde alltid i samma kö och förblir därmed bundna till samma kärna. Olika flöden fördelas jämnt, förutsatt att hashnycklar och protokollfält är korrekt konfigurerade. Arbetet hamnar därmed nära Hårdvara, vilket innebär att kärnan behöver balansera mindre och att overheaden minskar. Det är precis vad jag vill uppnå för att hålla paketbehandlingen per kärna låg vid 10G/25G.

Varför RSS från 10 och 25 Gbit/s är viktigt

Vid 1 Gbit/s är det ofta en enda Kärnan paketbelastningen, men från 10 Gbit/s vänds balansen snabbt. Små paket får PPS-värdet att stiga, vilket gör att en kärna snabbt når 100 procent belastning och paketförluster uppstår. Just då fungerar RSS som en multiplikator för användbar bandbredd. Jag fördelar belastningen över flera Kärnor, minskar kontextbyten och håller latenskurvorna mer stabila. Resultatet: den faktiska genomströmningen närmar sig först länkhastigheten med en ren RSS.

Konfigurera RSS på Linux-servern

I Linux hanterar jag RSS främst via ethtool, drivrutinsinställningar och sysfs, så att nätverkskortets funktioner verkligen utnyttjas. Först läser jag av det maximala antalet RX-kanaler, sedan ställer jag in ett antal köer som passar CPU:n. Därefter kontrollerar jag RSS-hash för TCP/UDP och valfritt för VLAN eller tunneling, så att belastningsprofilerna förblir jämnt fördelade. För att hantera grundbruset i avbrottsfördelningen hjälper mig IRQ-balansering, även om jag helst lägger in kritiska köer manuellt. Så här kopplar jag Ledtrådar anpassas noggrant till värddatorns topologi och förhindrar störande förflyttningar.

Indirection-Table, RSS-Key och finjustering av hash: specifika kommandon

Jag kontrollerar först den aktuella fördelningen och nyckeln för nätverkskortet:

ethtool -x eth0 # Visa indirektionstabell (RX-köer) och RSS-nyckel
ethtool -n eth0 rx-flow-hash tcp4
ethtool -n eth0 rx-flow-hash udp4

För en ren och jämn fördelning ställer jag in Indirection-Table på det önskade antalet köer. Vid 16 köer väljer jag en jämn fördelning:

ethtool -X eth0 equal 16  # Fördelning jämnt över 16 köer

Om det behövs anpassar jag hash-fälten. För TCP4 med 4-tupel (s=src-ip, d=dst-ip, f=src-port, n=dst-port):

ethtool -N eth0 rx-flow-hash tcp4 sdfn
ethtool -N eth0 rx-flow-hash udp4 sdfn
ethtool -N eth0 rx-flow-hash tcp6 sdfn
ethtool -N eth0 rx-flow-hash udp6 sdfn

Vissa drivrutiner tillåter även att man anger en egen RSS-nyckel (t.ex. för bättre spridning i särskilda fall):

ethtool -X eth0 hkey   # endast om drivrutinen/nätverkskortet stöder det

Ställa in CPU-affinitet och NUMA korrekt

Jag kartlägger varje RX-kö med hjälp av IRQ-affinitet till dedikerade kärnor och ta hänsyn till NUMA så att data endast passerar kort genom minneskontrollern. Om nätverkskortet körs på nod 0 kopplar jag även huvudköerna till kärnor på nod 0 och placerar arbetsbelastningarna i närheten. Denna närhet minskar fjärråtkomst och sänker minneslatensen avsevärt. Det är användbart att ha en profil för produktiva köer samt separata kärnor för hanterings- och avlastningsuppgifter. Den som vill fördjupa sig ytterligare hittar tips för finjustering under IRQ-affinitet, vad gäller planeringen per Kärna förenklad.

IRQ-affinitet – en handbok: från IRQ:er till stabil kärnbindning

Jag tar först reda på vilka IRQ:er som hör till RX-köerna och kopplar sedan fast dem:

grep -E "eth0.*Rx" /proc/interrupts
cat /sys/class/net/eth0/device/numa_node

Jag gör tilldelningen via smp_affinity_list, så att jag slipper räkna ut hexmasker. Exempel: RX-köer 0–7 på kärnorna 2–9:

# Exempel: Tilldela IRQ:er till kärnorna 2–9 (en rad per IRQ)
echo 2  > /proc/irq//smp_affinity_list
echo 3  > /proc/irq//smp_affinity_list
echo 4  > /proc/irq//smp_affinity_list
...
echo 9  > /proc/irq//smp_affinity_list

Viktigt: MSI-X måste vara aktiverat för att varje kö ska ha egna avbrott. Om jag använder manuell pinning spärrar jag irqbalans för dessa IRQ:er (t.ex. via en svartlista) eller inaktivera tjänsten specifikt på värddatorer med statisk layout. Jag kontrollerar dessutom NUMA med lscpu och PCIe-tilldelningen, så att jag inte skapar vägar mellan noder.

Hash-konfiguration och protokoll

Jag definierar hash-fälten så att äkta Trafik-Sprid mönstren jämnt istället för att de hamnar i endast ett fåtal köer. För TCP/UDP använder jag 4-tupeln, för IPv6 på liknande sätt, medan jag vid VXLAN eller GRE tar hänsyn till ytterligare fält i kapslingen. Vissa nätverkskort erbjuder konfigurerbara hashnycklar, som jag anpassar efter den dominerande arbetsbelastningen. Så snart jag ser belastningskluster i enskilda köer justerar jag hashvalet. Detta steg tar lite tid, men förhindrar en obalans vid högt antal anslutningar.

Interrupt-Coalescing och PPS

Jag kombinerar RSS med måttlig Sammanslagning av avbrott, för att samla PPS-intensiv trafik i hanterbara batcher. Detta minskar avbrottsöverheaden, men får inte försämra latensen för känsliga tjänster. Därför mäter jag rundturstider och justerar coalescing-värdena stegvis. Den som hanterar lagrings- eller säkerhetskopieringsbelastning kan sammanföra i större utsträckning än vid L7-API:er eller VoIP. Sammanfattningsvis balanserar jag Fördröjning mot genomströmning, tills båda stämmer överens.

Koalescens i praktiken: profiler och mätpunkter

Jag börjar med måttliga standardinställningar och arbetar mig fram mot det optimala för varje arbetsbelastning. Tre beprövade utgångsprofiler:

  • API/låg latens: rx-usecs 2–6, rx-ramar 16–32, adaptiv av
  • Allround: rx-usecs 8–16, rx-ramar 32–64, anpassningsbar till
  • Bulk/Lagring: 24–48 rx-usecs, rx-ramar 128–256, anpassningsbar till
ethtool -c eth0
ethtool -C eth0 rx-usecs 12 rx-frames 64 adaptive-rx on

För detta mäter jag p95/p99-latenser, PPS, CPU-belastning per kärna och återutsändningar. Så snart jag ser en ökande varians för API/VoIP går jag vidare med rx-usecs ner igen. När det gäller lagring skalar jag hellre upp via bildrutor för att spara på avbrott.

RSS i 10-Gbit-miljöer

På 10G-nätverkskort arbetar jag oftast med 8 till 16 Ledtrådar per port, förutsatt att CPU:n tillhandahåller tillräckligt många kärnor. Webbservrar, lagringsgateways och virtualiseringsvärdar skalar därmed smidigt över många parallella anslutningar. Jag kopplar huvudköerna till lediga kärnor och mäter därefter PPS, latens och återutsändningar. Om det uppstår förluster kontrollerar jag coalescing, hash och utnyttjandet per kö. Därefter finjusterar jag affinitet, tills belastningen ser jämn ut.

RSS i 25-Gbit- och Multi-25G-konfigurationer

Med 25 Gbit/s ökar PPS och bussbelastningen, vilket är anledningen till att jag NUMA-Var mer uppmärksam på tillräckligt med köer och avlastningar. Large Receive Offload (LRO) eller RSC kan minska paketbelastningen på stacken, förutsatt att applikationerna klarar det. Dessutom kontrollerar jag PCIe-banor för att utesluta flaskhalsar utanför nätverket. I värddatorer med flera 25G-länkar separerar jag köer och affinitet strikt efter uppgifter och noder. På så sätt använder jag Bandbredd och kärnor på ett effektivt sätt, utan att hamna i trafik mellan noder.

Hårdvaru- och drivrutinsinformation: vad jag tar hänsyn till

Alla nätverkskort fungerar inte på samma sätt. Intels olika generationer (t.ex. ixgbe, i40e, ice) erbjuder funktioner som Flow Director/ATR, som på ett målinriktat sätt kopplar flöden till köer – vilket är användbart när jag vill jämna ut flaskhalsar. Mellanox mlx5 kan stödja aRFS i hårdvaran, vilket minskar CPU-belastningen när stacken betjänar många socklar. Jag avgör från fall till fall om jag ska aktivera dessa funktioner och mäter om de förbättrar fördelningen. På routing-/NAT-system stänger jag ofta av LRO och använder istället GRO för att upprätthålla header-konsistensen; på rena serverarbetsbelastningar kan LRO/GRO bidra till att dämpa PPS-trycket. Det är dessutom viktigt med tillräckligt många MSI-X-vektorer per kö och korrekta firmwareversioner.

RSS inom virtualisering och containrar

I hypervisorn kombinerar jag fysiska RSS-Köer med vNIC:er som stöder flera köer, till exempel virtio-net, så att gästerna inte drabbas av konstgjorda flaskhalsar. Jag ser till att virtuella maskiner har CPU-pinning och fastställer deras vCPU-NUMA-närhet till det fysiska nätverkskortet. På så sätt stannar data lokalt och värden betalar mindre för minnesåtkomst. För containrar kopplar jag kritiska poddar till lämpliga kärnor och håller värdköerna fria från störande belastning. Denna ordning ökar Effektivitet vid mikrotjänster, där många små flöden uppstår.

Att använda SR-IOV och VF-RSS på rätt sätt

Med SR-IOV tilldelar jag virtuella maskiner (VM) egna virtuella funktioner (VF), som i sin tur kan tillhandahålla flera köer och RSS. Jag planerar in tillräckligt många virtuella funktioner (VF) per port, tar hänsyn till MSI-X-kapaciteten och kopplar VF-IRQ:erna i den virtuella maskinen så att de stämmer överens med dess vCPU:er. I Linux-gäster aktiverar jag Multi-Queue explicit, annars förblir vNIC:en ofta enstegs:

# i gäst (virtio-net-exempel)
ethtool -l eth0
ethtool -L eth0 combined 4

Jag fördelar värddatorer med flera virtuella funktioner (VF) strikt per NUMA-nod och arbetsbelastning, så att virtuella maskiner (VM) inte stör varandra i samma fysiska RX-vägar.

Övervakning och felsökning

Jag övervakar utnyttjandegraden per , enskilda kärnor, paketförluster och återutsändningar för att upptäcka obalanser i ett tidigt skede. Om en kärna lyfter medan andra förblir lediga stämmer ofta inte affiniteten eller antalet köer. I sådana fall kontrollerar jag hashfält, IRQ-masker och coalescing-värden ett efter ett. Dessutom tittar jag på SoftIRQ-belastning, eftersom den ger indikationer på utträngningseffekter. Först när dessa signaler ser stabila ut skalar jag upp trafiken eller utökar Ledtrådar fortsätta.

RPS, RFS och XPS: Programvarutillägg till RSS

Om ett nätverkskort bara har några få köer eller om jag använder nätverksbundning/tunneling kompletterar jag RSS med RPS (Receive Packet Steering) och RFS (Receive Flow Steering). RPS fördelar SoftIRQ:er mellan kärnorna, medan RFS kopplar flöden till den kärna där den tillhörande sockeln är aktiv. Jag aktiverar båda på ett målinriktat sätt:

# Öka antalet globala flödesposter (RFS)
echo 32768 > /proc/sys/net/core/rps_sock_flow_entries

# Ställa in antal CPU:er per RX-kö för RPS (exempelmask, anpassa!)
for f in /sys/class/net/eth0/queues/rx-*/rps_cpus; do echo ffff > "$f"; done

Ställ in flödestabellen för # Pro RX-kö för RFS
for f in /sys/class/net/eth0/queues/rx-*/rps_flow_cnt; do echo 4096 > "$f"; done

På TX-sidan använder jag XPS (Transmit Packet Steering), så att utgående paket skickas från den kärna som genererade dem:

for f in /sys/class/net/eth0/queues/tx-*/xps_cpus; do echo ffff > "$f"; done

RPS/RFS/XPS kräver lite CPU-resurser, men är till hjälp när jag saknar köer på hårdvarusidan eller när jag vill hålla strikt fast vid socket-lokaliteten.

Single-Flow-prestanda, GRO/TSO och Busy-Polling

En enskild ström förblir av goda skäl bunden till en kärna. Om jag vill öka bandbredden för en enskild ström satsar jag på avlastning (GRO/TSO), en hög kärnfrekvens och välavstämd sammanslagning. För latenskritiska vägar kan Upptagen polling hjälpa:

Ställ in # på ett lågt värde och mät
sysctl -w net.core.busy_read=25
sysctl -w net.core.busy_poll=25

Busy-Polling minskar antalet kontextbyten, men tar upp CPU-tid. Jag aktiverar det endast där p99-latenser spelar roll och kontrollerar alltid effekterna på den totala belastningen och tail-latensen. GRO brukar jag vanligtvis ha avstängt på servrar, medan jag använder LRO beroende på roll; när det gäller middleboxar är jag försiktig för att inte störa bearbetningen av rubriker och hash-konsistensen.

Rekommendationer och exempel: köer, affinitet, kommandon

Som utgångspunkt väljer jag ett antal köer som passar till CPU Justera, observera sedan belastningen per kö och justera stegvis. Vid 10G räcker det ofta med 8–16 köer, vid 25G sätter jag ofta ett högre antal, förutsatt att det finns tillräckligt med kärnor. För affinitet använder jag tydliga masker per IRQ, så att jag senare lättare kan analysera vägarna. Följande tabell ger kompakta riktvärden som jag sedan verifierar genom mätningar. Det är först mätresultaten som avgör om jag mer öka eller minska.

Länkhastighet Typiska RX-köer Exempel på kommandon Anteckningar
10 Gbit/s 8-16 ethtool -l eth0 | ethtool -L eth0 rx 16 Sammansmältning Håll en måttlig nivå, kontrollera L7-latensen
25 Gbit/s 16–32+ grep . /proc/interrupts | IRQ-masker per eko NUMA Observera, kontrollera PCIe-banorna
Multi-25G Per portion separat Aktivera vNIC Multi-Queue (t.ex. virtio) Köer på kärnor och Arbetsbelastning dela

Dessa riktvärden utgör endast en utgångspunkt, inte ett mål, eftersom arbetsbelastningarna varierar kraftigt. Jag dokumenterar ändringar, mäter före och efter justeringen och håller i övrigt miljön oförändrad. Så snart systemet förblir stabilt under produktionsbelastning låser jag konfigurationen. Senare upprepar jag mätningarna efter uppdateringar av kärnan eller drivrutinerna. På så sätt håller jag mig till RSS Håller kursen och ger säkra, reproducerbara resultat.

Vanliga hinder och åtgärder för att undvika dem

För få Ledtrådar Enstaka kärnor överbelastas, medan för många ökar administrationsarbetet och försämrar cache-träfffrekvensen. En olycklig affinitet förskjuter avbrott till redan belastade kärnor eller felaktiga NUMA-noder. Även en olämplig hash leder till att dominerande flöden täpper till köerna. Jag löser detta steg för steg: justera antalet köer, korrigera affiniteten, utöka hash-fälten, finjustera coalescing. Varje ändring dokumenterar jag med Mätetal, innan jag går vidare till nästa spak.

Praktiska scenarier

En lagringsserver med 10G drar snabbt nytta av 8–12 Ledtrådar samt måttlig koalescering för att genomföra bulköverföringar smidigt. En API-server med ett stort antal anslutningar behöver ofta finare hashfält och lägre latens vid avbrott. Virtualiseringsvärdar vinner betydligt på att ha vNIC-Multi-Queue aktiverat på gästsidan och att det passar värdens layout. Container-arbetsbelastningar fungerar smidigare när kritiska podar körs nära NIC och NUMA-minne. Dessa mönster utökar jag situationsanpassat genom att PPS, jämför vidarebefordringar och köfördelning.

Kraftfulla plattformar som en fördel

Webbhotellkonfigurationer med konsekvent inställda RSS, nätverkskort med flera köer och korrekt affinitet ger märkbara reserver vid toppbelastning. Den som utvärderar servererbjudanden bör specifikt fråga efter stöd för flera köer, NUMA-pinning och övervakning. En leverantör som tydligt implementerar dessa punkter uppnår ofta märkbart bättre genomströmningskurvor. För högpresterande server- och hostinglösningar vill jag här ge webhoster.de som en klar rekommendation. Denna inriktning lönar sig i Prestanda och stabilitet, särskilt vid många parallella flöden.

Sammanfattning för praktiken

Jag aktiverar Ta emot Sidosskalning: ställ in ett rimligt antal köer, koppla IRQ:er till lämpliga kärnor och kontrollera hashkonfigurationen. Därefter optimerar jag coalescing mot latens, ser till att NUMA-närheten är optimal och fördelar arbetsbelastningarna konsekvent. Vid virtualisering använder jag Multi-Queue ända in i gästerna och håller pinning och affinitet synkroniserade. Mätningar av PPS, köbelastning, återutsändningar och latens avgör nästa steg. Den som går tillväga på detta sätt utnyttjar 10G och 25G fullt ut och håller Fördröjning inom ramen för detta och uppnår pålitliga nätverksavkastningar.

Aktuella artiklar