
Avui dia, es requereix una alta disponibilitat de serveis sempre i a tot arreu, no només en grans projectes costosos. Encara es troben llocs temporalment no disponibles amb el missatge "Ho sentim, manteniment en curs", però normalment provoquen un somriure condescendent. Afegim vida als núvols a això, quan només cal una trucada a l'API per llançar un servidor addicional i no cal pensar en el funcionament "ferro". I ja no hi ha cap excusa per què un sistema crític no es va fer de manera fiable utilitzant tecnologies de clúster i redundància.
Us explicarem quines solucions hem considerat per garantir la fiabilitat de les bases de dades dels nostres serveis i a què hem arribat. A més d'una demostració amb conclusions de gran abast.
Llegat en l'Arquitectura d'Alta Disponibilitat
Això es veu encara millor en el context del desenvolupament de diversos sistemes de codi obert. Les solucions heretades es van veure obligades a afegir tecnologies d'alta disponibilitat a mesura que augmentava la demanda. I la seva qualitat variava. Les solucions de nova generació situen l'alta disponibilitat al nucli de la seva arquitectura. Per exemple, MongoDB situa el clustering com el seu cas d'ús principal. El clúster s'escala horitzontalment, la qual cosa és un fort avantatge competitiu d'aquest SGBD.
Tornem a PostgreSQL. Aquest és un dels projectes de codi obert populars més antics, el primer llançament del qual va tenir lloc l'any 95 del segle passat. L'equip del projecte durant molt de temps no va considerar l'alta disponibilitat com una tasca a resoldre pel sistema. Per tant, la tecnologia de rèplica per crear còpies de dades només es va incorporar a la versió 8.2 el 2006, però era un fitxer (enviament de registres). L'any 2010, la replicació en streaming va aparèixer a la versió 9.0 i és la base per crear una gran varietat de clústers. Això, de fet, és molt sorprenent per a les persones que es familiaritzen amb PostgreSQL després d'Enterprise SQL o NoSQL modern: la solució estàndard de la comunitat és simplement un parell de rèplica mestre amb rèplica síncrona o asíncrona. Al mateix temps, al desguàs, el mestre es canvia manualment i també es proposa que el problema de canviar de client es resolgui de manera independent.
Com vam decidir fer PostgreSQL fiable i què vam triar per a això
No obstant això, PostgreSQL no hauria esdevingut tan popular si no fos per un gran nombre de projectes i eines que ajuden a construir una solució tolerant a errors que no requereixi una atenció constant. Al núvol (MCS) des del llançament de DBaaS, hi ha disponibles servidors PostgreSQL únics i parells mestre-rèplica amb replicació asíncrona.
Naturalment, volíem facilitar la vida a tothom i posar a disposició una instal·lació de PostgreSQL que pogués servir de base per a serveis d'alta disponibilitat que no haurien de supervisar i despertar-se constantment a la nit per fer un canvi. En aquest segment, hi ha solucions antigues provades i una generació de noves utilitats que utilitzen els últims desenvolupaments.
Avui dia, el problema de l'alta disponibilitat no es basa en la reserva (això no cal dir-ho), sinó en el consens: l'algoritme per triar un líder (elecció de líder). Molt sovint, els accidents importants no es produeixen per manca de servidors, sinó per problemes de consens: un nou líder no va sortir, dos líders van aparèixer en diferents centres de dades, etc. Un exemple és un error en un clúster Github MySQL - van escriure .
La base matemàtica en aquesta matèria és molt seriosa. D'una banda, allà , que imposa restriccions teòriques a les possibilitats de construir solucions HA, d'altra banda, algorismes provats matemàticament per a la determinació de consens, com ara и . Sobre aquesta base, hi ha DCS (sistemes de consens descentralitzat) força populars: Zookeeper, etcd, Consul. Per tant, si el sistema de presa de decisions funciona amb alguns dels seus propis algorismes, escrits de manera independent, s'ha de tenir molta cura amb això. Després d'analitzar un gran nombre de sistemes, vam optar per Patroni, un sistema de codi obert desenvolupat principalment per Zalando.
Com a digressió lírica, diré que també hem considerat solucions multimaster, és a dir, clústers que es poden escalar horitzontalment per a la gravació. No obstant això, per dos motius principals van decidir no crear aquest clúster. En primer lloc, aquestes solucions tenen una gran complexitat i, en conseqüència, més vulnerabilitats. Serà difícil fer una solució estable per a tots els casos. En segon lloc, en aquest cas PostgreSQL deixa de ser pur (natiu), algunes funcions no estaran disponibles i algunes aplicacions poden tenir errors ocults durant el funcionament.
Patroni
Llavors, com funciona Patroni? Els desenvolupadors no van reinventar la roda i van suggerir utilitzar una de les solucions DCS provades com a base. A mercè d'ell es donen tots els problemes amb la sincronització de configuracions, l'elecció d'un líder i un quòrum. Vam triar etcd per a això.
A continuació, Patroni s'ocupa de l'aplicació correcta de tots els paràmetres de rèplica i de PostgreSQL, així com l'execució d'ordres per a la commutació i la commutació per error (és a dir, la commutació regular i no estàndard del mestre). Concretament, al núvol MCS, podeu crear un clúster a partir d'un mestre, una rèplica síncrona i una o més rèpliques asíncrones. La presència d'una rèplica síncrona garanteix la seguretat de les dades en almenys 2 servidors, i és aquesta rèplica la que serà el principal "candidat mestre".
Com que etcd es desplega als mateixos servidors, es recomanen 3 o 5 servidors per obtenir un quòrum òptim. Aquest grup s'escala horitzontalment per llegir (vaig escriure sobre l'escala per escriure més amunt). Tanmateix, tingueu en compte que les rèpliques asíncrones tendeixen a retardar-se, especialment sota càrregues pesades.
L'ús d'aquestes rèpliques per a la lectura (hot standby) es justifica per a tasques d'informes o d'anàlisi i descarrega el servidor mestre.
Si voleu fer un clúster d'aquest tipus vosaltres mateixos, necessitareu:
- preparar 3 o més servidors, configurar l'adreça IP i les regles del tallafoc entre ells;
- instal·lar paquets per als serveis etcd, Patroni, PostgreSQL;
- configurar el clúster etcd;
- configurar el servei patroni perquè funcioni amb PostgreSQL.
És a dir, en total, cal compondre correctament una dotzena de fitxers de configuració i no cometre cap error enlloc. Per fer-ho, definitivament hauríeu d'utilitzar una eina de gestió de configuració, com ara Ansible, per exemple. Tanmateix, encara no hi ha un equilibrador TCP d'alta disponibilitat. Fer-ho és una feina a part.
Per a aquells que necessiten un clúster ja preparat, però no volen profunditzar en tot això, hem intentat simplificar la vida i hem creat un clúster ja fet a Patroni al nostre núvol, podeu provar-lo de franc. A més del propi clúster, vam fer:
- equilibrador TCP; en diferents ports, sempre apunta a la rèplica mestra, síncrona o asíncrona actual, respectivament;
- API per canviar el patron Patroni actiu.
Es poden subclassificar tant a través de l'API del núvol MCS com de la consola web.
Demostració
Per provar les capacitats d'un clúster PostgreSQL al núvol MCS, vegem com es comporta una aplicació en directe en cas de problemes amb el SGBD.
A continuació es mostra el codi d'una aplicació que registrarà esdeveniments artificials i els informarà a la pantalla. En cas d'error, ho informarà i continuarà el seu treball en bucle fins que ho aturem amb la combinació 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("Connection opened to", 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("Logged a value, overall count: {}".format(record[0]))
except Exception as error:
print ("Error while connecting to PostgreSQL", error)
finally:
if connection:
cursor.close()
connection.close()
print("Connection closed")
if __name__ == '__main__':
try:
while True:
try:
print(datetime.now())
main()
sleep(3)
except Exception as e:
print("Caught error:n", e)
sleep(1)
except KeyboardInterrupt:
print("exit")
L'aplicació necessita PostgreSQL per funcionar. Creem un clúster al núvol MCS mitjançant l'API. En un terminal normal, on la variable OS_TOKEN conté un testimoni per accedir a l'API (es pot obtenir amb l'ordre openstack token issue), escriurem les ordres:
Creeu un clúster:
cat <<EОF > 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

Quan el clúster canvia a l'estat ACTIV, tots els camps rebran els valors actuals: el clúster està preparat.
A la GUI:

Intentem connectar i crear una taula:
psql -h 89.208.87.38 -U admin -d myproddb
Password for user admin:
psql (11.1, server 10.7)
Type "help" for help.
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=>

A l'aplicació, especificarem la configuració actual per connectar-nos a PostgreSQL. Especificarem l'adreça de l'equilibrador TCP, eliminant així la necessitat de canviar manualment a l'adreça del mestre. Llancem-ho. Com podeu veure, els esdeveniments s'han registrat correctament a la base de dades.

Canvi de mestre programat
Ara provem el funcionament de la nostra aplicació durant el canvi previst del mestre:

Estem veient l'aplicació. Veiem que l'aplicació està realment interrompuda, però només triga uns segons, en aquest cas concret, un màxim de 9.

accident de cotxe
Ara intentem simular la fallada d'una màquina virtual, la mestra actual. Simplement podríeu apagar la màquina virtual mitjançant la interfície Horizon, però això seria un apagat habitual. Aquest canvi serà processat per tots els serveis, inclòs Patroni.
Necessitem un tancament impredictible. Per tant, vaig demanar als nostres administradors amb finalitats de prova que apaguessin la màquina virtual, la mestra actual, d'una manera anormal.

Al mateix temps, la nostra aplicació va continuar funcionant. Naturalment, aquest canvi d'emergència del mestre no pot passar desapercebut.
2019-03-29 10:45:56.071234
Connection opened to PostgreSQL 10.7 on x86_64-pc-linux-gnu, compiled by gcc (GCC) 4.8.5 20150623 (Red Hat 4.8.5-36), 64-bit
Logged a value, overall count: 453
Connection closed
2019-03-29 10:45:59.205463
Connection opened to PostgreSQL 10.7 on x86_64-pc-linux-gnu, compiled by gcc (GCC) 4.8.5 20150623 (Red Hat 4.8.5-36), 64-bit
Logged a value, overall count: 454
Connection closed
2019-03-29 10:46:02.661440
Error while connecting to PostgreSQL server closed the connection unexpectedly
This probably means the server terminated abnormally
before or while processing the request.
Caught error:
local variable 'connection' referenced before assignment
……………………………………………………….. - здесь какое-то количество ошибок
2019-03-29 10:46:30.930445
Error while connecting to PostgreSQL server closed the connection unexpectedly
This probably means the server terminated abnormally
before or while processing the request.
Caught error:
local variable 'connection' referenced before assignment
2019-03-29 10:46:31.954399
Connection opened to PostgreSQL 10.7 on x86_64-pc-linux-gnu, compiled by gcc (GCC) 4.8.5 20150623 (Red Hat 4.8.5-36), 64-bit
Logged a value, overall count: 455
Connection closed
2019-03-29 10:46:35.409800
Connection opened to PostgreSQL 10.7 on x86_64-pc-linux-gnu, compiled by gcc (GCC) 4.8.5 20150623 (Red Hat 4.8.5-36), 64-bit
Logged a value, overall count: 456
Connection closed
^Cexit
Com podeu veure, l'aplicació va poder continuar el seu treball en menys de 30 segons. Sí, un cert nombre d'usuaris del servei tindran temps per notar problemes. Tanmateix, es tracta d'una avaria greu del servidor, això no passa tan sovint. Al mateix temps, una persona (administrador) difícilment hauria tingut temps de reaccionar tan ràpidament, tret que estigués assegut a la consola preparat amb un guió de canvi.
Sortida
Em sembla que aquest clúster proporciona un gran avantatge per als administradors. De fet, les avaries greus i els errors dels servidors de bases de dades no seran perceptibles per a l'aplicació i, en conseqüència, per a l'usuari. No cal arreglar res amb pressa i canviar a configuracions temporals, servidors, etc. I si aquesta solució s'utilitza com a servei ja fet al núvol, no caldrà dedicar temps a preparar-la. Pots fer alguna cosa més interessant.
Font: www.habr.com
