Cluster de failover PostgreSQL + Patroni. Experienta de implementare

În articol vă voi spune cum am abordat problema toleranței la erori PostgreSQL, de ce a devenit importantă pentru noi și ce s-a întâmplat în cele din urmă.

Avem un serviciu foarte încărcat: 2,5 milioane de utilizatori în întreaga lume, peste 50 de utilizatori activi în fiecare zi. Serverele sunt situate în Amazone într-o regiune a Irlandei: peste 100 de servere diferite funcționează constant, dintre care aproape 50 sunt cu baze de date.

Întregul backend este o aplicație Java monolitică mare care păstrează o conexiune websocket constantă cu clientul. Atunci când mai mulți utilizatori lucrează pe aceeași placă în același timp, toți văd modificările în timp real, deoarece scriem fiecare modificare în baza de date. Avem aproximativ 10 de solicitări pe secundă către bazele noastre de date. La sarcina maximă în Redis, scriem 80-100K cereri pe secundă.
Cluster de failover PostgreSQL + Patroni. Experienta de implementare

De ce am trecut de la Redis la PostgreSQL

Inițial, serviciul nostru a funcționat cu Redis, un magazin cheie-valoare care stochează toate datele în memoria RAM a serverului.

Avantajele Redis:

  1. Viteză mare de răspuns, pentru că totul este stocat în memorie;
  2. Ușurință de backup și replicare.

Contra Redis pentru noi:

  1. Nu există tranzacții reale. Am încercat să le simulăm la nivelul aplicației noastre. Din păcate, acest lucru nu a funcționat întotdeauna bine și a necesitat scrierea unui cod foarte complex.
  2. Cantitatea de date este limitată de cantitatea de memorie. Pe măsură ce cantitatea de date crește, memoria va crește și, în final, ne vom întâlni cu caracteristicile instanței selectate, ceea ce în AWS necesită oprirea serviciului nostru pentru a schimba tipul de instanță.
  3. Este necesar să se mențină constant un nivel scăzut de latență, deoarece. avem un numar foarte mare de solicitari. Nivelul optim de întârziere pentru noi este de 17-20 ms. La un nivel de 30-40 ms, primim răspunsuri lungi la solicitările din aplicația noastră și degradarea serviciului. Din păcate, asta ni s-a întâmplat în septembrie 2018, când una dintre instanțe cu Redis din anumite motive a primit o latență de 2 ori mai mare decât de obicei. Pentru a rezolva problema, am oprit serviciul la mijlocul zilei pentru întreținere neprogramată și am înlocuit instanța problematică Redis.
  4. Este ușor să obțineți inconsecvența datelor chiar și cu erori minore în cod și apoi să petreceți mult timp scriind cod pentru a corecta aceste date.

Am luat în calcul minusurile și am realizat că trebuie să trecem la ceva mai convenabil, cu tranzacții normale și mai puțină dependență de latență. Am efectuat cercetări, am analizat multe opțiuni și am ales PostgreSQL.

Ne mutăm la o nouă bază de date de 1,5 ani deja și am mutat doar o mică parte din date, așa că acum lucrăm simultan cu Redis și PostgreSQL. Sunt scrise mai multe informații despre etapele de mutare și comutare a datelor între baze de date articolul colegului meu.

Când am început să ne mutăm, aplicația noastră a lucrat direct cu baza de date și a accesat masterul Redis și PostgreSQL. Clusterul PostgreSQL a constat dintr-un master și o replică cu replicare asincronă. Iată cum arăta schema bazei de date:
Cluster de failover PostgreSQL + Patroni. Experienta de implementare

Implementarea PgBouncer

În timp ce ne mutam, produsul se dezvolta și el: numărul de utilizatori și numărul de servere care lucrau cu PostgreSQL a crescut și am început să ne lipsească conexiunile. PostgreSQL creează un proces separat pentru fiecare conexiune și consumă resurse. Puteți crește numărul de conexiuni până la un anumit punct, altfel există șansa de a obține o performanță suboptimă a bazei de date. Opțiunea ideală într-o astfel de situație ar fi să alegeți un manager de conexiune care să stea în fața bazei.

Aveam două opțiuni pentru managerul de conexiuni: Pgpool și PgBouncer. Dar primul nu acceptă modul tranzacțional de lucru cu baza de date, așa că am ales PgBouncer.

Am stabilit următoarea schemă de lucru: aplicația noastră accesează un PgBouncer, în spatele căruia sunt master PostgreSQL, iar în spatele fiecărui master este o replică cu replicare asincronă.
Cluster de failover PostgreSQL + Patroni. Experienta de implementare

În același timp, nu am putut stoca întreaga cantitate de date în PostgreSQL și viteza de lucru cu baza de date a fost importantă pentru noi, așa că am început sharding PostgreSQL la nivel de aplicație. Schema descrisă mai sus este relativ convenabilă pentru aceasta: atunci când adăugați un nou shard PostgreSQL, este suficient să actualizați configurația PgBouncer și aplicația poate funcționa imediat cu noul shard.

PgBouncer failover

Această schemă a funcționat până în momentul în care singura instanță PgBouncer a murit. Suntem în AWS, unde toate instanțele rulează pe hardware care moare periodic. În astfel de cazuri, instanța se mută pur și simplu la noul hardware și funcționează din nou. Acest lucru s-a întâmplat cu PgBouncer, dar a devenit indisponibil. Rezultatul acestei căderi a fost indisponibilitatea serviciului nostru timp de 25 de minute. AWS recomandă utilizarea redundanței la nivel de utilizator pentru astfel de situații, care nu era implementată în țara noastră la acel moment.

După aceea, ne-am gândit serios la toleranța la erori a clusterelor PgBouncer și PostgreSQL, deoarece o situație similară s-ar putea întâmpla cu orice instanță din contul nostru AWS.

Am construit schema de toleranță la erori PgBouncer după cum urmează: toate serverele de aplicații accesează Network Load Balancer, în spatele căruia se află două PgBouncer. Fiecare PgBouncer se uită la același master PostgreSQL al fiecărui fragment. Dacă se produce din nou o blocare a instanței AWS, tot traficul este redirecționat printr-un alt PgBouncer. Echilibrarea încărcăturii în rețea este furnizată de AWS.

Această schemă facilitează adăugarea de noi servere PgBouncer.
Cluster de failover PostgreSQL + Patroni. Experienta de implementare

Creați un cluster de failover PostgreSQL

Când am rezolvat această problemă, am luat în considerare diferite opțiuni: failover auto-scris, repmgr, AWS RDS, Patroni.

Scripturi auto-scrise

Aceștia pot monitoriza activitatea masterului și, dacă nu reușește, să promoveze replica către master și să actualizeze configurația PgBouncer.

Avantajele acestei abordări sunt simplitatea maximă, deoarece scrieți singur scripturile și înțelegeți exact cum funcționează.

Contra:

  • Este posibil ca masterul să nu fi murit, în schimb ar fi putut apărea o defecțiune a rețelei. Failover, neștiind acest lucru, va promova replica către master, în timp ce vechiul master va continua să funcționeze. Ca urmare, vom obține două servere în rol de master și nu vom ști care dintre ele are cele mai recente date actualizate. Această situație mai este numită și split-brain;
  • Am rămas fără răspuns. În configurația noastră, masterul și o replică, după comutare, replica se mută în sus la master și nu mai avem replici, așa că trebuie să adăugăm manual o nouă replică;
  • Avem nevoie de monitorizare suplimentară a operațiunii de failover, în timp ce avem 12 fragmente PostgreSQL, ceea ce înseamnă că trebuie să monitorizăm 12 clustere. Odată cu creșterea numărului de fragmente, trebuie să vă amintiți și să actualizați failover-ul.

Efectuarea în eroare scrisă de sine pare foarte complicată și necesită suport non-trivial. Cu un singur cluster PostgreSQL, aceasta ar fi cea mai ușoară opțiune, dar nu se scalează, așa că nu este potrivit pentru noi.

Repmgr

Manager de replicare pentru clustere PostgreSQL, care poate gestiona funcționarea unui cluster PostgreSQL. În același timp, nu are un failover automat din cutie, așa că pentru lucru va trebui să scrieți propriul „înveliș” deasupra soluției finite. Așa că totul poate deveni și mai complicat decât cu scripturile auto-scrise, așa că nici nu am încercat Repmgr.

AWS RDS

Acceptă tot ce avem nevoie, știe cum să facă copii de rezervă și menține un grup de conexiuni. Are comutare automată: când masterul moare, replica devine noul master, iar AWS schimbă înregistrarea dns cu noul master, în timp ce replicile pot fi localizate în diferite AZ.

Dezavantajele includ lipsa ajustărilor fine. Ca exemplu de reglare fină: instanțele noastre au restricții pentru conexiunile tcp, care, din păcate, nu se pot face în RDS:

net.ipv4.tcp_keepalive_time=10
net.ipv4.tcp_keepalive_intvl=1
net.ipv4.tcp_keepalive_probes=5
net.ipv4.tcp_retries2=3

În plus, AWS RDS este aproape de două ori mai scump decât prețul obișnuit al instanței, care a fost principalul motiv pentru a renunța la această soluție.

Patroni

Acesta este un șablon python pentru gestionarea PostgreSQL cu documentație bună, failover automat și cod sursă pe github.

Avantajele Patroni:

  • Fiecare parametru de configurare este descris, este clar cum funcționează;
  • Efectuarea automată a erorilor funcționează imediat;
  • Scris în python, și din moment ce noi înșine scriem mult în python, ne va fi mai ușor să ne ocupăm de probleme și, poate, chiar să ajutăm la dezvoltarea proiectului;
  • Gestionează complet PostgreSQL, vă permite să schimbați configurația pe toate nodurile clusterului simultan și, dacă clusterul trebuie repornit pentru a aplica noua configurație, atunci acest lucru se poate face din nou folosind Patroni.

Contra:

  • Nu este clar din documentație cum să lucrați corect cu PgBouncer. Deși este greu să-l numim minus, deoarece sarcina lui Patroni este să gestioneze PostgreSQL, iar modul în care vor merge conexiunile la Patroni este deja problema noastră;
  • Există puține exemple de implementare a Patroni pe volume mari, în timp ce există multe exemple de implementare de la zero.

Ca rezultat, am ales Patroni pentru a crea un cluster de failover.

Procesul de implementare Patroni

Înainte de Patroni, aveam 12 fragmente PostgreSQL într-o configurație de un master și o replică cu replicare asincronă. Serverele de aplicații au accesat bazele de date prin Network Load Balancer, în spatele căruia se aflau două instanțe cu PgBouncer, iar în spatele lor erau toate serverele PostgreSQL.
Cluster de failover PostgreSQL + Patroni. Experienta de implementare

Pentru a implementa Patroni, trebuia să selectăm o configurație de cluster de stocare distribuită. Patroni lucrează cu sisteme de stocare de configurare distribuită precum etcd, Zookeeper, Consul. Avem pe piață doar un cluster Consul cu drepturi depline, care funcționează împreună cu Vault și nu îl mai folosim. Un motiv excelent pentru a începe să utilizați Consul în scopul propus.

Cum lucrează Patroni cu Consul

Avem un cluster Consul, care este format din trei noduri, și un cluster Patroni, care constă dintr-un lider și o replică (în Patroni, stăpânul se numește liderul clusterului, iar sclavii sunt numiți replici). Fiecare instanță a clusterului Patroni trimite constant informații despre starea clusterului către Consul. Prin urmare, de la Consul puteți afla oricând configurația actuală a clusterului Patroni și cine este liderul în acest moment.

Cluster de failover PostgreSQL + Patroni. Experienta de implementare

Pentru a conecta Patroni la Consul, este suficient să studiezi documentația oficială, care spune că trebuie să specificați o gazdă în format http sau https, în funcție de modul în care lucrăm cu Consul, și schema de conectare, opțional:

host: the host:port for the Consul endpoint, in format: http(s)://host:port
scheme: (optional) http or https, defaults to http

Pare simplu, dar aici încep capcanele. Cu Consul, lucrăm printr-o conexiune securizată prin https și configurația noastră de conexiune va arăta astfel:

consul:
  host: https://server.production.consul:8080 
  verify: true
  cacert: {{ consul_cacert }}
  cert: {{ consul_cert }}
  key: {{ consul_key }}

Dar asta nu merge. La pornire, Patroni nu se poate conecta la Consul, pentru că oricum încearcă să treacă prin http.

Codul sursă al Patroni a ajutat la rezolvarea problemei. Bine că este scris în python. Se pare că parametrul gazdă nu este analizat în niciun fel, iar protocolul trebuie specificat în schemă. Iată cum arată blocul de configurare de lucru pentru lucrul cu Consul pentru noi:

consul:
  host: server.production.consul:8080
  scheme: https
  verify: true
  cacert: {{ consul_cacert }}
  cert: {{ consul_cert }}
  key: {{ consul_key }}

consul-șablon

Deci, am ales stocarea pentru configurare. Acum trebuie să înțelegem cum își va schimba configurația PgBouncer la schimbarea liderului în clusterul Patroni. Nu există un răspuns la această întrebare în documentație, deoarece. acolo, în principiu, lucrul cu PgBouncer nu este descris.

În căutarea unei soluții, am găsit un articol (din păcate nu-mi amintesc titlul) în care scria că Сonsul-template a ajutat foarte mult la împerecherea PgBouncer și Patroni. Acest lucru ne-a determinat să investigăm cum funcționează Consul-template.

S-a dovedit că Consul-template monitorizează constant configurația clusterului PostgreSQL din Consul. Când liderul se schimbă, actualizează configurația PgBouncer și trimite o comandă pentru a o reîncărca.

Cluster de failover PostgreSQL + Patroni. Experienta de implementare

Un mare plus al șablonului este că este stocat ca cod, așa că atunci când adăugați un nou shard, este suficient să faceți un nou commit și să actualizați automat șablonul, susținând principiul Infrastructurii ca cod.

Arhitectură nouă cu Patroni

Ca rezultat, am obținut următoarea schemă de lucru:
Cluster de failover PostgreSQL + Patroni. Experienta de implementare

Toate serverele de aplicații accesează echilibrul → există două instanțe de PgBouncer în spatele acestuia → pe fiecare instanță este lansat Consul-template, care monitorizează starea fiecărui cluster Patroni și monitorizează relevanța configurației PgBouncer, care trimite cereri liderului curent a fiecărui cluster.

Testare manuală

Am rulat această schemă înainte de a o lansa într-un mediu de testare mic și am verificat funcționarea comutării automate. Au deschis tabla, au mutat autocolantul și în acel moment l-au „omorât” pe liderul grupului. În AWS, acest lucru este la fel de simplu ca și închiderea instanței prin consolă.

Cluster de failover PostgreSQL + Patroni. Experienta de implementare

Autocolantul a revenit înapoi în 10-20 de secunde și apoi a început din nou să se miște normal. Aceasta înseamnă că cluster-ul Patroni a funcționat corect: a schimbat liderul, a trimis informațiile către Сonsul, iar Сonsul-template a preluat imediat aceste informații, a înlocuit configurația PgBouncer și a trimis comanda de reîncărcare.

Cum să supraviețuiești la sarcină mare și să menții timpul de nefuncționare la minimum?

Totul funcționează perfect! Dar există noi întrebări: Cum va funcționa sub sarcină mare? Cum să lansați rapid și în siguranță totul în producție?

Mediul de testare în care efectuăm testele de încărcare ne ajută să răspundem la prima întrebare. Este complet identic cu producția în ceea ce privește arhitectura și a generat date de testare care sunt aproximativ egale ca volum cu producția. Decidem să „omorâm” unul dintre maeștrii PostgreSQL în timpul testului și să vedem ce se întâmplă. Dar înainte de asta, este important să verificăm rularea automată, deoarece pe acest mediu avem mai multe shard-uri PostgreSQL, așa că vom obține o testare excelentă a scripturilor de configurare înainte de producție.

Ambele sarcini par ambițioase, dar avem PostgreSQL 9.6. Putem face upgrade imediat la 11.2?

Hotărâm să o facem în 2 pași: mai întâi upgrade la 11.2, apoi lansăm Patroni.

Actualizare PostgreSQL

Pentru a actualiza rapid versiunea PostgreSQL, utilizați opțiunea -k, în care link-urile hard sunt create pe disc și nu este nevoie să vă copiați datele. Pe baze de 300-400 GB, actualizarea durează 1 secundă.

Avem o mulțime de fragmente, așa că actualizarea trebuie făcută automat. Pentru a face acest lucru, am scris un manual Ansible care se ocupă de întregul proces de actualizare pentru noi:

/usr/lib/postgresql/11/bin/pg_upgrade 
<b>--link </b>
--old-datadir='' --new-datadir='' 
 --old-bindir=''  --new-bindir='' 
 --old-options=' -c config_file=' 
 --new-options=' -c config_file='

Este important de reținut aici că înainte de a începe upgrade-ul, trebuie să o efectuați cu parametrul --Verificapentru a vă asigura că puteți face upgrade. Scriptul nostru face, de asemenea, înlocuirea configurațiilor pe durata actualizării. Scenariul nostru s-a finalizat în 30 de secunde, ceea ce este un rezultat excelent.

Lansează Patroni

Pentru a rezolva a doua problemă, trebuie doar să priviți configurația Patroni. Depozitul oficial are un exemplu de configurare cu initdb, care este responsabil pentru inițializarea unei noi baze de date atunci când porniți pentru prima dată Patroni. Dar, deoarece avem deja o bază de date gata făcută, pur și simplu am eliminat această secțiune din configurație.

Când am început să instalăm Patroni pe un cluster PostgreSQL deja existent și să-l rulăm, am întâlnit o nouă problemă: ambele servere au început ca lider. Patroni nu știe nimic despre starea timpurie a clusterului și încearcă să pornească ambele servere ca două clustere separate cu același nume. Pentru a rezolva această problemă, trebuie să ștergeți directorul cu date de pe slave:

rm -rf /var/lib/postgresql/

Acest lucru trebuie făcut numai pe sclav!

Atunci când o replică curată este conectată, Patroni face un lider de backup de bază și o restabilește în replică, apoi ajunge din urmă cu starea curentă conform jurnalelor de wal.

O altă dificultate pe care am întâlnit-o este că toate clusterele PostgreSQL sunt denumite implicit principal. Când fiecare grup nu știe nimic despre celălalt, acest lucru este normal. Dar când doriți să utilizați Patroni, atunci toate clusterele trebuie să aibă un nume unic. Soluția este să schimbați numele clusterului în configurația PostgreSQL.

test de sarcină

Am lansat un test care simulează experiența utilizatorului pe plăci. Când încărcarea a atins valoarea medie zilnică, am repetat exact același test, am oprit o instanță cu un lider PostgreSQL. Efectuarea automată a erorilor a funcționat așa cum ne așteptam: Patroni a schimbat liderul, Consul-template a actualizat configurația PgBouncer și a trimis o comandă de reîncărcare. Conform graficelor noastre din Grafana, era clar că există întârzieri de 20-30 de secunde și o cantitate mică de erori de la serverele asociate conexiunii la baza de date. Aceasta este o situație normală, astfel de valori sunt acceptabile pentru failover-ul nostru și sunt cu siguranță mai bune decât timpul de nefuncționare a serviciului.

Aducerea Patroni în producție

Drept urmare, am venit cu următorul plan:

  • Implementați șablonul Consul pe serverele PgBouncer și lansați;
  • Actualizări PostgreSQL la versiunea 11.2;
  • Schimbați numele clusterului;
  • Pornirea Clusterului Patroni.

În același timp, schema noastră ne permite să facem primul punct aproape în orice moment, putem elimina pe rând fiecare PgBouncer de la locul de muncă și putem implementa și rula consul-template pe el. Așa am făcut.

Pentru implementare rapidă, am folosit Ansible, deoarece am testat deja toate manualele pe un mediu de testare, iar timpul de execuție a scriptului complet a fost de la 1,5 până la 2 minute pentru fiecare fragment. Am putea rula totul pe rând în fiecare fragment fără a ne opri serviciul, dar ar trebui să oprim fiecare PostgreSQL pentru câteva minute. În acest caz, utilizatorii ale căror date se află pe acest fragment nu ar putea funcționa complet în acest moment, iar acest lucru este inacceptabil pentru noi.

Ieșirea din această situație a fost întreținerea planificată, care are loc la fiecare 3 luni. Aceasta este o fereastră pentru lucrul programat, când ne închidem complet serviciul și ne actualizăm instanțele bazei de date. A mai rămas o săptămână până la următoarea fereastră și am decis să așteptăm și să ne pregătim în continuare. În timpul timpului de așteptare, ne-am asigurat suplimentar: pentru fiecare shard PostgreSQL, am ridicat o replică de rezervă în caz de eșec în păstrarea celor mai recente date și am adăugat o nouă instanță pentru fiecare shard, care ar trebui să devină o nouă replică în clusterul Patroni, pentru a nu executa o comandă de ștergere a datelor . Toate acestea au contribuit la minimizarea riscului de eroare.
Cluster de failover PostgreSQL + Patroni. Experienta de implementare

Ne-am repornit serviciul, totul a funcționat așa cum trebuie, utilizatorii au continuat să lucreze, dar pe grafice am observat o încărcare anormal de mare pe serverele Consul.
Cluster de failover PostgreSQL + Patroni. Experienta de implementare

De ce nu am văzut asta în mediul de testare? Această problemă ilustrează foarte bine faptul că este necesar să urmați principiul Infrastructurii ca cod și să rafinați întreaga infrastructură, de la mediile de testare până la producție. În caz contrar, este foarte ușor să obținem problema pe care o avem. Ce s-a întâmplat? Consul a apărut mai întâi în producție, iar apoi în medii de testare, ca urmare, în medii de testare, versiunea Consul a fost mai mare decât în ​​producție. Doar într-una dintre versiuni, o scurgere de procesor a fost rezolvată atunci când lucrați cu consul-template. Prin urmare, pur și simplu am actualizat Consul, rezolvând astfel problema.

Reporniți clusterul Patroni

Cu toate acestea, am avut o nouă problemă, pe care nici măcar nu am bănuit-o. Când actualizăm Consul, pur și simplu eliminăm nodul Consul din cluster folosind comanda consul leave → Patroni se conectează la un alt server Consul → totul funcționează. Dar când am ajuns la ultima instanță a clusterului Consul și i-am trimis comanda consul leave, toate clusterele Patroni pur și simplu s-au repornit, iar în jurnale am văzut următoarea eroare:

ERROR: get_cluster
Traceback (most recent call last):
...
RetryFailedError: 'Exceeded retry deadline'
ERROR: Error communicating with DCS
<b>LOG: database system is shut down</b>

Clusterul Patroni nu a putut prelua informații despre clusterul său și a repornit.

Pentru a găsi o soluție, am contactat autorii Patroni printr-o problemă pe github. Au sugerat îmbunătățiri ale fișierelor noastre de configurare:

consul:
 consul.checks: []
bootstrap:
 dcs:
   retry_timeout: 8

Am putut reproduce problema într-un mediu de testare și am testat aceste opțiuni acolo, dar, din păcate, nu au funcționat.

Problema rămâne încă nerezolvată. Intenționăm să încercăm următoarele soluții:

  • Utilizați Consul-agent pe fiecare instanță de cluster Patroni;
  • Remediați problema din cod.

Înțelegem unde a apărut eroarea: problema este probabil utilizarea timeout-ului implicit, care nu este suprascris prin fișierul de configurare. Când ultimul server Consul este eliminat din cluster, întregul cluster Consul se blochează mai mult de o secundă, din această cauză, Patroni nu poate obține starea clusterului și repornește complet întregul cluster.

Din fericire, nu am mai întâlnit erori.

Rezultatele utilizării Patroni

După lansarea cu succes a Patroni, am adăugat o replică suplimentară în fiecare cluster. Acum, în fiecare cluster există o aparență de cvorum: un lider și două replici, pentru plasa de siguranță în cazul creierului divizat la comutare.
Cluster de failover PostgreSQL + Patroni. Experienta de implementare

Patroni lucrează la producție de mai bine de trei luni. În acest timp, el a reușit deja să ne ajute. Recent, liderul unuia dintre clustere a murit în AWS, failover-ul automat a funcționat și utilizatorii au continuat să lucreze. Patroni și-a îndeplinit sarcina principală.

Un mic rezumat al utilizării Patroni:

  • Ușurința modificărilor de configurare. Este suficient să schimbați configurația pe o singură instanță și va fi trasă în întregul cluster. Dacă este necesară o repornire pentru a aplica noua configurație, atunci Patroni vă va anunța. Patroni poate reporni întregul cluster cu o singură comandă, ceea ce este, de asemenea, foarte convenabil.
  • Efectuarea automată a erorilor funcționează și a reușit deja să ne ajute.
  • Actualizare PostgreSQL fără întreruperi ale aplicației. Mai întâi trebuie să actualizați replicile la noua versiune, apoi să schimbați liderul în clusterul Patroni și să actualizați vechiul lider. În acest caz, are loc testarea necesară a failover-ului automat.

Sursa: www.habr.com

Adauga un comentariu