135 lines
5.6 KiB
YAML
135 lines
5.6 KiB
YAML
# =============================================================================
|
|
# P4.7 — PgBouncer em transaction mode, UM POOL POR DATABASE E ROLE.
|
|
#
|
|
# ── POR QUE UM POOL POR ROLE, E NAO UM POOL SO ───────────────────────────────
|
|
#
|
|
# Em transaction mode, a conexao de SERVIDOR e devolvida ao pool no fim de cada
|
|
# transacao e entregue a outro cliente. Se dois tenants dividissem o mesmo pool,
|
|
# o segundo herdaria a sessao autenticada do primeiro: mesmo `search_path`,
|
|
# mesmas GUCs de sessao, mesmo usuario efetivo no servidor.
|
|
#
|
|
# Todo o isolamento de P2.1 — database por tenant, role por tenant, CONNECT
|
|
# revogado — seria contornado por baixo, sem erro nenhum e sem log.
|
|
#
|
|
# O PgBouncer separa por (database, user), e e por isso que cada tenant tem o
|
|
# proprio par. Conferido em 2026-09-18 com `SHOW POOLS`: seis clientes de dois
|
|
# tenants produziram seis conexoes de servidor, tres em cada database, sem
|
|
# cruzamento.
|
|
#
|
|
# ── A LISTA DE DATABASES NAO E GERADA SOZINHA, E ISSO E PROPOSITAL ───────────
|
|
#
|
|
# Cada tenant novo acrescenta uma linha aqui. Um `*` generico funcionaria e
|
|
# tiraria a fronteira: qualquer database da instancia viraria alcancavel pelo
|
|
# bouncer, inclusive `postgres` e `template1`. A lista explicita e a mesma ideia
|
|
# do `endurecer_instancia.sql` — o que nao esta escrito nao existe.
|
|
#
|
|
# O provisionamento automatico da fase 5 (card 163) e quem passa a escrever esta
|
|
# linha; ate la, e passo do runbook.
|
|
#
|
|
# ── userlist.txt NAO FICA AQUI ───────────────────────────────────────────────
|
|
#
|
|
# Ele carrega os hashes SCRAM dos roles. Vem de um SealedSecret, gerado no
|
|
# servidor a partir do proprio `pg_authid` — do mesmo jeito que as outras
|
|
# credenciais deste tenant. Ver `scripts/gerar_credenciais_plataforma.sh`.
|
|
# =============================================================================
|
|
apiVersion: v1
|
|
kind: ConfigMap
|
|
metadata:
|
|
name: pgbouncer-conf
|
|
namespace: plataforma-prod
|
|
data:
|
|
pgbouncer.ini: |
|
|
[databases]
|
|
; Uma linha por tenant. Sem `*`: ver o cabecalho deste arquivo.
|
|
athleticmap_plataforma = host=pg-compartilhado port=5432 dbname=athleticmap_plataforma
|
|
athleticmap_escolinha = host=pg-compartilhado port=5432 dbname=athleticmap_escolinha
|
|
athleticmap_piloto = host=pg-compartilhado port=5432 dbname=athleticmap_piloto
|
|
athleticmap_demo = host=pg-compartilhado port=5432 dbname=athleticmap_demo
|
|
athleticmap_acme = host=pg-compartilhado port=5432 dbname=athleticmap_acme
|
|
|
|
[pgbouncer]
|
|
listen_addr = 0.0.0.0
|
|
listen_port = 6432
|
|
auth_type = scram-sha-256
|
|
auth_file = /etc/pgbouncer/userlist.txt
|
|
|
|
; TRANSACTION, e nao session: com session pooling a conexao de servidor fica
|
|
; presa ao cliente ate ele desconectar, e um runtime com pool de 20 seguraria
|
|
; 20 conexoes reais o dia inteiro. O ganho do bouncer viria a zero.
|
|
pool_mode = transaction
|
|
|
|
; 400 clientes contra ate 15 servidores por pool. A conta esta no
|
|
; 12-postgres-compartilhado.yaml: 18 tenants x 15 = 270, e o
|
|
; max_connections do Postgres e 300.
|
|
max_client_conn = 400
|
|
default_pool_size = 10
|
|
min_pool_size = 0
|
|
reserve_pool_size = 5
|
|
reserve_pool_timeout = 3
|
|
|
|
; VAZIO de proposito. O padrao (`DISCARD ALL`) e para session pooling; em
|
|
; transaction mode o PgBouncer ja garante que nada de sessao atravessa, e
|
|
; rodar DISCARD a cada transacao custa uma ida ao servidor por transacao.
|
|
server_reset_query =
|
|
|
|
; O driver JDBC envia `extra_float_digits` no startup, e o PgBouncer recusa
|
|
; parametro que nao conhece. Sem esta linha, TODA conexao Java falha com
|
|
; "unsupported startup parameter" — e a mensagem nao diz que o culpado e o
|
|
; bouncer.
|
|
ignore_startup_parameters = extra_float_digits
|
|
|
|
admin_users = postgres
|
|
stats_users = postgres
|
|
|
|
log_connections = 0
|
|
log_disconnections = 0
|
|
---
|
|
apiVersion: apps/v1
|
|
kind: Deployment
|
|
metadata:
|
|
name: pgbouncer
|
|
namespace: plataforma-prod
|
|
spec:
|
|
# Pode ter mais de uma replica: o PgBouncer nao guarda estado entre conexoes.
|
|
# Mas cada replica tem o PROPRIO pool — duas replicas dobram as conexoes de
|
|
# servidor, e a conta de max_connections precisa ser refeita antes de subir a
|
|
# segunda.
|
|
replicas: 1
|
|
selector:
|
|
matchLabels: { app: pgbouncer }
|
|
template:
|
|
metadata:
|
|
labels: { app: pgbouncer }
|
|
spec:
|
|
containers:
|
|
- name: pgbouncer
|
|
# A tag e `v1.25.2-p0`, e NAO `1.25.2`: essa segunda nao existe no Docker Hub
|
|
# e o pod ficaria em ImagePullBackOff. Achado pelo ensaio local
|
|
# (scripts/verificar_migracao_compartilhado.sh) antes do primeiro deploy.
|
|
image: docker.io/edoburu/pgbouncer:v1.25.2-p0
|
|
ports: [{ containerPort: 6432 }]
|
|
volumeMounts:
|
|
- { name: conf, mountPath: /etc/pgbouncer/pgbouncer.ini, subPath: pgbouncer.ini }
|
|
- { name: userlist, mountPath: /etc/pgbouncer/userlist.txt, subPath: userlist.txt }
|
|
resources:
|
|
requests: { cpu: 50m, memory: 32Mi }
|
|
limits: { cpu: 500m, memory: 128Mi }
|
|
readinessProbe:
|
|
tcpSocket: { port: 6432 }
|
|
initialDelaySeconds: 5
|
|
periodSeconds: 10
|
|
volumes:
|
|
- name: conf
|
|
configMap: { name: pgbouncer-conf }
|
|
- name: userlist
|
|
secret: { secretName: pgbouncer-userlist }
|
|
---
|
|
apiVersion: v1
|
|
kind: Service
|
|
metadata:
|
|
name: pgbouncer
|
|
namespace: plataforma-prod
|
|
spec:
|
|
selector: { app: pgbouncer }
|
|
ports: [{ port: 6432, targetPort: 6432 }]
|