Aula 24: PostgreSQL em produção — alta disponibilidade com Patroni e pgBouncer

Aula 24: PostgreSQL em produção — alta disponibilidade com Patroni e pgBouncer

Bem-vindo à aula final do curso PostgreSQL — Do Zero ao Avançado. Nesta etapa, vamos elevar o nível de exigência e mergulhar em um dos temas mais críticos para qualquer equipe de infraestrutura e banco de dados: PostgreSQL em produção com alta disponibilidade. Ao longo das aulas anteriores, você construiu bases sólidas em administração, tuning, replicação, backup e segurança. Agora chegou o momento de integrar todos esses conhecimentos para projetar uma arquitetura resiliente, capaz de minimizar downtime e garantir conexões eficientes, mesmo sob picos de carga. O foco desta aula será a combinação de duas ferramentas poderosas e amplamente adotadas no mercado: Patroni, responsável pelo gerenciamento automático de failover e alta disponibilidade, e pgBouncer, um pooler de conexões leve e essencial para aplicações com muitos clientes simultâneos.

Por que este tema importa tanto? Em ambientes de produção, a disponibilidade do banco de dados é um requisito inegociável. Um único ponto de falha pode derrubar sistemas inteiros, gerar prejuízos financeiros e comprometer a confiança dos usuários. O PostgreSQL, por padrão, não oferece failover automático nativo — é preciso orquestrar o processo com ferramentas externas. É exatamente aqui que o Patroni entra: ele automatiza a eleição de um novo primário, gerencia a replicação e mantém o cluster saudável de forma autônoma. Já o pgBouncer resolve outro gargalo clássico: o custo de abrir e fechar conexões no PostgreSQL. Ao atuar como um proxy que mantém um pool de conexões persistentes, o pgBouncer reduz drasticamente a latência e o consumo de recursos, especialmente em aplicações web com centenas ou milhares de conexões simultâneas.

Neste artigo, você vai aprender a projetar, instalar, configurar e validar uma solução completa de alta disponibilidade para PostgreSQL em produção. Vamos começar com a teoria essencial sobre arquitetura de clusters e failover, passando pela instalação do etcd (o armazenamento distribuído de consenso usado pelo Patroni), do Patroni em si e do pgBouncer. Cada passo será demonstrado com comandos reais, comentados linha por linha, e com a saída esperada no terminal, para que você possa reproduzir tudo em seu próprio ambiente. Também abordaremos as configurações completas dos arquivos, testes de failover, verificação de integridade e os erros mais comuns que encontramos em campo, com suas respectivas soluções.

É importante destacar que, em nossos projetos na JRT Technology Solutions, nossos especialistas utilizam diariamente essa mesma stack — Patroni, etcd e pgBouncer — para implementar soluções de alta disponibilidade em clientes de diversos portes. Observamos na prática que a combinação dessas ferramentas oferece um equilíbrio excelente entre robustez, simplicidade operacional e maturidade. Ao final desta aula, você terá a confiança necessária para implantar essa arquitetura em seu próprio ambiente de produção, aplicar boas práticas de operação e solucionar problemas recorrentes. Prepare-se para a reta final do curso: esta aula é densa, prática e repleta de conhecimento aplicável imediatamente.

Ao concluir esta lição, você será capaz de: entender a arquitetura de alta disponibilidade com Patroni; instalar e configurar o etcd como camada de consenso; implantar um cluster PostgreSQL com failover automático; configurar o pgBouncer para otimizar conexões; realizar testes de failover e recuperação; diagnosticar e corrigir falhas comuns; e, por fim, projetar uma topologia de produção madura e resiliente. Vamos começar!

O que você vai aprender nesta aula

  • Compreender os conceitos de alta disponibilidade, failover automático e arquitetura de clusters no PostgreSQL.
  • Instalar e configurar o etcd como armazenamento de consenso distribuído para o Patroni.
  • Instalar o Patroni e configurar um cluster PostgreSQL com replicação síncrona e assíncrona.
  • Configurar o pgBouncer para pooling de conexões, reduzindo overhead e latência.
  • Realizar testes práticos de failover, promover réplicas e restaurar o nó primário original.
  • Diagnosticar e corrigir erros operacionais frequentes em ambientes de produção.
  • Aplicar boas práticas de monitoramento, segurança e otimização para PostgreSQL em produção.

Pré-requisitos e Ambiente

Antes de iniciar os procedimentos desta aula, é fundamental que você possua um ambiente mínimo preparado. Recomendamos a utilização de três servidores Linux para simular o cluster de alta disponibilidade, embora seja possível praticar com máquinas virtuais em um único host. Os pré-requisitos básicos são: domínio de linha de comando Linux, conhecimento de instalação de pacotes, familiaridade com arquivos de configuração do PostgreSQL e acesso administrativo (root ou sudo). Se você concluiu as aulas anteriores, já está apto a seguir esta etapa sem dificuldades.

Nosso cenário de laboratório utiliza três nós com as seguintes características: node1 (192.168.56.101), node2 (192.168.56.102) e node3 (192.168.56.103). Todos executam um sistema operacional Linux recente — você pode escolher entre Ubuntu 22.04 LTS, Debian 12 ou Rocky Linux 9 / CentOS Stream 9. Cada nó deve ter o PostgreSQL instalado (versão 14 ou superior recomendada), juntamente com os pacotes de desenvolvimento e Python, pois o Patroni é escrito em Python. É importante que os três nós consigam se comunicar entre si via rede, sem bloqueios de firewall entre as portas utilizadas (2379 para etcd, 5432 para PostgreSQL, 6432 para pgBouncer e 8008 para a API REST do Patroni).

Além da infraestrutura, você precisará dos seguintes componentes instalados em cada nó: PostgreSQL (servidor e cliente), Python 3 e pip, etcd (binário ou pacote), Patroni (via pip) e pgBouncer (via pacote ou compilação). Nos exemplos a seguir, mostraremos os comandos para instalar cada componente em sistemas baseados em Debian (Ubuntu/Debian) e em sistemas baseados em Red Hat (Rocky Linux/CentOS/RHEL). Acompanhe atentamente cada bloco, pois as diferenças entre as famílias de distribuições podem impactar o resultado final.

Por fim, certifique-se de que cada nó possua um nome de host único e que o arquivo /etc/hosts contenha as entradas corretas para os três servidores. Isso evita problemas de resolução de nomes e simplifica a configuração das ferramentas. Se você estiver utilizando um ambiente em nuvem ou contêineres, adapte os endereços IP conforme necessário. Lembre-se de que a consistência entre os nós é um pilar da alta disponibilidade: todos os servidores devem ter a mesma versão do PostgreSQL e do Patroni para evitar incompatibilidades durante o failover.

Arquitetura de Alta Disponibilidade com Patroni e pgBouncer — Fundamentos Teóricos

Antes de colocar a mão na massa, é essencial compreender como as peças se encaixam. O Patroni é um template de orquestração de alta disponibilidade para PostgreSQL, originalmente desenvolvido pela Zalando e hoje mantido pela comunidade. Ele funciona como um supervisor que controla o ciclo de vida de uma instância PostgreSQL em cada nó do cluster. O Patroni utiliza um Distributed Consensus Store (DCS) para manter o estado do cluster e coordenar as eleições. O DCS mais comum é o etcd, um armazenamento chave-valor distribuído e consistente, projetado especificamente para esse tipo de cenário. Outras opções incluem ZooKeeper e Consul, mas o etcd é o mais adotado em conjunto com o Patroni.

O papel do Patroni é garantir que, em qualquer momento, exista exatamente um nó primário (master) aceitando escritas, e que os demais nós atuem como réplicas (standby) prontas para assumir em caso de falha. Ele monitora a saúde da instância PostgreSQL via checagens periódicas, escreve atualizações no etcd e, se detectar que o primário não está respondendo, inicia automaticamente um processo de eleição. O Patroni também gerencia a configuração da replicação, ajustando automaticamente os parâmetros do PostgreSQL conforme o papel do nó. Tudo isso é exposto por uma API REST (porta 8008) e por um utilitário de linha de comando chamado patronictl, que permite inspecionar o estado do cluster, promover réplicas, realizar failovers manuais e restaurar nós.

Já o pgBouncer atua em uma camada diferente: ele é um pooler de conexões que fica entre a aplicação e o PostgreSQL. O PostgreSQL tradicionalmente cria um processo separado para cada conexão, o que consome memória e CPU à medida que o número de conexões cresce. Em aplicações web, onde centenas de usuários podem gerar milhares de conexões simultâneas (muitas vezes ociosas), esse modelo se torna um gargalo. O pgBouncer resolve isso mantendo um pool de conexões persistentes com o banco e reutilizando-as entre os clientes. Ele suporta três modos de pooling: session (uma conexão do cliente é atribuída a uma conexão do servidor durante toda a sessão), transaction (a conexão é liberada ao final de cada transação) e statement (liberada a cada statement). Em produção, o modo transaction é o mais recomendado para a maioria das cargas, pois oferece o melhor equilíbrio entre compatibilidade e eficiência.

Quando combinamos Patroni e pgBouncer, obtemos uma arquitetura robusta de PostgreSQL em produção. O Patroni garante a disponibilidade dos dados e a continuidade do serviço em caso de falha de um nó, enquanto o pgBouncer otimiza o uso de conexões, reduzindo latência e prevenindo sobrecarga. Para o pgBouncer se integrar ao cluster gerenciado pelo Patroni, é comum configurá-lo em cada nó de aplicação ou em um servidor dedicado, apontando para o endereço virtual do primário do cluster. Em alguns cenários avançados, utiliza-se um proxy adicional, como o HAProxy, para rotear o tráfego para o primário correto, mas nesta aula focaremos na configuração direta do pgBouncer com o Patroni, usando a API REST para identificar o nó primário.

Passo a Passo — Instalação do etcd em PostgreSQL em produção

O primeiro componente a ser instalado é o etcd, pois ele serve como o cérebro distribuído do cluster. Nos ambientes baseados em Debian, o etcd está disponível nos repositórios oficiais, embora a versão possa variar. Para garantir uma versão estável e compatível, recomendamos utilizar o binário oficial do projeto ou o pacote disponível no repositório da distribuição. Vamos demonstrar ambos os métodos, começando pelo Ubuntu/Debian.

Em Ubuntu 22.04 LTS ou Debian 12, execute os seguintes comandos para instalar o etcd a partir do repositório oficial:

# Atualize a lista de pacotes e instale o etcd
sudo apt update
sudo apt install -y etcd

# Verifique a versão instalada
etcd --version

Em Rocky Linux 9 ou CentOS Stream 9, o etcd não está nos repositórios padrão na versão mais recente, então utilizaremos o binário oficial. Execute:

# Baixe o binário do etcd (substitua a versão se necessário)
cd /tmp
curl -LO https://github.com/etcd-io/etcd/releases/download/v3.5.11/etcd-v3.5.11-linux-amd64.tar.gz

# Extraia o arquivo baixado
tar -xzf etcd-v3.5.11-linux-amd64.tar.gz

# Mova os binários para /usr/local/bin
sudo mv etcd-v3.5.11-linux-amd64/etcd /usr/local/bin/
sudo mv etcd-v3.5.11-linux-amd64/etcdctl /usr/local/bin/

# Verifique a versão instalada
etcd --version
etcdctl version

A saída esperada para o comando de verificação deve indicar a versão do etcd instalada, semelhante a:

etcd Version: 3.5.11
Git SHA: d3c3a0e
Go Version: go1.20.10
Go OS/Arch: linux/amd64

Após a instalação, é necessário criar a estrutura de diretórios e os arquivos de configuração. Em qualquer das distribuições, o etcd precisa de um diretório de dados, um usuário dedicado (recomendado) e um arquivo de unidade do systemd. Vamos configurar isso em cada um dos três nós. Primeiro, crie o usuário e o diretório de dados:

# Crie o usuário etcd (se não existir)
sudo useradd -r -s /sbin/nologin etcd

# Crie o diretório de dados e o diretório de configuração
sudo mkdir -p /var/lib/etcd
sudo mkdir -p /etc/etcd

# Ajuste as permissões
sudo chown -R etcd:etcd /var/lib/etcd
sudo chown -R etcd:etcd /etc/etcd

Em seguida, crie o arquivo de configuração do etcd. O conteúdo completo abaixo deve ser ajustado com os nomes e IPs dos seus nós. Este arquivo é o mesmo nos três nós, exceto pelos valores de name, initial-advertise-peer-urls, listen-peer-urls, listen-client-urls e advertise-client-urls, que devem refletir cada nó específico.

# Arquivo /etc/etcd/etcd.conf (exemplo para node1)

# Nome único deste membro do cluster
ETCD_NAME="node1"

# Diretório onde os dados do etcd serão armazenados
ETCD_DATA_DIR="/var/lib/etcd"

# URLs para comunicação entre os membros do cluster (peers)
ETCD_LISTEN_PEER_URLS="http://192.168.56.101:2380"
ETCD_INITIAL_ADVERTISE_PEER_URLS="http://192.168.56.101:2380"

# URLs para comunicação com clientes (Patroni, etcdctl)
ETCD_LISTEN_CLIENT_URLS="http://192.168.56.101:2379,http://127.0.0.1:2379"
ETCD_ADVERTISE_CLIENT_URLS="http://192.168.56.101:2379"

# Endereços iniciais de todos os nós do cluster
ETCD_INITIAL_CLUSTER="node1=http://192.168.56.101:2380,node2=http://192.168.56.102:2380,node3=http://192.168.56.103:2380"

# Token único para o cluster (deve ser igual em todos os nós)
ETCD_INITIAL_CLUSTER_TOKEN="postgresql-ha-token"

# Estado inicial do cluster (new para o primeiro boot)
ETCD_INITIAL_CLUSTER_STATE="new"

Repita a criação do arquivo nos demais nós, ajustando os valores correspondentes. Depois, crie o arquivo de unidade do systemd para gerenciar o etcd. Em sistemas Debian, o pacote pode já incluir um serviço; se você instalou via binário, crie manualmente:

# Arquivo /etc/systemd/system/etcd.service

[Unit]
Description=etcd key-value store
Documentation=https://etcd.io/docs
After=network-online.target
Wants=network-online.target

[Service]
Type=notify
User=etcd
Group=etcd
ExecStart=/usr/local/bin/etcd \
  --name=${ETCD_NAME} \
  --data-dir=${ETCD_DATA_DIR} \
  --listen-peer-urls=${ETCD_LISTEN_PEER_URLS} \
  --initial-advertise-peer-urls=${ETCD_INITIAL_ADVERTISE_PEER_URLS} \
  --listen-client-urls=${ETCD_LISTEN_CLIENT_URLS} \
  --advertise-client-urls=${ETCD_ADVERTISE_CLIENT_URLS} \
  --initial-cluster=${ETCD_INITIAL_CLUSTER} \
  --initial-cluster-token=${ETCD_INITIAL_CLUSTER_TOKEN} \
  --initial-cluster-state=${ETCD_INITIAL_CLUSTER_STATE}
EnvironmentFile=/etc/etcd/etcd.conf
Restart=always
RestartSec=10s
LimitNOFILE=65536

[Install]
WantedBy=multi-user.target

Após criar o arquivo de serviço, recarregue o systemd, habilite o serviço para iniciar no boot e inicie-o em cada nó. Execute os seguintes comandos em todos os três servidores:

# Recarregue as unidades do systemd
sudo systemctl daemon-reload

# Habilite o etcd para iniciar automaticamente no boot
sudo systemctl enable etcd

# Inicie o serviço etcd
sudo systemctl start etcd

# Verifique o status do serviço
sudo systemctl status etcd --no-pager -l

A saída esperada deve mostrar o serviço ativo e em execução, sem erros. Um exemplo de saída bem-sucedida é:

● etcd.service - etcd key-value store
     Loaded: loaded (/etc/systemd/system/etcd.service; enabled; vendor preset: enabled)
     Active: active (running) since Sun 2026-09-20 10:15:32 UTC; 2s ago
       Docs: https://etcd.io/docs
   Main PID: 1523 (etcd)
      Tasks: 12 (limit: 2345)
     Memory: 24.5M
        CPU: 180ms
     CGroup: /system.slice/etcd.service
             └─1523 /usr/local/bin/etcd --name=node1 --data-dir=/var/lib/etcd ...

Com o etcd em execução nos três nós, você pode validar a saúde do cluster usando o etcdctl. Execute o comando abaixo em qualquer um dos nós para listar os membros:

# Liste os membros do cluster etcd
etcdctl --endpoints=http://192.168.56.101:2379 member list

A saída deve mostrar os três nós com seus respectivos IDs e endereços, confirmando que o consenso foi estabelecido corretamente. Este é um marco crítico: sem o etcd saudável, o Patroni não conseguirá coordenar o cluster PostgreSQL.

Instalação e Configuração do Patroni para PostgreSQL em produção

Agora que o etcd está operacional, vamos instalar o Patroni em cada um dos três nós. O Patroni é distribuído como um pacote Python e pode ser instalado via pip em um ambiente virtual ou diretamente no sistema. Para simplificar e manter a consistência, recomendamos a instalação via pip3 em um virtualenv, garantindo isolamento de dependências. Os passos a seguir funcionam tanto em Debian quanto em Red Hat, desde que o Python e o pip estejam presentes.

Primeiro, instale as dependências de sistema necessárias para o Patroni e para a integração com o PostgreSQL. Em Ubuntu/Debian, execute:

# Instale as dependências do sistema
sudo apt update
sudo apt install -y python3 python3-pip python3-venv libpq-dev postgresql-client

# Crie um ambiente virtual para o Patroni
sudo mkdir -p /opt/patroni
sudo chown $USER:$USER /opt/patroni
python3 -m venv /opt/patroni/venv

# Ative o ambiente virtual
source /opt/patroni/venv/bin/activate

# Instale o Patroni e o psycopg2 dentro do virtualenv
pip install patroni psycopg2-binary

# Verifique a versão instalada
patroni --version

Em Rocky Linux/CentOS Stream, os comandos são ligeiramente diferentes devido ao gerenciador de pacotes. Execute:

# Instale as dependências do sistema
sudo dnf install -y python3 python3-pip python3-virtualenv libpq-devel postgresql

# Crie um ambiente virtual para o Patroni
sudo mkdir -p /opt/patroni
sudo chown $USER:$USER /opt/patroni
python3 -m venv /opt/patroni/venv

# Ative o ambiente virtual
source /opt/patroni/venv/bin/activate

# Instale o Patroni e o psycopg2 dentro do virtualenv
pip install patroni psycopg2-binary

# Verifique a versão instalada
patroni --version

A saída esperada para o comando de verificação deve mostrar a versão do Patroni, algo como:

patroni 3.3.0

Com o Patroni instalado, precisamos criar seu arquivo de configuração principal. O Patroni utiliza um arquivo YAML para definir os parâmetros de conexão com o etcd, os dados da instância PostgreSQL, as opções de replicação e o endereço da API REST. Vamos criar um arquivo chamado /etc/patroni/patroni.yml em cada nó, ajustando os valores específicos de cada máquina. Abaixo está o conteúdo completo para o node1, com comentários explicativos:

# Arquivo /etc/patroni/patroni.yml (exemplo para node1)

# Escopo do cluster — todos os nós devem ter o mesmo valor
scope: postgresql-ha

# Nome deste nó — deve ser único para cada membro do cluster
name: node1

# Configuração do DCS (etcd)
restapi:
  listen: 0.0.0.0:8008          # Porta da API REST do Patroni
  connect_address: 192.168.56.101:8008  # Endereço anunciado para outros nós

etcd3:
  hosts: 192.168.56.101:2379,192.168.56.102:2379,192.168.56.103:2379
  # Lista de todos os nós do etcd para tolerância a falhas

# Configurações do PostgreSQL gerenciado pelo Patroni
bootstrap:
  dcs:
    ttl: 30                    # Time-to-live para o lock de liderança no etcd
    loop_wait: 10              # Intervalo entre as checagens do Patroni
    retry_timeout: 10          # Tempo para tentar reconectar ao etcd
    maximum_lag_on_failover: 1048576  # Lag máximo em bytes para failover
    postgresql:
      use_pg_rewind: true      # Habilita o pg_rewind para reintegração
      use_slots: true          # Usa replication slots para evitar perda de WAL
      parameters:
        wal_level: replica
        hot_standby: "on"
        max_connections: 100
        max_worker_processes: 8
        max_wal_senders: 10
        max_replication_slots: 10
        hot_standby_feedback: "on"
        wal_log_hints: "on"
        archive_mode: "on"
        archive_command: '/bin/true'  # Placeholder; configure em produção
        synchronous_commit: "on"
        synchronous_standby_names: '*'

  initdb:
    - encoding: UTF8
    - data-checksums

  pg_hba:
    - host replication replicator 192.168.56.0/24 md5
    - host all all 192.168.56.0/24 md5
    - host all all 127.0.0.1/32 trust

  users:
    admin:
      password: adminpass123
      options:
        - createrole
        - createdb
    replicator:
      password: replicapass123
      options:
        - replication

# Configuração da instância PostgreSQL em si
postgresql:
  listen: 0.0.0.0:5432
  connect_address: 192.168.56.101:5432
  data_dir: /var/lib/postgresql/14/main
  bin_dir: /usr/lib/postgresql/14/bin
  pgpass: /tmp/pgpass
  authentication:
    replication:
      username: replicator
      password: replicapass123
    superuser:
      username: postgres
      password: postgrespass123
    rewind:
      username: postgres
      password: postgrespass123
  parameters:
    unix_socket_directories: '/var/run/postgresql'

# Tags para identificação do nó
tags:
  nofailover: false
  noloadbalance: false
  clonefrom: false
  nosync: false

Este arquivo deve ser replicado nos outros nós, alterando os campos name, restapi.connect_address e postgresql.connect_address para corresponder a cada máquina. Observe que o Patroni gerenciará a configuração do PostgreSQL automaticamente, incluindo a criação do cluster no primeiro boot. Certifique-se de que os diretórios referenciados existam e que o usuário postgres possua permissões adequadas.

Depois de criar o arquivo de configuração, vamos criar a unidade de serviço do systemd para o Patroni. O arquivo abaixo deve ser criado em /etc/systemd/system/patroni.service em todos os nós:

# Arquivo /etc/systemd/system/patroni.service

[Unit]
Description=Patroni - PostgreSQL High Availability
Documentation=https://patroni.readthedocs.io
After=network-online.target etcd.service
Wants=network-online.target

[Service]
Type=simple
User=postgres
Group=postgres
ExecStart=/opt/patroni/venv/bin/patroni /etc/patroni/patroni.yml
Restart=always
RestartSec=5s
LimitNOFILE=65536
Environment=PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

[Install]
WantedBy=multi-user.target

Após criar o arquivo de serviço em cada nó, execute os comandos abaixo para iniciar o Patroni. É importante iniciar primeiro o nó que será o primário inicial (por exemplo, o node1), aguardar alguns segundos e então iniciar os demais nós. O Patroni criará automaticamente o cluster PostgreSQL no primeiro nó e configurará a replicação nos outros.

# Recarregue as unidades do systemd
sudo systemctl daemon-reload

# Habilite o Patroni para iniciar no boot
sudo systemctl enable patroni

# Inicie o serviço Patroni (comece pelo node1, depois node2 e node3)
sudo systemctl start patroni

# Verifique o status do serviço no node1
sudo systemctl status patroni --no-pager -l

A saída esperada para o node1 deve mostrar o Patroni ativo e, nos logs, mensagens indicando a inicialização do PostgreSQL e a aquisição de liderança. Exemplo:

● patroni.service - Patroni - PostgreSQL High Availability
     Loaded: loaded (/etc/systemd/system/patroni.service; enabled; vendor preset: enabled)
     Active: active (running) since Sun 2026-09-20 10:22:45 UTC; 3s ago
   Main PID: 2210 (patroni)
      Tasks: 7 (limit: 2345)
     Memory: 18.3M
        CPU: 250ms
     CGroup: /system.slice/patroni.service
             └─2210 /opt/patroni/venv/bin/patroni /etc/patroni/patroni.yml
Sep 20 10:22:45 node1 patroni[2210]: 2026-09-20 10:22:45,123 INFO: Lock owner: node1; I am node1
Sep 20 10:22:45 node1 patroni[2210]: 2026-09-20 10:22:45,456 INFO: pg_controldata:
Sep 20 10:22:45 node1 patroni[2210]:   PostgreSQL cluster state:  in production
Sep 20 10:22:45 node1 patroni[2210]: 2026-09-20 10:22:45,890 INFO: initialized a new cluster
Sep 20 10:22:45 node1 patroni[2210]: 2026-09-20 10:22:45,901 INFO: Lock owner: node1; I am node1
Sep 20 10:22:45 node1 patroni[2210]: 2026-09-20 10:22:45,912 INFO: No action. I am (node1), the leader

Após iniciar o Patroni nos três nós, você pode verificar o estado do cluster com o utilitário patronictl. Execute o comando abaixo:

# Verifique o status do cluster
/opt/patroni/venv/bin/patronictl -c /etc/patroni/patroni.yml list

A saída esperada deve listar os três membros, destacando o líder e o estado da replicação:

+ Cluster: postgresql-ha (7381512345678901234) ----+----+-----------+
| Member | Host                | Role    | State   | TL | Lag in MB |
+--------+---------------------+---------+---------+----+-----------+
| node1  | 192.168.56.101:5432 | Leader  | running |  1 |           |
| node2  | 192.168.56.102:5432 | Replica | running |  1 |         0 |
| node3  | 192.168.56.103:5432 | Replica | running |  1 |         0 |
+--------+---------------------+---------+---------+----+-----------+

Neste momento, você tem um cluster PostgreSQL de alta disponibilidade totalmente funcional. O Patroni está monitorando a saúde do líder e das réplicas, e o etcd está armazenando o estado do cluster de forma consistente. Qualquer falha no nó primário será detectada automaticamente e uma nova eleição será realizada em segundos.

Configuração Detalhada do pgBouncer para Pooling de Conexões

Com o cluster PostgreSQL em alta disponibilidade operacional, o próximo componente essencial para PostgreSQL em produção é o pgBouncer. Ele atua como um proxy de conexões que reduz o overhead de abertura/fechamento de conexões, permitindo que milhares de clientes compartilhem um número menor de conexões reais com o banco. A instalação do pgBouncer pode ser feita via gerenciador de pacotes na maioria das distribuições. Em Ubuntu/Debian, execute:

# Instale o pgBouncer
sudo apt update
sudo apt install -y pgbouncer

# Verifique a versão instalada
pgbouncer --version

Em Rocky Linux/CentOS Stream, utilize o dnf:

# Instale o pgBouncer
sudo dnf install -y pgbouncer

# Verifique a versão instalada
pgbouncer --version

A saída esperada deve indicar a versão do pgBouncer, por exemplo:

PgBouncer 1.19.1
libevent 2.1.12-stable
adns: evdns2
tls: OpenSSL 3.0.2

O arquivo de configuração principal do pgBouncer está localizado em /etc/pgbouncer/pgbouncer.ini. Vamos configurá-lo para apontar para o nó primário do cluster Patroni. Em um cenário ideal, você pode usar um endereço virtual ou um proxy como o HAProxy para rotear automaticamente para o líder. Nesta aula, para simplificar, configuraremos o pgBouncer para se conectar ao endereço IP do nó primário atual (node1), mas explicaremos como adaptar para o endereço virtual. O conteúdo completo do arquivo é:

# Arquivo /etc/pgbouncer/pgbouncer.ini

[databases]
# Mapeie o banco de dados desejado para a instância do PostgreSQL
# A sintaxe é: nome_do_pool = host=endereço port=porta dbname=banco
# Em produção, substitua o IP pelo endereço virtual do cluster
appdb = host=192.168.56.101 port=5432 dbname=appdb

# Para ambientes com HAProxy, você usaria algo como:
# appdb = host=127.0.0.1 port=5432 dbname=appdb
# e o HAProxy encaminharia para o primário correto.

[pgbouncer]
# Endereço e porta onde o pgBouncer escutará as conexões dos clientes
listen_addr = 0.0.0.0
listen_port = 6432

# Usuário do sistema operacional que executa o pgBouncer
user = postgres
group = postgres

# Arquivo de log
logfile = /var/log/pgbouncer/pgbouncer.log
pidfile = /var/run/pgbouncer/pgbouncer.pid

# Modo de pooling: session, transaction ou statement
# O modo transaction é o recomendado para a maioria das aplicações web
pool_mode = transaction

# Número máximo de conexões que o pgBouncer aceitará dos clientes
max_client_conn = 1000

# Número total de conexões que o pgBouncer abrirá com o PostgreSQL
default_pool_size = 25

# Número mínimo de conexões por pool
min_pool_size = 5

# Tempo de reserva de uma conexão do pool (em segundos)
reserve_pool_size = 5

# Tempo máximo de vida de uma conexão (em segundos)
server_lifetime = 3600

# Tempo de inatividade antes de encerrar uma conexão (em segundos)
server_idle_timeout = 60

# Arquivo de autenticação dos clientes (senhas)
auth_type = md5
auth_file = /etc/pgbouncer/userlist.txt

# Configurações administrativas
admin_users = postgres
stats_users = postgres

# Habilitar consulta de estatísticas
stats_period = 60

# Log de conexões
log_connections = 1
log_disconnections = 1

# Timeouts
query_timeout = 0
client_idle_timeout = 0

# Configurações de TLS (opcional)
# client_tls_sslmode = prefer
# client_tls_ca_file = /etc/pgbouncer/ca.crt
# client_tls_key_file = /etc/pgbouncer/server.key
# client_tls_cert_file = /etc/pgbouncer/server.crt

Além do arquivo principal, o pgBouncer utiliza um arquivo de usuários para autenticação, chamado userlist.txt. Nele, você deve listar os usuários do PostgreSQL que poderão se conectar através do pooler, juntamente com suas senhas (preferencialmente hashes MD5). Para gerar o hash MD5 da senha, utilize o comando pg_md5, que acompanha o pgBouncer:

# Gere o hash MD5 da senha do usuário postgres
pg_md5 postgrespass123

# Gere o hash MD5 da senha do usuário replicator (se necessário)
pg_md5 replicapass123

A saída esperada para o primeiro comando é algo como:

md5e8a5f2c1b3d4e5f6a7b8c9d0e1f2a3b4

Com os hashes em mãos, crie o arquivo /etc/pgbouncer/userlist.txt no formato abaixo:

# Arquivo /etc/pgbouncer/userlist.txt

# Usuário administrador do PostgreSQL
"postgres" "md5e8a5f2c1b3d4e5f6a7b8c9d0e1f2a3b4"

# Usuário de replicação (se necessário para aplicações)
"replicator" "md5f6e5d4c3b2a1908877665544332211ab"

Após configurar os arquivos, ajuste as permissões e inicie o serviço do pgBouncer. Em distribuições baseadas em Debian, o serviço pode ser gerenciado pelo systemd, mas primeiro edite o arquivo de configuração padrão se necessário. Execute:

# Ajuste as permissões dos arquivos de configuração
sudo chown postgres:postgres /etc/pgbouncer/pgbouncer.ini
sudo chown postgres:postgres /etc/pgbouncer/userlist.txt
sudo chmod 600 /etc/pgbouncer/userlist.txt

# Crie o diretório de log
sudo mkdir -p /var/log/pgbouncer
sudo chown postgres:postgres /var/log/pgbouncer

# Recarregue o systemd e inicie o pgBouncer
sudo systemctl daemon-reload
sudo systemctl enable pgbouncer
sudo systemctl start pgbouncer

# Verifique o status do serviço
sudo systemctl status pgbouncer --no-pager

A saída esperada deve mostrar o pgBouncer ativo e escutando na porta 6432:

● pgbouncer.service - pgbouncer lightweight connection pooler
     Loaded: loaded (/lib/systemd/system/pgbouncer.service; enabled; vendor preset: enabled)
     Active: active (running) since Sun 2026-09-20 10:35:10 UTC; 1s ago
   Main PID: 3012 (pgbouncer)
      Tasks: 1 (limit: 2345)
     Memory: 1.2M
        CPU: 85ms
     CGroup: /system.slice/pgbouncer.service
             └─3012 /usr/sbin/pgbouncer /etc/pgbouncer/pgbouncer.ini
Sep 20 10:35:10 node1 systemd[1]: Started pgbouncer lightweight connection pooler.

Para confirmar que o pgBouncer está operacional e aceitando conexões, você pode se conectar a ele usando o cliente PostgreSQL, apontando para a porta 6432:

# Conecte-se ao pgBouncer (que encaminhará para o PostgreSQL)
psql -h 127.0.0.1 -p 6432 -U postgres -d appdb -c "SELECT version();"

A saída esperada deve mostrar a versão do PostgreSQL, indicando que a conexão foi estabelecida com sucesso através do pooler:

                                                                       version
-----------------------------------------------------------------------------------------------------------------------------------------------------
 PostgreSQL 14.11 (Ubuntu 14.11-1.pgdg22.04+1) on x86_64-pc-linux-gnu, compiled by gcc (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0, 64-bit
(1 row)

Neste ponto, a camada de pooling está configurada e pronta para uso. Em um ambiente real de PostgreSQL em produção, você instalaria o pgBouncer em servidores de aplicação ou em um host dedicado, configurando o endereço do host virtual do cluster para que as conexões sejam sempre roteadas para o primário atual, mesmo após um failover.

Verificando a Instalação / Testando a Configuração

Agora que todos os componentes estão instalados e configurados, é hora de validar o funcionamento do cluster e do pooler. Esta seção é obrigatória e tem como objetivo garantir que tudo esteja operando conforme o esperado. Vamos executar uma série de comandos de verificação e testes de failover para comprovar a resiliência da arquitetura.

O primeiro teste é consultar o status do cluster Patroni. Execute o comando patronictl list e verifique se todos os nós estão no estado running e se há um líder eleito:

# Verifique o status do cluster
/opt/patroni/venv/bin/patronictl -c /etc/patroni/patroni.yml list

A saída esperada deve ser semelhante à exibida anteriormente, com o node1 como Leader e os demais como Replica. Para obter mais detalhes sobre o líder e a replicação, use o comando patronictl show:

# Mostre informações detalhadas do cluster
/opt/patroni/venv/bin/patronictl -c /etc/patroni/patroni.yml show postgresql-ha

Este comando exibirá informações como o endereço do líder, o estado da replicação, o lag das réplicas e a linha do tempo atual. Uma saída bem-sucedida deve mostrar State: running e Role: Leader para o nó primário, além de Role: Replica com lag zero para os demais.

Em seguida, teste a conectividade através do pgBouncer. Conecte-se ao pooler e execute uma consulta que insira dados em uma tabela de teste, verificando se a escrita chega ao PostgreSQL corretamente:

# Crie uma tabela de teste via pgBouncer
psql -h 127.0.0.1 -p 6432 -U postgres -d appdb -c "CREATE TABLE IF NOT EXISTS teste_ha (id serial PRIMARY KEY, descricao text);"

# Insira um registro de teste
psql -h 127.0.0.1 -p 6432 -U postgres -d appdb -c "INSERT INTO teste_ha (descricao) VALUES ('Aula 24 - PostgreSQL em produção');"

# Consulte os dados inseridos
psql -h 127.0.0.1 -p 6432 -U postgres -d appdb -c "SELECT * FROM teste_ha;"

A saída esperada para a última consulta deve mostrar o registro inserido, confirmando que o pgBouncer está encaminhando as requisições corretamente:

 id |            descricao
----+----------------------------------
  1 | Aula 24 - PostgreSQL em produção
(1 row)

Agora, o teste mais importante: o failover automático. Para simular uma falha no nó primário, derrube o serviço do Patroni no node1 (o líder atual) e observe o comportamento do cluster. Execute no node1:

# Simule a queda do nó primário
sudo systemctl stop patroni

Imediatamente após parar o serviço, monitore o estado do cluster a partir de outro nó, por exemplo, no node2. Execute repetidamente o comando patronictl list para acompan

Quer aprender na prática com especialistas?

A JRT Technology Solutions oferece treinamentos e implementação de PostgreSQL para equipes corporativas.



Falar no WhatsApp

Avatar photo

Thiago Paes Rodrigues

Com mais de 22 anos de experiência em Tecnologia da Informação, este profissional construiu uma trajetória sólida como empresário, atuando de forma estratégica na implementação de soluções tecnológicas que otimizam processos e impulsionam resultados em diferentes setores.