
Sempre più spesso riceviamo richieste dai clienti: «Vogliamo qualcosa come Amazon RDS, ma più economico»; «Vogliamo qualcosa come RDS, ma ovunque, in qualsiasi infrastruttura». Per realizzare una soluzione managed simile su Kubernetes, abbiamo esaminato lo stato attuale dei principali operatori per PostgreSQL (Stolon, operatori di Crunchy Data e Zalando) e abbiamo fatto la nostra scelta.
Questo articolo rappresenta la nostra esperienza acquisita sia dal punto di vista teorico (panoramica delle soluzioni) che pratico (cosa è stato scelto e cosa ne è venuto fuori). Ma prima di tutto, definiamo quali sono le esigenze richieste per una potenziale sostituzione di RDS...
Cos'è RDS
Quando le persone parlano di RDS, per la nostra esperienza, si riferiscono a un servizio di database gestito (managed) che:
- è facilmente configurabile;
- ha la possibilità di lavorare con snapshot e di ripristinarli (preferibilmente con supporto per );
- consente di creare topologie master-slave;
- ha un'ampia lista di estensioni;
- fornisce audit e gestione degli utenti/accessi.
Parlando in generale, gli approcci per affrontare la questione possono essere molto diversi, tuttavia la strada con il presunto Ansible non ci si addice. (Nello stesso modo sono giunti a una conclusione simile anche i colleghi di 2GIS a seguito della loro di creare uno «strumento per il rapido dispiegamento di un cluster ad alta disponibilità basato su Postgres».)
Gli operatori sono l'approccio convenzionale per affrontare tali questioni nell'ecosistema Kubernetes. Abbiamo già parlato di questo in relazione ai database eseguiti all'interno di Kubernetes, il CTO di «Flanta» , in .
NB: Per creare rapidamente operatori semplici, ti consigliamo di dare un'occhiata alla nostra utilità Open Source . Utilizzando essa, puoi farlo senza conoscere Go, attraverso modi più familiari per i sistemi, come Bash, Python, ecc.
Per PostgreSQL esistono diversi popolari operatori K8s:
- Stolon;
- Crunchy Data PostgreSQL Operator;
- Zalando Postgres Operator.
Esaminiamoli più da vicino.
Scelta dell'operatore
Oltre alle importanti funzionalità già menzionate, noi – come ingegneri di gestione dell'infrastruttura in Kubernetes – ci aspettavamo anche dai nostri operatori quanto segue:
- deploy da Git e con ;
- supporto per pod anti-affinity;
- installazione di node affinity o node selector;
- installazione di tolerations;
- disponibilità di opzioni di tuning;
- tecnologie comprensibili e anche comandi.
Senza entrare nei dettagli su ciascun punto (chiedi nei commenti se hai domande dopo aver letto l'intero articolo), vorrei sottolineare in generale che questi parametri sono necessari per una descrizione più dettagliata della specializzazione dei nodi del cluster, in modo da poterli ordinare per applicazioni specifiche. In questo modo possiamo raggiungere un equilibrio ottimale tra prestazioni e costi.
Ora passiamo agli operatori PostgreSQL.
1. Stolon
dall'azienda italiana Sorint.lab in è stato considerato un certo standard tra gli operatori per DBMS. Questo è un progetto piuttosto vecchio: la sua prima versione pubblica risale a novembre 2015 (!), e il repository su GitHub può vantare quasi 3000 stelle e oltre 40 collaboratori.
E in effetti, Stolon è un ottimo esempio di architettura ben progettata:

Puoi approfondire il funzionamento di questo operatore nella relazione o . In generale, basta dire che è in grado di fare tutto ciò che è descritto: failover, proxy per accesso trasparente ai client, backup... Inoltre, i proxy forniscono accesso tramite un unico endpoint di servizio - a differenza delle due altre soluzioni esaminate più avanti (che hanno due servizi per accedere al database).
Tuttavia, Stolon , il che rende impossibile implementarlo in modo che si possano creare istanze di DBMS in Kubernetes in modo semplice e veloce - "come panini caldi". La gestione avviene tramite l'utilità stolonctl, mentre il deploy avviene tramite un chart Helm e le definizioni utente sono impostate in ConfigMap.
Da un lato, sembra che l'operatore non sia proprio un operatore (dopotutto non utilizza CRD). D'altra parte, è un sistema flessibile che consente di configurare le risorse in K8s come preferisci.
In sintesi, non ci è sembrato ottimale avere un chart separato per ogni DB. Pertanto, abbiamo cominciato a cercare alternative.
2. Crunchy Data PostgreSQL Operator
, una giovane startup americana, sembrava un'alternativa logica. La sua storia pubblica inizia con il primo rilascio nel marzo 2017, da allora il repository su GitHub ha ricevuto poco meno di 1300 stelle e oltre 50 collaboratori. L'ultimo rilascio di settembre è stato testato per funzionare con Kubernetes 1.15—1.18, OpenShift 3.11+ e 4.4+, GKE e VMware Enterprise PKS 1.3+.
L'architettura del Crunchy Data PostgreSQL Operator soddisfa anche i requisiti dichiarati:

La gestione avviene tramite l'utilità pgo, tuttavia essa a sua volta genera Custom Resources per Kubernetes. Pertanto, come potenziali utenti, l'operatore ci ha soddisfatto:
- c'è gestione tramite CRD;
- comoda gestione degli utenti (anch'essa tramite CRD);
- integrazione con altri componenti — una collezione specializzata di immagini di container per PostgreSQL e utilità per lavorarci (incluso pgBackRest, pgAudit, estensioni da contrib, ecc.).
Tuttavia, i tentativi di iniziare a utilizzare l'operatore di Crunchy Data hanno rivelato alcuni problemi:
- Non c'era la possibilità di tolerations — era previsto solo nodeSelector.
- I pod creati facevano parte di un Deployment, nonostante stessimo deployando un'applicazione stateful. A differenza di StatefulSet, i Deployment non possono creare dischi.
Questo ultimo difetto porta a momenti divertenti: nell'ambiente di test siamo riusciti a far partire 3 repliche con un solo disco storage locale, con il risultato che l'operatore riportava che 3 repliche stavano funzionando (anche se non era così).
Un'altra caratteristica di questo operatore è la sua integrazione pronta con vari sistemi ausiliari. Ad esempio, è facile installare pgAdmin e pgBounce, e in sono considerate Grafana e Prometheus preconfigurate. Nel recente si segnala un miglioramento dell'integrazione con il progetto , il che consente all'operatore di offrire una visualizzazione chiara delle metriche PgSQL «out of the box».
Tuttavia, la strana scelta di risorse Kubernetes generate ci ha portato alla necessità di trovare un'altra soluzione.
3. Zalando Postgres Operator
I prodotti Zalando ci sono noti da tempo: abbiamo esperienza nell'utilizzo di Zalenium e, naturalmente, abbiamo provato — la loro soluzione HA popolare per PostgreSQL. Uno degli autori, Alexey Klukin, ha parlato dell'approccio dell'azienda nella creazione nella diretta del , e ci è piaciuto.
Questa è la soluzione più recente tra quelle considerate nell'articolo: il primo rilascio è avvenuto nell'agosto 2018. Tuttavia, nonostante il numero ridotto di rilasci formali, il progetto ha fatto molta strada, superando già la soluzione di Crunchy Data con oltre 1300 stelle su GitHub e il numero massimo di contributori (oltre 70).
Sotto il cofano, questo operatore utilizza soluzioni collaudate nel tempo:
- Patroni e per la gestione,
- — per i backup,
- — come pool di connessioni.
Ecco come è rappresentata l'architettura dell'operatore di Zalando:

L'operatore è completamente gestito tramite Custom Resources, creando automaticamente un StatefulSet da contenitori, che possono poi essere personalizzati aggiungendo vari sidecar nel pod. Tutto ciò rappresenta un notevole vantaggio rispetto all'operatore di Crunchy Data.
Poiché abbiamo scelto la soluzione di Zalando tra 3 opzioni considerate, la descrizione delle sue funzionalità verrà presentata di seguito, insieme alla pratica di applicazione.
Pratica con Postgres Operator di Zalando
Il deploy dell'operatore avviene in modo molto semplice: è sufficiente scaricare l'ultima versione da GitHub e applicare i file YAML dalla directory. In alternativa, è possibile anche utilizzare .
Dopo l'installazione, è opportuno preoccuparsi della configurazione . Si effettua tramite ConfigMap postgres-operator nello spazio dei nomi in cui hai installato l'operatore. Una volta configurati gli archivi, puoi avviare il primo cluster PostgreSQL.
Ad esempio, il deploy standard appare come segue:
apiVersion: acid.zalan.do/v1
kind: postgresql
metadata:
name: staging-db
spec:
numberOfInstances: 3
patroni:
synchronous_mode: true
postgresql:
version: "12"
resources:
limits:
cpu: 100m
memory: 1Gi
requests:
cpu: 100m
memory: 1Gi
sidecar:
- env:
- name: DATA_SOURCE_URI
value: 127.0.0.1:5432
- name: DATA_SOURCE_PASS
valueFrom:
secretKeyRef:
key: password
name: postgres.staging-db.credentials
- name: DATA_SOURCE_USER
value: postgres
image: wrouesnel/postgres_exporter
name: prometheus-exporter
resources:
limits:
cpu: 500m
memory: 100Mi
requests:
cpu: 100m
memory: 100Mi
teamId: staging
volume:
size: 2Gi
Questo manifesto deploya un cluster di 3 istanze con un sidecar in forma di , dal quale raccogliamo le metriche dell'applicazione. Come puoi vedere, è tutto molto semplice e, se lo desideri, puoi creare un numero praticamente illimitato di cluster.
Vale la pena prestare attenzione anche alla interfaccia web per la gestione — . Viene fornita insieme all'operatore e consente di creare ed eliminare cluster, così come di gestire i backup effettuati dall'operatore.

Elenco dei cluster PostgreSQL

Gestione dei backup
Un'altra caratteristica interessante è il supporto per . Questo meccanismo crea automaticamente ruoli in PostgreSQL, basandosi sull'elenco ricevuto di nomi degli utenti. Successivamente, l'API consente di restituire l'elenco degli utenti per i quali vengono creati automaticamente i ruoli.
Problemi e loro soluzione
Tuttavia, l'uso dell'operatore ha presto rivelato alcuni significativi svantaggi:
- mancanza di supporto per nodeSelector;
- impossibilità di disattivare i backup;
- quando si utilizza la funzione di creazione delle basi non compaiono privilegi di default;
- periodicamente manca la documentazione o questa è obsoleta.
Fortunatamente, molti di essi possono essere risolti. Iniziamo dalla fine: problemi con documentazione.
Probabilmente vi imbatterete nel fatto che non è sempre chiaro come specificare il backup e come collegare il bucket di backup all'Operator UI. Questo viene citato brevemente nella documentazione, ma la descrizione reale si trova in :
- è necessario creare un segreto;
- passarlo all'operatore nel parametro
pod_environment_secret_namenel CRD con le impostazioni dell'operatore o nel ConfigMap (dipende da come avete deciso di installare l'operatore).
Tuttavia, come si è rivelato, attualmente non è possibile. Ecco perché abbiamo raccolto con alcune funzionalità aggiuntive di terze parti. Maggiori dettagli su di essa — vedi sotto.
Se si passano all'operatore parametri per il backup, in particolare — wal_s3_bucket e le chiavi di accesso in AWS S3, allora lui effettuerà il backup di tutto: non solo le basi in produzione, ma anche quelle di staging. Questo non ci ha soddisfatti.
Nella descrizione dei parametri per Spilo, che è il wrapper Docker di base per PgSQL utilizzando l'operatore, è emerso: è possibile passare il parametro WAL_S3_BUCKET vuoto, disattivando così i backup. Inoltre, con grande gioia è stato trovato anche un , che abbiamo immediatamente accettato nel nostro fork. Ora è sufficiente aggiungere enableWALArchiving: false alle risorse del cluster PostgreSQL.
Sì, era possibile farlo diversamente, avviando 2 operatori: uno per staging (senza backup) e l'altro per produzione. Ma così siamo riusciti a farne a meno di uno.
Ok, abbiamo imparato a fornire accesso alle basi per S3 e i backup hanno iniziato a essere memorizzati. Come far funzionare le pagine dei backup nell'Operator UI?

Nell'Operator UI sarà necessario aggiungere 3 variabili:
-
SPILO_S3_BACKUP_BUCKET -
AWS_ACCESS_KEY_ID -
AWS_SECRET_ACCESS_KEY
Dopo di che la gestione dei backup diventerà disponibile, il che nel nostro caso semplificherà il lavoro con lo staging, consentendo di portare lì tagli dalla produzione senza script aggiuntivi.
Come ulteriore vantaggio, è stata menzionata la gestione con l'API Teams e le ampie possibilità per la creazione di basi e ruoli tramite l'operatore. Tuttavia, i ruoli creati non avevano diritti di default. Di conseguenza, un utente con diritti di lettura non poteva leggere nuove tabelle.
Perché è così? Nonostante nel codice ci siano necessari GRANT, non vengono applicati sempre. Ci sono 2 metodi: syncPreparedDatabases e syncDatabases. In syncPreparedDatabases — nonostante che nella sezione preparedDatabases ci sia una condizione defaultRoles e defaultUsers per la creazione di ruoli, i permessi predefiniti non vengono applicati. Siamo nel processo di preparazione di una patch affinché questi diritti vengano applicati automaticamente.
E l'ultimo punto nelle modifiche per noi rilevanti è , che aggiunge Node Affinity nel StatefulSet creato. I nostri clienti spesso preferiscono ridurre i costi utilizzando istanze spot, e su di esse chiaramente non si dovrebbero collocare servizi DB. Questo problema potrebbe essere risolto anche tramite tolerations, ma la presenza di Node Affinity offre maggiore sicurezza.
Cosa è emerso?
A seguito della risoluzione dei problemi elencati, abbiamo forkato il Postgres Operator di Zalando nel , dove viene compilato con patch così utili. E per maggior comodità abbiamo raccolto anche .
Elenco delle PR accettate nel fork:
- ;
- ;
- ;
- .
Sarebbe fantastico se la comunità supportasse queste PR affinché possano entrare nell'upstream con la prossima versione dell'operatore (1.6).
Bonus! Storia di successo con la migrazione della produzione
Se utilizzi Patroni, puoi migrare una produzione live con un minimo di downtime.
Spilo consente la creazione di cluster standby tramite archiviazione S3 con , quando il log binario PgSQL viene prima salvato in S3 e poi estratto dal replica. Ma cosa fare se hai non Wal-E utilizzato nella tua vecchia infrastruttura? La soluzione a questo problema è già su Habr.
La replica logica di PostgreSQL viene in soccorso. Tuttavia, non ci addentreremo nei dettagli su come creare pubblicazioni e sottoscrizioni, perché... il nostro piano ha subito un fallimento.
Il fatto è che nel DB c'erano alcune tabelle sovraccariche con milioni di righe, che, inoltre, venivano continuamente riempite e svuotate. con copy_data, in cui il nuovo replica copia tutto il contenuto dal master, semplicemente non riusciva a stare dietro al master. La copia del contenuto ha funzionato per una settimana, ma non ha mai raggiunto il master. Alla fine, per risolvere il problema ci hanno aiutato colleghi di Avito: è possibile trasferire i dati utilizzando pg_dump. Descriverò la nostra versione (leggermente modificata) di questo algoritmo.
L'idea è quella di poter creare una sottoscrizione disattivata, legata a uno specifico slot di replica, e poi correggere il numero della transazione. Erano disponibili repliche per il lavoro di produzione. Questo è importante perché la replica aiuterà a creare un dump coerente e a continuare a ricevere modifiche dal master.
Nelle successive istruzioni che descrivono il processo di migrazione, verranno utilizzate le seguenti designazioni per gli host:
- master — server sorgente;
- replica1 — replica in streaming sulla vecchia produzione;
- replica2 — nuova replica logica.
Piano di migrazione
1. Creiamo sul master una sottoscrizione per tutte le tabelle nello schema pubblico del database dbname:
psql -h master -d dbname -c "CREATE PUBLICATION dbname FOR ALL TABLES;"
2. Creiamo uno slot di replica sul master:
psql -h master -c "select pg_create_logical_replication_slot('repl', 'pgoutput');"
3. Fermeremo la replica sulla vecchia replica:
psql -h replica1 -c "select pg_wal_replay_pause();"
4. Otteniamo il numero della transazione dal master:
psql -h master -c "select replay_lsn from pg_stat_replication where client_addr = 'replica1';"
5. Eseguiremo un dump dalla vecchia replica. Lo faremo in più thread, il che aiuterà ad accelerare il processo:
pg_dump -h replica1 --no-publications --no-subscriptions -O -C -F d -j 8 -f dump/ dbname
6. Caricheremo il dump sul nuovo server:
pg_restore -h replica2 -F d -j 8 -d dbname dump/
7. Dopo aver caricato il dump, possiamo avviare la replica sulla replica in streaming:
psql -h replica1 -c "select pg_wal_replay_resume();"
7. Creiamo una sottoscrizione sulla nuova replica logica:
psql -h replica2 -c "create subscription oldprod connection 'host=replica1 port=5432 user=postgres password=secret dbname=dbname' publication dbname with (enabled = false, create_slot = false, copy_data = false, slot_name='repl');"
8. Otteniamo oid della sottoscrizione:
psql -h replica2 -d dbname -c "select oid, * from pg_subscription;"
9. Supponiamo di aver ottenuto oid=1000. Applicheremo il numero della transazione alla sottoscrizione:
psql -h replica2 -d dbname -c "select pg_replication_origin_advance('pg_1000', 'AA/AAAAAAAA');"
10. Avviamo la replica:
psql -h replica2 -d dbname -c "alter subscription oldprod enable;"
11. Controlliamo lo stato della sottoscrizione; la replica dovrebbe funzionare:
psql -h replica2 -d dbname -c "select * from pg_replication_origin_status;"
psql -h master -d dbname -c "select slot_name, restart_lsn, confirmed_flush_lsn from pg_replication_slots;"
12. Dopo che la replica è stata avviata e i database sono stati sincronizzati, si può effettuare il switch.
13. Dopo aver disabilitato la replica, è necessario correggere le sequenze. Questo è ben descritto .
Grazie a questo piano, il passaggio è avvenuto con minime attese.
Conclusione
I operator di Kubernetes semplificano diverse operazioni, riducendole alla creazione di risorse K8s. Tuttavia, una volta raggiunta una straordinaria automazione con il loro aiuto, è importante ricordare che possono portare anche una serie di sfide inaspettate, quindi scegliete gli operatori con saggezza.
Esaminando i tre operatori Kubernetes più popolari per PostgreSQL, abbiamo deciso di optare per il progetto di Zalando. Con questo abbiamo dovuto affrontare alcune difficoltà, ma il risultato è stato davvero soddisfacente, tanto che prevediamo di estendere questa esperienza ad alcune altre installazioni PgSQL. Se avete esperienza con soluzioni simili, saremo felici di vedere i dettagli nei commenti!
P.S.
Leggi anche nel nostro blog:
- «»;
- «»;
- «».
Fonte: habr.com
