{"id":35706,"date":"2019-10-31T22:05:50","date_gmt":"2019-10-31T19:05:50","guid":{"rendered":"https:\/\/prohoster.info\/blog\/otkazoustojchivyj-klaster-postgresql-patroni-opyt-vnedreniya\/"},"modified":"2026-05-18T20:58:46","modified_gmt":"2026-05-18T18:58:46","slug":"otkazoustojchivyj-klaster-postgresql-patroni-opyt-vnedreniya","status":"publish","type":"post","link":"https:\/\/prohoster.info\/it\/blog\/administrirovanie\/otkazoustojchivyj-klaster-postgresql-patroni-opyt-vnedreniya","title":{"rendered":"Cluster PostgreSQL a prova di guasto + Patroni. Esperienza di implementazione.","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p>Nell'articolo parler\u00f2 di come ci siamo avvicinati al problema della resilienza di PostgreSQL, perch\u00e9 \u00e8 diventato importante per noi e quale \u00e8 stato il risultato finale.<\/p>\n<p>Abbiamo un servizio ad alta richiesta: 2,5 milioni di utenti in tutto il mondo, oltre 50K utenti attivi ogni giorno. I server si trovano su Amazon in un unico regione dell'Irlanda: lavoriamo costantemente con oltre 100 server diversi, di cui quasi 50 con basi di dati.<\/p>\n<p>L'intero backend \u00e8 un grande monolite stateful applicazione in Java, che mantiene una connessione websocket costante con il cliente. Quando pi\u00f9 utenti lavorano contemporaneamente su una stessa bacheca, tutti vedono le modifiche in tempo reale, perch\u00e9 ogni modifica viene registrata nel database. Riceviamo circa 10K richieste al secondo dai nostri database. Durante i picchi di carico su Redis scriviamo tra 80-100K richieste al secondo.<br \/>\n<img decoding=\"async\" style=\"display: block; margin: 0 auto;\" src=\"\/wp-content\/uploads\/2019\/06\/443f85815b0560fade7db5b639942935.png\" alt=\"Cluster PostgreSQL a prova di guasto + Patroni. Esperienza di implementazione.\" \/><br \/>\n<a rel=\"nofollow\" name=\"habracut\"><\/a><\/p>\n<h2>Perch\u00e9 siamo passati da Redis a PostgreSQL<\/h2>\n<p>Inizialmente, il nostro servizio funzionava con Redis, uno storage key-value che memorizza tutti i dati in memoria. <a href=\"https:\/\/prohoster.info\/it\/server\/\">server<\/a>.<\/p>\n<p>Vantaggi di Redis:<\/p>\n<ol>\n<li>Alta velocit\u00e0 di risposta, poich\u00e9 tutto \u00e8 memorizzato in memoria;<\/li>\n<li>Facilit\u00e0 di backup e replica.<\/li>\n<\/ol>\n<p>Svantaggi di Redis per noi:<\/p>\n<ol>\n<li>Non ci sono transazioni reali. Abbiamo cercato di imitarle a livello della nostra applicazione. Sfortunatamente, ci\u00f2 non ha sempre funzionato bene e ha richiesto di scrivere codice molto complesso.<\/li>\n<li>La quantit\u00e0 di dati \u00e8 limitata dalla quantit\u00e0 di memoria. Aumentando la quantit\u00e0 di dati, la memoria crescer\u00e0 e, alla fine, ci scontreremo con le caratteristiche dell'istanza scelta, il che in AWS richiede di fermare il nostro servizio per modificare il tipo di istanza.<\/li>\n<li>\u00c8 necessario mantenere costantemente un livello di latenza basso, poich\u00e9 abbiamo un numero molto elevato di richieste. Il livello di latenza ottimale per noi \u00e8 tra 17-20 ms. Con un livello di 30-40 ms riceviamo risposte lunghe alle richieste della nostra applicazione e degradazione del servizio. Sfortunatamente, questo \u00e8 accaduto a settembre 2018, quando uno degli istanze con Redis ha ricevuto per qualche motivo una latenza doppia rispetto al normale. Per risolvere il problema, abbiamo fermato il servizio a met\u00e0 giornata lavorativa per una manutenzione straordinaria e sostituito l'istanza problematica di Redis.<\/li>\n<li>\u00c8 facile ottenere incoerenze nei dati anche con piccoli errori nel codice e poi spendere molto tempo a scrivere codice per correggere questi dati.<\/li>\n<\/ol>\n<p>Abbiamo considerato i lati negativi e capito che era necessario passare a qualcosa di pi\u00f9 conveniente, con transazioni normali e minore dipendenza dalla latenza. Abbiamo condotto una ricerca, analizzato numerose opzioni e scelto PostgreSQL.<\/p>\n<p>Stiamo migrando al nuovo DB da 1,5 anni e abbiamo trasferito solo una piccola parte dei dati, quindi attualmente lavoriamo simultaneamente con Redis e PostgreSQL. Maggiori dettagli sulle fasi della migrazione e sullo switch dei dati tra i DB sono scritti in <a href=\"https:\/\/habr.com\/ru\/company\/miro\/blog\/437826\/\" rel=\"nofollow\">un articolo del mio collega<\/a>.<\/p>\n<p>Quando abbiamo iniziato la migrazione, la nostra applicazione interagiva direttamente con il DB e si collegava al master di Redis e PostgreSQL. Il cluster PostgreSQL era composto da un master e una replica con replica asincrona. Questa era la configurazione del lavoro con i database:<br \/>\n<img decoding=\"async\" style=\"display: block; margin: 0 auto;\" src=\"\/wp-content\/uploads\/2019\/06\/674dd77de4c8b48a946c9f64c0b2fdd1.png\" alt=\"Cluster PostgreSQL a prova di guasto + Patroni. Esperienza di implementazione.\" \/><\/p>\n<h2>Implementazione di PgBouncer<\/h2>\n<p>Mentre stavamo migrando, il prodotto si stava anche evolvendo: aumentava il numero di utenti e il numero di server che lavoravano con PostgreSQL, e abbiamo iniziato a esaurire le connessioni. PostgreSQL crea un processo separato per ogni connessione e consuma risorse. Il numero di connessioni pu\u00f2 essere aumentato fino a un certo punto, altrimenti c'\u00e8 il rischio di un funzionamento subottimale del DB. La soluzione ideale in questa situazione sarebbe scegliere un gestore di connessioni, che si collocherebbe davanti al database.<\/p>\n<p>Avevamo due opzioni per il gestore di connessioni: Pgpool e PgBouncer. Tuttavia, il primo non supporta la modalit\u00e0 di lavoro transazionale con il database, quindi abbiamo scelto PgBouncer.<\/p>\n<p>Abbiamo impostato la seguente configurazione: la nostra applicazione si connette a un PgBouncer, che gestisce i master di PostgreSQL, e dietro ogni master c'\u00e8 una replica con replica asincrona.<br \/>\n<img decoding=\"async\" style=\"display: block; margin: 0 auto;\" src=\"\/wp-content\/uploads\/2019\/06\/dabcb7f6006520c4fec85fb925e8975e.png\" alt=\"Cluster PostgreSQL a prova di guasto + Patroni. Esperienza di implementazione.\" \/><\/p>\n<p>Tuttavia, non potevamo memorizzare l'intero volume di dati in PostgreSQL e per noi la velocit\u00e0 di lavoro con il database era importante, quindi abbiamo iniziato a shardare PostgreSQL a livello applicativo. Lo schema descritto sopra \u00e8 abbastanza comodo per questo: aggiungendo un nuovo shard, \u00e8 sufficiente aggiornare la configurazione di PgBouncer e l'applicazione pu\u00f2 immediatamente lavorare con il nuovo shard.<\/p>\n<h3>Resilienza di PgBouncer<\/h3>\n<p>Questo schema ha funzionato fino a quando l'unico istanza di PgBouncer non \u00e8 andato in crash. Ci troviamo in AWS, dove tutte le istanze sono eseguite su hardware che muore periodicamente. In tali casi, l'istanza semplicemente si sposta su nuovo hardware e riprende a funzionare. Questo \u00e8 successo anche con PgBouncer, ma \u00e8 diventato inaccessibile. Il risultato di questo crash \u00e8 stata l'indisponibilit\u00e0 del nostro servizio per 25 minuti. AWS consiglia di utilizzare la ridondanza dal lato dell'utente per queste situazioni, cosa che non era stata implementata da noi in quel momento.<\/p>\n<p>Dopo ci\u00f2 abbiamo seriamente riflettuto sulla resilienza di PgBouncer e dei cluster PostgreSQL, poich\u00e9 una situazione simile potrebbe ripetersi con qualsiasi istanza nel nostro account AWS.<\/p>\n<p>Abbiamo costruito lo schema di resilienza per PgBouncer nel seguente modo: tutti i server delle applicazioni si collegano a un Network Load Balancer, dietro il quale ci sono due PgBouncer. Ognuno dei PgBouncer guarda gli stessi master PostgreSQL di ciascun shard. In caso di ripetizione della situazione di crash dell'istanza AWS, tutto il traffico viene reindirizzato attraverso l'altro PgBouncer. La resilienza del Network Load Balancer \u00e8 garantita da AWS.<\/p>\n<p>Questo schema consente di aggiungere nuovi server PgBouncer senza problemi.<br \/>\n<img decoding=\"async\" style=\"display: block; margin: 0 auto;\" src=\"\/wp-content\/uploads\/2019\/06\/2c1f1e7d9f7be4fdeb43858520d55e36.png\" alt=\"Cluster PostgreSQL a prova di guasto + Patroni. Esperienza di implementazione.\" \/><\/p>\n<h2>Creazione di un cluster PostgreSQL resiliente<\/h2>\n<p>Nel risolvere questo problema abbiamo considerato diverse opzioni: failover personalizzato, repmgr, AWS RDS, Patroni.<\/p>\n<h3>Script personalizzati<\/h3>\n<p>Possono monitorare il funzionamento del master e, in caso di crash, promuovere la replica a master e aggiornare la configurazione di PgBouncer.<\/p>\n<p>I vantaggi di questo approccio risiedono nella massima semplicit\u00e0, poich\u00e9 si scrivono da soli gli script e si comprende esattamente come funzionano.<\/p>\n<p>Contro:<\/p>\n<ul>\n<li>Il master potrebbe non essere andato offline, potrebbe invece essersi verificato un guasto di rete. Il failover, non sapendo di ci\u00f2, promuover\u00e0 la replica a master, e il vecchio master continuer\u00e0 a funzionare. Di conseguenza avremo due server nel ruolo di master e non sapremo su quale di essi siano i dati pi\u00f9 recenti. Questa situazione viene chiamata anche split-brain;<\/li>\n<li>Siamo rimasti senza replica. Nella nostra configurazione c'\u00e8 un master e una replica; dopo il commutamento, la replica viene promossa a master e non abbiamo pi\u00f9 repliche, quindi dobbiamo aggiungere manualmente una nuova replica;<\/li>\n<li>\u00c8 necessario un monitoraggio aggiuntivo del funzionamento del failover, tenendo presente che abbiamo 12 shard PostgreSQL, il che significa che dobbiamo monitorare 12 cluster. Con l'aumento del numero di shard, non bisogna dimenticare di aggiornare il failover.<\/li>\n<\/ul>\n<p>Un failover scritto a mano appare molto complesso e richiede un supporto non triviale. Con un unico cluster PostgreSQL, questa sarebbe l'opzione pi\u00f9 semplice, ma non \u00e8 scalabile, quindi non \u00e8 adatta a noi.<\/p>\n<h3>Repmgr<\/h3>\n<p>Replication Manager per cluster PostgreSQL, in grado di gestire il funzionamento del cluster PostgreSQL. Tuttavia, non offre un failover automatico \u201cdi default\u201d, quindi sar\u00e0 necessario scrivere un proprio \u201cwrapper\u201d sopra la soluzione predefinita. Pertanto, potrebbe risultare persino pi\u00f9 complesso rispetto agli script scritti a mano, quindi non abbiamo nemmeno provato Repmgr.<\/p>\n<h3>AWS RDS<\/h3>\n<p>Supporta tutto il necessario per noi, \u00e8 in grado di eseguire backup e supporta un pool di connessioni. Ha uno switch automatico: quando il master muore, la replica diventa il nuovo master, e AWS aggiorna il record DNS sul nuovo master, mentre le repliche possono trovarsi in diverse AZ.<\/p>\n<p>Tra i contro c'\u00e8 l'assenza di impostazioni dettagliate. Un esempio di impostazioni dettagliate: nei nostri istanti ci sono limitazioni per le connessioni tcp, cosa che, sfortunatamente, non pu\u00f2 essere fatta in RDS:<\/p>\n<pre><code class=\"python\">net.ipv4.tcp_keepalive_time=10\nnet.ipv4.tcp_keepalive_intvl=1\nnet.ipv4.tcp_keepalive_probes=5\nnet.ipv4.tcp_retries2=3\n<\/code><\/pre>\n<p>Inoltre, il prezzo di AWS RDS \u00e8 quasi il doppio del prezzo normale di un'istanza, il che \u00e8 stata la causa principale del rifiuto di questa soluzione.<\/p>\n<h3>Patroni<\/h3>\n<p>\u00c8 un template in python per gestire PostgreSQL con una buona documentazione, failover automatico e codice sorgente su github.<\/p>\n<p>Vantaggi di Patroni:<\/p>\n<ul>\n<li>Ogni parametro di configurazione \u00e8 dettagliato, \u00e8 chiaro come funziona tutto;<\/li>\n<li>Il failover automatico funziona di default;<\/li>\n<li>Scritto in python, e dato che anche noi scriviamo molto in python, ci sar\u00e0 pi\u00f9 facile affrontare i problemi e, forse, persino contribuire allo sviluppo del progetto;<\/li>\n<li>Gestisce completamente PostgreSQL, consente di modificare la configurazione su tutti i nodi del cluster e, se per applicare una nuova configurazione \u00e8 necessario riavviare il cluster, questo pu\u00f2 essere fatto nuovamente tramite Patroni.<\/li>\n<\/ul>\n<p>Contro:<\/p>\n<ul>\n<li>Dalla documentazione non \u00e8 chiaro come lavorare correttamente con PgBouncer. Anche se \u00e8 difficile chiamarlo un difetto, perch\u00e9 il compito di Patroni \u00e8 gestire PostgreSQL, e come avverranno le connessioni a Patroni \u00e8 gi\u00e0 un nostro problema;<\/li>\n<li>Ci sono pochi esempi di implementazione di Patroni su grandi volumi, mentre ci sono molti esempi di implementazione da zero.<\/li>\n<\/ul>\n<p>Alla fine, per creare un cluster ad alta disponibilit\u00e0, abbiamo scelto proprio Patroni.<\/p>\n<h2>Il processo di implementazione di Patroni<\/h2>\n<p>Fino a Patroni avevamo 12 shard di PostgreSQL configurati con un master e una replica con replica asincrona. I server delle applicazioni accedevano ai database tramite un Network Load Balancer, dietro il quale si trovavano due instance con PgBouncer, e dietro di essi erano presenti tutti i server PostgreSQL.<br \/>\n<img decoding=\"async\" style=\"display: block; margin: 0 auto;\" src=\"\/wp-content\/uploads\/2019\/06\/8e5350d2466311593e11add30d5b1bdc.png\" alt=\"Cluster PostgreSQL a prova di guasto + Patroni. Esperienza di implementazione.\" \/><\/p>\n<p>Per implementare Patroni dovevamo scegliere uno storage distribuito per la configurazione del cluster. Patroni lavora con sistemi di storage distribuiti come etcd, Zookeeper e Consul. Proprio nel nostro ambiente di produzione abbiamo un cluster Consul completamente operativo, che lavora in combinazione con Vault e non lo utilizziamo per altri scopi. Un'ottima occasione per iniziare a utilizzare Consul per il suo scopo.<\/p>\n<h3>Come funziona Patroni con Consul<\/h3>\n<p>Abbiamo un cluster Consul composto da tre nodi e un cluster Patroni che consiste in un leader e una replica (in Patroni il master \u00e8 chiamato leader del cluster e gli slave sono le repliche). Ogni istanza del cluster Patroni invia costantemente a Consul informazioni sullo stato del cluster. Pertanto, da Consul \u00e8 sempre possibile conoscere la configurazione attuale del cluster Patroni e chi \u00e8 il leader in quel momento.<\/p>\n<p><img decoding=\"async\" style=\"display: block; margin: 0 auto;\" src=\"\/wp-content\/uploads\/2019\/06\/3234594b363904424fdce64c9d77fca5.png\" alt=\"Cluster PostgreSQL a prova di guasto + Patroni. Esperienza di implementazione.\" \/><\/p>\n<p>Per connettere Patroni a Consul \u00e8 sufficiente consultare la documentazione ufficiale, nella quale \u00e8 specificato che \u00e8 necessario indicare l'host nel formato http o https a seconda di come stiamo lavorando con Consul, e lo schema di connessione, opzionalmente:<\/p>\n<pre><code class=\"plaintext\">host: l'host:port per il punto finale Consul, nel formato: http(s):\/\/host:port\nschema: (opzionale) http o https, predefinito a http<\/code><\/pre>\n<p>Appare semplice, ma qui iniziano gli insidie. Con Consul lavoriamo tramite una connessione sicura tramite https e la nostra configurazione di connessione apparir\u00e0 come segue:<\/p>\n<pre><code class=\"python\">consul:\n  host: https:\/\/server.production.consul:8080 \n  verify: true\n  cacert: {{ consul_cacert }}\n  cert: {{ consul_cert }}\n  key: {{ consul_key }}<\/code><\/pre>\n<p>Ma cos\u00ec non funziona. All'avvio Patroni non riesce a connettersi a Consul perch\u00e9 tenta comunque di andare su http.<\/p>\n<p>Per risolvere il problema \u00e8 stato utile il codice sorgente di Patroni. \u00c8 stato fortunato che sia scritto in python. Si scopre che il parametro host non viene elaborato e il protocollo deve essere indicato nello schema. Ecco come appare il blocco di configurazione funzionante per lavorare con Consul nel nostro caso:<\/p>\n<pre><code class=\"python\">consul:\n  host: server.production.consul:8080\n  schema: https\n  verify: true\n  cacert: {{ consul_cacert }}\n  cert: {{ consul_cert }}\n  key: {{ consul_key }}<\/code><\/pre>\n<h3>Consul-template<\/h3>\n<p>Quindi, abbiamo scelto lo storage per la configurazione. Ora dobbiamo capire come PgBouncer cambier\u00e0 la sua configurazione quando ci sar\u00e0 un cambio di leader nel cluster Patroni. Nella documentazione non troviamo risposta a questa domanda, poich\u00e9 non viene descritta in generale la gestione di PgBouncer.<\/p>\n<p>In cerca di una soluzione, abbiamo trovato un articolo (purtroppo non ricordo il titolo) dove era scritto che Consul-template ha aiutato molto nel collegamento tra PgBouncer e Patroni. Questo ci ha spinto ad approfondire il funzionamento di Consul-template.<\/p>\n<p>Si \u00e8 scoperto che Consul-template monitora continuamente la configurazione del cluster PostgreSQL in Consul. Quando c'\u00e8 un cambio di leader, aggiorna la configurazione di PgBouncer e invia un comando per il suo riavvio.<\/p>\n<p><img decoding=\"async\" style=\"display: block; margin: 0 auto;\" src=\"\/wp-content\/uploads\/2019\/06\/6cf92996a127bb6637ab81dc45fdd60a.png\" alt=\"Cluster PostgreSQL a prova di guasto + Patroni. Esperienza di implementazione.\" \/><\/p>\n<p>Un grande vantaggio del template \u00e8 che viene conservato come codice, quindi quando si aggiunge un nuovo shard \u00e8 sufficiente fare un nuovo commit e aggiornare il template in modo automatico, mantenendo il principio dell'Infrastructure as Code.<\/p>\n<h3>Nuova architettura con Patroni<\/h3>\n<p>Di conseguenza, abbiamo ottenuto questo schema di lavoro:<br \/>\n<img decoding=\"async\" style=\"display: block; margin: 0 auto;\" src=\"\/wp-content\/uploads\/2019\/06\/bf9a9eff675a26039e3f76b16c6cd713.png\" alt=\"Cluster PostgreSQL a prova di guasto + Patroni. Esperienza di implementazione.\" \/><\/p>\n<p>Tutti i server dell'applicazione si collegano al bilanciatore \u2192 dietro di esso ci sono due istanze di PgBouncer \u2192 in ogni istanza \u00e8 in esecuzione Consul-template, che monitora lo stato di ciascun cluster Patroni e controlla l'aggiornamento della configurazione di PgBouncer, che indirizza le richieste al leader attuale di ciascun cluster.<\/p>\n<h3>Test manuali<\/h3>\n<p>Prima di mettere in produzione questo schema, lo abbiamo avviato in un piccolo ambiente di test e abbiamo verificato il funzionamento della commutazione automatica. Abbiamo aperto la bacheca, spostato un post-it e in quel momento abbiamo 'abbattuto' il leader del cluster. In AWS per questo \u00e8 sufficiente spegnere l'istanza tramite la console.<\/p>\n<p><img decoding=\"async\" style=\"display: block; margin: 0 auto;\" src=\"\/wp-content\/uploads\/2019\/06\/d670fe373b774f1b52bfd8a8c3b53a21.png\" alt=\"Cluster PostgreSQL a prova di guasto + Patroni. Esperienza di implementazione.\" \/><\/p>\n<p>Il post-it tornava indietro dopo 10-20 secondi, e poi iniziava a muoversi normalmente di nuovo. Questo significa che il cluster Patroni ha funzionato correttamente: ha cambiato il leader, ha inviato le informazioni a Consul e Consul-template ha subito raccolto queste informazioni, sostituito la configurazione di PgBouncer e inviato il comando per il reload.<\/p>\n<h2>Come sopravvivere a carichi elevati e mantenere un downtime minimo?<\/h2>\n<p>Tutto funziona benissimo! Ma sorgono nuove domande: Come funzioner\u00e0 sotto carichi elevati? Come distribuire tutto in modo rapido e sicuro in produzione?<\/p>\n<p>Rispondere alla prima domanda ci aiuta l'ambiente di test, in cui eseguiamo il test di carico. \u00c8 completamente identico a production per architettura e dispone di dati di test generati, che sono di volume approssimativamente pari a production. Decidiamo semplicemente di \"uccidere\" uno dei master PostgreSQL durante il test e vedere cosa succede. Ma prima \u00e8 importante controllare il rollout automatico, poich\u00e9 in questo ambiente abbiamo diversi shard PostgreSQL, quindi otterremo un ottimo test degli script di configurazione prima di andare in produzione.<\/p>\n<p>Entrambi i compiti sembrano ambiziosi, ma abbiamo PostgreSQL 9.6. Forse possiamo aggiornare direttamente alla 11.2?<\/p>\n<p>Decidiamo di farlo in 2 fasi: prima aggiornare la versione a 11.2, poi avviare Patroni.<\/p>\n<h3>Aggiornamento di PostgreSQL<\/h3>\n<p>Per un aggiornamento rapido della versione di PostgreSQL \u00e8 necessario utilizzare l'opzione <b>-k<\/b>, in cui vengono creati hard link sul disco e non \u00e8 necessario copiare i tuoi dati. Sugli archivi da 300-400 GB, l'aggiornamento richiede 1 secondo.<\/p>\n<p>Abbiamo molti shard, quindi l'aggiornamento deve essere fatto in modo automatico. Per questo abbiamo scritto un playbook Ansible, che esegue tutto il processo di aggiornamento per noi:<\/p>\n<pre><code class=\"plaintext\">\/usr\/lib\/postgresql\/11\/bin\/pg_upgrade \n&lt;b&gt;--link &lt;\/b&gt;\n--old-datadir=&#039;&#039; --new-datadir=&#039;&#039; \n --old-bindir=&#039;&#039;  --new-bindir=&#039;&#039; \n --old-options=&#039; -c config_file=&#039; \n --new-options=&#039; -c config_file=&#039;<\/code><\/pre>\n<p>Qui \u00e8 importante notare che prima di avviare l'aggiornamento \u00e8 necessario eseguirlo con il parametro <b>\u2014check<\/b>, per essere certi della possibilit\u00e0 dell'aggiornamento. Inoltre, il nostro script effettua la sostituzione dei file di configurazione durante l'aggiornamento. Lo script \u00e8 stato eseguito in 30 secondi, un ottimo risultato.<\/p>\n<h3>Avvio di Patroni<\/h3>\n<p>Per risolvere il secondo problema, \u00e8 sufficiente dare un'occhiata alla configurazione di Patroni. Nel repository ufficiale c'\u00e8 un esempio di configurazione con initdb, che si occupa di inizializzare un nuovo database al primo avvio di Patroni. Ma poich\u00e9 abbiamo gi\u00e0 un database pronto, abbiamo semplicemente rimosso questa sezione dalla configurazione.<\/p>\n<p>Quando abbiamo iniziato a installare Patroni su un cluster PostgreSQL gi\u00e0 esistente e a farlo partire, ci siamo imbattuti in un nuovo problema: entrambi i server si avviavano come leader. Patroni non conosce lo stato precedente del cluster e cerca di avviare entrambi i server come due cluster distinti con lo stesso nome. Per risolvere questo problema \u00e8 necessario eliminare la directory dei dati sullo slave:<\/p>\n<pre><code class=\"plaintext\">rm -rf \/var\/lib\/postgresql\/<\/code><\/pre>\n<p><b>Questo deve essere fatto solo sullo slave!<\/b><\/p>\n<p>Collegandosi a una replica pulita, Patroni esegue il basebackup del leader e lo ripristina sulla replica, poi recupera lo stato attuale tramite i log wal.<\/p>\n<p>Un'altra difficolt\u00e0 con cui ci siamo confrontati \u00e8 che tutti i cluster PostgreSQL si chiamano main per default. Quando ogni cluster non sa nulla dell'altro, va bene. Ma quando si vuole utilizzare Patroni, tutti i cluster devono avere un nome unico. La soluzione \u00e8 cambiare il nome del cluster nella configurazione di PostgreSQL.<\/p>\n<h3>Test di carico<\/h3>\n<p>Abbiamo eseguito un test che simula il lavoro degli utenti sui tabelloni. Quando il carico ha raggiunto la nostra media giornaliera, abbiamo ripetuto esattamente lo stesso test, spegnendo un'istanza con leader PostgreSQL. Il failover automatico ha funzionato come ci aspettavamo: Patroni ha cambiato il leader, Sconsul-template ha aggiornato la configurazione di PgBouncer e ha inviato un comando di ricarica. Dai nostri grafici in Grafana si vedeva che c'erano ritardi di 20-30 secondi e un piccolo numero di errori dai server, relativi alla connessione al database. Questa \u00e8 una situazione normale, questi valori sono accettabili per il nostro failover e sono decisamente migliori del downtime del servizio.<\/p>\n<h2>Output di Patroni in produzione<\/h2>\n<p>Alla fine abbiamo ottenuto il seguente piano:<\/p>\n<ul>\n<li>Deployment di Sconsul-template sui server PgBouncer e avvio;<\/li>\n<li>Aggiornamento di PostgreSQL alla versione 11.2;<\/li>\n<li>Cambio del nome del cluster;<\/li>\n<li>Avvio del cluster Patroni.<\/li>\n<\/ul>\n<p>In questo modo, il nostro schema consente di eseguire il primo punto praticamente in qualsiasi momento, possiamo togliere ciascun PgBouncer dal lavoro uno alla volta e procedere con il deployment e l'avvio di consul-template. Cos\u00ec abbiamo fatto.<\/p>\n<p>Per un rapido rollout abbiamo utilizzato Ansible, poich\u00e9 tutti i playbook erano gi\u00e0 stati verificati nell'ambiente di test, e il tempo necessario per eseguire l'intero scenario variava da 1,5 a 2 minuti per ogni shard. Potevamo effettuare il rollout uno alla volta su ciascun shard senza fermare il nostro servizio, ma sarebbe stato necessario spegnere ogni PostgreSQL per alcuni minuti. In questo caso, gli utenti i cui dati risiedono su quello shard non avrebbero potuto lavorare a pieno ritmo durante quel tempo, il che per noi \u00e8 inaccettabile.<\/p>\n<p>L'uscita da questa situazione \u00e8 stata una manutenzione programmata, che si svolge ogni 3 mesi. Questa \u00e8 una finestra per lavori pianificati, durante la quale spegniamo completamente il nostro servizio e aggiorniamo le istanze del database. Una settimana prima della prossima finestra, abbiamo deciso di aspettare e prepararci ulteriormente. Durante il tempo di attesa, abbiamo fatto delle precauzioni: per ogni shard di PostgreSQL abbiamo attivato una replica di backup nel caso di un fallimento per mantenere i dati pi\u00f9 recenti, e abbiamo aggiunto una nuova istanza per ciascun shard, che dovrebbe diventare una nuova replica nel cluster Patroni, per evitare di eseguire il comando per eliminare i dati. Tutto ci\u00f2 ha aiutato a ridurre al minimo il rischio di errore.<br \/>\n<img decoding=\"async\" style=\"display: block; margin: 0 auto;\" src=\"\/wp-content\/uploads\/2019\/06\/29e5c5f50dbfebfe5665fa0aeb009728.png\" alt=\"Cluster PostgreSQL a prova di guasto + Patroni. Esperienza di implementazione.\" \/><\/p>\n<p>Abbiamo riavviato il nostro servizio, tutto ha funzionato come previsto, gli utenti hanno continuato a lavorare, ma nei grafici abbiamo notato un carico anomalo sui server Consul.<br \/>\n<img decoding=\"async\" style=\"display: block; margin: 0 auto;\" src=\"\/wp-content\/uploads\/2019\/06\/af0eef9ac235706c0aac62d2e1ee179d.png\" alt=\"Cluster PostgreSQL a prova di guasto + Patroni. Esperienza di implementazione.\" \/><\/p>\n<p>Perch\u00e9 non lo abbiamo visto nell'ambiente di test? Questo problema illustra molto bene l'importanza di seguire il principio di Infrastructure as Code e di migliorare tutta l'infrastruttura, a partire dagli ambienti di test fino alla produzione. Altrimenti, \u00e8 molto facile imbattersi in un problema come quello che abbiamo avuto. Cosa \u00e8 successo? Consul \u00e8 apparso prima in produzione e poi negli ambienti di test; di conseguenza, negli ambienti di test la versione di Consul era pi\u00f9 alta rispetto a quella di produzione. Proprio in uno dei rilasci \u00e8 stata risolta una perdita di CPU durante l'uso di consul-template. Pertanto, abbiamo semplicemente aggiornato Consul, risolvendo in questo modo il problema.<\/p>\n<h3>Riavvia il cluster Patroni<\/h3>\n<p>Tuttavia, abbiamo riscontrato un nuovo problema di cui non sospettavamo affatto. Durante l'aggiornamento di Consul, semplicemente rimuoviamo il nodo Consul dal cluster con il comando consul leave \u2192 Patroni si connette a un altro server Consul \u2192 tutto funziona. Ma quando siamo arrivati all'ultima istanza del cluster Consul e gli abbiamo inviato il comando consul leave, tutti i cluster Patroni si sono semplicemente riavviati, e nei log abbiamo visto il seguente errore:<\/p>\n<pre><code class=\"plaintext\">ERRORE: get_cluster\nTraceback (chiamata pi&ugrave; recente per ultima):\n...\nRetryFailedError: &#039;Superato il termine di riprova&#039;\nERRORE: Errore nella comunicazione con DCS\n&lt;b&gt;LOG: il sistema database &egrave; stato arrestato&lt;\/b&gt;<\/code><\/pre>\n<p>Il cluster Patroni non \u00e8 riuscito a ricevere informazioni sul proprio cluster e si \u00e8 riavviato.<\/p>\n<p>Per cercare una soluzione ci siamo rivolti agli autori di Patroni tramite un issue su GitHub. Hanno suggerito miglioramenti ai nostri file di configurazione:<\/p>\n<pre><code class=\"python\">consul:\n consul.checks: []\nbootstrap:\n dcs:\n   retry_timeout: 8<\/code><\/pre>\n<p>Siamo riusciti a riprodurre il problema nell'ambiente di test e abbiamo testato l\u00ec questi parametri, ma, sfortunatamente, non hanno funzionato.<\/p>\n<p>Il problema rimane irrisolto. Stiamo pianificando di provare le seguenti opzioni per la soluzione:<\/p>\n<ul>\n<li>Utilizzare il Sconsul-agent su ciascun'istanza del cluster Patroni;<\/li>\n<li>Correggere il problema nel codice.<\/li>\n<\/ul>\n<p>Ci \u00e8 chiaro il luogo di origine dell'errore: probabilmente, il problema \u00e8 dovuto all'uso del timeout predefinito, che non viene ridefinito tramite il file di configurazione. Quando l'ultimo server Sconsul viene rimossa dal cluster, l'intero cluster Sconsul va in stallo per pi\u00f9 di un secondo, per questo Patroni non riesce a ottenere lo stato del cluster e riavvia completamente l'intero cluster.<\/p>\n<p>Fortunatamente, non abbiamo riscontrato ulteriori errori.<\/p>\n<h2>Risultati dell'utilizzo di Patroni<\/h2>\n<p>Dopo aver avviato con successo Patroni, abbiamo aggiunto una replica aggiuntiva in ogni cluster. Ora in ogni cluster esiste una sorta di quorum: un leader e due repliche, per coprire eventuali situazioni di split-brain durante il failover.<br \/>\n<img decoding=\"async\" style=\"display: block; margin: 0 auto;\" src=\"\/wp-content\/uploads\/2019\/06\/3bb4c0495fea274b04edd2e99b59eacb.png\" alt=\"Cluster PostgreSQL a prova di guasto + Patroni. Esperienza di implementazione.\" \/><\/p>\n<p>In produzione, Patroni \u00e8 attivo da pi\u00f9 di tre mesi. Durante questo periodo, \u00e8 gi\u00e0 riuscito ad aiutarci. Recentemente, in AWS, il leader di uno dei cluster \u00e8 andato offline, il failover automatico \u00e8 scattato e gli utenti hanno continuato a lavorare. Patroni ha svolto il suo compito principale.<\/p>\n<p><b>Un breve riepilogo sull'utilizzo di Patroni:<\/b><\/p>\n<ul>\n<li>Facilit\u00e0 di modifica della configurazione. \u00c8 sufficiente modificare la configurazione su un'istanza affinch\u00e9 venga applicata a tutto il cluster. Se \u00e8 necessario un riavvio per applicare la nuova configurazione, Patroni lo notificher\u00e0. Patroni pu\u00f2 riavviare l'intero cluster con un solo comando, il che \u00e8 molto comodo.<\/li>\n<li>Il failover automatico funziona ed \u00e8 gi\u00e0 riuscito ad aiutarci.<\/li>\n<li>Aggiornamento di PostgreSQL senza downtime dell'applicazione. \u00c8 necessario prima aggiornare le repliche alla nuova versione, quindi cambiare il leader nel cluster Patroni e aggiornare il vecchio leader. Durante questo processo si esegue il necessario test del failover automatico.<\/li>\n<\/ul>\n<p>Fonte: <a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/miro\/blog\/457326\/\">habr.com<\/a><\/p>","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"excerpt":{"rendered":"<p>\u0412 \u0441\u0442\u0430\u0442\u044c\u0435 \u044f \u0440\u0430\u0441\u0441\u043a\u0430\u0436\u0443, \u043a\u0430\u043a \u043c\u044b \u043f\u043e\u0434\u043e\u0448\u043b\u0438 \u043a \u0432\u043e\u043f\u0440\u043e\u0441\u0443 \u043e\u0442\u043a\u0430\u0437\u043e\u0443\u0441\u0442\u043e\u0439\u0447\u0438\u0432\u043e\u0441\u0442\u0438 PostgreSQL, \u043f\u043e\u0447\u0435\u043c\u0443 \u044d\u0442\u043e \u0441\u0442\u0430\u043b\u043e \u0434\u043b\u044f \u043d\u0430\u0441 \u0432\u0430\u0436\u043d\u043e \u0438 \u0447\u0442\u043e \u0432 \u0438\u0442\u043e\u0433\u0435 \u043f\u043e\u043b\u0443\u0447\u0438\u043b\u043e\u0441\u044c. \u0423 \u043d\u0430\u0441 \u0432\u044b\u0441\u043e\u043a\u043e\u043d\u0430\u0433\u0440\u0443\u0436\u0435\u043d\u043d\u044b\u0439 \u0441\u0435\u0440\u0432\u0438\u0441: 2,5 \u043c\u043b\u043d \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 \u043f\u043e \u0432\u0441\u0435\u043c\u0443 \u043c\u0438\u0440\u0443, 50\u041a+ \u0430\u043a\u0442\u0438\u0432\u043d\u044b\u0445 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 \u043a\u0430\u0436\u0434\u044b\u0439 \u0434\u0435\u043d\u044c. \u0421\u0435\u0440\u0432\u0435\u0440\u0430 \u043d\u0430\u0445\u043e\u0434\u044f\u0442\u0441\u044f \u0432 Amazone \u0432 \u043e\u0434\u043d\u043e\u043c \u0440\u0435\u0433\u0438\u043e\u043d\u0435 \u0418\u0440\u043b\u0430\u043d\u0434\u0438\u0438: \u0432 \u0440\u0430\u0431\u043e\u0442\u0435 \u043f\u043e\u0441\u0442\u043e\u044f\u043d\u043d\u043e 100+ \u0440\u0430\u0437\u043b\u0438\u0447\u043d\u044b\u0445 \u0441\u0435\u0440\u0432\u0435\u0440\u043e\u0432, \u0438\u0437 \u043d\u0438\u0445 \u043f\u043e\u0447\u0442\u0438 50 [&hellip;]<\/p>\n","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"author":1,"featured_media":26749,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[688],"tags":[],"class_list":["post-35706","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-administrirovanie"],"aioseo_notices":[],"aioseo_head":"\n\t\t<!-- All in One SEO 5.0.1.1 - aioseo.com -->\n\t<meta name=\"description\" content=\"\u0412 \u0441\u0442\u0430\u0442\u044c\u0435 \u044f \u0440\u0430\u0441\u0441\u043a\u0430\u0436\u0443, \u043a\u0430\u043a \u043c\u044b \u043f\u043e\u0434\u043e\u0448\u043b\u0438 \u043a \u0432\u043e\u043f\u0440\u043e\u0441\u0443 \u043e\u0442\u043a\u0430\u0437\u043e\u0443\u0441\u0442\u043e\u0439\u0447\u0438\u0432\u043e\u0441\u0442\u0438 PostgreSQL, \u043f\u043e\u0447\u0435\u043c\u0443 \u044d\u0442\u043e \u0441\u0442\u0430\u043b\u043e \u0434\u043b\u044f \u043d\u0430\u0441 \u0432\u0430\u0436\u043d\u043e \u0438 \u0447\u0442\u043e \u0432 \u0438\u0442\u043e\u0433\u0435 \u043f\u043e\u043b\u0443\u0447\u0438\u043b\u043e\u0441\u044c.\" \/>\n\t<meta name=\"robots\" content=\"max-image-preview:large\" \/>\n\t<meta name=\"author\" content=\"Yuri Gagarin\"\/>\n\t<link rel=\"canonical\" href=\"https:\/\/prohoster.info\/it\/blog\/administrirovanie\/otkazoustojchivyj-klaster-postgresql-patroni-opyt-vnedreniya\" \/>\n\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 5.0.1.1\" \/>\n\t\t<meta property=\"og:locale\" content=\"it_IT\" \/>\n\t\t<meta property=\"og:site_name\" content=\"ProHoster | \u041a\u0443\u043f\u0438\u0442\u044c \u043d\u0430\u0434\u0435\u0436\u043d\u044b\u0439 \u0445\u043e\u0441\u0442\u0438\u043d\u0433 \u0434\u043b\u044f \u0441\u0430\u0439\u0442\u043e\u0432 \u0441 \u0437\u0430\u0449\u0438\u0442\u043e\u0439 \u043e\u0442 DDoS, VPS VDS \u0441\u0435\u0440\u0432\u0435\u0440\u044b\" \/>\n\t\t<meta property=\"og:type\" content=\"article\" \/>\n\t\t<meta property=\"og:title\" content=\"\ud83e\udd47\u041e\u0442\u043a\u0430\u0437\u043e\u0443\u0441\u0442\u043e\u0439\u0447\u0438\u0432\u044b\u0439 \u043a\u043b\u0430\u0441\u0442\u0435\u0440 PostgreSQL + Patroni. \u041e\u043f\u044b\u0442 \u0432\u043d\u0435\u0434\u0440\u0435\u043d\u0438\u044f | ProHoster\" \/>\n\t\t<meta property=\"og:description\" content=\"\u0412 \u0441\u0442\u0430\u0442\u044c\u0435 \u044f \u0440\u0430\u0441\u0441\u043a\u0430\u0436\u0443, \u043a\u0430\u043a \u043c\u044b \u043f\u043e\u0434\u043e\u0448\u043b\u0438 \u043a \u0432\u043e\u043f\u0440\u043e\u0441\u0443 \u043e\u0442\u043a\u0430\u0437\u043e\u0443\u0441\u0442\u043e\u0439\u0447\u0438\u0432\u043e\u0441\u0442\u0438 PostgreSQL, \u043f\u043e\u0447\u0435\u043c\u0443 \u044d\u0442\u043e \u0441\u0442\u0430\u043b\u043e \u0434\u043b\u044f \u043d\u0430\u0441 \u0432\u0430\u0436\u043d\u043e \u0438 \u0447\u0442\u043e \u0432 \u0438\u0442\u043e\u0433\u0435 \u043f\u043e\u043b\u0443\u0447\u0438\u043b\u043e\u0441\u044c.\" \/>\n\t\t<meta property=\"og:url\" content=\"https:\/\/prohoster.info\/it\/blog\/administrirovanie\/otkazoustojchivyj-klaster-postgresql-patroni-opyt-vnedreniya\" \/>\n\t\t<meta property=\"og:image\" content=\"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg\" \/>\n\t\t<meta property=\"og:image:secure_url\" content=\"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg\" \/>\n\t\t<meta property=\"og:image:width\" content=\"350\" \/>\n\t\t<meta property=\"og:image:height\" content=\"350\" \/>\n\t\t<meta property=\"article:published_time\" content=\"2019-10-31T19:05:50+00:00\" \/>\n\t\t<meta property=\"article:modified_time\" content=\"2026-05-18T18:58:46+00:00\" \/>\n\t\t<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/prohoster\" \/>\n\t\t<meta property=\"article:author\" content=\"https:\/\/www.facebook.com\/prohoster\" \/>\n\t\t<!-- All in One SEO -->\n\n","aioseo_head_json":{"title":"\ud83e\udd47Cluster PostgreSQL resistente agli errori + Patroni. Esperienza di implementazione | ProHoster","description":"Nell'articolo parler\u00f2 di come ci siamo avvicinati al problema della resilienza di PostgreSQL, perch\u00e9 \u00e8 diventato importante per noi e quale \u00e8 stato il risultato finale.","canonical_url":"https:\/\/prohoster.info\/it\/blog\/administrirovanie\/otkazoustojchivyj-klaster-postgresql-patroni-opyt-vnedreniya","robots":"max-image-preview:large","keywords":"","webmasterTools":{"miscellaneous":""},"schema":null,"og:locale":"it_IT","og:site_name":"ProHoster | \u041a\u0443\u043f\u0438\u0442\u044c \u043d\u0430\u0434\u0435\u0436\u043d\u044b\u0439 \u0445\u043e\u0441\u0442\u0438\u043d\u0433 \u0434\u043b\u044f \u0441\u0430\u0439\u0442\u043e\u0432 \u0441 \u0437\u0430\u0449\u0438\u0442\u043e\u0439 \u043e\u0442 DDoS, VPS VDS \u0441\u0435\u0440\u0432\u0435\u0440\u044b","og:type":"article","og:title":"\ud83e\udd47\u041e\u0442\u043a\u0430\u0437\u043e\u0443\u0441\u0442\u043e\u0439\u0447\u0438\u0432\u044b\u0439 \u043a\u043b\u0430\u0441\u0442\u0435\u0440 PostgreSQL + Patroni. \u041e\u043f\u044b\u0442 \u0432\u043d\u0435\u0434\u0440\u0435\u043d\u0438\u044f | ProHoster","og:description":"\u0412 \u0441\u0442\u0430\u0442\u044c\u0435 \u044f \u0440\u0430\u0441\u0441\u043a\u0430\u0436\u0443, \u043a\u0430\u043a \u043c\u044b \u043f\u043e\u0434\u043e\u0448\u043b\u0438 \u043a \u0432\u043e\u043f\u0440\u043e\u0441\u0443 \u043e\u0442\u043a\u0430\u0437\u043e\u0443\u0441\u0442\u043e\u0439\u0447\u0438\u0432\u043e\u0441\u0442\u0438 PostgreSQL, \u043f\u043e\u0447\u0435\u043c\u0443 \u044d\u0442\u043e \u0441\u0442\u0430\u043b\u043e \u0434\u043b\u044f \u043d\u0430\u0441 \u0432\u0430\u0436\u043d\u043e \u0438 \u0447\u0442\u043e \u0432 \u0438\u0442\u043e\u0433\u0435 \u043f\u043e\u043b\u0443\u0447\u0438\u043b\u043e\u0441\u044c.","og:url":"https:\/\/prohoster.info\/it\/blog\/administrirovanie\/otkazoustojchivyj-klaster-postgresql-patroni-opyt-vnedreniya","og:image":"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg","og:image:secure_url":"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg","og:image:width":350,"og:image:height":350,"article:published_time":"2019-10-31T19:05:50+00:00","article:modified_time":"2026-05-18T18:58:46+00:00","article:publisher":"https:\/\/www.facebook.com\/prohoster","article:author":"https:\/\/www.facebook.com\/prohoster"},"aioseo_meta_data":{"post_id":"35706","title":null,"description":null,"keywords":null,"keyphrases":null,"primary_term":null,"canonical_url":null,"og_title":null,"og_description":null,"og_object_type":"default","og_image_type":"default","og_image_url":null,"og_image_width":null,"og_image_height":null,"og_image_custom_url":null,"og_image_custom_fields":null,"og_video":null,"og_custom_url":null,"og_article_section":null,"og_article_tags":null,"twitter_use_og":false,"twitter_card":"default","twitter_image_type":"default","twitter_image_url":null,"twitter_image_custom_url":null,"twitter_image_custom_fields":null,"twitter_title":null,"twitter_description":null,"schema":{"blockGraphs":[],"customGraphs":[],"default":{"data":{"Article":[],"Course":[],"Dataset":[],"FAQPage":[],"Movie":[],"Person":[],"Product":[],"ProductReview":[],"Car":[],"Recipe":[],"Service":[],"SoftwareApplication":[],"WebPage":[]},"graphName":"","isEnabled":true},"graphs":[]},"schema_type":null,"schema_type_options":null,"pillar_content":false,"robots_default":true,"robots_noindex":false,"robots_noarchive":false,"robots_nosnippet":false,"robots_nofollow":false,"robots_noimageindex":false,"robots_noodp":false,"robots_notranslate":false,"robots_max_snippet":null,"robots_max_videopreview":null,"robots_max_imagepreview":"large","priority":null,"frequency":null,"local_seo":null,"seo_analyzer_scan_date":"2026-01-22 00:26:19","breadcrumb_settings":null,"limit_modified_date":false,"reviewed_by":null,"ai":null,"created":"2021-02-28 13:51:06","updated":"2026-01-22 00:26:19","focus_keyword":null,"additional_keywords":null,"truseo_locale":null},"gt_translate_keys":[{"key":"link","format":"url"}],"_links":{"self":[{"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/posts\/35706","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/comments?post=35706"}],"version-history":[{"count":1,"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/posts\/35706\/revisions"}],"predecessor-version":[{"id":172654,"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/posts\/35706\/revisions\/172654"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/media\/26749"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/media?parent=35706"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/categories?post=35706"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/tags?post=35706"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}