
Idag krÀvs hög tillgÀnglighet för tjÀnster alltid och överallt, inte bara i stora dyra projekt. TillfÀlligt otillgÀngliga webbplatser med meddelandet "FörlÄt, tekniskt underhÄll pÄgÄr" förekommer fortfarande, men framkallar vanligtvis en överseende leende. LÀgg dÀrtill livet i molnet, dÀr det bara krÀvs ett API-anrop för att starta en extra server, utan att behöva tÀnka pÄ 'hÄrdvaru' drift. Det finns dÀrmed inga ursÀkter för varför ett kritiskt system inte har byggts pÄ ett tillförlitligt sÀtt med hjÀlp av klusterteknologier och redundans.
Vi kommer att berÀtta om de lösningar vi övervÀgde för att sÀkerstÀlla pÄlitlighet i databaser i vÄra tjÀnster och vad vi kom fram till. Plus en demo med lÄngtgÄende slutsatser.
Legacy i arkitektur för hög tillgÀnglighet
Detta blir tydligare nÀr man ser pÄ utvecklingen av olika opensource-system. Gamla lösningar tvingades att lÀgga till teknologier för hög tillgÀnglighet i takt med att efterfrÄgan ökade. Kvaliteten pÄ dessa lösningar varierade. Nyare generationers lösningar bygger hög tillgÀnglighet i grunden av sin arkitektur. Till exempel positionerar MongoDB kluster som det primÀra anvÀndningsomrÄdet. Kluster kan skalas horisontellt, vilket utgör en stark konkurrensfördel för denna databashanterare.
LÄt oss tillbaka till PostgreSQL. Det Àr ett av de Àldsta populÀra opensource-projekten, vars första version slÀpptes 1995. Projektteamet ansÄg lÀnge inte att hög tillgÀnglighet var en uppgift som behövde lösas frÄn systemets sida. DÀrför blev replikationstekniken för att skapa datakopior först inbyggd i version 8.2 2006, men det var en filbaserad (log shipping). I version 9.0 2010 introducerades strömreplikation, och den Àr grundlÀggande för att skapa olika kluster. Detta förvÄnar faktiskt mÀnniskor som lÀr kÀnna PostgreSQL efter Enterprise SQL eller moderna NoSQL - standardlösningen frÄn communityn Àr helt enkelt ett par master-replica med synkron eller asynkron replikation. Vid det hÀr laget görs vÀxling av master manuellt, och frÄgan om klientvÀxling uppmanas ocksÄ att lösas sjÀlvstÀndigt.
Hur vi beslutade att skapa en pÄlitlig PostgreSQL och vad vi valde för det
Trots allt skulle inte PostgreSQL vara sÄ populÀrt om det inte fanns ett stort antal projekt och verktyg som hjÀlper till att bygga en hög tillgÀnglighetslösning som inte krÀver stÀndig uppmÀrksamhet. I molnet (MCS) har enskilda PostgreSQL-servrar och par av master-replikor med asynkron replikering varit tillgÀngliga sedan starten av DBaaS.
SjÀlvklart ville vi förenkla livet för alla och göra installationen av PostgreSQL tillgÀnglig, sÄ att den kunde fungera som grund för hög tillgÀngliga tjÀnster, utan att man hela tiden behövde övervaka den eller vakna pÄ natten för att göra en failover. I detta segment finns bÄde gamla beprövade lösningar och en generation av nya verktyg som anvÀnder de senaste framstegen.
I dag handlar problemet med hög tillgĂ€nglighet inte om redundans (det Ă€r en sjĂ€lvklarhet) utan om konsensus â algoritmen för ledarval (Leader election). Oftast intrĂ€ffar stora haverier inte pĂ„ grund av brist pĂ„ servrar, utan pĂ„ grund av problem med konsensus: ingen ny ledare valdes, det fanns tvĂ„ ledare i olika datacenter, etc. Ett exempel Ă€r haveriet pĂ„ MySQL-klustret Github â de skrev .
Den matematiska grunden för denna frĂ„ga Ă€r mycket seriös. Ă ena sidan finns det , som stĂ€ller teoretiska begrĂ€nsningar för möjligheterna att bygga HA-lösningar, och Ă„ den andra sidan finns det matematiskt bevisade algoritmer för att avgöra konsensus, sĂ„som och . PĂ„ dessa grunder finns det ganska populĂ€ra DCS (decentraliserade konsensusystem) â Zookeeper, etcd, Consul. DĂ€rför, om beslutsfattande-systemet arbetar med nĂ„gon egen algoritm som skrivits sjĂ€lvstĂ€ndigt, bör man vara extremt försiktig med den. Efter att ha analyserat ett stort antal system valde vi Patroni â ett open-source-system som huvudsakligen utvecklas av företaget Zalando.
Som ett lyriskt avsnitt kan jag sÀga att vi ocksÄ övervÀgde multi-master-lösningar, det vill sÀga kluster som kan skalas horisontellt för skrivning. Men av tvÄ huvudskÀl beslutade vi oss för att inte göra ett sÄdant kluster. För det första har sÄdana lösningar hög komplexitet och dÀrmed fler sÄrbara punkter. Det kommer att vara svÄrt att skapa en stabil lösning för alla situationer. För det andra slutar PostgreSQL att vara rent (native), vissa funktioner kommer att vara otillgÀngliga och vissa applikationer kan fÄ dolda buggar under drift.
Patroni
SÄ hur fungerar Patroni? Utvecklarna valde att inte uppfinna hjulet pÄ nytt och föreslog att anvÀnda en av de beprövade DCS-lösningarna som grund. Allt som rör synkronisering av konfigurationer, val av ledare och kvorum lÀmnas till det. Vi valde etcd för detta.
DÀrefter ansvarar Patroni för korrekt tillÀmpning av alla instÀllningar pÄ PostgreSQL och instÀllning av replikering samt utförande av kommandon för switchover och failover (det vill sÀga - planerat och oplanerat byte av master). Specifikt i molnet MCS kan man skapa ett kluster av en master, en synkron replik och en eller flera asynkrona replikar. NÀrvaron av en synkron replik sÀkerstÀller dataskydd pÄ minst 2 servrar, och just denna replik kommer att vara den frÀmsta "kandidaten till master".
Eftersom etcd körs pÄ samma servrar rekommenderas antalet servrar att vara 3 eller 5 för ett optimalt kvorum. Ett sÄdant kluster kan skalas horisontellt för lÀsning (om horizontal skalning för skrivning har jag skrivit ovan). Det bör dock beaktas att asynkrona replikar kan ha efterslÀpning, sÀrskilt vid hög belastning.
AnvÀndningen av sÄdana lÀsreplikar (hot standby) Àr rimlig för rapporterings- eller analyssyften och avlastar masterservrar.
Om du vill skapa ett sÄdant kluster sjÀlv behöver du:
- förbereda 3 eller fler servrar, konfigurera IP-adressering och brandvÀggsregler mellan dem;
- installera paket för tjÀnsterna etcd, Patroni, PostgreSQL;
- konfigurera etcd-klustret;
- konfigurera patroni-tjÀnsten för att arbeta med PostgreSQL.
Det vill sÀga, i stort sett behöver du korrekt skapa ett tiotal konfigurationsfiler och inte göra nÄgra misstag. För detta Àr det definitivt vÀrt att anvÀnda ett verktyg för konfigurationshantering, som Ansible till exempel. Samtidigt saknas fortfarande en högtillgÀnglig TCP-lastbalanserare. Att skapa en sÄdan Àr ett separat arbete.
För dem som behöver en fÀrdig klusterlösning, men inte vill grÀva sig ner i allt detta, har vi försökt förenkla livet och gjort en fÀrdig klusterlösning pÄ Patroni i vÄrt moln, som kan testas kostnadsfritt. Förutom sjÀlva klustret har vi ocksÄ skapat:
- En TCP-lastbalanserare; den pekar alltid pÄ den aktuella masterservern via olika portar, samt en synkron eller asynkron replik, beroende pÄ situationen;
- Ett API för att byta aktiv master pÄ Patroni.
De kan anslutas bÄde via MCS-molnets API och via webbkonsolet.
Demo
För att testa möjligheterna hos PostgreSQL-klustret i MCS-molnet, lÄt oss se hur en levande applikation beter sig vid problem med databashanteringssystemet.
Nedan presenteras koden för en applikation som kommer att logga konstgjorda hÀndelser och rapportera detta pÄ skÀrmen. Vid eventuella fel kommer den att meddela detta och fortsÀtta sitt arbete i en loop tills vi stoppar den med en Ctrl + C.
from __future__ import print_function
from datetime import datetime
from random import randint
from time import sleep
import psycopg2
def main():
try:
connection = psycopg2.connect(user = "admin",
password = "P@ssw0rd",
host = "89.208.87.38",
port = "5432",
database = "myproddb")
cursor = connection.cursor()
cursor.execute("SELECT version();")
record = cursor.fetchone()
print("Anslutning öppen till", record[0])
cursor.execute(
"INSERT INTO log VALUES ({});".format(randint(1, 10000)))
connection.commit()
cursor.execute("SELECT COUNT(event_id) from log;")
record = cursor.fetchone()
print("En vÀrde har registrerats, totalt antal: {}".format(record[0]))
except Exception as error:
print ("Fel vid anslutning till PostgreSQL", error)
finally:
if connection:
cursor.close()
connection.close()
print("Anslutning stÀngd")
if __name__ == '__main__':
try:
while True:
try:
print(datetime.now())
main()
sleep(3)
except Exception as e:
print("FÄngad fel:n", e)
sleep(1)
except KeyboardInterrupt:
print("avsluta")
Applikationen för arbete behöver PostgreSQL. LÄt oss skapa ett kluster i MCS-molnet med hjÀlp av API:et. I en vanlig terminal, dÀr OS_TOKEN-variabeln innehÄller token för Ätkomst till API:et (kan fÄs med kommandot openstack token issue), skriver vi in kommandona:
Vi skapar ett kluster:
cat < pgc10.json
{"cluster":{"name":"postgres10","allow_remote_access":true,"datastore":{"type":"postgresql","version":"10"},"databases":[{"name":"myproddb"}],"users":[{"databases":[{"name":"myproddb"}],"name":"admin","password":"P@ssw0rd"}],"instances":[{"key_name":"shared","availability_zone":"DP1","flavorRef":"d659fa16-c7fb-42cf-8a5e-9bcbe80a7538","nics":[{"net-id":"b91eafed-12b1-4a46-b000-3984c7e01599"}],"volume":{"size":50,"type":"DP1"}},{"key_name":"shared","availability_zone":"DP1","flavorRef":"d659fa16-c7fb-42cf-8a5e-9bcbe80a7538","nics":[{"net-id":"b91eafed-12b1-4a46-b000-3984c7e01599"}],"volume":{"size":50,"type":"DP1"}},{"key_name":"shared","availability_zone":"DP1","flavorRef":"d659fa16-c7fb-42cf-8a5e-9bcbe80a7538","nics":[{"net-id":"b91eafed-12b1-4a46-b000-3984c7e01599"}],"volume":{"size":50,"type":"DP1"}}]}}
EOF
curl -s -H "X-Auth-Token: $OS_TOKEN"
-H 'Accept: application/json'
-H 'Content-Type: application/json'
-d @pgc10.json https://infra.mail.ru:8779/v1.0/ce2a41bbd1434013b85bdf0ba07c770f/clusters

NÀr klustret gÄr in i status ACTIVE kommer alla fÀlt att fÄ aktuella vÀrden - klustret Àr redo.
I GUI:

LÄt oss försöka ansluta och skapa en tabell:
psql -h 89.208.87.38 -U admin -d myproddb
Lösenord för anvÀndare admin:
psql (11.1, server 10.7)
Skriv "help" för hjÀlp.
myproddb=> CREATE TABLE log (event_id integer NOT NULL);
CREATE TABLE
myproddb=> INSERT INTO log VALUES (1),(2),(3);
INSERT 0 3
myproddb=> SELECT * FROM log;
event_id
----------
1
2
3
(3 rows)
myproddb=>

I applikationen anger vi aktuella instÀllningar för att ansluta till PostgreSQL. Vi anger TCP-balancerarens adress, vilket innebÀr att vi slipper byta manuellt till mÀstarens adress. Vi startar den. Som syns, loggas hÀndelserna framgÄngsrikt i databasen.

Planerat byte av mÀstare
Nu testar vi vÄr applikations funktionalitet vid planerat byte av mÀstare:

Vi observerar applikationen. Vi ser att applikationens funktionalitet faktiskt avbryts, men det tar bara nÄgra sekunder, i det hÀr specifika fallet, max 9.

NedstÀngning av maskin
Nu ska vi försöka simulera nedstÀngning av den virtuella maskinen, som för nÀrvarande Àr mÀstare. Man skulle enkelt kunna stÀnga av den virtuella maskinen via Horizon-grÀnssnittet, men det skulle vara en normal avstÀngning. En sÄdan omkoppling kommer att hanteras av alla tjÀnster, inklusive Patroni.
Vi behöver en oförutsĂ€gbar avstĂ€ngning. DĂ€rför har jag bett vĂ„ra administratörer att för testĂ€ndamĂ„l stĂ€nga av den virtuella maskinen â den nuvarande mĂ€staren â pĂ„ ett onormalt sĂ€tt.

Under tiden fortsatte vÄr applikation att fungera. Naturligtvis kan ett sÄdant akut byte av mÀstare inte ske utan att mÀrkas.
2019-03-29 10:45:56.071234
Anslutning öppnad till PostgreSQL 10.7 pÄ x86_64-pc-linux-gnu, kompilerad av gcc (GCC) 4.8.5 20150623 (Red Hat 4.8.5-36), 64-bit
Loggad ett vÀrde, totalt antal: 453
Anslutning stÀngd
2019-03-29 10:45:59.205463
Anslutning öppnad till PostgreSQL 10.7 pÄ x86_64-pc-linux-gnu, kompilerad av gcc (GCC) 4.8.5 20150623 (Red Hat 4.8.5-36), 64-bit
Loggad ett vÀrde, totalt antal: 454
Anslutning stÀngd
2019-03-29 10:46:02.661440
Fel vid anslutning till PostgreSQL-servern stÀngde anslutningen ovÀntat
Detta betyder troligtvis att servern avslutades onormalt
innan eller medan begÀran behandlades.
Hittat fel:
lokal variabel 'connection' refererad innan tilldelning
âŠâŠâŠâŠâŠâŠâŠâŠâŠâŠâŠâŠâŠâŠâŠâŠâŠâŠâŠâŠâŠ.. - hĂ€r Ă€r ett antal fel
2019-03-29 10:46:30.930445
Fel vid anslutning till PostgreSQL-servern stÀngde anslutningen ovÀntat
Detta betyder troligtvis att servern avslutades onormalt
innan eller medan begÀran behandlades.
Hittat fel:
lokal variabel 'connection' refererad innan tilldelning
2019-03-29 10:46:31.954399
Anslutning öppnad till PostgreSQL 10.7 pÄ x86_64-pc-linux-gnu, kompilerad av gcc (GCC) 4.8.5 20150623 (Red Hat 4.8.5-36), 64-bit
Loggad ett vÀrde, totalt antal: 455
Anslutning stÀngd
2019-03-29 10:46:35.409800
Anslutning öppnad till PostgreSQL 10.7 pÄ x86_64-pc-linux-gnu, kompilerad av gcc (GCC) 4.8.5 20150623 (Red Hat 4.8.5-36), 64-bit
Loggad ett vÀrde, totalt antal: 456
Anslutning stÀngd
^Cexit
Som framgÄr kunde applikationen fortsÀtta sitt arbete pÄ mindre Àn 30 sekunder. Ja, vissa anvÀndare av tjÀnsten kommer att mÀrka problemen. Men det hÀr Àr en allvarlig serverkrasch, nÄgot som inte intrÀffar sÄ ofta. Dessutom skulle en person (administratören) troligtvis inte ha kunnat reagera sÄ snabbt, om hen inte satt vid konsolen med ett skript för att byta.
Utdata
Enligt min Äsikt ger en sÄdan kluster en enorm fördel för administratörer. I princip kommer allvarliga krasch och systemfel pÄ databasservrarna inte att mÀrkas av applikationen och dÀrmed inte av anvÀndaren. Det blir inte nödvÀndigt att snabbt ÄtgÀrda nÄgot och byta till temporÀra konfigurationer, servrar etc. Om en sÄdan lösning anvÀnds som en fÀrdig tjÀnst i molnet, behöver man inte lÀgga tid pÄ förberedelse. Man kan istÀllet fokusera pÄ mer intressanta uppgifter.
KĂ€lla: habr.com
