Alta disponibilidade PostgreSQL com dados crocantes PGO: implantação empresarial Kubernetes
Enterprise PostgreSQL HA com Crunchy Data PGO na Kubernetes
Executar o PostgreSQL em produção no Kubernetes exige mais do que um StatefulSet com volume persistente. Você precisa de failover automatizado, backup contínuo e recuperação pontual, pool de conexões, criptografia TLS, monitoramento e capacidade de implantação consistente em provedores de nuvem e bare metal. O Crunchy Data PGO (Postgres Operator) v5 oferece tudo isso por meio de um único recurso personalizado nativo do Kubernetes — oPostgresClusterCRD — apoiado por componentes testados em batalha: Patroni para HA baseado em consenso, pgBackRest para backup corporativo, PgBouncer para pool de conexões e pgMonitor para observabilidade compatível com Prometheus.
Este guia cobre tudo o que é necessário para levar um cluster PostgreSQL gerenciado por PGO desde a implantação inicial até a operação de nível de produção em AWS EKS, Azure AKS, GCP GKE e bare metal k3s com Rancher. Cada seção inclui valores concretos de YAML, Helm e procedimentos operacionais que você pode adaptar ao seu ambiente.
ArquiteturaCrunchy Data PGO v5
PGO v5 é uma reescrita completa do Crunchy Postgres Operator. Ele substitui os CRDs pgcluster/pgreplica/pgpolicy anteriores por um único recursoPostgresClusterque descreve declarativamente todos os aspectos de uma implantação PostgreSQL. O operador observa alterações neste recurso e reconcilia os objetos Kubernetes subjacentes — StatefulSets, Services, ConfigMaps, Secrets, Jobs — para corresponder ao estado desejado.
A arquitetura é construída sobre quatro pilares.Patronié executado como um sidecar em cada pod PostgreSQL e gerencia a eleição de líder, topologia de replicação e failover automático usando consenso distribuído nativo do Kubernetes.pgBackRestlida com backups completos, diferenciais e incrementais, além de arquivamento WAL contínuo para armazenamento de objetos (S3, GCS, Azure Blob) ou PVCs locais.PgBouncerfornece pooling de conexão leve que protege o PostgreSQL contra tempestades de conexão.pgMonitorexpõe métricas PostgreSQL por meio de um sidecar exportador Prometheus para integração com sua pilha de observabilidade existente.
O operador em si não tem estado — todo o estado persistente reside no cluster PostgreSQL e em seu repositório de backup. Isso significa que você pode atualizar ou reiniciar o operador sem afetar os bancos de dados em execução. O loop de reconciliação do operador é idempotente: aplicar a mesma especificaçãoPostgresClustervárias vezes produz o mesmo conjunto de objetos Kubernetes.
Instalando PGO v5
PGO v5 pode ser instalado via Helm ou manifestos diretos do kubectl. A abordagem Helm é preferida para produção porque se integra perfeitamente aos fluxos de trabalho da GitOps e fornece atualizações controladas por versão.
# Add the Crunchy Data Helm repository
helm repo add crunchydata https://charts.crunchydata.com
helm repo update
# Install PGO into its own namespace
helm install pgo crunchydata/pgo \
--namespace postgres-operator \
--create-namespace \
--set controllerImages.cluster=registry.crunchydata.com/crunchydata/postgres-operator:ubi8-5.7.2-0 \
--set relatedImages.postgres_16=registry.crunchydata.com/crunchydata/crunchy-postgres:ubi8-16.6-1 \
--set relatedImages.pgbackrest=registry.crunchydata.com/crunchydata/crunchy-pgbackrest:ubi8-2.54.1-0 \
--set relatedImages.pgbouncer=registry.crunchydata.com/crunchydata/crunchy-pgbouncer:ubi8-1.23.1-0 \
--set relatedImages.pgexporter=registry.crunchydata.com/crunchydata/crunchy-postgres-exporter:ubi8-0.16.0-0Para ambientes isolados, espelhe as imagens necessárias em seu registro interno e atualize os valoresrelatedImagesadequadamente. O PGO extrairá apenas imagens especificadas nos valores Helm ou na especificaçãoPostgresCluster– ele nunca alcança registros externos em tempo de execução, a menos que seja explicitamente configurado para fazer isso.
# Verify the operator is running
kubectl get pods -n postgres-operator
NAME READY STATUS RESTARTS AGE
pgo-controller-manager-7d8f9b6c4-xk2pt 1/1 Running 0 45sPostgresCluster CRD Especificação
OPostgresClusterCRD é a superfície declarativa única para toda a implantação do PostgreSQL. Abaixo está uma especificação pronta para produção que demonstra as seções principais. Cada seção é discutida em detalhes nas partes subsequentes deste guia.
apiVersion: postgres-operator.crunchydata.com/v1beta1
kind: PostgresCluster
metadata:
name: production-db
namespace: databases
spec:
postgresVersion: 16
image: registry.crunchydata.com/crunchydata/crunchy-postgres:ubi8-16.6-1
instances:
- name: pgha1
replicas: 3
resources:
requests:
cpu: "2"
memory: 8Gi
limits:
cpu: "4"
memory: 16Gi
dataVolumeClaimSpec:
storageClassName: gp3-csi
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 100Gi
walVolumeClaimSpec:
storageClassName: gp3-csi
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 20Gi
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- topologyKey: kubernetes.io/hostname
labelSelector:
matchLabels:
postgres-operator.crunchydata.com/cluster: production-db
tolerations:
- key: "workload"
operator: "Equal"
value: "database"
effect: "NoSchedule"
backups:
pgbackrest:
image: registry.crunchydata.com/crunchydata/crunchy-pgbackrest:ubi8-2.54.1-0
configuration:
- secret:
name: pgbackrest-s3-creds
global:
repo1-retention-full: "7"
repo1-retention-diff: "14"
repo1-path: /pgbackrest/production-db/repo1
repo1-s3-uri-style: path
repos:
- name: repo1
schedules:
full: "0 2 * * 0" # Sunday 2am
differential: "0 2 * * 1-6" # Mon-Sat 2am
incremental: "0 */4 * * *" # Every 4 hours
s3:
bucket: mycompany-pg-backups
endpoint: s3.eu-west-1.amazonaws.com
region: eu-west-1
proxy:
pgBouncer:
image: registry.crunchydata.com/crunchydata/crunchy-pgbouncer:ubi8-1.23.1-0
replicas: 2
resources:
requests:
cpu: 500m
memory: 256Mi
limits:
cpu: "1"
memory: 512Mi
config:
global:
pool_mode: transaction
max_client_conn: "1000"
default_pool_size: "25"
min_pool_size: "5"
reserve_pool_size: "5"
reserve_pool_timeout: "3"
monitoring:
pgmonitor:
exporter:
image: registry.crunchydata.com/crunchydata/crunchy-postgres-exporter:ubi8-0.16.0-0
resources:
requests:
cpu: 100m
memory: 128Mi
patroni:
dynamicConfiguration:
synchronous_mode: true
postgresql:
parameters:
shared_buffers: 2GB
effective_cache_size: 6GB
work_mem: 32MB
maintenance_work_mem: 512MB
max_connections: 200
wal_buffers: 64MB
checkpoint_completion_target: 0.9
random_page_cost: 1.1
effective_io_concurrency: 200
min_wal_size: 1GB
max_wal_size: 4GB
max_worker_processes: 8
max_parallel_workers_per_gather: 4
max_parallel_workers: 8
log_min_duration_statement: 500
log_checkpoints: "on"
log_connections: "on"
log_disconnections: "on"
log_lock_waits: "on"
users:
- name: appuser
databases: ["appdb"]
options: "NOSUPERUSER"
- name: readonly
databases: ["appdb"]
options: "NOSUPERUSER"
databaseInitSQL:
name: init-sql-configmap
key: init.sqlEsta especificação cria um cluster PostgreSQL 16 de três instâncias com dados separados e volumes WAL, backups apoiados por S3 em uma programação completa/diferencial/incremental, pool de conexões PgBouncer em modo de transação, exportação de métricas Prometheus e replicação síncrona Patroni. A regra de antiafinidade do pod garante que todas as três instâncias cheguem a nós Kubernetes diferentes.
HA baseado em Patroni e failover automático
Patroni é a estrutura HA incorporada em cada pod PostgreSQL gerenciado por PGO. Ele usa o Kubernetes API como seu armazenamento de configuração distribuído (DCS) — nenhum cluster etcd ou ZooKeeper separado é necessário. Patroni monitora continuamente a integridade do primário e das réplicas do PostgreSQL. Quando o primário deixa de responder, Patroni inicia um failover automático: ele promove a réplica mais atualizada para primária e reconfigura as réplicas restantes para seguir o novo líder.
O processo de failover no PGO funciona da seguinte maneira. Patroni em cada pod mantém um bloqueio líder no Kubernetes (por meio de Endpoints ou objetos ConfigMap). O primário atual deve renovar esse bloqueio em um intervalo configurável (padrão 10 segundos TTL, 3 segundos de espera de loop). Se o primário não for renovado – porque travou, o nó morreu ou a rede o particionou – uma réplica que esteja mais próxima da posição WAL do primário adquirirá o bloqueio e se promoverá. Todo o processo normalmente é concluído em 10 a 30 segundos.
O modo de replicação síncrona, habilitado viasynchronous_mode: truena configuração Patroni, garante zero perda de dados (RPO = 0) ao custo de uma latência de gravação um pouco maior. No modo síncrono, uma transação não é reconhecida ao cliente até que pelo menos uma réplica tenha confirmado o recebimento do WAL. Se nenhuma réplica síncrona estiver disponível, o Patroni desabilita temporariamente o modo síncrono para manter a disponibilidade – você pode substituir isso pelosynchronous_mode_strict: truese preferir sacrificar a disponibilidade pela consistência.
# Check Patroni cluster status
kubectl exec -it production-db-pgha1-0 -n databases -- patronictl list
+----------------------------+-------------------+---------+---------+----+-----------+
| Member | Host | Role | State | TL | Lag in MB |
+----------------------------+-------------------+---------+---------+----+-----------+
| production-db-pgha1-0 | 10.244.0.15 | Leader | running | 3 | |
| production-db-pgha1-1 | 10.244.1.22 | Replica | running | 3 | 0 |
| production-db-pgha1-2 | 10.244.2.18 | Replica | running | 3 | 0 |
+----------------------------+-------------------+---------+---------+----+-----------+
# Manual switchover (planned, zero downtime)
kubectl exec -it production-db-pgha1-0 -n databases -- \
patronictl switchover --master production-db-pgha1-0 --candidate production-db-pgha1-1 --force
# Manual failover (forced, for emergencies)
kubectl exec -it production-db-pgha1-1 -n databases -- \
patronictl failover --candidate production-db-pgha1-1 --forceOPGO configura automaticamente os serviços Kubernetes para rastrear o líder Patroni. O serviçoproduction-db-primarysempre aponta para qualquer pod que atualmente detém o bloqueio líder, de modo que os aplicativos que se conectam por meio desse serviço experimentam um failover contínuo com apenas uma breve redefinição da conexão.
pgBackRest Backup e restauração
pgBackRest é o mecanismo de backup integrado ao PGO. Ele oferece suporte a três tipos de backup – completo, diferencial e incremental – além de arquivamento WAL contínuo para recuperação pontual (PITR). Compreender como eles se encaixam é essencial para projetar uma estratégia de backup que equilibre custo de armazenamento, velocidade de backup e tempo de recuperação.
Um backup completocopia todo o diretório de dados PostgreSQL e é a linha de base para todos os outros tipos de backup. Um backup diferencialOcopia apenas as páginas que foram alteradas desde o último backup completo. Um backup incrementalOcopia apenas as páginas que foram alteradas desde o último backup de qualquer tipo. Durante a restauração, o pgBackRest encadeia automaticamente os backups necessários – por exemplo, a restauração de um incremental requer o incremental mais o diferencial anterior (ou completo) mais o backup completo, mais quaisquer segmentos WAL necessários para atingir o ponto de recuperação de destino.
Configuração de agendamento de backupO agendamento de backup é definido na seçãoreposda configuração pgBackRest dentro da especificaçãoPostgresCluster. Um cronograma de produção sólido normalmente executa backups semanais completos, diferenciais diários e backups incrementais mais frequentes.
backups:
pgbackrest:
global:
repo1-retention-full: "4" # Keep 4 full backups
repo1-retention-full-type: count
repo1-retention-diff: "14" # Keep 14 differentials
repos:
- name: repo1
schedules:
full: "0 2 * * 0" # Weekly full: Sunday 2am
differential: "0 2 * * 1-6" # Daily diff: Mon-Sat 2am
incremental: "0 */6 * * *" # Every 6 hours
s3:
bucket: mycompany-pg-backups
endpoint: s3.eu-west-1.amazonaws.com
region: eu-west-1Backup e restauração manual do# Trigger a manual full backup
kubectl annotate postgrescluster production-db -n databases \
postgres-operator.crunchydata.com/pgbackrest-backup="$(date +%s)" \
--overwrite
# Check backup status
kubectl exec -it production-db-pgha1-0 -n databases -- \
pgbackrest info --stanza=db
# Restore to a specific point in time
# First, update the PostgresCluster spec:
spec:
backups:
pgbackrest:
restore:
enabled: true
repoName: repo1
options:
- --type=time
- --target="2026-04-12 08:30:00+00"
- --target-action=promote
# Apply the restore annotation
kubectl annotate postgrescluster production-db -n databases \
postgres-operator.crunchydata.com/pgbackrest-restore="$(date +%s)" \
--overwriteDurante uma restauração PITR, o PGO desliga todas as instâncias PostgreSQL, restaura a partir do backup completo ou diferencial mais próximo, reproduz os segmentos WAL até o tempo alvo e, em seguida, inicia o cluster. Toda a operação é orquestrada pelo operador – não é necessária nenhuma intervenção manual em pods individuais.
Pool de conexõesPgBouncer
PostgreSQL cria um novo processo para cada conexão de cliente. Em escala – centenas ou milhares de pods de aplicativos, cada um mantendo pools de conexões – esse modelo falha. O PgBouncer fica entre seu aplicativo e o PostgreSQL, multiplexando muitas conexões de clientes em um número menor de conexões de servidores.
PGO implanta o PgBouncer como uma implantação separada com seu próprio serviço. O serviçoproduction-db-pgbounceré ao qual seus aplicativos devem se conectar, não diretamente ao serviço primário PostgreSQL. PgBouncer suporta três modos de pool:
- Pool de sessões— uma conexão de servidor é atribuída a um cliente durante a vida útil da conexão do cliente. Mais seguro, mas menos eficiente.
- Pool de transações— uma conexão de servidor é atribuída apenas durante uma transação. Mais eficiente para cargas de trabalho da Web. Este é o padrão recomendado.
- Conjunto de instruções— uma conexão de servidor é atribuída para uma única instrução. Funciona apenas para cargas de trabalho simples e não transacionais.
proxy:
pgBouncer:
replicas: 3
config:
global:
pool_mode: transaction
max_client_conn: "2000"
default_pool_size: "30"
min_pool_size: "10"
reserve_pool_size: "10"
reserve_pool_timeout: "3"
server_idle_timeout: "300"
server_lifetime: "3600"
client_idle_timeout: "600"
tcp_keepalive: "1"
tcp_keepidle: "30"
tcp_keepintvl: "10"
tcp_keepcnt: "3"
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
topologyKey: kubernetes.io/hostname
labelSelector:
matchLabels:
postgres-operator.crunchydata.com/role: pgbouncerObserve as configurações dotcp_keepalive– elas são críticas quando o PgBouncer é executado atrás de um balanceador de carga em nuvem ou serviço Kubernetes. Sem manutenção de atividade agressiva, as conexões ociosas podem ser descartadas silenciosamente por componentes intermediários da rede, causando erros de aplicativo na próxima tentativa de consulta.
pgMonitor e monitoramento Prometheus/Grafana
A integração de monitoramento doPGO implanta um sidecarcrunchy-postgres-exporterem cada pod PostgreSQL. Este exportador extrai as visualizações de estatísticas internas do PostgreSQL e as expõe como métricas Prometheus na porta 9187. O exportador cobre mais de 150 métricas prontas para uso, incluindo conexões, atraso de replicação, taxas de transação, taxas de acertos de cache, estatísticas de tabela e índice, contenção de bloqueio e taxas de geração de WAL.
# ServiceMonitor for Prometheus Operator auto-discovery
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: postgres-monitoring
namespace: databases
labels:
release: kube-prometheus-stack
spec:
selector:
matchLabels:
postgres-operator.crunchydata.com/cluster: production-db
postgres-operator.crunchydata.com/crunchy-postgres-exporter: "true"
endpoints:
- port: exporter
interval: 15s
scrapeTimeout: 10sOCrunchy Data fornece um conjunto de painéis Grafana pré-construídos que você pode importar diretamente. Eles cobrem a visão geral do PostgreSQL, status de replicação, status de backup pgBackRest, estatísticas PgBouncer e uso de recursos em nível de pod. Importe-os por meio do provisionamento do painel do Grafana ou manualmente do repositório de exemplos do Crunchy Data.
# Install Grafana dashboards as ConfigMaps
kubectl create configmap grafana-pg-overview \
--from-file=pg-overview.json=dashboards/pg-overview.json \
-n monitoring
kubectl label configmap grafana-pg-overview grafana_dashboard=1 -n monitoringRegras de alerta críticopara PostgreSQL
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
name: postgres-alerts
namespace: databases
labels:
release: kube-prometheus-stack
spec:
groups:
- name: postgresql-health
rules:
- alert: PostgresReplicationLagHigh
expr: ccp_replication_lag_size_bytes > 104857600
for: 5m
labels:
severity: warning
annotations:
summary: "Replica {{ $labels.pod }} lag exceeds 100MB"
- alert: PostgresConnectionsExhausted
expr: ccp_connection_stats_active / ccp_connection_stats_max_connections > 0.85
for: 2m
labels:
severity: critical
annotations:
summary: "PostgreSQL connections above 85% capacity"
- alert: PostgresDeadlockDetected
expr: rate(ccp_stat_database_deadlocks[5m]) > 0
for: 1m
labels:
severity: warning
annotations:
summary: "Deadlocks detected in {{ $labels.datname }}"
- alert: PostgresCacheHitRatioLow
expr: ccp_stat_database_blks_hit / (ccp_stat_database_blks_hit + ccp_stat_database_blks_read) < 0.95
for: 10m
labels:
severity: warning
annotations:
summary: "Cache hit ratio below 95% for {{ $labels.datname }}"
- alert: PgBackRestStaleBackup
expr: time() - ccp_backrest_last_full_backup_time_since_completion_seconds > 604800
for: 1h
labels:
severity: critical
annotations:
summary: "No full backup in the last 7 days"AWS Implantação EKS
A implantação do PGO no AWS EKS requer a configuração do driver EBS CSI para volumes persistentes e funções IAM para contas de serviço (IRSA) para acesso de backup S3. Essa abordagem evita o armazenamento de credenciais AWS de longa duração em segredos Kubernetes.
# Install the EBS CSI driver (required for gp3 volumes)
eksctl create addon --name aws-ebs-csi-driver --cluster my-cluster --region eu-west-1 \
--service-account-role-arn arn:aws:iam::111122223333:role/AmazonEKS_EBS_CSI_DriverRole
# Create a gp3 StorageClass
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: gp3-csi
provisioner: ebs.csi.aws.com
parameters:
type: gp3
iops: "3000"
throughput: "125"
encrypted: "true"
volumeBindingMode: WaitForFirstConsumer
allowVolumeExpansion: true# IAM policy for pgBackRest S3 access
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:PutObject",
"s3:GetObject",
"s3:DeleteObject",
"s3:ListBucket"
],
"Resource": [
"arn:aws:s3:::mycompany-pg-backups",
"arn:aws:s3:::mycompany-pg-backups/*"
]
}
]
}
# Create an IRSA-enabled service account
eksctl create iamserviceaccount \
--name pgbackrest-sa \
--namespace databases \
--cluster my-cluster \
--attach-policy-arn arn:aws:iam::111122223333:policy/PGBackRestS3Policy \
--approveNa especificaçãoPostgresCluster, faça referência ao bucket S3 e à região. O PGO usará as credenciais da conta de serviço do pod (via IRSA) automaticamente – sem necessidade de chaves de acesso explícitas.
backups:
pgbackrest:
repos:
- name: repo1
s3:
bucket: mycompany-pg-backups
endpoint: s3.eu-west-1.amazonaws.com
region: eu-west-1Para resiliência multi-AZ, certifique-se de que seus grupos de nós EKS abranjam pelo menos três zonas de disponibilidade e use as restrições de propagação de topologia mostradas anteriormente para distribuir pods PostgreSQL entre eles.
ImplantaçãoAzure AKS
OAzure AKS usa discos gerenciados para armazenamento persistente e armazenamento de blob Azure para backups pgBackRest. A classe de armazenamento recomendada usa Premium SSD v2 ou Premium LRS para cargas de trabalho de banco de dados.
# StorageClass for Azure Premium SSD
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: azure-premium-ssd
provisioner: disk.csi.azure.com
parameters:
skuName: Premium_LRS
kind: Managed
volumeBindingMode: WaitForFirstConsumer
allowVolumeExpansion: true
---
# pgBackRest with Azure Blob Storage
backups:
pgbackrest:
configuration:
- secret:
name: pgbackrest-azure-creds
global:
repo1-azure-account: mystorageaccount
repo1-retention-full: "7"
repos:
- name: repo1
schedules:
full: "0 2 * * 0"
differential: "0 2 * * 1-6"
azure:
container: pg-backups
---
# Secret with Azure credentials
apiVersion: v1
kind: Secret
metadata:
name: pgbackrest-azure-creds
namespace: databases
stringData:
azure.conf: |
[global]
repo1-azure-key=BASE64_STORAGE_ACCOUNT_KEYPara Identidade de Carga de Trabalho (o equivalente Azure do AWS IRSA), configure o cluster AKS com o emissor OIDC e crie uma credencial de identidade federada para a conta de serviço pgBackRest. Isto elimina a necessidade de chaves de conta de armazenamento em segredos.
ImplantaçãoGCP GKE
OGKE usa disco permanente (pd-ssd) para armazenamento e GCS para backups pgBackRest. O GKE Workload Identity mapeia contas de serviço Kubernetes para contas de serviço do Google Cloud para autenticação segura e sem chave.
# StorageClass for GKE SSD Persistent Disk
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: pd-ssd
provisioner: pd.csi.storage.gke.io
parameters:
type: pd-ssd
volumeBindingMode: WaitForFirstConsumer
allowVolumeExpansion: true
---
# pgBackRest with GCS
backups:
pgbackrest:
configuration:
- secret:
name: pgbackrest-gcs-creds
repos:
- name: repo1
gcs:
bucket: mycompany-pg-backups
---
# GCS service account key secret
apiVersion: v1
kind: Secret
metadata:
name: pgbackrest-gcs-creds
namespace: databases
stringData:
gcs-key.json: |
{
"type": "service_account",
"project_id": "my-project",
...
}# Enable Workload Identity on GKE
gcloud container clusters update my-cluster \
--workload-pool=my-project.svc.id.goog
# Create and bind IAM service account
gcloud iam service-accounts create pgbackrest-gcs \
--display-name="pgBackRest GCS Access"
gcloud projects add-iam-policy-binding my-project \
--member="serviceAccount:pgbackrest-gcs@my-project.iam.gserviceaccount.com" \
--role="roles/storage.objectAdmin"
gcloud iam service-accounts add-iam-policy-binding \
pgbackrest-gcs@my-project.iam.gserviceaccount.com \
--role="roles/iam.workloadIdentityUser" \
--member="serviceAccount:my-project.svc.id.goog[databases/production-db-pgbackrest]"Recuperação de desastres multirregionais
OPGO oferece suporte a clusters em espera para recuperação de desastres entre regiões. Um cluster em espera reproduz continuamente o WAL do repositório pgBackRest do cluster primário, mantendo uma cópia quente que pode ser promovida a um primário independente no caso de uma falha regional.
# Standby cluster in Region B
apiVersion: postgres-operator.crunchydata.com/v1beta1
kind: PostgresCluster
metadata:
name: production-db-standby
namespace: databases
spec:
postgresVersion: 16
image: registry.crunchydata.com/crunchydata/crunchy-postgres:ubi8-16.6-1
standby:
enabled: true
repoName: repo1
instances:
- name: pgha1
replicas: 2
dataVolumeClaimSpec:
storageClassName: gp3-csi
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 100Gi
backups:
pgbackrest:
image: registry.crunchydata.com/crunchydata/crunchy-pgbackrest:ubi8-2.54.1-0
configuration:
- secret:
name: pgbackrest-s3-creds
repos:
- name: repo1
s3:
bucket: mycompany-pg-backups
endpoint: s3.us-east-1.amazonaws.com
region: us-east-1Para promover o cluster em espera durante um desastre, definastandby.enabled: falsena especificação e aplique a alteração. O PGO promove o standby para um cluster primário independente. Esta promoção é irreversível — você precisará reconstruir o relacionamento de replicação do zero após a recuperação da região primária original.
Bare Metal k3s/Implantação Rancher
A execução do PGO em bare metal k3s com gerenciamento Rancher requer atenção cuidadosa ao armazenamento e à rede, já que você não possui discos gerenciados pelo provedor de nuvem ou balanceadores de carga.
Armazenamento LonghornOLonghorn é um sistema de armazenamento em bloco distribuído e leve para Kubernetes, ideal para ambientes bare metal. Ele replica volumes em vários nós para maior durabilidade e oferece suporte a snapshots, backups e expansão de volumes.
# Install Longhorn via Helm
helm repo add longhorn https://charts.longhorn.io
helm repo update
helm install longhorn longhorn/longhorn \
--namespace longhorn-system \
--create-namespace \
--set defaultSettings.defaultReplicaCount=3 \
--set defaultSettings.storageOverProvisioningPercentage=100 \
--set defaultSettings.storageMinimalAvailablePercentage=15
---
# Longhorn StorageClass for PostgreSQL
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: longhorn-pg
provisioner: driver.longhorn.io
parameters:
numberOfReplicas: "3"
staleReplicaTimeout: "2880"
fsType: ext4
dataLocality: best-effort
volumeBindingMode: WaitForFirstConsumer
allowVolumeExpansion: true
reclaimPolicy: RetainMetalLB para balanceamento de carga
# Install MetalLB
helm repo add metallb https://metallb.github.io/metallb
helm install metallb metallb/metallb --namespace metallb-system --create-namespace
---
# Configure IP address pool
apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
name: pg-pool
namespace: metallb-system
spec:
addresses:
- 192.168.1.200-192.168.1.210
---
apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
name: pg-l2
namespace: metallb-system
spec:
ipAddressPools:
- pg-poolCom o MetalLB configurado, os serviços PGO do tipoLoadBalancerreceberão endereços IP do seu pool, tornando os endpoints primários PostgreSQL e PgBouncer diretamente acessíveis de sua rede sem encaminhamento manual de porta.
pgBackRest com PVC local ou NFS para Bare Metal
# For environments without S3/GCS/Azure Blob, use a PVC-based repo
backups:
pgbackrest:
repos:
- name: repo1
schedules:
full: "0 2 * * 0"
differential: "0 2 * * 1-6"
volume:
volumeClaimSpec:
storageClassName: longhorn-pg
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 200GiPara ambientes air-gapped, você também pode configurar um repositório pgBackRest secundário que envia backups para um compartilhamento NFS ou uma instância MinIO em execução no local, fornecendo uma cópia de backup fora do cluster sem dependências de nuvem.
Configuração de criptografiaTLS/SSL
OPGO gera certificados TLS autoassinados para todas as comunicações internas por padrão – instâncias PostgreSQL, replicação, pgBackRest e PgBouncer se comunicam por canais criptografados sem qualquer gerenciamento manual de certificados. No entanto, para ambientes de produção normalmente você deseja usar certificados assinados pela CA da sua organização.
# Create a TLS secret with your CA-signed certificates
kubectl create secret tls production-db-tls \
--cert=server.crt \
--key=server.key \
-n databases
kubectl create secret generic production-db-ca \
--from-file=ca.crt=ca.crt \
-n databases
---
# Reference in PostgresCluster spec
spec:
customTLSSecret:
name: production-db-tls
customReplicationTLSSecret:
name: production-db-replication-tlsPGO configura PostgreSQL comssl = one define os parâmetrosssl_cert_file,ssl_key_fileessl_ca_fileapropriados. O PgBouncer é configurado de forma semelhante para exigir TLS para conexões de cliente e para usar TLS ao conectar-se a back-ends PostgreSQL.
# Enforce TLS for all client connections via pg_hba.conf
spec:
patroni:
dynamicConfiguration:
postgresql:
pg_hba:
- hostssl all all 0.0.0.0/0 scram-sha-256
- hostssl replication all 0.0.0.0/0 scram-sha-256Configuração PostgreSQL personalizadaPGO expõe a configuração do PostgreSQL por meio da seção de configuração dinâmica do Patroni. Esta é a abordagem recomendada porque Patroni garante que todas as instâncias mantenham uma configuração consistente e lide com as reinicializações quando necessário.
patroni:
dynamicConfiguration:
postgresql:
parameters:
# Memory
shared_buffers: 4GB
effective_cache_size: 12GB
work_mem: 64MB
maintenance_work_mem: 1GB
huge_pages: "try"
# WAL
wal_buffers: 128MB
min_wal_size: 2GB
max_wal_size: 8GB
checkpoint_completion_target: 0.9
wal_compression: lz4
# Query Planning
random_page_cost: 1.1
effective_io_concurrency: 200
default_statistics_target: 200
# Parallelism
max_worker_processes: 16
max_parallel_workers_per_gather: 4
max_parallel_workers: 8
max_parallel_maintenance_workers: 4
# Connections
max_connections: 200
idle_in_transaction_session_timeout: 60000
statement_timeout: 300000
# Logging
log_min_duration_statement: 250
log_checkpoints: "on"
log_connections: "on"
log_disconnections: "on"
log_lock_waits: "on"
log_temp_files: 0
log_autovacuum_min_duration: 0
# Autovacuum Tuning
autovacuum_max_workers: 5
autovacuum_naptime: 30
autovacuum_vacuum_cost_limit: 800
autovacuum_vacuum_scale_factor: 0.02
autovacuum_analyze_scale_factor: 0.01Esses parâmetros assumem um nó com 16 núcleos CPU e 32 GB de RAM dedicados ao PostgreSQL. Ajuste oshared_bufferspara aproximadamente 25% da RAM disponível e oeffective_cache_sizepara aproximadamente 75%. As configurações do WAL e do ponto de verificação são ajustadas para cargas de trabalho com uso intenso de gravação — reduza omax_wal_sizepara sistemas com uso intenso de leitura, onde a frequência do ponto de verificação é menos importante.
Gerenciamento de usuários e banco de dados
OPGO gerencia usuários e bancos de dados PostgreSQL de forma declarativa por meio da seçãousersda especificaçãoPostgresCluster. Quando você adiciona um usuário, o PGO cria a função no PostgreSQL, gera uma senha aleatória e armazena as credenciais em um segredo Kubernetes.
users:
- name: appuser
databases: ["appdb"]
options: "NOSUPERUSER NOCREATEDB NOCREATEROLE"
- name: readonly
databases: ["appdb"]
options: "NOSUPERUSER NOCREATEDB NOCREATEROLE"
- name: migrations
databases: ["appdb"]
options: "NOSUPERUSER CREATEDB"
- name: monitoring
databases: ["postgres"]
options: "NOSUPERUSER LOGIN"
---
# Access credentials from the generated secret
kubectl get secret production-db-pguser-appuser -n databases -o jsonpath='{.data.password}' | base64 -d
# The secret also contains a full connection URI
kubectl get secret production-db-pguser-appuser -n databases -o jsonpath='{.data.uri}' | base64 -d
# postgresql://appuser:PASSWORD@production-db-primary.databases.svc:5432/appdbPara conceder acesso somente leitura, use o recursodatabaseInitSQLpara executar o SQL na criação do cluster que cria uma função somente leitura e concede SELECT em todas as tabelas.
# init.sql ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
name: init-sql-configmap
namespace: databases
data:
init.sql: |
-- Read-only role for reporting users
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO readonly;
GRANT CONNECT ON DATABASE appdb TO readonly;
GRANT USAGE ON SCHEMA public TO readonly;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO readonly;
-- Extensions
CREATE EXTENSION IF NOT EXISTS pg_stat_statements;
CREATE EXTENSION IF NOT EXISTS pgcrypto;
CREATE EXTENSION IF NOT EXISTS pg_trgm;Atualizações contínuas e atualizações de versão principal doOPGO lida com atualizações de versões secundárias por meio de atualizações contínuas. Quando você altera a tag de imagem na especificaçãoPostgresClusterpara uma versão de patch mais recente, o PGO atualiza as instâncias uma de cada vez, começando com réplicas e terminando com a primária (o que aciona uma alternância Patroni para minimizar o tempo de inatividade).
# Minor version upgrade: change the image tag
spec:
image: registry.crunchydata.com/crunchydata/crunchy-postgres:ubi8-16.7-0As atualizações de versão principal (por exemplo, PostgreSQL 15 a 16) requerempg_upgrade, que o PGO orquestra por meio de umPGUpgradeCRD separado.
apiVersion: postgres-operator.crunchydata.com/v1beta1
kind: PGUpgrade
metadata:
name: production-db-upgrade
namespace: databases
spec:
postgresClusterName: production-db
fromPostgresVersion: 15
toPostgresVersion: 16
image: registry.crunchydata.com/crunchydata/crunchy-upgrade:ubi8-5.7.2-0O processo de atualização desliga o cluster existente, executa opg_upgrade --linkpara realizar uma atualização local usando links físicos (minimizando a cópia de dados), verifica a atualização e inicia o cluster na nova versão. Sempre faça um backup completo antes de iniciar uma atualização de versão principal e teste primeiro o procedimento em um cluster que não seja de produção.
# Pre-upgrade checklist
# 1. Take a full backup
kubectl annotate postgrescluster production-db -n databases \
postgres-operator.crunchydata.com/pgbackrest-backup="$(date +%s)" \
--overwrite
# 2. Verify backup completed
kubectl exec -it production-db-pgha1-0 -n databases -- pgbackrest info --stanza=db
# 3. Shutdown the cluster (set replicas to 0 or use PGO shutdown annotation)
kubectl annotate postgrescluster production-db -n databases \
postgres-operator.crunchydata.com/shutdown="$(date +%s)" \
--overwrite
# 4. Apply the PGUpgrade resource
kubectl apply -f pg-upgrade.yaml
# 5. Update the PostgresCluster spec with the new version and image
# 6. Remove the shutdown annotation to start the upgraded clusterRecomendações de ajuste de produçãoA execução do PGO em produção requer atenção a diversas áreas operacionais além da implantação inicial.
Desempenho de armazenamento- Dados separados e volumes WAL: Sempre use
walVolumeClaimSpecpara colocar WAL em um PVC dedicado. Isso evita que atividades pesadas de gravação do WAL contenham com E/S de dados. - Use armazenamento de alto IOPS: gp3 (AWS), Premium SSD v2 (Azure), pd-ssd (GCP) ou Longhorn com suporte NVMe em bare metal. Os padrões de E/S aleatórios da PostgreSQL exigem armazenamento de baixa latência.
- Expansão de volume: Certifique-se de que seu StorageClass tenha
allowVolumeExpansion: true. O PGO pode expandir PVCs sem interrupções em provedores de armazenamento compatíveis.
PGO cria automaticamente PDBs para suas instâncias PostgreSQL, mas verifica se eles são apropriados para seus requisitos de HA.
# Check PDBs
kubectl get pdb -n databases
NAME MIN AVAILABLE MAX UNAVAILABLE ALLOWED DISRUPTIONS AGE
production-db-pgha1 1 N/A 2 7dSolicitações e limites de recursos- Defina solicitações de memória iguais aos limites para pods de banco de dados para evitar eliminações de OOM e garantir classe de QoS garantida.
- Defina solicitações CPU de forma conservadora e limite mais alto para permitir intermitência durante operações de vácuo ou manutenção.
- Monitore o uso real por meio das métricas do pgMonitor e ajuste trimestralmente.
- Sempre conecte através do PgBouncer, não diretamente ao PostgreSQL.
- Defina
max_connectionsem PostgreSQL para um valor que leve em conta o tamanho do pool do PgBouncer mais as conexões do sistema (replicação, monitoramento, superusuário). - Use o modo de pool de transações para aplicativos da web. Mude para o pool de sessões somente se seu aplicativo usar instruções preparadas ou estado no nível da sessão (por exemplo, comandos
SET, tabelas temporárias).
- Teste restaurações regularmente criando um cluster clone a partir de backup e executando testes de fumaça em nível de aplicativo.
- Monitore o alerta
PgBackRestStaleBackuppara garantir que os backups sejam concluídos dentro do cronograma. - Valide o PITR restaurando carimbos de data/hora específicos e verificando a consistência dos dados.
# Clone cluster for backup validation
apiVersion: postgres-operator.crunchydata.com/v1beta1
kind: PostgresCluster
metadata:
name: backup-validation
namespace: databases
spec:
postgresVersion: 16
image: registry.crunchydata.com/crunchydata/crunchy-postgres:ubi8-16.6-1
dataSource:
postgresCluster:
clusterName: production-db
repoName: repo1
options:
- --type=time
- --target="2026-04-12 06:00:00+00"
instances:
- name: pgha1
replicas: 1
dataVolumeClaimSpec:
storageClassName: gp3-csi
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 100Gi
backups:
pgbackrest:
repos:
- name: repo1
volume:
volumeClaimSpec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 50GiPolíticas de Rede# Restrict PostgreSQL traffic to known namespaces
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: postgres-network-policy
namespace: databases
spec:
podSelector:
matchLabels:
postgres-operator.crunchydata.com/cluster: production-db
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchLabels:
app-access: database
- podSelector:
matchLabels:
postgres-operator.crunchydata.com/cluster: production-db
ports:
- port: 5432
protocol: TCP
- port: 9187
protocol: TCPLista de verificação de monitoramento- Atraso de replicação: Alerta quando qualquer réplica excede 100 MB ou 60 segundos de atraso.
- Saturação de conexão: Alerta quando conexões ativas ultrapassam 80% do
max_connections. - Anomalias na taxa de transação: Defina como base seu TPS e alerte sobre desvios significativos.
- Uso do disco: Alerta nos limites de 70% e 85% com runbooks de expansão de volume.
- Atualização do backup: Alerta quando o último backup completo é mais antigo que sua janela RPO.
- Contenção de bloqueio: Alerta sobre bloqueios prolongados (> 30 segundos) que podem indicar bugs no aplicativo.
- Autovacuum health: Alerta quando as mesas não foram aspiradas em mais de 24 horas.
Um plano de recuperação de desastres só é útil se tiver sido testado. O runbook a seguir descreve os principais procedimentos para recuperação de cenários de falha comuns.
Falha de pod únicoPatroni e Kubernetes cuidam disso automaticamente. Se o pod primário travar, Patroni promove uma réplica dentro de 10 a 30 segundos. A Kubernetes reinicia o pod com falha, que se junta novamente como uma réplica.
Falha de nó únicoSe um nó que executa um pod PostgreSQL morrer, o Kubernetes reagendará o pod em um nó íntegro. O pod se conecta ao PVC existente (se o armazenamento estiver conectado à rede) ou restaura a partir do backup (se o armazenamento local foi usado). As regras de antiafinidade do pod garantem que as instâncias restantes continuem atendendo ao tráfego.
Perda Completa de Cluster
Se todo o cluster Kubernetes for perdido, implemente um novo cluster, instale o PGO e crie um novoPostgresClustercom umdataSourceapontando para o repositório de backup. O PGO restaura o backup mais recente e reproduz o WAL para o ponto disponível mais recente.
Se a região primária for perdida, promova o cluster em espera configurandostandby.enabled: false. Atualize seu DNS ou balanceador de carga para apontar para a nova região primária. Depois que a região original for recuperada, você poderá reconstruir o relacionamento de espera ao contrário.
# Promote standby cluster
kubectl patch postgrescluster production-db-standby -n databases --type merge \
-p '{"spec":{"standby":{"enabled":false}}}'
# Verify the cluster is now an independent primary
kubectl exec -it production-db-standby-pgha1-0 -n databases -- patronictl listReferência Rápida dos Comandos Operacionais# Cluster status
kubectl get postgrescluster -n databases
kubectl describe postgrescluster production-db -n databases
# Patroni status
kubectl exec -it production-db-pgha1-0 -n databases -- patronictl list
kubectl exec -it production-db-pgha1-0 -n databases -- patronictl history
# Connect to PostgreSQL
kubectl exec -it production-db-pgha1-0 -n databases -- psql -U postgres
# Backup info
kubectl exec -it production-db-pgha1-0 -n databases -- pgbackrest info --stanza=db
# Manual backup
kubectl annotate postgrescluster production-db -n databases \
postgres-operator.crunchydata.com/pgbackrest-backup="$(date +%s)" --overwrite
# Restart PostgreSQL (rolling)
kubectl annotate postgrescluster production-db -n databases \
postgres-operator.crunchydata.com/restart="$(date +%s)" --overwrite
# Logs
kubectl logs production-db-pgha1-0 -n databases -c database
kubectl logs production-db-pgha1-0 -n databases -c pgbackrest
kubectl logs production-db-pgha1-0 -n databases -c exporter
# PgBouncer status
kubectl exec -it $(kubectl get pod -l postgres-operator.crunchydata.com/role=pgbouncer -n databases -o name | head -1) -n databases -- psql -U pgbouncer pgbouncer -c "SHOW POOLS;"
# Scale replicas
kubectl patch postgrescluster production-db -n databases --type merge \
-p '{"spec":{"instances":[{"name":"pgha1","replicas":5}]}}'ConclusãoOCrunchy Data PGO transforma o PostgreSQL no Kubernetes de uma carga operacional em um sistema declarativo e gerenciável. OPostgresClusterCRD captura toda a implantação — instâncias, replicação, backup, pooling, monitoramento, TLS — em um único recurso controlado por versão. Patroni fornece failover automático testado em batalha. O pgBackRest oferece backup de nível empresarial com recuperação pontual para qualquer armazenamento de objetos na nuvem. O PgBouncer lida com o pool de conexões que o modelo de processo por conexão do PostgreSQL exige em escala. E o pgMonitor alimenta as métricas necessárias no Prometheus e Grafana para obter visibilidade operacional.
Os padrões de implantação no AWS EKS, Azure AKS, GCP GKE e bare metal k3s compartilham a mesma especificação principal doPostgresCluster– o que muda é a classe de armazenamento, a configuração do repositório de backup e a camada de rede. Essa consistência é o valor real de uma abordagem baseada em operadores: sua equipe aprende uma ferramenta, um modelo operacional e um conjunto de runbooks que funcionam em qualquer lugar.
Comece com um cluster de três instâncias, PgBouncer no modo de pooling de transações, uma programação semanal de backup completo mais diferencial diário e os principais alertas Prometheus. Valide seu procedimento de restauração de backup no primeiro dia — não quando você precisar dele pela primeira vez. Expanda para clusters de espera multirregionais, replicação síncrona e ajuste avançado à medida que seus requisitos de disponibilidade e maturidade operacional aumentam. O operador cuida da mecânica; sua responsabilidade é entender a arquitetura o suficiente para fazer as compensações certas para sua carga de trabalho.