157 lines
6.8 KiB
Markdown
157 lines
6.8 KiB
Markdown
---
|
||
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주 관측 후 하드닝 필요
|