feat(plataforma): PgBouncer do compartilhado (P4.7)
This commit is contained in:
@@ -0,0 +1,15 @@
|
||||
---
|
||||
apiVersion: bitnami.com/v1alpha1
|
||||
kind: SealedSecret
|
||||
metadata:
|
||||
creationTimestamp: null
|
||||
name: pgbouncer-userlist
|
||||
namespace: plataforma-prod
|
||||
spec:
|
||||
encryptedData:
|
||||
userlist.txt: AgAnzQprfzf6gFrchs2gc7gm5fLfrbnvyQkJtLCqxuzHda0ufLoFojRS+JWzTcMrRb/R1N+EP5BHZ9uqXHV7uf8VgsSVUbT8DjjhtHjdZQcrK5NL1VNEKkkPERDBwR+a1rR5xIBQ6M/DQy7jqlTNC32AsfP+MbGScUw8Weq3FKN1MSxzsNIIduxFvosYeirJPvHk7mklfUPw4jd3OPwYeMlQlinbPzNEIK9ulbbqNdkkmgdzWt48soCJVSdSN9zi1XVlVhqxQpL/PDBTIgmfGJP0+iQerfqEFH0+yi0PV8RugmRoBWFU29eQjQcs7ra+jcI6hzxKfd8CaYQI1CkA+O1R0hzzxUZC92SAJ8XwVnb6JioWB8uSDV59cxKjtu+ug4hhoL42nepTd6O506fFV80Ax3qAu+PSKXtYlWsihncKS7JJcdy7BxS2VvBHzOV4W3+vVHNcs0T2tLL7afzfUlU01wFhnbC5ZfBbansayxSaoj41rKH/v+I3WtIJmu9JHbgqlNQu6KTCM3xhyN5fp1pgl7kNGIYrsQ+vYTW0y1jNEIWNNk8+McWkvu662CdPYxR+JqdI1aelVzMJP1X1UtgNHa7rvw7FSzELdE6yuvHRbGisuW+LXmCiiJ4X33VpB5mc1e6tdJBaMdeLsbmfwcv7MDGLUx0v9FfBWZe4IOAkDCrqlMCXm7WXoE5kokcTf7Zcz+ME0wY/awrIQtWHs0XSUZc70dToe9L7BPLN5t8zHbM8j0NYGA8KG4/W389oTg6XuV7OJn/7ZaNL+ef2aDIFa+AY//g7TgeeVfP/DmMiV077pRIiUt4Px7EalnIkGZlVKjRXL7dBW/6vU6sQguh4Phghe1uOxCHG3wYWms6E4brZ6Bi5G7d5WNz8ssx1JPzfqiPnh2uQmT9fkCY2RywsrkFumZvV8LGPwsU/VPWaTiBGd9tR7x6cwTHhmBKHSLyrt6FGjeYeBzhE8FbJXx7R4SgBYBkNRL7gG/I/0R/gzS1GtaprLXWCA3QVerie/FjMkMoCW6J6A7ABEfF58fv2FbIot234wueajv9/GtkeJ7KPpBqBUNKr3OWMwH3urJ9xN1fm
|
||||
template:
|
||||
metadata:
|
||||
creationTimestamp: null
|
||||
name: pgbouncer-userlist
|
||||
namespace: plataforma-prod
|
||||
@@ -0,0 +1,134 @@
|
||||
# =============================================================================
|
||||
# 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 }]
|
||||
Reference in New Issue
Block a user