Come abbiamo costruito un cluster PostgreSQL robusto su Patroni

Come abbiamo costruito un cluster PostgreSQL robusto su Patroni

Oggi la disponibilità elevata dei servizi è richiesta ovunque e sempre, non solo nei grandi progetti costosi. I siti temporaneamente non disponibili con il messaggio "Ci scusiamo, manutenzione in corso" sono ancora comuni, ma di solito suscitano solo un sorriso tiepido. A questo si aggiunge la vita nel cloud, dove per avviare un server aggiuntivo è necessaria solo una chiamata API, senza doversi preoccupare dell'operatività "hardware". Non ci sono più scuse per cui un sistema cruciale non sia stato realizzato in modo affidabile utilizzando tecnologie di clustering e di ridondanza.

Vi racconteremo quali soluzioni abbiamo considerato per garantire l'affidabilità dei database nei nostri servizi e a cosa siamo giunti. Inoltre, ci sarà una demo con conclusioni significative.

Legacy nell'architettura per garantire alta disponibilità

Ciò è ancora più evidente nell'evoluzione di vari sistemi open source. Le vecchie soluzioni sono state costrette a integrare tecnologie di alta disponibilità man mano che cresceva la domanda. E la loro qualità variava. Le soluzioni di nuova generazione pongono l'alta disponibilità alla base della loro architettura. Ad esempio, MongoDB propone il clustering come principale variante di utilizzo. Il cluster si scalda orizzontalmente, il che rappresenta un forte vantaggio competitivo per questo DBMS.

Torniamo a PostgreSQL. È uno dei progetti open source più antichi e popolari, il primo rilascio del quale risale al 1995. Il team del progetto a lungo non ha considerato l'alta disponibilità come un compito da risolvere a livello di sistema. Pertanto, la tecnologia di replica per creare copie dei dati è stata integrata solo nella versione 8.2 nel 2006, ed era basata su file (log shipping). Nel 2010, con la versione 9.0, è stata introdotta la replicazione streaming, che è alla base della creazione di vari cluster. Questo, in effetti, sorprende molte persone che si avvicinano a PostgreSQL dopo Enterprise SQL o moderni NoSQL — la soluzione standard della comunità è semplicemente un paio di master-replica con replicazione sincrona o asincrona. In questo caso, il passaggio al master avviene manualmente, e anche la questione della commutazione dei client è suggerita come da risolvere autonomamente.

Come abbiamo deciso di realizzare un PostgreSQL affidabile e cosa abbiamo scelto per farlo

Tuttavia, PostgreSQL non sarebbe diventato così popolare se non ci fosse stata una vasta gamma di progetti e strumenti che aiutano a costruire soluzioni ad alta disponibilità che non richiedono una vigilanza costante. Nel cloud Mail.ru Cloud Solutions (MCS) fin dal lancio del DBaaS, sono stati disponibili server PostgreSQL singoli e coppie master-replica con replicazione asincrona.

Naturalmente, volevamo semplificare la vita a tutti e rendere accessibile un'installazione di PostgreSQL che potesse fungere da base per servizi ad alta disponibilità, senza doverci svegliare di notte per effettuare switch. In questo segmento ci sono sia soluzioni consolidate che una nuova generazione di strumenti che utilizzano le ultime innovazioni.

Oggi, il problema dell'alta disponibilità non riguarda più la ridondanza (questo è scontato), ma il consenso — l'algoritmo per la scelta del leader (Leader election). Spesso, i guasti gravi non si verificano a causa della mancanza di server, ma a causa di problemi di consenso: non è stato scelto un nuovo leader, sono emersi due leader in diversi data center, ecc. Un esempio è il guasto nel cluster MySQL di Github — hanno scritto un dettagliato post mortem.

La base matematica in questo campo è molto seria. Da un lato, esiste il teorema CAP, che impone restrizioni teoriche sulle possibilità di costruzione di soluzioni HA, e dall'altro — algoritmi di consenso matematicamente provati, come Paxos e Raft. Sulla base di questi, esistono DCS (sistemi di consenso decentralizzato) abbastanza popolari — Zookeeper, etcd, Consul. Pertanto, se il sistema di decisione opera su un proprio algoritmo scritto internamente, è necessario prestare molta attenzione. Dopo aver analizzato un'enorme varietà di sistemi, ci siamo concentrati su Patroni — un sistema open source, principalmente sviluppato dall'azienda Zalando.

A titolo di digressione, dirò che abbiamo anche preso in considerazione soluzioni multi-master, ovvero cluster che possono essere scalati orizzontalmente per la scrittura. Tuttavia, per due motivi principali abbiamo deciso di non implementare tale cluster. In primo luogo, tali soluzioni presentano un'elevata complessità e, di conseguenza, un maggior numero di punti vulnerabili. Sarà difficile realizzare una soluzione stabile per tutti i casi. In secondo luogo, in tal caso PostgreSQL smette di essere nativo, alcune funzioni non saranno disponibili e potrebbero insorgere bug nascosti in alcune applicazioni durante l'uso.

Patroni

Quindi, come funziona Patroni? Gli sviluppatori non hanno reinventato la ruota e hanno proposto di utilizzare come base una delle soluzioni DCS collaudate. A lui vengono affidate tutte le questioni relative alla sincronizzazione delle configurazioni, alla scelta del leader e al quorum. Abbiamo scelto etcd per questo.

Successivamente, Patroni si occupa dell'applicazione corretta di tutte le impostazioni su PostgreSQL, della configurazione della replica e dell'esecuzione di comandi di switchover e failover (cioè dello switch tra master in modo standard e non standard). In particolare nel cloud MCS è possibile creare un cluster composto da un master, una replica sincrona e una o più repliche asincrone. La presenza di una replica sincrona garantisce la sicurezza dei dati su almeno 2 server, e questa replica sarà il principale "candidato a master".

Poiché etcd viene distribuito sugli stessi server, si raccomanda un numero di server pari a 3 o 5 per un valore ottimale di quorum. Questo cluster può essere scalato orizzontalmente per la lettura (ho scritto sopra riguardo alla scalabilità per la scrittura). Tuttavia, è importante considerare che le repliche asincrone tendono a ritardare, specialmente sotto carichi elevati.

L'uso di tali repliche in lettura (hot standby) è giustificato per compiti di reporting o analisi e alleggerisce il server master.

Se desideri creare un tale cluster autonomamente, avrai bisogno di:

  • preparare 3 o più server, configurare l'IP e le regole del firewall tra di essi;
  • installare i pacchetti per i servizi etcd, Patroni, PostgreSQL;
  • configurare il cluster etcd;
  • configurare il servizio patroni per lavorare con PostgreSQL.

In altre parole, è necessario creare correttamente una decina di file di configurazione senza commettere errori. Per questo è consigliabile utilizzare uno strumento di gestione della configurazione, come Ansible, ad esempio. Tuttavia, manca comunque un bilanciatore TCP ad alta disponibilità. Realizzarlo è un lavoro a parte.

Per coloro che desiderano un cluster pronto ma non vogliono immergersi nei dettagli, abbiamo cercato di semplificare la vita creando un cluster pronto su Patroni nel nostro cloud, che è possibile testare gratuitamente. Oltre al cluster stesso, abbiamo realizzato:

  • Un bilanciatore TCP; su diverse porte indica sempre il master attuale, replica sincrona o asincrona, di conseguenza;
  • Un'API per il passaggio del master attivo di Patroni.

Possono essere integrati sia tramite l'API del cloud MCS che con la console web.

Demo

Per testare le capacità del cluster PostgreSQL nel cloud MCS, vediamo come si comporterà un'applicazione reale in caso di problemi con il DBMS.

Di seguito è riportato il codice dell'applicazione, che registrerà eventi artificiali e li mostrerà sullo schermo. In caso di errori, ne darà notifica e continuerà a funzionare in ciclo finché non lo interromperemo con la combinazione 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("Connessione aperta a", 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("Valore registrato, conteggio totale: {}".format(record[0]))
    except Exception as error:
        print ("Errore durante la connessione a PostgreSQL", error)
    finally:
        if connection:
            cursor.close()
            connection.close()
            print("Connessione chiusa")


if __name__ == '__main__':
    try:
        while True:
            try:
                print(datetime.now())
                main()
                sleep(3)
            except Exception as e:
                print("Errore catturato:n", e)
                sleep(1)
    except KeyboardInterrupt:
        print("uscita")

L'applicazione necessita di PostgreSQL per funzionare. Creiamo un cluster nel cloud MCS utilizzando l'API. In un normale terminale, dove la variabile OS_TOKEN contiene il token per accedere all'API (può essere ottenuto con il comando openstack token issue), digiteremo i seguenti comandi:

Creiamo un cluster:

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

Come abbiamo costruito un cluster PostgreSQL robusto su Patroni

Quando il cluster passerà allo stato ACTIVE, tutti i campi riceveranno valori aggiornati: il cluster è pronto.

Nell'interfaccia grafica:

Come abbiamo costruito un cluster PostgreSQL robusto su Patroni

Proviamo a connetterci e a creare una tabella:

psql -h 89.208.87.38 -U admin -d myproddb
Password per l'utente admin:
psql (11.1, server 10.7)
Digita "help" per assistenza.

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 righe)

myproddb=>

Come abbiamo costruito un cluster PostgreSQL robusto su Patroni

Nell'applicazione indicheremo le impostazioni aggiornate per la connessione a PostgreSQL. Indicheremo l'indirizzo del bilanciatore TCP, in modo da non dover passare manualmente all'indirizzo del master. Lo avvieremo. Come si può vedere, gli eventi vengono registrati con successo nel database.

Come abbiamo costruito un cluster PostgreSQL robusto su Patroni

Failover programmato

Ora testeremo il funzionamento della nostra applicazione durante il failover programmato:

Come abbiamo costruito un cluster PostgreSQL robusto su Patroni

Osserviamo l'applicazione. Vedo che il funzionamento dell'applicazione viene effettivamente interrotto, ma dura solo pochi secondi, in questo caso specifico, al massimo 9.

Come abbiamo costruito un cluster PostgreSQL robusto su Patroni

Guasto della macchina

Ora proviamo a simulare il guasto della macchina virtuale, dell'attuale master. Potremmo semplicemente spegnere la macchina virtuale tramite l'interfaccia Horizon, ma sarebbe uno spegnimento normale. Questo failover verrebbe gestito da tutti i servizi, incluso Patroni.

Ci serve uno spegnimento imprevedibile. Pertanto, ho chiesto ai nostri amministratori di spegnere la macchina virtuale attuale — il master — in modo non ordinario per scopi di test.

Come abbiamo costruito un cluster PostgreSQL robusto su Patroni

Nel frattempo, la nostra applicazione continuava a funzionare. Naturalmente, tale failover d'emergenza non può passare inosservato.

2019-03-29 10:45:56.071234
Connessione aperta a PostgreSQL 10.7 su x86_64-pc-linux-gnu, compilato da gcc (GCC) 4.8.5 20150623 (Red Hat 4.8.5-36), 64-bit
Valore registrato, conteggio totale: 453
Connessione chiusa
2019-03-29 10:45:59.205463
Connessione aperta a PostgreSQL 10.7 su x86_64-pc-linux-gnu, compilato da gcc (GCC) 4.8.5 20150623 (Red Hat 4.8.5-36), 64-bit
Valore registrato, conteggio totale: 454

Connessione chiusa
2019-03-29 10:46:02.661440
Errore durante la connessione al server PostgreSQL, la connessione è stata chiusa in modo imprevisto
        Ciò significa probabilmente che il server è terminato anormalmente
        prima o durante l'elaborazione della richiesta.

Errore catturato:
 variabile locale 'connection' riferita prima dell'assegnazione
……………………………………………………….. - qui ci sono alcuni errori
2019-03-29 10:46:30.930445
Errore durante la connessione al server PostgreSQL, la connessione è stata chiusa in modo imprevisto
        Ciò significa probabilmente che il server è terminato anormalmente
        prima o durante l'elaborazione della richiesta.

Errore catturato:
 variabile locale 'connection' riferita prima dell'assegnazione
2019-03-29 10:46:31.954399
Connessione aperta a PostgreSQL 10.7 su x86_64-pc-linux-gnu, compilato da gcc (GCC) 4.8.5 20150623 (Red Hat 4.8.5-36), 64-bit
Valore registrato, conteggio totale: 455
Connessione chiusa
2019-03-29 10:46:35.409800
Connessione aperta a PostgreSQL 10.7 su x86_64-pc-linux-gnu, compilato da gcc (GCC) 4.8.5 20150623 (Red Hat 4.8.5-36), 64-bit
Valore registrato, conteggio totale: 456
Connessione chiusa
^Cexit

Come si può vedere, l'applicazione è riuscita a continuare la sua operatività in meno di 30 secondi. Sì, un certo numero di utenti del servizio potrebbe notare i problemi. Tuttavia, si tratta di un serio guasto del server, un evento non molto frequente. Inoltre, una persona (amministratore) probabilmente non sarebbe riuscita a rispondere altrettanto rapidamente, a meno che non fosse già in console pronta con uno script di switch.

Conclusione

A mio avviso, un cluster di questo tipo offre un enorme vantaggio per gli amministratori. Fondamentalmente, seri guasti e malfunzionamenti dei server di database non saranno percepibili per l'applicazione e, di conseguenza, per l'utente. Non sarà necessario riparare nulla in fretta o passare a configurazioni temporanee, server, ecc. E se questa soluzione viene utilizzata come servizio pronto in cloud, non ci sarà bisogno di perdere tempo nella sua preparazione. Sarà possibile dedicarsi a qualcosa di più interessante.

Fonte: habr.com

Acquista hosting affidabile per siti web con protezione DDoS, VPS VDS server 🔥 Acquista hosting affidabile per siti web con protezione DDoS, VPS VDS server | ProHoster