Comentarii

Copiați linkul Citat răspuns

vide comentat 4 octombrie 2017

Descriere

Executarea unui număr mare de conexiuni paralele împotriva Docker simplu și Docker Swarms duce la 2 rezultate complet diferite de performanță, Swarm fiind cel mai lent de un 50x factor!
Testul este reproductibil (cel puțin pe VM-urile mele) cu ușurință cu Siege și imaginea oficială Nginx, dar de fapt mă confrunt cu problema în producție cu microserviciul nostru HTTP bazat pe Java. Nu pot vedea niciun mesaj de eroare evident în jurnalele Docker sau jurnalele kernelului.

Pași pentru a reproduce problema:
Rulați containerul nginx:

Asediați containerul, iar rezultatele sunt bune, peste 13k trans/sec, iar CPU în stresstest01 este 100% utilizat de procesul nginx.

Acum, să încercăm cu Docker Swarm (un roi de noduri, 1 stivă de containere)

După prima rundă, rezultatele sunt deja mult mai proaste decât cu Docker simplu, dar după a doua este
doar un dezastru:( Mai mult, CPU-ul gazdă este ușor utilizat și doar prin procesul nginx. Niciun proces legat de docker (dockerd, cointanerd etc.) nu pare să fie conținut de CPU.

Descrieți rezultatele pe care le-ați primit:
Performanțe bune cu Docker simplu.
Performanțe foarte proaste cu Docker Swarm activat.

Descrieți rezultatele pe care le așteptați:
Performanțe similare pentru cele două arome Docker pe aceeași mașină

Informații suplimentare pe care le considerați importante (de exemplu, problema apare doar ocazional):

Ieșirea versiunii de andocare:

Ieșirea informațiilor despre andocare:

Detalii suplimentare despre mediu (AWS, VirtualBox, fizic etc.):
Este o mașină virtuală KVM (sub oVirt), dar același lucru se întâmplă atunci când se utilizează o mașină fizică.

Textul a fost actualizat cu succes, dar s-au întâlnit aceste erori:

vide comentat 4 octombrie 2017

Această problemă este un blocaj total pentru mine și pentru implementarea Swarm în producție. Acesta este un grafic al modului în care s-a schimbat timpul de răspuns după trecerea unei componente din arhitectura noastră de la Swarm la docker simplu, pe aceleași gazde exacte

performanță

Cred că voi începe să mă mut la Kubernetes
(linia verde este operații/sec, axa Y stângă)

(comentariu copiat din # 35009 pentru că la început am crezut că este aceeași problemă)

mavenugo comentat 4 octombrie 2017

@vide intrarea în mod swarm este gestionată de IPVS și conexiunile sunt trimise către sarcinile de backend prin rețeaua de intrare suprapusă. Dar, deoarece este o configurare cu un singur nod, scăderea performanței nu se poate întâmpla din cauza anteturilor VXLAN utilizate în rețeaua de suprapunere. Singurul motiv posibil ar putea fi IPVS și ar putea necesita ajustarea performanței pentru cazul dvs.

Putem confirma teoria dacă puteți schimba fișierul stivei cu un mod de parametri suplimentar: gazdă în secțiunea porturi. Acest lucru va ocoli IPVS și va utiliza maparea portului nativ la fel cum o face rularea docker. Puteți confirma pls? ?

vide comentat 4 octombrie 2017

@mavenugo Da, IPVS a fost și suspectul meu numărul 1, nu m-am gândit la modul: trucul gazdei.
Benchmarking din nou cu setările pe care le-ați sugerat:

Ceea ce este comparabil cu rezultatele simple docker.

Deci, ce reglaj pot face pe IPVS în acest caz? Actualizarea kernel-ului poate? Evident, am nevoie de echilibrare a sarcinii IPVS în producție:)

mavenugo comentat 5 octombrie 2017

@vide mulțumesc pentru confirmare. Ar trebui să petrecem un pic mai mult timp analizând problema înainte de a privi IPVS ca sursă a problemei de performanță (deși am menționat asta în comentariul meu anterior:)). Voi încerca asediul și mă voi întoarce la tine.

vide comentat 6 octombrie 2017

@mavenugo Am încercat din nou pe aceeași casetă CentOS cu cel mai recent kernel 4.13 (4.13.4-1.el7.elrepo.x86_64) și rezultatele sunt aceleași.
În plus, am încercat instalarea Ubuntu 17.04 a laptopului meu și rezultatele sunt proaste și aici.

vide comentat 10 octombrie 2017

@mavenugo l-ai putea reproduce pe aparatul tău?

xinfengliu comentat 30 octombrie 2017

Pot reproduce exact problema. Testarea face o nouă conexiune la fiecare cerere.
Conexiunile inactive s-au îngrămădit în curând în ipv-uri.

Dacă nu puteți aștepta ca InActConn să fie zero și să rulați din nou testarea, veți obține un rezultat chiar slab așa cum este descris mai sus.

Pe partea de client sunt pline de „SYN_SENT”.

Dacă doriți să rezolvați această problemă, setați connection = keep-alive în fișierul dvs. .siegerc (utilizați siege.config pentru a genera un șablon .siegerc).

mavenugo comentat 2 noiembrie 2017 •

@vide @xinfengliu L-aș putea reproduce și restrânge la stările Conntracker care cauzează problema. Vedem performanțe mult mai bune făcând ca IPVS să nu folosească conntracker (prin --sysctl net.ipv4.vs.conntrack = 0 doar pentru containerul de asediu).

BTW, Vă rugăm să rețineți că folosesc serviciul VIP direct. Utilizarea numelui serviciului are ca rezultat un impact asupra performanței, deoarece Siege efectuează căutări DNS pentru fiecare interogare și aceasta întârzie procesul. Utilizarea serviciului VIP elimină direct căutările DNS și performanța este mult mai bună.

vide comentat 3 noiembrie 2017 •

@mavenugo Ok, deci, cum pot seta conexiunea de server virtual la 0 în modul Swarm? Conform https://docs.docker.com/compose/compose-file/#not-supported-for-docker-stack-deploy, reglarea sysctl nu este acceptată cu implementarea stivei docker:(

Există o problemă deschisă despre asta: moby/libentitlement # 35

vide comentat 3 noiembrie 2017 •

Și această problemă pare legată: # 31746

mavenugo comentat 3 noiembrie 2017

@vide idk despre stiva de andocare implementează suport. Dar puteți confirma dacă soluția sugerată funcționează într-un caz de implementare non-stack ?

BSWANG comentat 4 noiembrie 2017 •

--sysctl net.ipv4.vs.conntrack = 0 nu poate fi folosit la rețeaua de intrare mesh pe ingress_sbox. Deoarece ipv-urile vor face SNAT după redirecționare.

În serviciul kube-proxy al kubenetes. va seta acești parametri ai nucleului:
https://github.com/kubernetes/kubernetes/blob/master/pkg/proxy/ipvs/proxier.go#L88-L91
și net.netfilter.nf_conntrack_buckets, net.netfilter.nf_conntrack_max .

az-z comentat 15 decembrie 2017

Sunt pe RHEL7.4 și docker 1.12. Testarea pe un cluster de 2 noduri cu nginx: ultima implementare în modul mash. Pot reproduce rezultatele @vide. Dar cazul meu de testare este ușor diferit.
În loc să rulez asediul ca container, îl rulez din afara clusterului pentru a încărca o pereche de containere nginx. Experimentez o 10x degradare în timp de răspuns și debit.

împotriva clusterului:

împotriva unui singur nginx:

acesta este un blocaj complet pentru orice implementare ulterioară a swarm-ului pentru noi. Care este soluția și calendarul propus în acest sens? mulțumesc.

EmarMikey comentat 9 ianuarie 2018 •

Ne-am lovit de aceeași problemă cu echilibrarea LVS cu rețea, avem performanțe foarte slabe.
În prezent, am lucrat cu configurația modului gazdă, dar sper că este doar o soluție temporară.
Orice plan pentru a remedia acest lucru?

test în modul gazdă cu ab (doar 1 container):

testați în modul de intrare cu ab:
netstat pe client:

ieșire ipvsadm în spațiul de nume de intrare:

JacksonHill comentat 17 ianuarie 2018

@ az-z care este? docker-ce 17.12 sau cel vechi 1.12 sau smth?

sbrattla comentat 1 februarie 2018 •

@vide ați verificat dacă există retransmisii TCP în configurarea dvs. de roi. Vedem o mulțime de retransmisii pentru trafic care se îndreaptă prin intrare-sbox (unde este tratat IPVS). Intrarea-sbox ar fi cea cu IP 172.18.0.2 pe docker_gwbridge .

Acest lucru s-a putut vedea cu ușurință în cazul nostru între un container nginx și un memcached, unde o secundă a fost adesea adăugată în plus față de timpul total de solicitare - ceva care indica puternic retransmisii. Captarea a 20 de secunde de trafic cu wireshark pe gazdă a arătat că într-adevăr a lot de retransmisiuni treceau docker_gwbridge.

Nu am ajuns încă la o soluție cu privire la problema # 36032, despre care trebuie să spun că este destul de critică. Avem această problemă într-un sistem de producție în funcțiune și începem să devenim destul de disperați în legătură cu aceasta.

Rulăm Ubuntu 16.04 și Docker 17.09 (recent am trecut la 17.12, dar a fost un dezastru în multe privințe, așa că am retrogradat din nou).

wuzhefang comentat 31 martie 2018

@vide salut, există progrese în acest caz?

vide comentat 3 aprilie 2018

@wuzhefang Nu, îmi pare rău, m-am mutat la Kubernetes din cauza acestei probleme

tmarti comentat 18 aprilie 2018

Conform acestei ediții și a postării de la # 31746, pot adăuga câteva informații aici.

Pași foarte ușor de reprodus, cu un singur nod de roi.

Sistemul de operare al mașinii pe care rulează docker:

a) Instalați roiul pe un singur nod și pe acel nod:

docker service create --name nginx -p 80:80 --replicas 1 nginx

b) Pe aceeași consolă, executați:

ceas -n 0,5 "sudo nsenter --net =/var/run/docker/netns/ingress_sbox cat/proc/net/ip_vs_conn | grep TIME_WAIT | wc -l"

Aceasta va monitoriza rețeaua de intrare pentru conexiuni în starea TIME_WAIT și la fiecare jumătate de secundă va scuipa câte dintre ele există în acel moment.

c) De la o altă mașină din aceeași rețea, utilizați un generator de sarcină (am folosit ab de la apache2-utils):

(Adresa IP a mașinii mele de docker swarm este 192.168.1.11)

ab -n 10000 -c 100 http://192.168.1.11/

d) Dacă executați fragmentul din c), +/- următoarele vor fi afișate în comanda de ceas din b) pe o perioadă bună de timp:

Unde 10064 sunt conexiunile de 10k de la testul de încărcare plus câteva conexiuni suplimentare (nu contează cu adevărat pentru treaba noastră).

e) Dacă reușiți să executați fragmentul din c) astfel încât rezultatul din b) să obțină aceeași valoare ca rezultatul din următoarea comandă de pe nodul swarm:

sysctl net.ipv4.ip_local_port_range | awk ''

Congestia va începe să se întâmple. Nu mai există porturi sursă disponibile pentru această combinație „sursă IP + dest IP + dest port”.

f) Elaborând de aici, se întâmplă ca mecanismul de echilibrare a încărcării din roiul de andocare să utilizeze facilități de la ipvs (un modul din kernel-ul Linux care poate acționa el însuși ca un echilibru de sarcină).

g) O variație a comenzii în b) este:

sudo nsenter --net =/var/run/docker/netns/ingress_sbox cat/proc/net/ip_vs_conn | cap

Dacă îl executați imediat după executarea testului de încărcare, veți vedea ceva de genul:

Ceea ce ne spune că valoarea de expirare pentru starea TIME_WAIT de pe conexiuni este foarte probabilă (cel puțin în configurarea mea de testare) 120s.

h) Puteți sistema nodul unde rulează roiul căutând acea valoare 120 (obținută din g))

sysctl -a | grep 120

i) Și rețeaua de docker roi pentru aceeași valoare:

sudo nsenter --net =/var/run/docker/netns/ingress_sbox sysctl -a | grep 120

j) Și acesta este sfârșitul

Din acest punct, niciun parametru pe care l-am reglat folosind nici unul

Sau

sudo nsenter --net =/var/run/docker/netns/ingress_sbox sysctl -w .

a afectat timpul de expirare TIME_WAIT.

Nu știu cu adevărat dacă ipvs/netfilter (mecanismul de bază utilizat de ipvs) folosește cu adevărat acele valori sysctl-ed (cel puțin atunci când este declanșat de un swarm docker).

Și din acest punct într-o culegere.

tmarti comentat 19 aprilie 2018 •

În cele din urmă a găsit problema.

Ca opțiune de ultimă instanță și știind că roiul se bazează pe netfilter facilitează echilibrarea sarcinii interne pentru rețelele suprapuse (ca caz foarte simplu pentru serviciul din postul anterior care implicit folosește o rețea suprapusă), am descărcat Linux Kernel și a modificat puțin fișierele.

Sursele de interes se află în următorul folder:

[kernel source dir]/net/netfilter

Acest timeout TIME_WAIT este codificat în interiorul acestui fișier din ip_vs modul, în interior:

[direcția sursei kernelului] /net/netfilter/ipvs/ip_vs_proto_tcp.c

Puteți verifica cea mai recentă versiune a acestui fișier (care suferă de aceeași problemă) aici:

În interiorul acestui fișier, puteți vedea următoarea bucată de cod:

Deci, vinovatul acestui timp de expirare ridicat este:

Dacă cel precedent este schimbat în:

Timpul de expirare TIME_WAIT este redus de la 120s la 2s.

Apoi, recompilați modulul, înlocuiți modulul de sistem cu cel compilat, reporniți mașina de swarm, reporniți serviciul și repetați testul de încărcare dă rezultate incredibil de bune. Nu se mai observă inundarea conexiunilor în starea TIME_WAIT pentru încărcări moderat mari (2000 req/s).

Dacă codul din restul fișierului este inspectat, nu există într-adevăr nici o modalitate (sau nu îl văd) de a reîncărca acele expirări. Acea tcp_timeouts vectorul pare a fi folosit pentru a inițializa tabelul de expirare intern care va fi utilizat pentru gestionarea conexiunii, (fără a aparent nici un mod de a-l regla) în această funcție:

Fișierul ip_vs_ctl.c, care pare să fie însărcinat cu actualizarea reglării pentru modul, expune următorii parametri de sistem:

Nimic ca expirările expuse aici.

Deci, nu există o modalitate eficientă de a actualiza parametrul de expirare TIME_WAIT pentru acest modul odată ce acesta a început (și nici de a-l modifica astfel încât modulul să citească valoarea reglată în timpul inițierii).

Dacă cineva are o idee despre cum ar putea fi soluționată această problemă, va merita îmbrățișări mari.

În prezent, din nou într-un sac de culcare. (nu este foarte practic să recompilați modulele kernelului după fiecare actualizare a imaginii kernelului)

raarts comentat 19 aprilie 2018 •

Lucrare fantastică ! Dar lista de corespondență a nucleului pare a fi următorul pas.

thaJeztah comentat 19 aprilie 2018

Mulțumesc @tmarti care este cu siguranță o descoperire interesantă!

ctelfer comentat 20 aprilie 2018 •

O expirare de 2 minute pentru TIME_WAIT este foarte standardă în practică. Este de 2 ori durata de viață maximă pe internet (proiectată) a unui segment TCP și intenția este de a se asigura că ACK final este livrat. Dacă se pierde, cealaltă parte va încerca să retransmită FIN și statul trebuie să rămână acolo pentru ca celălalt capăt să răspundă cu ACK final. (consultați https://en.wikipedia.org/wiki/Maximum_segment_lifetime și bineînțeles https://www.ietf.org/rfc/rfc793.txt) Puteți seta MSL în kernel-ul Linux. dar rareori este ceva ce face cineva. Se pare că IPVS nici nu vă oferă opțiunea.

Nu a fost conștient de această problemă, dar va citi din nou peste ea. Un număr maxim mai mare de mapări IPVS ar putea rezolva problema și ar fi probabil ceva ce ar putea fi stabilit. (dacă mapările maxime au fost suficiente pentru a absorbi comportamentul în stare stabilă.) Care este rata de conexiune dorită?

tmarti comentat 20 aprilie 2018 •

Desigur! Ce prostie din partea mea.

Am o mică teorie pe care vreau să o împărtășesc cu voi.

A trebuit să mă împiedic cu această postare.

. pentru a realiza un fapt foarte simplu.

Ne-am săturat să inspectăm din când în când ieșirea netsat -nolap . și în fiecare zi vedem că o conexiune TCP este identificată prin valori:

  • IP sursă
  • port sursă
  • IP de destinație
  • portul de destinație

De obicei, există 2 grade de libertate în această combinație:

IP sursă: deoarece acceptați în mod normal conexiuni de la mai mulți clienți diferiți, vă puteți asuma această valoare pentru a se răspândi între diferite valori

port sursă: acesta va corespunde unor porturi efemere de la client (cele din (ubuntu land) variază de obicei între 32768 și 60999

Și celelalte două sunt fixe:

  • IP de destinație: IP-ul public al serverului
  • port de destinație: portul serverului web (80 în acest caz)

Care este problema cu testul de încărcare inițial de la @vide? (și a mea, desigur)

Problema cu această configurare este că efectiv corectați IP-ul sursă (deoarece conexiunile sub testul de încărcare provin de la un singur PC, care este nodul de unde lansăm testul loa) și obțineți un grad mai mic de libertate.

Deci, pentru testul de încărcare, combinațiile posibile ale "cheii" care identifică în mod unic o conexiune sunt reduse la numărul disponibil de porturi efemere de pe client (acel număr magic 28231), deoarece toți ceilalți parametri sunt fixați.

Ceea ce a făcut să analizeze alte probleme pentru această problemă?

Am încercat foarte greu în această după-amiază să sap în codul modulului ipvs. Nu este atât de ușor pe cât pare: 16k linii de cod și își implementează propria stivă TCP cu încărcare albă și NAT ca bonus.

Un lucru frumos în acest sens este că am putut vedea că lista „conexiunilor curente” „cheia” este compusă exact din adresa sursă: port (cea client!) Și adresa de destinație: port (așa cum se face în funcția nf_nat_used_tuple modulul).

Deci, ce se întâmplă atunci când clientul încearcă să refolosească un port (amintiți-vă că ceilalți 3 parametri sunt întotdeauna aceiași în acest test de încărcare contaminată) care corespund unei conexiuni în starea TIME_WAIT? Ei bine, la sfârșit, încercarea de conectare este eliminată (nu sunt sigur dacă se datorează unei nepotriviri TCP sec. Număr în starea conexiunii sau orice altceva).

Deci ce urmează?

Pentru a confirma că nu sunt necesare modificări ale sistemului, nu este necesară modificarea sursei modulului kernel, nu este nevoie de nimic.

În loc să faceți un test de încărcare cu 2000 de req/s dintr-o singură sursă IP (care va epuiza conexiunile în aproximativ 14 secunde în conformitate cu intervalul de port 32767-60999 și ceilalți parametri fixați), lansați doar 200 req/s din 10 surse IP diferite și confirmați că debitul rămâne constant.

Luni, voi încerca să fac testul propus și mă voi întoarce aici.

Multe mulțumiri @raarts și @thaJeztah pentru încurajare.

Și multe mulțumiri @ctelfer pentru comentariu. Am rămas cu adevărat blocat cu ideea de a sistema modulul ipvs, iar comentariul tău m-a copleșit total la început, dar în cele din urmă m-a determinat să mă uit în alte locuri.