{"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\/pt\/blog\/administrirovanie\/otkazoustojchivyj-klaster-postgresql-patroni-opyt-vnedreniya","title":{"rendered":"PostgreSQL High Availability Cluster + Patroni. Implementation Experience","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p>Neste artigo, vou explicar como abordamos a quest\u00e3o da disponibilidade do PostgreSQL, por que isso se tornou importante para n\u00f3s e o que conseguimos no final.<\/p>\n<p>Temos um servi\u00e7o de alta demanda: 2,5 milh\u00f5es de usu\u00e1rios em todo o mundo, mais de 50 mil usu\u00e1rios ativos todos os dias. Os servidores est\u00e3o na Amazon em uma \u00fanica regi\u00e3o da Irlanda: operamos continuamente mais de 100 servidores diferentes, dos quais quase 50 t\u00eam bancos de dados.<\/p>\n<p>Todo o backend \u00e9 um grande aplicativo monol\u00edtico stateful em Java, que mant\u00e9m uma conex\u00e3o websocket permanente com o cliente. Com v\u00e1rios usu\u00e1rios trabalhando simultaneamente em um quadro, todos veem as altera\u00e7\u00f5es em tempo real, pois registramos cada mudan\u00e7a em nosso banco de dados. Temos aproximadamente 10 mil requisi\u00e7\u00f5es por segundo em nossos bancos. Em picos de carga, escrevemos de 80 a 100 mil requisi\u00e7\u00f5es por segundo no Redis.<br \/>\n<img decoding=\"async\" style=\"display: block; margin: 0 auto;\" src=\"\/wp-content\/uploads\/2019\/06\/443f85815b0560fade7db5b639942935.png\" alt=\"PostgreSQL High Availability Cluster + Patroni. Implementation Experience\" \/><br \/>\n<a rel=\"nofollow\" name=\"habracut\"><\/a><\/p>\n<h2>Por que mudamos do Redis para o PostgreSQL<\/h2>\n<p>Inicialmente, nosso servi\u00e7o operava com o Redis, um armazenamento chave-valor que mant\u00e9m todos os dados na mem\u00f3ria. <a href=\"https:\/\/prohoster.info\/pt\/server\/\">servidor<\/a>.<\/p>\n<p>Vantagens do Redis:<\/p>\n<ol>\n<li>Alta velocidade de resposta, j\u00e1 que tudo est\u00e1 armazenado na mem\u00f3ria;<\/li>\n<li>Facilidade de backup e replica\u00e7\u00e3o.<\/li>\n<\/ol>\n<p>Desvantagens do Redis para n\u00f3s:<\/p>\n<ol>\n<li>N\u00e3o h\u00e1 transa\u00e7\u00f5es reais. Tentamos simular isso no n\u00edvel de nosso aplicativo. Infelizmente, isso nem sempre funcionou bem e exigia a escrita de um c\u00f3digo muito complicado.<\/li>\n<li>O volume de dados \u00e9 limitado pela quantidade de mem\u00f3ria. Com o aumento da quantidade de dados, a mem\u00f3ria cresce e, eventualmente, nos deparamos com as caracter\u00edsticas da inst\u00e2ncia escolhida, o que na AWS requer a parada de nosso servi\u00e7o para alterar o tipo de inst\u00e2ncia.<\/li>\n<li>\u00c9 necess\u00e1rio manter constantemente um n\u00edvel baixo de lat\u00eancia, j\u00e1 que temos um n\u00famero muito alto de requisi\u00e7\u00f5es. O n\u00edvel de lat\u00eancia ideal para n\u00f3s \u00e9 de 17 a 20 ms. Com lat\u00eancias de 30 a 40 ms, recebemos respostas longas \u00e0s requisi\u00e7\u00f5es de nosso aplicativo e degrada\u00e7\u00e3o do servi\u00e7o. Infelizmente, isso ocorreu em setembro de 2018, quando uma das inst\u00e2ncias com Redis inexplicavelmente apresentou lat\u00eancia duas vezes maior que o normal. Para resolver o problema, paramos o servi\u00e7o no meio do dia para manuten\u00e7\u00e3o n\u00e3o planejada e substitu\u00edmos a inst\u00e2ncia problem\u00e1tica do Redis.<\/li>\n<li>\u00c9 f\u00e1cil obter inconsist\u00eancias nos dados mesmo com erros m\u00ednimos no c\u00f3digo e depois gastar muito tempo escrevendo c\u00f3digo para corrigir esses dados.<\/li>\n<\/ol>\n<p>Consideramos as desvantagens e entendemos que precis\u00e1vamos migrar para algo mais conveniente, com transa\u00e7\u00f5es normais e menor depend\u00eancia de lat\u00eancia. Realizamos uma pesquisa, analisamos v\u00e1rias op\u00e7\u00f5es e escolhemos o PostgreSQL.<\/p>\n<p>Estamos migrando para o novo banco de dados h\u00e1 1,5 ano e apenas transportamos uma pequena parte dos dados, portanto, atualmente trabalhamos simultaneamente com Redis e PostgreSQL. Mais detalhes sobre as etapas da migra\u00e7\u00e3o e a troca de dados entre os bancos de dados est\u00e3o descritos na <a href=\"https:\/\/habr.com\/ru\/company\/miro\/blog\/437826\/\" rel=\"nofollow\">artigo do meu colega<\/a>.<\/p>\n<p>Quando come\u00e7amos a migra\u00e7\u00e3o, nosso aplicativo se conectava diretamente ao banco de dados, acessando o mestre do Redis e do PostgreSQL. O cluster PostgreSQL consistia em um mestre e uma r\u00e9plica com replica\u00e7\u00e3o ass\u00edncrona. Assim era o esquema de trabalho com os bancos:<br \/>\n<img decoding=\"async\" style=\"display: block; margin: 0 auto;\" src=\"\/wp-content\/uploads\/2019\/06\/674dd77de4c8b48a946c9f64c0b2fdd1.png\" alt=\"PostgreSQL High Availability Cluster + Patroni. Implementation Experience\" \/><\/p>\n<h2>Implementa\u00e7\u00e3o do PgBouncer<\/h2>\n<p>Enquanto migr\u00e1vamos, o produto tamb\u00e9m estava se desenvolvendo: o n\u00famero de usu\u00e1rios e a quantidade de servidores trabalhando com PostgreSQL aumentavam, e come\u00e7amos a ficar sem conex\u00f5es. O PostgreSQL cria um processo separado para cada conex\u00e3o e consome recursos. O aumento do n\u00famero de conex\u00f5es pode ser feito at\u00e9 certo ponto; caso contr\u00e1rio, h\u00e1 risco de se obter um desempenho sub\u00f3timo do banco de dados. A solu\u00e7\u00e3o ideal nessa situa\u00e7\u00e3o seria escolher um gerenciador de conex\u00f5es que atuasse na frente do banco de dados.<\/p>\n<p>T\u00ednhamos duas op\u00e7\u00f5es para o gerenciador de conex\u00f5es: Pgpool e PgBouncer. Mas o primeiro n\u00e3o suporta o modo de transa\u00e7\u00f5es com o banco, ent\u00e3o optamos pelo PgBouncer.<\/p>\n<p>Configuramos o seguinte esquema de trabalho: nosso aplicativo se conecta a um PgBouncer, atr\u00e1s do qual est\u00e3o os mestres do PostgreSQL, e atr\u00e1s de cada mestre h\u00e1 uma r\u00e9plica com replica\u00e7\u00e3o ass\u00edncrona.<br \/>\n<img decoding=\"async\" style=\"display: block; margin: 0 auto;\" src=\"\/wp-content\/uploads\/2019\/06\/dabcb7f6006520c4fec85fb925e8975e.png\" alt=\"PostgreSQL High Availability Cluster + Patroni. Implementation Experience\" \/><\/p>\n<p>No entanto, n\u00e3o pod\u00edamos armazenar todo o volume de dados no PostgreSQL e a velocidade de acesso ao banco era importante para n\u00f3s, ent\u00e3o come\u00e7amos a fragmentar o PostgreSQL no n\u00edvel da aplica\u00e7\u00e3o. O esquema descrito acima \u00e9 relativamente conveniente para isso: ao adicionar uma nova fragmenta\u00e7\u00e3o, \u00e9 suficiente atualizar a configura\u00e7\u00e3o do PgBouncer e o aplicativo pode come\u00e7ar a trabalhar imediatamente com a nova fragmenta\u00e7\u00e3o.<\/p>\n<h3>Alta disponibilidade do PgBouncer<\/h3>\n<p>Este esquema funcionou at\u00e9 o momento em que a \u00fanica inst\u00e2ncia do PgBouncer falhou. Estamos na AWS, onde todas as inst\u00e2ncias s\u00e3o executadas em hardware que falha periodicamente. Nesses casos, a inst\u00e2ncia simplesmente \u00e9 movida para um novo hardware e volta a operar. O mesmo aconteceu com o PgBouncer, no entanto, ele se tornou inacess\u00edvel. O resultado dessa falha foi a indisponibilidade do nosso servi\u00e7o por 25 minutos. A AWS recomenda o uso de redund\u00e2ncia do lado do usu\u00e1rio para essas situa\u00e7\u00f5es, o que n\u00e3o t\u00ednhamos implementado at\u00e9 aquele momento.<\/p>\n<p>Depois disso, come\u00e7amos a considerar seriamente a resili\u00eancia do PgBouncer e dos clusters PostgreSQL, pois tal situa\u00e7\u00e3o poderia se repetir com qualquer inst\u00e2ncia em nossa conta da AWS.<\/p>\n<p>Constru\u00edmos a arquitetura de resili\u00eancia do PgBouncer da seguinte maneira: todos os servidores de aplica\u00e7\u00e3o se conectam ao Network Load Balancer, que tem dois PgBouncer atr\u00e1s dele. Cada PgBouncer monitora os mesmos master PostgreSQL de cada shard. Caso a situa\u00e7\u00e3o de falha da inst\u00e2ncia da AWS se repita, todo o tr\u00e1fego \u00e9 redirecionado atrav\u00e9s de outro PgBouncer. A resili\u00eancia do Network Load Balancer \u00e9 garantida pela AWS.<\/p>\n<p>Esse esquema permite a adi\u00e7\u00e3o sem problemas de novos servidores PgBouncer.<br \/>\n<img decoding=\"async\" style=\"display: block; margin: 0 auto;\" src=\"\/wp-content\/uploads\/2019\/06\/2c1f1e7d9f7be4fdeb43858520d55e36.png\" alt=\"PostgreSQL High Availability Cluster + Patroni. Implementation Experience\" \/><\/p>\n<h2>Cria\u00e7\u00e3o de um cluster PostgreSQL resiliente<\/h2>\n<p>Ao resolver essa tarefa, consideramos diferentes op\u00e7\u00f5es: failover personalizado, repmgr, AWS RDS, Patroni.<\/p>\n<h3>Scripts personalizados<\/h3>\n<p>Podem monitorar a opera\u00e7\u00e3o do master e, em caso de falha, promover a r\u00e9plica a master e atualizar a configura\u00e7\u00e3o do PgBouncer.<\/p>\n<p>Os pontos positivos dessa abordagem est\u00e3o na m\u00e1xima simplicidade, j\u00e1 que voc\u00ea mesmo escreve os scripts e entende exatamente como eles funcionam.<\/p>\n<p>Pontos negativos:<\/p>\n<ul>\n<li>O master pode n\u00e3o ter falhado, ao inv\u00e9s disso, uma falha de rede pode ter ocorrido. O failover, sem saber disso, promover\u00e1 a r\u00e9plica a master, enquanto o antigo master continuar\u00e1 funcionando. Como resultado, teremos dois servidores atuando como master e n\u00e3o saberemos em qual deles est\u00e3o os dados mais recentes. Essa situa\u00e7\u00e3o \u00e9 chamada de split-brain;<\/li>\n<li>Ficamos sem r\u00e9plica. Em nossa configura\u00e7\u00e3o, h\u00e1 um master e uma r\u00e9plica, e ap\u00f3s a troca, a r\u00e9plica \u00e9 promovida a master e n\u00e3o temos mais r\u00e9plicas, ent\u00e3o \u00e9 necess\u00e1rio adicionar uma nova r\u00e9plica manualmente;<\/li>\n<li>\u00c9 necess\u00e1rio monitoramento adicional da opera\u00e7\u00e3o do failover, e temos 12 shards PostgreSQL, o que significa que devemos monitorar 12 clusters. Ao aumentar o n\u00famero de shards, n\u00e3o devemos esquecer de atualizar o failover.<\/li>\n<\/ul>\n<p>Um failover personalizado parece muito complicado e exige suporte n\u00e3o trivial. Com um cluster PostgreSQL, esta ser\u00e1 a op\u00e7\u00e3o mais simples, mas n\u00e3o escal\u00e1vel, portanto, n\u00e3o \u00e9 adequada para n\u00f3s.<\/p>\n<h3>Repmgr<\/h3>\n<p>Gerenciador de Replica\u00e7\u00e3o para clusters PostgreSQL, que pode gerenciar o funcionamento do cluster PostgreSQL. Por\u00e9m, n\u00e3o possui failover autom\u00e1tico 'pronto para uso', portanto, ser\u00e1 necess\u00e1rio escrever uma 'camada' personalizada em cima da solu\u00e7\u00e3o existente. Isso pode resultar em algo at\u00e9 mais complicado do que scripts personalizados, por isso nem sequer tentamos o Repmgr.<\/p>\n<h3>AWS RDS<\/h3>\n<p>Suporta tudo o que precisamos, realiza backups e oferece suporte a pool de conex\u00f5es. Possui troca autom\u00e1tica: quando o mestre falha, a r\u00e9plica se torna o novo mestre, e a AWS altera o registro DNS para o novo mestre, enquanto as r\u00e9plicas podem estar em diferentes AZs.<\/p>\n<p>Entre os pontos negativos, est\u00e1 a falta de configura\u00e7\u00f5es detalhadas. Um exemplo de configura\u00e7\u00f5es detalhadas: nos nossos inst\u00e2ncias, h\u00e1 restri\u00e7\u00f5es para conex\u00f5es tcp, o que, infelizmente, n\u00e3o pode ser feito no 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>Al\u00e9m disso, o pre\u00e7o do AWS RDS \u00e9 quase o dobro do pre\u00e7o normal da inst\u00e2ncia, que foi a principal raz\u00e3o para desistirmos dessa solu\u00e7\u00e3o.<\/p>\n<h3>Patroni<\/h3>\n<p>Este \u00e9 um template em python para gerenciar PostgreSQL com boa documenta\u00e7\u00e3o, failover autom\u00e1tico e c\u00f3digo fonte no github.<\/p>\n<p>Vantagens do Patroni:<\/p>\n<ul>\n<li>Cada par\u00e2metro de configura\u00e7\u00e3o \u00e9 detalhado, e \u00e9 f\u00e1cil entender como funciona;<\/li>\n<li>O failover autom\u00e1tico funciona pronto para uso;<\/li>\n<li>Escrito em python, e como n\u00f3s mesmos escrevemos muito em python, ser\u00e1 mais f\u00e1cil lidarmos com problemas e at\u00e9 mesmo ajudar no desenvolvimento do projeto;<\/li>\n<li>Gerencia completamente o PostgreSQL, permitindo a altera\u00e7\u00e3o de configura\u00e7\u00f5es em todos os n\u00f3s do cluster de uma vez, e se a nova configura\u00e7\u00e3o exigir a reinicializa\u00e7\u00e3o do cluster, isso tamb\u00e9m pode ser feito com o Patroni.<\/li>\n<\/ul>\n<p>Pontos negativos:<\/p>\n<ul>\n<li>A documenta\u00e7\u00e3o n\u00e3o deixa claro como trabalhar corretamente com o PgBouncer. Embora isso seja dif\u00edcil de chamar de desvantagem, j\u00e1 que a tarefa do Patroni \u00e9 gerenciar o PostgreSQL, e como as conex\u00f5es ser\u00e3o direcionadas ao Patroni \u00e9 um problema nosso;<\/li>\n<li>Existem poucos exemplos de implementa\u00e7\u00e3o do Patroni em grandes volumes, mas muitos exemplos de implanta\u00e7\u00e3o desde o in\u00edcio.<\/li>\n<\/ul>\n<p>Como resultado, escolhemos o Patroni para criar um cluster com alta disponibilidade.<\/p>\n<h2>O processo de implementa\u00e7\u00e3o do Patroni<\/h2>\n<p>Antes do Patroni, t\u00ednhamos 12 shards do PostgreSQL na configura\u00e7\u00e3o de um mestre e uma r\u00e9plica com replica\u00e7\u00e3o ass\u00edncrona. Os servidores de aplica\u00e7\u00e3o se conectavam aos bancos de dados atrav\u00e9s de um Network Load Balancer, que tinha dois inst\u00e2ncias com PgBouncer, seguidos por todos os servidores PostgreSQL.<br \/>\n<img decoding=\"async\" style=\"display: block; margin: 0 auto;\" src=\"\/wp-content\/uploads\/2019\/06\/8e5350d2466311593e11add30d5b1bdc.png\" alt=\"PostgreSQL High Availability Cluster + Patroni. Implementation Experience\" \/><\/p>\n<p>Para implementar o Patroni, precis\u00e1vamos escolher um armazenamento de configura\u00e7\u00e3o distribu\u00eddo para o cluster. O Patroni funciona com sistemas de armazenamento de configura\u00e7\u00f5es distribu\u00eddas, como etcd, Zookeeper e Consul. Temos um cluster Consul completo em produ\u00e7\u00e3o, que opera em conjunto com o Vault e n\u00e3o o utilizamos de outra forma. \u00c9 uma \u00f3tima oportunidade para come\u00e7ar a utilizar o Consul para seu prop\u00f3sito planejado.<\/p>\n<h3>Como o Patroni funciona com o Consul<\/h3>\n<p>Temos um cluster Consul que consiste em tr\u00eas n\u00f3s e um cluster Patroni, que \u00e9 composto por um l\u00edder e uma r\u00e9plica (no Patroni, o mestre \u00e9 chamado de l\u00edder do cluster, e os escravos s\u00e3o as r\u00e9plicas). Cada inst\u00e2ncia do cluster Patroni envia constantemente informa\u00e7\u00f5es sobre o estado do cluster para o Consul. Portanto, \u00e9 sempre poss\u00edvel consultar a configura\u00e7\u00e3o atual do cluster Patroni no Consul e quem \u00e9 o l\u00edder no momento.<\/p>\n<p><img decoding=\"async\" style=\"display: block; margin: 0 auto;\" src=\"\/wp-content\/uploads\/2019\/06\/3234594b363904424fdce64c9d77fca5.png\" alt=\"PostgreSQL High Availability Cluster + Patroni. Implementation Experience\" \/><\/p>\n<p>Para conectar o Patroni ao Consul, \u00e9 suficiente consultar a documenta\u00e7\u00e3o oficial, onde est\u00e1 escrito que precisamos fornecer um host no formato http ou https, dependendo de como estamos trabalhando com o Consul, e o esquema de conex\u00e3o, opcionalmente:<\/p>\n<pre><code class=\"plaintext\">host: o host:porta para o endpoint do Consul, no formato: http(s):\/\/host:porta\nscheme: (opcional) http ou https, padr\u00e3o \u00e9 http<\/code><\/pre>\n<p>Parece simples, mas aqui come\u00e7am as armadilhas. Estamos trabalhando com o Consul atrav\u00e9s de uma conex\u00e3o segura via https, e nossa configura\u00e7\u00e3o de conex\u00e3o ficar\u00e1 assim:<\/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>Mas assim n\u00e3o funciona. Ao iniciar, o Patroni n\u00e3o consegue se conectar ao Consul, porque est\u00e1 tentando usar http.<\/p>\n<p>Resolver o problema foi poss\u00edvel gra\u00e7as ao c\u00f3digo-fonte do Patroni. Felizmente, ele \u00e9 escrito em Python. Descobriu-se que o par\u00e2metro host n\u00e3o \u00e9 analisado, e o protocolo deve ser especificado no esquema. Veja como fica o bloco de configura\u00e7\u00e3o funcional para operar com o Consul em nosso caso:<\/p>\n<pre><code class=\"python\">consul:\n  host: server.production.consul:8080\n  scheme: 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>Assim, escolhemos o armazenamento para a configura\u00e7\u00e3o. Agora precisamos entender como o PgBouncer alternar\u00e1 sua configura\u00e7\u00e3o ao mudar o l\u00edder no cluster Patroni. N\u00e3o h\u00e1 resposta para essa quest\u00e3o na documenta\u00e7\u00e3o, j\u00e1 que n\u00e3o descreve a opera\u00e7\u00e3o com o PgBouncer.<\/p>\n<p>Na busca por uma solu\u00e7\u00e3o, encontramos um artigo (n\u00e3o me lembro o nome, infelizmente), onde era mencionado que o Consul-template ajudou muito na integra\u00e7\u00e3o entre PgBouncer e Patroni. Isso nos motivou a investigar o funcionamento do Consul-template.<\/p>\n<p>Descobrimos que o Consul-template monitora continuamente a configura\u00e7\u00e3o do cluster PostgreSQL no Consul. Quando o l\u00edder muda, ele atualiza a configura\u00e7\u00e3o do PgBouncer e envia o comando para recarreg\u00e1-la.<\/p>\n<p><img decoding=\"async\" style=\"display: block; margin: 0 auto;\" src=\"\/wp-content\/uploads\/2019\/06\/6cf92996a127bb6637ab81dc45fdd60a.png\" alt=\"PostgreSQL High Availability Cluster + Patroni. Implementation Experience\" \/><\/p>\n<p>Uma grande vantagem do template \u00e9 que ele \u00e9 armazenado como c\u00f3digo, portanto, ao adicionar um novo shard, basta fazer um novo commit e atualizar o template automaticamente, mantendo o princ\u00edpio de Infrastructure as Code.<\/p>\n<h3>Nova arquitetura com Patroni<\/h3>\n<p>Como resultado, obtivemos o seguinte esquema de trabalho:<br \/>\n<img decoding=\"async\" style=\"display: block; margin: 0 auto;\" src=\"\/wp-content\/uploads\/2019\/06\/bf9a9eff675a26039e3f76b16c6cd713.png\" alt=\"PostgreSQL High Availability Cluster + Patroni. Implementation Experience\" \/><\/p>\n<p>Todos os servidores da aplica\u00e7\u00e3o se conectam ao balanceador \u2192 atr\u00e1s dele h\u00e1 duas inst\u00e2ncias do PgBouncer \u2192 em cada inst\u00e2ncia est\u00e1 em execu\u00e7\u00e3o o Consul-template, que monitora o estado de cada cluster Patroni e verifica a atualidade da configura\u00e7\u00e3o do PgBouncer, que redireciona as solicita\u00e7\u00f5es para o atual l\u00edder de cada cluster.<\/p>\n<h3>Teste manual<\/h3>\n<p>Antes de implantar em produ\u00e7\u00e3o, testamos esse esquema em um pequeno ambiente de teste e verificamos o funcionamento da troca autom\u00e1tica. Abrimos um quadro, movemos um adesivo e, nesse momento, 'matamos' o l\u00edder do cluster. Na AWS, basta desligar a inst\u00e2ncia atrav\u00e9s do console.<\/p>\n<p><img decoding=\"async\" style=\"display: block; margin: 0 auto;\" src=\"\/wp-content\/uploads\/2019\/06\/d670fe373b774f1b52bfd8a8c3b53a21.png\" alt=\"PostgreSQL High Availability Cluster + Patroni. Implementation Experience\" \/><\/p>\n<p>O adesivo retornou ao seu lugar em 10-20 segundos e depois come\u00e7ou a se mover normalmente novamente. Isso significa que o cluster Patroni funcionou corretamente: trocou de l\u00edder, enviou as informa\u00e7\u00f5es ao Consul, e o Consul-template imediatamente capturou essas informa\u00e7\u00f5es, substituiu a configura\u00e7\u00e3o do PgBouncer e enviou o comando para reload.<\/p>\n<h2>Como sobreviver sob alta carga e manter o downtime m\u00ednimo?<\/h2>\n<p>Tudo est\u00e1 funcionando perfeitamente! Mas surgem novas perguntas: como isso funcionar\u00e1 sob alta carga? Como implantar tudo de forma r\u00e1pida e segura em produ\u00e7\u00e3o?<\/p>\n<p>Responder \u00e0 primeira pergunta \u00e9 facilitado pelo ambiente de teste, onde realizamos testes de carga. Ele \u00e9 id\u00eantico \u00e0 produ\u00e7\u00e3o em termos de arquitetura e possui dados de teste gerados que s\u00e3o aproximadamente iguais em volume \u00e0 produ\u00e7\u00e3o. Decidimos \"eliminar\" um dos mestres PostgreSQL durante o teste e ver o que acontece. Mas antes disso, \u00e9 importante verificar a automa\u00e7\u00e3o, pois neste ambiente temos v\u00e1rios shards PostgreSQL, ent\u00e3o teremos uma \u00f3tima testagem dos scripts de configura\u00e7\u00e3o antes de irem para a produ\u00e7\u00e3o.<\/p>\n<p>Ambas as tarefas parecem ambiciosas, mas estamos com o PostgreSQL 9.6. Podemos atualizar diretamente para a vers\u00e3o 11.2?<\/p>\n<p>Decidimos fazer isso em duas etapas: primeiro atualizar para a vers\u00e3o 11.2, depois iniciar o Patroni.<\/p>\n<h3>Atualiza\u00e7\u00e3o do PostgreSQL<\/h3>\n<p>Para uma r\u00e1pida atualiza\u00e7\u00e3o da vers\u00e3o do PostgreSQL, \u00e9 necess\u00e1rio usar a op\u00e7\u00e3o <b>-k<\/b>, que cria hard links no disco, sem a necessidade de copiar seus dados. Em bancos de 300-400 GB, a atualiza\u00e7\u00e3o leva 1 segundo.<\/p>\n<p>Temos muitos shards, por isso a atualiza\u00e7\u00e3o precisa ser feita de forma autom\u00e1tica. Para isso, escrevemos um playbook do Ansible, que executa todo o processo de atualiza\u00e7\u00e3o para n\u00f3s:<\/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>Aqui \u00e9 importante notar que antes de iniciar a atualiza\u00e7\u00e3o, \u00e9 necess\u00e1rio rod\u00e1-la com o par\u00e2metro <b>\u2014verifique<\/b>, para ter certeza de que a atualiza\u00e7\u00e3o \u00e9 poss\u00edvel. Al\u00e9m disso, nosso script faz a substitui\u00e7\u00e3o dos configs durante a atualiza\u00e7\u00e3o. O script foi executado em 30 segundos, o que \u00e9 um \u00f3timo resultado.<\/p>\n<h3>In\u00edcio do Patroni<\/h3>\n<p>Para resolver o segundo problema, basta olhar a configura\u00e7\u00e3o do Patroni. No reposit\u00f3rio oficial h\u00e1 um exemplo de configura\u00e7\u00e3o com initdb, que \u00e9 respons\u00e1vel pela inicializa\u00e7\u00e3o do novo banco na primeira execu\u00e7\u00e3o do Patroni. Mas como j\u00e1 temos um banco pronto, apenas removemos essa se\u00e7\u00e3o da configura\u00e7\u00e3o.<\/p>\n<p>Quando come\u00e7amos a instalar o Patroni em um cluster PostgreSQL existente e a execut\u00e1-lo, nos deparamos com um novo problema: ambos os servidores estavam sendo iniciados como l\u00edderes. O Patroni n\u00e3o tem conhecimento do estado anterior do cluster e tenta iniciar ambos os servidores como dois clusters separados com o mesmo nome. Para resolver esse problema, \u00e9 necess\u00e1rio remover o diret\u00f3rio de dados no slave:<\/p>\n<pre><code class=\"plaintext\">rm -rf \/var\/lib\/postgresql\/<\/code><\/pre>\n<p><b>Isso deve ser feito apenas no slave!<\/b><\/p>\n<p>Ao conectar uma r\u00e9plica limpa, o Patroni faz um basebackup do l\u00edder e o restaura na r\u00e9plica, e ent\u00e3o sincroniza o estado atual pelos logs wal.<\/p>\n<p>Mais uma dificuldade que encontramos \u00e9 que todos os clusters PostgreSQL s\u00e3o nomeados como main por padr\u00e3o. Quando cada cluster n\u00e3o sabe nada sobre o outro, isso \u00e9 aceit\u00e1vel. Mas quando voc\u00ea quer usar o Patroni, todos os clusters devem ter um nome \u00fanico. A solu\u00e7\u00e3o \u00e9 alterar o nome do cluster na configura\u00e7\u00e3o do PostgreSQL.<\/p>\n<h3>Teste de carga<\/h3>\n<p>Executamos um teste que simula o trabalho dos usu\u00e1rios em pain\u00e9is. Quando a carga atingiu nossa m\u00e9dia di\u00e1ria, repetimos exatamente o mesmo teste, desligando uma inst\u00e2ncia com o leader PostgreSQL. O failover autom\u00e1tico funcionou como esper\u00e1vamos: o Patroni trocou o l\u00edder, o Consul-template atualizou a configura\u00e7\u00e3o do PgBouncer e emitiu o comando para reload. Nossos gr\u00e1ficos no Grafana mostraram que havia atrasos de 20 a 30 segundos e um pequeno volume de erros dos servidores relacionados \u00e0 conex\u00e3o com o banco. Essa \u00e9 uma situa\u00e7\u00e3o normal, esses valores s\u00e3o aceit\u00e1veis para nosso failover e definitivamente melhores do que o tempo de inatividade do servi\u00e7o.<\/p>\n<h2>Sa\u00edda do Patroni em produ\u00e7\u00e3o<\/h2>\n<p>No final, nosso plano ficou assim:<\/p>\n<ul>\n<li>Implanta\u00e7\u00e3o do Consul-template nos servidores PgBouncer e in\u00edcio;<\/li>\n<li>Atualiza\u00e7\u00e3o do PostgreSQL para a vers\u00e3o 11.2;<\/li>\n<li>Mudan\u00e7a do nome do cluster;<\/li>\n<li>In\u00edcio do cluster Patroni.<\/li>\n<\/ul>\n<p>Ao mesmo tempo, nosso esquema permite que o primeiro ponto seja feito a qualquer momento, podemos remover cada PgBouncer da opera\u00e7\u00e3o um por um e realizar a implanta\u00e7\u00e3o e o in\u00edcio do consul-template. E foi isso que fizemos.<\/p>\n<p>Para uma r\u00e1pida implementa\u00e7\u00e3o, usamos o Ansible, j\u00e1 que todos os playbooks foram testados em ambiente de teste, e o tempo de execu\u00e7\u00e3o do cen\u00e1rio completo variou de 1,5 a 2 minutos por sharding. Poder\u00edamos implantar cada sharding um por um sem parar nosso servi\u00e7o, mas ter\u00edamos que desligar cada PostgreSQL por alguns minutos. Nesse caso, os usu\u00e1rios cujos dados est\u00e3o nesse shard n\u00e3o poderiam trabalhar plenamente nesse intervalo, o que \u00e9 inaceit\u00e1vel para n\u00f3s.<\/p>\n<p>A solu\u00e7\u00e3o para essa situa\u00e7\u00e3o foi uma manuten\u00e7\u00e3o programada que realizamos a cada 3 meses. Este \u00e9 um per\u00edodo para trabalhos planejados, quando desligamos completamente nosso servi\u00e7o e atualizamos as inst\u00e2ncias dos bancos de dados. Restava uma semana at\u00e9 a pr\u00f3xima janela, e decidimos apenas esperar e nos preparar mais. Durante o tempo de espera, tomamos algumas precau\u00e7\u00f5es: levantamos uma r\u00e9plica de reserva para cada shard do PostgreSQL por precau\u00e7\u00e3o contra falhas, para manter os dados mais recentes, e adicionamos uma nova inst\u00e2ncia para cada shard, que deve se tornar uma nova r\u00e9plica no cluster Patroni, de modo que n\u00e3o precis\u00e1ssemos executar o comando para excluir dados. Tudo isso ajudou a minimizar o risco de erro.<br \/>\n<img decoding=\"async\" style=\"display: block; margin: 0 auto;\" src=\"\/wp-content\/uploads\/2019\/06\/29e5c5f50dbfebfe5665fa0aeb009728.png\" alt=\"PostgreSQL High Availability Cluster + Patroni. Implementation Experience\" \/><\/p>\n<p>Reiniciamos nosso servi\u00e7o, tudo funcionou como deveria, os usu\u00e1rios continuaram a trabalhar, mas notamos uma carga anormalmente alta nos servidores do Consul nos gr\u00e1ficos.<br \/>\n<img decoding=\"async\" style=\"display: block; margin: 0 auto;\" src=\"\/wp-content\/uploads\/2019\/06\/af0eef9ac235706c0aac62d2e1ee179d.png\" alt=\"PostgreSQL High Availability Cluster + Patroni. Implementation Experience\" \/><\/p>\n<p>Por que n\u00e3o vimos isso no ambiente de teste? Este problema ilustra bem a necessidade de seguir o princ\u00edpio de Infrastructure as Code e aprimorar toda a infraestrutura, desde os ambientes de teste at\u00e9 o de produ\u00e7\u00e3o. Caso contr\u00e1rio, \u00e9 muito f\u00e1cil enfrentar um problema como o que tivemos. O que aconteceu? O Consul apareceu primeiro no ambiente de produ\u00e7\u00e3o e depois nos ambientes de teste; no final, a vers\u00e3o do Consul nos ambientes de teste era mais alta que a do ambiente de produ\u00e7\u00e3o. Em uma das vers\u00f5es, uma vazamento de CPU ao trabalhar com consul-template foi resolvido. Portanto, simplesmente atualizamos o Consul, resolvendo assim o problema.<\/p>\n<h3>Reiniciar o cluster Patroni<\/h3>\n<p>No entanto, encontramos um novo problema do qual nem suspeit\u00e1vamos. Ao atualizar o Consul, simplesmente removemos o n\u00f3 do Consul do cluster com o comando consul leave \u2192 o Patroni se conecta a outro servidor Consul \u2192 tudo funciona. Mas, quando chegamos \u00e0 \u00faltima inst\u00e2ncia do cluster Consul e enviamos a ele o comando consul leave, todos os clusters Patroni simplesmente reiniciaram, e nos logs vimos o seguinte erro:<\/p>\n<pre><code class=\"plaintext\">ERRO: get_cluster\nRastreamento (chamada mais recente por &uacute;ltimo):\n...\nRetryFailedError: &#039;Prazo de repeti&ccedil;&atilde;o excedido&#039;\nERRO: Erro na comunica&ccedil;&atilde;o com DCS\n&lt;b&gt;LOG: o sistema de banco de dados foi encerrado&lt;\/b&gt;<\/code><\/pre>\n<p>O cluster Patroni n\u00e3o conseguiu obter informa\u00e7\u00f5es sobre o seu cluster e reiniciou.<\/p>\n<p>Para encontrar uma solu\u00e7\u00e3o, entramos em contato com os autores do Patroni atrav\u00e9s de uma issue no GitHub. Eles sugeriram melhorias em nossos arquivos de configura\u00e7\u00e3o:<\/p>\n<pre><code class=\"python\">consul:\n consul.checks: []\nbootstrap:\n dcs:\n   retry_timeout: 8<\/code><\/pre>\n<p>Conseguimos reproduzir o problema no ambiente de teste e testamos esses par\u00e2metros l\u00e1, mas, infelizmente, eles n\u00e3o funcionaram.<\/p>\n<p>O problema ainda n\u00e3o foi resolvido. Pretendemos tentar as seguintes op\u00e7\u00f5es de solu\u00e7\u00e3o:<\/p>\n<ul>\n<li>Usar o agente Consul em cada inst\u00e2ncia do cluster Patroni;<\/li>\n<li>Corrigir o problema no c\u00f3digo.<\/li>\n<\/ul>\n<p>Entendemos onde a falha ocorre: provavelmente, o problema est\u00e1 no uso do timeout padr\u00e3o, que n\u00e3o \u00e9 redefinido atrav\u00e9s do arquivo de configura\u00e7\u00e3o. Ao remover o \u00faltimo servidor Consul do cluster, todo o cluster Consul congela por mais de um segundo, o que impede o Patroni de obter o estado do cluster e faz com que ele reinicie todo o cluster.<\/p>\n<p>Felizmente, n\u00e3o encontramos mais erros.<\/p>\n<h2>Resultados do uso do Patroni<\/h2>\n<p>Ap\u00f3s o lan\u00e7amento bem-sucedido do Patroni, adicionamos uma r\u00e9plica extra em cada cluster. Agora, cada cluster possui um tipo de quorum: um l\u00edder e duas r\u00e9plicas, para garantir a seguran\u00e7a em caso de split-brain durante a troca.<br \/>\n<img decoding=\"async\" style=\"display: block; margin: 0 auto;\" src=\"\/wp-content\/uploads\/2019\/06\/3bb4c0495fea274b04edd2e99b59eacb.png\" alt=\"PostgreSQL High Availability Cluster + Patroni. Implementation Experience\" \/><\/p>\n<p>No ambiente de produ\u00e7\u00e3o, o Patroni est\u00e1 em funcionamento h\u00e1 mais de tr\u00eas meses. Durante esse tempo, j\u00e1 nos ajudou bastante. Recentemente, o l\u00edder de um dos clusters na AWS falhou, e o failover autom\u00e1tico ocorreu, permitindo que os usu\u00e1rios continuassem trabalhando. O Patroni cumpriu sua principal tarefa.<\/p>\n<p><b>Um pequeno resumo do uso do Patroni:<\/b><\/p>\n<ul>\n<li>Facilidade de altera\u00e7\u00e3o da configura\u00e7\u00e3o. Basta alterar a configura\u00e7\u00e3o em uma inst\u00e2ncia e ela se aplicar\u00e1 ao todo o cluster. Se for necess\u00e1ria uma reinicializa\u00e7\u00e3o para aplicar a nova configura\u00e7\u00e3o, o Patroni informar\u00e1 sobre isso. O Patroni pode reiniciar todo o cluster com um \u00fanico comando, o que tamb\u00e9m \u00e9 muito conveniente.<\/li>\n<li>O failover autom\u00e1tico funciona e j\u00e1 nos ajudou.<\/li>\n<li>Atualiza\u00e7\u00e3o do PostgreSQL sem downtime da aplica\u00e7\u00e3o. Primeiro, \u00e9 necess\u00e1rio atualizar as r\u00e9plicas para a nova vers\u00e3o, em seguida, trocar o l\u00edder no cluster Patroni e atualizar o l\u00edder antigo. Durante esse processo, o teste do failover autom\u00e1tico \u00e9 realizado.<\/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.3 - 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\/pt\/blog\/administrirovanie\/otkazoustojchivyj-klaster-postgresql-patroni-opyt-vnedreniya\" \/>\n\t\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 5.0.3\" \/>\n\t\t<meta property=\"og:locale\" content=\"pt_PT\" \/>\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\/pt\/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 \u00e0 prova de falhas + Patroni. Experi\u00eancia de implementa\u00e7\u00e3o | ProHoster","description":"Neste artigo, vou explicar como abordamos a quest\u00e3o da disponibilidade do PostgreSQL, por que isso se tornou importante para n\u00f3s e o que conseguimos no final.","canonical_url":"https:\/\/prohoster.info\/pt\/blog\/administrirovanie\/otkazoustojchivyj-klaster-postgresql-patroni-opyt-vnedreniya","robots":"max-image-preview:large","keywords":"","webmasterTools":{"miscellaneous":""},"schema":null,"og:locale":"pt_PT","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\/pt\/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\/pt\/wp-json\/wp\/v2\/posts\/35706","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/pt\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/pt\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/pt\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/pt\/wp-json\/wp\/v2\/comments?post=35706"}],"version-history":[{"count":1,"href":"https:\/\/prohoster.info\/pt\/wp-json\/wp\/v2\/posts\/35706\/revisions"}],"predecessor-version":[{"id":172654,"href":"https:\/\/prohoster.info\/pt\/wp-json\/wp\/v2\/posts\/35706\/revisions\/172654"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/prohoster.info\/pt\/wp-json\/wp\/v2\/media\/26749"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/pt\/wp-json\/wp\/v2\/media?parent=35706"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/pt\/wp-json\/wp\/v2\/categories?post=35706"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/pt\/wp-json\/wp\/v2\/tags?post=35706"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}