Files
obsidian/history/2026-04-16-pgpool-n8n-poc.md

157 lines
6.8 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
date: 2026-04-16
topic: pgpool-II PoC (n8n 전용 전환)
areas:
- infra/postgresql-ha.md
tags: [history, pgpool, pgcat, patroni, postgresql, poc]
---
n8n 이 Patroni failover 와 etcd 순단에 풀 좀비로 취약 — pgcat 의 클라이언트-측 연결 관리로는 해소 불가. pgpool-II streaming_replication 모드 PoC 로 n8n 만 전환, 검증 결과 양쪽 시나리오에서 `무에러 또는 <2초 자가복구` 달성.
## 전환 경로
- 전환 전: n8n → pgcat.db.svc:6432 → OpenWrt HAProxy 192.168.9.1:5432 → Patroni leader
- 전환 후: n8n → pgpool.db.svc:9999 → Patroni 3노드 직결
NocoDB·Outline 은 pgcat 유지. 7일 관측 후 확대 여부 판단.
## 이미지 선택
- kappa 원 스펙: `pgpool/pgpool:4.6.3 (공식)` — 존재하지 않음. `pgpool/pgpool` 리포지토리 최대 태그 **4.4.3** (2024 이후 방치), 이후 버전은 `bitnamilegacy/pgpool` 에만 존재
- 1차 시도: Bitnami 4.6.3 — env 기반 설정 자동화가 scram-sha-256 패스워드를 pgpool 내부 포맷으로 재해시해서 백엔드 인증 실패. 디버깅 폐기 (kappa 지시)
- 2차 채택: **`pgpool/pgpool:4.4.3`** (공식) + 최소 ConfigMap 마운트
## 최종 설정 (`helm-charts/pgpool/`)
ArgoCD 관리 대상. `kaffa/helm-charts` 리포 `pgpool/` 디렉토리 raw manifest. Application 매니페스트 `kubectl apply` 로 1회 부트스트랩.
### 핵심 구성
`pgpool.conf` (~30 라인):
```
listen_addresses = '*'
port = 9999
backend_clustering_mode = 'streaming_replication'
backend_hostname0 = '10.100.2.5' # postgres-1
backend_port0 = 5432
backend_weight0 = 1
backend_flag0 = 'ALLOW_TO_FAILOVER'
# ... backend1=postgres-2, backend2=postgres-3 동일 패턴
sr_check_user = 'sr_check'
sr_check_password = '<plaintext>'
sr_check_database = 'postgres'
sr_check_period = 10
health_check_user = 'sr_check'
health_check_password = '<plaintext>'
health_check_period = 10
health_check_timeout = 5
failover_on_backend_error = off # Patroni 가 promotion 수행
failover_command = ''
follow_primary_command = ''
use_watchdog = off
enable_pool_hba = on
allow_clear_text_frontend_auth = on
load_balance_mode = off # 모든 쿼리 primary
num_init_children = 32
max_pool = 4
connection_cache = on
```
`pool_hba.conf`:
```
local all all trust
host all all 0.0.0.0/0 password
host all all ::/0 password
```
### 인증 설계 핵심
Postgres 가 cluster-wide `password_encryption = scram-sha-256` 이라 모든 역할 비밀번호는 SCRAM 해시로 저장. pgpool 이 백엔드에 scram-sha-256 auth 하려면 plaintext 필요.
- `pool_passwd` **미사용** — pgpool 이 plaintext 엔트리를 자동 md5 로 변환하면 backend SCRAM 거절되어 `failed to authenticate with backend using SCRAM` 발생
- `allow_clear_text_frontend_auth=on` + pool_hba `password` 메소드 → 클라이언트가 plaintext 로 전송 → pgpool 이 그 값을 backend SCRAM challenge-response 에 그대로 사용
- K8s Service 내부 트래픽이므로 clear-text 는 클러스터 내 허용
- 장기: pgpool PGPOOLKEYFILE + AES 암호화 password 도입 검토 (Bitnami 가 하는 방식)
### Secret 처리
`pgpool-secrets` K8s Secret (수동 `kubectl create`, git 미추적):
- `sr_check_password` — 16-byte hex 랜덤
- `n8n_password``n8n` (pgcat 와 동일)
entrypoint.sh 가 env 에서 sed 로 `pgpool.conf.tmpl``__SR_CHECK_PASSWORD__` 를 치환하여 emptyDir 에 `pgpool.conf` 렌더. 정규 운영 승격 시 ExternalSecret + Vault 로 이관.
### Patroni 에 sr_check role 생성
```sql
CREATE ROLE sr_check WITH LOGIN REPLICATION PASSWORD '<hex>';
GRANT pg_monitor TO sr_check;
```
postgres-2(당시 leader) 에서 실행, async streaming 으로 postgres-1/postgres-3 에 자동 전파. pg_hba 는 `host all all 0.0.0.0/0 md5` 이미 설정되어 있어 추가 변경 불필요.
## 검증 시나리오
### 1. Patroni switchover (postgres-3 → postgres-2)
```
T0 T1(+4.0s) T1+2s
switchover → done new primary 라우팅 확립
```
- n8n 로그: `failed to create a backend 2 connection` × 1 → `Database connection recovered`
- pgpool `SHOW POOL_NODES`: `last_status_change` 가 switchover 시점에 갱신, `role=primary` 가 새 노드로 이동
- n8n.inouter.com 200 유지
- **자동 복구 ~2초**
비교: 같은 시나리오 pgcat 에서는 client 측 pg 풀이 idle 소켓 재사용으로 좀비 → pod restart 필요 (2026-04-15 19:53 UTC 사고 1038 에러).
### 2. mbp etcd 60초 stop
```
T0 T+30s 중간 쓰기 T+60s T+70s
mbp stop → SELECT OK, write OK → mbp start → 확인: 계속 쓰기 OK
```
- n8n 에러 **0건**, HTTP 200 유지
- pgpool 이 backend Patroni 자체를 직접 봄 — etcd 쿼럼은 2노드(NAS + jp1) 로 유지되어 Patroni leader 변경 없음, pgpool 경로 영향 없음
비교: pgcat 경로에서는 mbp etcd 56초 다운 시 n8n `Database connection timed out` 캐스케이드 → 503 → pod restart 필수 (오늘 오후 인시던트).
## 배포 아티팩트
- helm-charts 디렉토리: `pgpool/``configmap.yaml`, `deployment.yaml`, `service.yaml`, `pdb.yaml`
- ArgoCD Application: `pgpool` (namespace `argocd`, source path `pgpool`, selfHeal + prune)
- K8s 리소스: namespace `db` (pgcat 와 공존)
## 롤백
n8n ConfigMap `n8n-app-config` 에서 `DB_POSTGRESDB_HOST``pgpool.db.svc.cluster.local:9999` 에서 `pgcat.db.svc.cluster.local:6432` 로 되돌리고 rollout restart. 소요 시간 ~30초.
## 7일 관측 계획 (만료 2026-04-23)
- 자연 Patroni failover 발생 시 n8n 에러 창 <5초 유지 여부
- pgpool `SHOW STATS` / `SHOW POOL_NODES` 주간 샘플링
- n8n 일별 DB 에러 카운트 (목표 <10/day 비-failover 시)
- pgpool 리소스 사용량 (num_init_children=32 × max_pool=4 = 최대 128 backend connections per pod × 2 pod = 256 total. 현재 n8n 평균 idle 수준 확인 필요)
긍정 관측이면 NocoDB → pgpool, Outline → pgpool 단계 전환. 부정이면 pgpool 해체 + pgcat 복귀.
## 참조
- helm-charts commit 시리즈: a6f5991(초기) → e1dcd6d(bitnami 전환) → 213babb(scram) → 13dfae1(port 수정) → d3dde47(detach_false_primary) → bc6faae(공식 이미지 피봇) → 74ca477(pool_passwd 제거) → **9bc3a24(clear-text auth, 최종)**
- 선행 조사: n8n 풀 좀비 `230ec530-b8a6-406c-9165-35c9eb2d8282`
- pgcat 후속: TCP keepalive `129fbf50-e69b-47fd-ad55-3f5ff9066caf`
- retry_timeout 상향 → mbp etcd hiccup 시 n8n 503 사고 (오늘 오후) → pgpool PoC 의 직접 동기
## 미해결 / Syn 공유
- `pgpool/pgpool` 4.4.3 이후 방치 — pgpool 공식 이미지의 미래 불확실. 대안: Bitnami legacy 계속 사용하거나 우리가 커스텀 빌드
- plaintext pool_passwd 우회 — scram-sha-256 백엔드 + pgpool 백엔드 auth 에 권고 방식은 AES 암호화. 1주 관측 후 하드닝 필요