Så er jeg her igen, og denne gang skal vi snakke bits og bytes, eller mere specifikt Mbits og TCP protokollen. Forhåbentlig noget som kan finde praktisk anvendelse hos jer.
Lad os starte med nogle kedelige definitioner,
TCP Transmission Control Protocol, en del af IP/TCP - eller TCP/IP som vi typisk kalder den vifte af protokoller som vi kalder IP, Internet Protocol suite.
Stevens, henviser til Richard W. Stevens forfatter og skribent om TCP/IP i detaljer
TCP bruges som transport protokol for mange af vores applikationsprotokoller, heriblandt
SMTP - email protokollen som tidligere var den protokol som sendte fleste bits
HTTP - hele web revolutionen og .com byggede på TCP forbindelser
DNS Domain Name System navneservice ville ikke virke uden TCP, idet de fleste slave servere i DNS får deres data fra master via en TCP forbindelse. Derudover er der mange DNS records og svar som gør at man idag skal tillade DNS over TCP, husk det lige!
TCP er således et af vores vigtigste værktøjer, og formentlig et af dem som I skænker færrest tanker i dagligdagen.
Hvis du arbejder med netværk til daglig og ikke kender Stevens så bør du straks tage et smut forbi
http://www.kohala.com/start/ som er hans hjemmeside - han er desværre død 1. september 1999. Jeg bruger jævnligt hans bøger når jeg har spørgsmål angående IP protokollerne, og ofte er svaret der.
Godt, TCP er beskrevet mange steder eksempelvis har Wikipedia ca. 19 sider om denne protokol hvis jeg trykker æble-p for at printe den:
http://en.wikipedia.org/wiki/Transmission_Control_Protocol og endnu en side omkring tuning af denne http://en.wikipedia.org/wiki/TCP_tuning (som igen har reference til en hel side om Bandwidth-delay product)
Netop tuning er årsagen til dette indlæg, og lad os for eksemplets skyld antage at vi har en gazillion bytes på noget super duper high performance disksystem i København. Disse mange bytes skal flyttes (af en eller anden årsag) ned til midt i Europa, lad os sige Luxembourg og det skal gå nogenlunde hurtigt - uden at det er specificeret hvor hurtigt.
Lyder det bekendt? Det er min dagligdag, lidt løse krav, bare det ikke går for langsomt!
Godt hvis vi så antager at vi har en server til rådighed i København og en server nede i Luxembourg. Begge disse servere har 1Gbit netværkskort og har adgang til disksystemerne i begge ender. Hvordan får vi overført data hurtigst muligt mellem dem?
Nogle vil måske bare starte en overførsel og se hvordan det går, det kan jeg fortælle at mine kunder også gør :-)
- det går ikke altid godt. Nogle gange bruges Microsoft windows med CIFS/SMB.
Godt, så er næste skridt at teste forbindelsen, det er altid netværkets skyld, eller firewallen - som også er netværkets - skyld.
Til test foretrækker jeg at starte med iperf, http://en.wikipedia.org/wiki/Iperf
Dette program er nemt at bruge, kendt af de fleste der arbejder med netværk og mere simpelt end diverse Bandwidth test som bygger på flash og giver billeder som output.
iperf on LAN links
Lad os prøve det på server patchman som er en CentOS release 5.7 og client solido01.ring.nlnog.net som er en Ubuntu 10.10
[hlk@patchman ~]$ sudo iperf -s ------------------------------------------------------------ Server listening on TCP port 5001 TCP window size: 85.3 KByte (default) ------------------------------------------------------------ [ 4] local 109.238.48.53 port 5001 connected with 109.238.49.65 port 50395 [ ID] Interval Transfer Bandwidth [ 4] 0.0-10.0 sec 1.08 GBytes 931 Mbits/sec
og fra clienten, som er en Ubuntu 10.10:
solido@solido01:~$ sudo iperf -c 109.238.48.53 ------------------------------------------------------------ Client connecting to 109.238.48.53, TCP port 5001 TCP window size: 16.0 KByte (default) ------------------------------------------------------------ [ 3] local 109.238.49.65 port 50395 connected with 109.238.48.53 port 5001 [ ID] Interval Transfer Bandwidth [ 3] 0.0-10.0 sec 1.08 GBytes 932 Mbits/sec
Det ser jo godt ud, solido01 som er en virtuel maskine på ESXi med 1Gbit ud får god hastighed 932 Mbits/sec.
Tip til iperf:
* kør altid mindst tre gange
* kør gerne iperf fra flere systemer, med forskellige operativsystemer
* kør mindst med 30-60 sekunder, -t 60 tilføjes til kommandolinien
og et lille check af latency
solido@solido01:~$ ping -c 5 109.238.48.53 PING 109.238.48.53 (109.238.48.53) 56(84) bytes of data. 64 bytes from 109.238.48.53: icmp_req=1 ttl=62 time=1.33 ms 64 bytes from 109.238.48.53: icmp_req=2 ttl=62 time=1.26 ms 64 bytes from 109.238.48.53: icmp_req=3 ttl=62 time=1.17 ms 64 bytes from 109.238.48.53: icmp_req=4 ttl=62 time=1.25 ms 64 bytes from 109.238.48.53: icmp_req=5 ttl=62 time=1.26 ms --- 109.238.48.53 ping statistics --- 5 packets transmitted, 5 received, 0% packet loss, time 4005ms rtt min/avg/max/mdev = 1.179/1.259/1.332/0.053 ms
Alt er godt i vores tilfælde og vi kan konkludere at Linux systemerne fungerer efter hensigten.
Tester vi med en anden server får vi følgende resultat:
freeperf# uname -a FreeBSD freeperf.solido.net 8.2-RELEASE FreeBSD 8.2-RELEASE #0: Thu Feb 17 02:41:51 UTC 2011 root@mason.cse.buffalo.edu:/usr/obj/usr/src/sys/GENERIC amd64 freeperf# iperf -t 30 -i 5 -c 109.238.48.53 ------------------------------------------------------------ Client connecting to 109.238.48.53, TCP port 5001 TCP window size: 32.5 KByte (default) ------------------------------------------------------------ [ 3] local 91.102.91.29 port 46832 connected with 109.238.48.53 port 5001 [ ID] Interval Transfer Bandwidth [ 3] 0.0- 5.0 sec 542 MBytes 910 Mbits/sec [ 3] 5.0-10.0 sec 554 MBytes 930 Mbits/sec [ 3] 10.0-15.0 sec 556 MBytes 932 Mbits/sec [ 3] 15.0-20.0 sec 555 MBytes 932 Mbits/sec [ 3] 20.0-25.0 sec 556 MBytes 933 Mbits/sec [ 3] 0.0-30.0 sec 3.24 GBytes 928 Mbits/sec
Note: her tester jeg over 30 sekunder, bedre fordi det giver TCP mulighed for at justere, læs: TCP slow start http://en.wikipedia.org/wiki/Slow-start
Jeg bruger også -i 5 som under testen giver lidt feedback. Se også at iperf straks angiver IP adresserne der sendes imellem - vital information for debugging.
Dejligt en FreeBSD performer også ret godt og vi får ens resultater - i iperf test i København, med lille latency. Denne freeperf er på samme virtuelle miljø som solido01.ring.nlnog.net serveren - samme netkort, næsten samme "hardware konfiguration".
CPH-LUX latency
Tester vi så mod en server lidt længere væk, får vi følgende resultater fra FreeBSD CPH - Linux Lux
freeperf# iperf -t 30 -i 5 -c 109.238.56.12 ------------------------------------------------------------ Client connecting to 109.238.56.12, TCP port 5001 TCP window size: 32.5 KByte (default) ------------------------------------------------------------ [ 3] local 91.102.91.29 port 41371 connected with 109.238.56.12 port 5001 [ ID] Interval Transfer Bandwidth [ 3] 0.0- 5.0 sec 45.8 MBytes 76.8 Mbits/sec [ 3] 5.0-10.0 sec 46.5 MBytes 78.0 Mbits/sec [ 3] 10.0-15.0 sec 47.1 MBytes 79.1 Mbits/sec [ 3] 15.0-20.0 sec 47.2 MBytes 79.3 Mbits/sec [ 3] 20.0-25.0 sec 46.0 MBytes 77.2 Mbits/sec [ 3] 0.0-30.0 sec 279 MBytes 78.1 Mbits/sec
Resultatet er voldsomt ringere end vores tidligere test fra samme maskine. Forskellen er at der er større afstand mellem enhederne, og dermed er latency væsentligt højere. Pudsigt nok giver mine Linux servere næsten samme hastighed på samme afstand (ikke gengivet her for at spare plads).
Dette resultat er forøvrigt ikke nyt, og skyldes blandt andet TCP window size, som er en funktion der tillader flere data at være undervejs, sendt men ikke modtaget acknowledgement. Denne funktion gør det specielt over større afstande muligt at sende en masse data og så når der modtages acknowledge flyttes vinduet henover data der skal sendes. Bemærk at der tales om både send window og receive window - som for optimal hastighed skal matche hinanden.
Hvad ER latency mellem CPH og LUX:
freeperf# ping -c 5 109.238.56.12 PING 109.238.56.12 (109.238.56.12): 56 data bytes 64 bytes from 109.238.56.12: icmp_seq=0 ttl=58 time=24.322 ms 64 bytes from 109.238.56.12: icmp_seq=1 ttl=58 time=24.183 ms 64 bytes from 109.238.56.12: icmp_seq=2 ttl=58 time=24.170 ms 64 bytes from 109.238.56.12: icmp_seq=3 ttl=58 time=24.153 ms 64 bytes from 109.238.56.12: icmp_seq=4 ttl=58 time=24.062 ms --- 109.238.56.12 ping statistics --- 5 packets transmitted, 5 packets received, 0.0% packet loss round-trip min/avg/max/stddev = 24.062/24.178/24.322/0.084 ms
Der findes ligeledes også mange steder på internet hvor man kan indtaste data og få udregnet hvor meget data der burde kunne sendes over TCP. Disse kaldes generelt for "tcp bandwidth calculator" og dette giver mange søgeresultater.
Calculate Bandwidth-delay Product and TCP buffer size
BDP ( Bits of data in transit between hosts) = bottleneck link capacity (BW) * RTT
throughput <= TCP buffer size / RTT
TCP window size >= BW * RTT
Gengivet fra http://www.switch.ch/network/tools/tcp_throughput/index.html som efter min mening er en af de bedre
Et eksempel med 1000Mbit/sec - hastigheden vi ønsker, med latency på ca. 25ms som målt med ping, kan der udregnes med en standard TCP window 64 size:
Bandwidth-delay Product and buffer size
BDP (1000 Mbit/sec, 25.0 ms) = 3.12 MByte
required tcp buffer to reach 1000 Mbps with RTT of 25.0 ms >= 3200.0 KByte
maximum throughput with a TCP window of 64 KByte and RTT of 25.0 ms <= 20.00 Mbit/sec.
Hov, det er da ikke så godt. Vores window size er for lille og vores netværk udnyttes således ikke helt!
Tunet FreeBSD
Jeg vidste jo det med TCP window size og har da tidligere tunet diverse operativsystemer, blandt andet de Linux systemer som vi normalt bruger - som giver fuld båndbredde på 1Gbit mellem CPH og LUX.
Først uden tuning:
freeperf# date Thu Apr 19 14:23:37 CEST 2012 freeperf# iperf -t 60 -i 5 -c 109.238.63.201 ------------------------------------------------------------ Client connecting to 109.238.63.201, TCP port 5001 TCP window size: 32.5 KByte (default) ------------------------------------------------------------ [ 3] local 91.102.91.29 port 41890 connected with 109.238.63.201 port 5001 [ ID] Interval Transfer Bandwidth [ 3] 0.0- 5.0 sec 51.6 MBytes 86.6 Mbits/sec [ 3] 5.0-10.0 sec 52.8 MBytes 88.5 Mbits/sec [ 3] 10.0-15.0 sec 53.0 MBytes 88.9 Mbits/sec [ 3] 15.0-20.0 sec 52.9 MBytes 88.7 Mbits/sec [ 3] 20.0-25.0 sec 53.0 MBytes 88.9 Mbits/sec [ 3] 25.0-30.0 sec 53.1 MBytes 89.1 Mbits/sec [ 3] 30.0-35.0 sec 52.4 MBytes 87.9 Mbits/sec [ 3] 35.0-40.0 sec 52.9 MBytes 88.7 Mbits/sec [ 3] 40.0-45.0 sec 51.0 MBytes 85.6 Mbits/sec [ 3] 45.0-50.0 sec 52.8 MBytes 88.5 Mbits/sec [ 3] 50.0-55.0 sec 53.0 MBytes 88.9 Mbits/sec [ 3] 0.0-60.0 sec 630 MBytes 88.0 Mbits/sec
til at tune FreeBSD brugte jeg et lille hjælpescript, således at jeg kunne gentage test med kendte parametre:
freeperf# cat > run.sh freeperf# cat run.sh # FreeBSD network tuning # tested on FreeBSD 8.2 amd64 # low - defaults from this system, used for verification sysctl -w kern.ipc.maxsockbuf=262144 sysctl -w net.inet.tcp.sendspace=32768 sysctl -w net.inet.tcp.recvspace=65536 sysctl -w kern.ipc.nmbclusters=8768 # high #sysctl -w kern.ipc.maxsockbuf=16777216 #sysctl -w net.inet.tcp.sendspace=4194304 #sysctl -w net.inet.tcp.recvspace=4194304 # higher #sysctl -w kern.ipc.maxsockbuf=33554432 #sysctl -w net.inet.tcp.sendspace=8388608 #sysctl -w net.inet.tcp.recvspace=8388608 #sysctl -w kern.ipc.nmbclusters=16535
Lad os prøve at tune lidt på memory til netværkslagene - angive flere Kb til sendspace på clienten (sendspace), som så igen kræver mere memory generelt til netværk (maxsockbuf) :
freeperf# sh run.sh kern.ipc.maxsockbuf: 262144 -> 16777216 net.inet.tcp.sendspace: 32768 -> 4194304 net.inet.tcp.recvspace: 65536 -> 4194304 freeperf# iperf -t 60 -i 5 -c 109.238.63.201 ------------------------------------------------------------ Client connecting to 109.238.63.201, TCP port 5001 TCP window size: 4.00 MByte (default) ------------------------------------------------------------ [ 3] local 91.102.93.189 port 52555 connected with 109.238.63.201 port 5001 [ ID] Interval Transfer Bandwidth [ 3] 0.0- 5.0 sec 529 MBytes 887 Mbits/sec [ 3] 5.0-10.0 sec 561 MBytes 941 Mbits/sec [ 3] 10.0-15.0 sec 556 MBytes 934 Mbits/sec [ 3] 15.0-20.0 sec 561 MBytes 941 Mbits/sec [ 3] 20.0-25.0 sec 559 MBytes 938 Mbits/sec [ 3] 25.0-30.0 sec 556 MBytes 933 Mbits/sec [ 3] 30.0-35.0 sec 558 MBytes 937 Mbits/sec [ 3] 35.0-40.0 sec 561 MBytes 941 Mbits/sec [ 3] 40.0-45.0 sec 557 MBytes 935 Mbits/sec [ 3] 45.0-50.0 sec 561 MBytes 941 Mbits/sec [ 3] 50.0-55.0 sec 560 MBytes 940 Mbits/sec [ 3] 55.0-60.0 sec 561 MBytes 941 Mbits/sec [ 3] 0.0-60.0 sec 6.52 GBytes 934 Mbits/sec
Meget bedre! Men er det nu KUN de parametre der gjorde så stor forskel? Det virker jo voldsomt at den pludselig fra den samme server kan få fuld throughput i test!
Verifikation af parametre ved at sætte dem tilbage til det oprindelige:
freeperf# sh run.sh kern.ipc.maxsockbuf: 16777216 -> 262144 net.inet.tcp.sendspace: 4194304 -> 32768 net.inet.tcp.recvspace: 4194304 -> 65536 freeperf# iperf -t 60 -i 5 -c 109.238.63.201 ------------------------------------------------------------ Client connecting to 109.238.63.201, TCP port 5001 TCP window size: 32.5 KByte (default) ------------------------------------------------------------ [ 3] local 91.102.93.189 port 42153 connected with 109.238.63.201 port 5001 [ ID] Interval Transfer Bandwidth [ 3] 0.0- 5.0 sec 51.9 MBytes 87.0 Mbits/sec [ 3] 5.0-10.0 sec 54.1 MBytes 90.8 Mbits/sec [ 3] 10.0-15.0 sec 53.4 MBytes 89.5 Mbits/sec [ 3] 15.0-20.0 sec 53.0 MBytes 88.9 Mbits/sec [ 3] 20.0-25.0 sec 53.4 MBytes 89.5 Mbits/sec [ 3] 25.0-30.0 sec 52.8 MBytes 88.5 Mbits/sec [ 3] 30.0-35.0 sec 53.5 MBytes 89.8 Mbits/sec [ 3] 35.0-40.0 sec 53.9 MBytes 90.4 Mbits/sec [ 3] 40.0-45.0 sec 53.9 MBytes 90.4 Mbits/sec [ 3] 45.0-50.0 sec 53.5 MBytes 89.8 Mbits/sec [ 3] 50.0-55.0 sec 54.0 MBytes 90.6 Mbits/sec [ 3] 55.0-60.0 sec 53.8 MBytes 90.2 Mbits/sec [ 3] 0.0-60.0 sec 641 MBytes 89.6 Mbits/sec freeperf#
aha, en FreeBSD i København der sender til en Linux i Luxembourg skal altså tunes for at opnå gode resultater. Mit valg af parametre resulterede i større TCP window size - bemærk her at jeg IKKE angav TCP window size til iperf, som er en parameter -w. Det var således tuning som vil komme andre applikationer til glæde på samme server!
Den store forskel var TCP window size: 4.00 MByte som gav en voldsom forskel. Der var dog også nogle åbne spørgsmål:
Var det nok tuning - jeg prøvede med en Window size på 8.00 MByte og det gav sådan set ikke bedre resultat - surprise, testen viste 934Mbits/sec fra CPH-LUX :-)
Hvad er de optimale værdier - regneeksemplet sagde 3200.0 KByte for at nå 1000Mbps med en latency på 25ms, hvor stor margen giver det, hvis routingen ændrer sig til 30ms! TCP calculator ovenfor siger "required tcp buffer to reach 1000 Mbps with RTT of 30.0 ms >= 3840.0 KByte"
Hvilke andre tuningsparametre spiller ind, hvad er kernen bygget med, er FreeBSD bare dårlig fra starten, hvad med FreeBSD i begge ender?
Hvorfor kunne Linux med en TCP window size 285K nå fuld båndbredde, test ikke gengivet her - mens FreeBSD skulle tunes VOLDSOMT på disse parametre - hint: der findes mange måder at være mere aggressiv, men hvad der lige gav den store forskel var ikke tydeligt.
Der er altså nok at tage fat i og et helt andet problem med at overfør store datamængder er at starte og stoppe kopieringen, hvor programmer som rsync ofte kan være nødvendige - men kan bruge voldsomme mængder memory hvis der er mange små filer.
Mit afsluttende råd er således, check dine forbindelser og brug iperf. Prøv dig frem med tuning og find det som virker for dig i din brugssituation - og husk at generelt er vores operativsystemer ret konservative og allokerer alt for lidt memory til netværk, selvom vi har gigabytes af memory i vores servere (48Gb er ikke unormalt i vores servere!).
Start værdier for Linux tuning kan kopieres direkte til /etc/sysctl.conf (på en testserver):
# increase TCP max buffer size setable using setsockopt() # 16 MB with a few parallel streams is recommended for most 10G paths # 32 MB might be needed for some very long end-to-end 10G or 40G paths net.core.rmem_max=16777216 net.core.wmem_max=16777216 # increase Linux autotuning TCP buffer limits # min, default, and max number of bytes to use # (only change the 3rd value, and make it 16 MB or more) net.ipv4.tcp_rmem="4096 87380 16777216" net.ipv4.tcp_wmem="4096 65536 16777216" # recommended to increase this for 10G NICS net.core.netdev_max_backlog=30000 # these should be the default, but just to be sure net.ipv4.tcp_timestamps=1 net.ipv4.tcp_sack=1
Hvis de skrives ind i /etc/sysctl.conf sættes de ved boot, hvis man vil eksperimentere kan man bruge kommandoen sysctl -w direkte, som:
sysctl -w net.core.rmem_max=16777216 sysctl -w net.core.wmem_max=16777216
Start værdier for FreeBSD tuning:
sysctl -w kern.ipc.maxsockbuf=16777216 sysctl -w net.inet.tcp.sendspace=4194304 sysctl -w net.inet.tcp.recvspace=4194304 sysctl -w kern.ipc.nmbclusters=8768
Testet på FreeBSD 8.2 YMMV og du bør selv udføre test til dine applikationer.
Husk også at mange protokoller ovenpå TCP vil have indflydelse på overførselshastigheden, samt I/O på diske osv.
Tak til #bsd-dk på EFNet IRC netværket for input.

...men det er dyrt at lave god journalistik. Derfor beder vi dig overveje at tegne abonnement på Version2.
Digitaliseringen buldrer derudaf, og it-folkene tegner fremtidens Danmark. Derfor er det vigtigere end nogensinde med et kvalificeret bud på, hvordan it bedst kan være med til at udvikle det danske samfund og erhvervsliv.
Og der har aldrig været mere akut brug for en kritisk vagthund, der råber op, når der tages forkerte it-beslutninger.
Den rolle har Version2 indtaget siden 2006 - og det bliver vi ved med.