diff --git a/history/2026-04-15-pgcat-ha-promotion.md b/history/2026-04-15-pgcat-ha-promotion.md new file mode 100644 index 0000000..6678642 --- /dev/null +++ b/history/2026-04-15-pgcat-ha-promotion.md @@ -0,0 +1,67 @@ +--- +date: 2026-04-15 +topic: pgcat HA 승격 (Step 0) +areas: + - infra/postgresql-ha.md +tags: [history, pgcat, patroni, postgresql, ha] +--- + +Patroni multi-host 마이그레이션 Step 0 — pgcat 자체를 HA로 승격. 이후 Step 1에서 pgcat 를 Patroni REST API aware 로 전환할 때, pgcat 자체가 새 SPoF 가 되지 않도록 선행. + +## 변경 내용 + +### helm-charts (`kaffa/helm-charts`) + +- `charts/app` v0.3.0 → **v0.5.0** + - `templates/deployment.yaml`: `affinity`, `topologySpreadConstraints` 블록 지원 추가 + - `templates/pdb.yaml` 신규 — `podDisruptionBudget.minAvailable` / `maxUnavailable` 지원 +- `values/pgcat.yaml`: + - `replicaCount: 2` + - `affinity.podAntiAffinity.requiredDuringSchedulingIgnoredDuringExecution` (`app=pgcat`, `topologyKey: kubernetes.io/hostname`) + - `podDisruptionBudget.minAvailable: 1` + +Commit: `421baef` (`pgcat HA: replicas=2, podAntiAffinity(hostname), PDB minAvailable=1`) + +### ArgoCD + +`pgcat` Application 은 `syncPolicy.automated { prune: true, selfHeal: true }` 설정. git push 직후 `argocd.argoproj.io/refresh=hard` annotate 로 즉시 sync 트리거. + +## 롤아웃 관찰 + +- 시작: pgcat-545b8878b9-n45h8 (kr2) 단독 Running +- 중간: 구 RS + 신 RS surge 상태 (최대 4 pod), `maxSurge=25%` / `maxUnavailable=25%` 기본 strategy 동작 +- 종료 (~55초): pgcat-549446cd6b-4hnk2 (**kr1**) + pgcat-549446cd6b-9rhk4 (**hp2**) 2/2 Running +- PDB `pgcat`: `minAvailable=1`, `AllowedDisruptions=1` — 정상 + +### 노드 분산 검증 + +``` +pgcat-549446cd6b-4hnk2 2/2 Running incus-kr1 10.42.1.107 +pgcat-549446cd6b-9rhk4 2/2 Running incus-hp2 10.42.2.175 +``` + +kr1 + hp2 — 서로 다른 노드. PodAntiAffinity 동작 확인. + +### 서비스 Endpoints + +``` +pgcat 10.42.1.107:6432,10.42.2.175:6432 +``` + +두 replica 모두 Service 뒤에 등록. + +## 다운스트림 영향 + +rollout 중 NocoDB/n8n 로그 tail. 결과: + +- NocoDB: 단일 `Connection Error: Connection ended unexpectedly` 1회 — pod 재시작 없음, 45h 업타임 유지. knex/pg 자동 재연결 동작. +- n8n: 에러 없음. + +## 롤백 경로 + +- `values/pgcat.yaml` 에서 `replicaCount: 2` → `1`, `affinity`/`podDisruptionBudget` 블록 제거 +- 또는 chart 측: 구 버전 (v0.3.0) 으로 `targetRevision` 지정 불가능 (단일 리포지토리 path) — 필요 시 역커밋 + +## 후속 작업 + +- Step 1: pgcat `pgcat.toml` 에 `use_patroni_api = true` + `patroni_api_port = 8008` 추가, 각 pool `shards.0.servers` 를 Patroni 3노드 IP (`10.100.2.5`, `10.100.3.185`, `10.100.1.83`) 로 교체. 관련 Outline 문서: `07497dd8-c8f7-4027-bb27-7d2cf10623a0` diff --git a/infra/postgresql-ha.md b/infra/postgresql-ha.md index 46a80ac..26c521c 100644 --- a/infra/postgresql-ha.md +++ b/infra/postgresql-ha.md @@ -121,6 +121,18 @@ NocoDB, n8n 등 K3s 내부 애플리케이션은 **pgcat**(연결 풀링)을 통 nocodb/n8n → pgcat (db.svc.cluster.local:6432) → HAProxy 192.168.9.1:5432 → Patroni Leader ``` +### pgcat HA 구성 + +`db/pgcat` Deployment는 **2 replica**, 서로 다른 K3s 노드에 강제 분산. + +- `replicaCount: 2` (Helm values `values/pgcat.yaml`) +- `podAntiAffinity.requiredDuringSchedulingIgnoredDuringExecution` (topologyKey `kubernetes.io/hostname`) +- `PodDisruptionBudget` `minAvailable: 1` — 최소 1개 pod 유지 보장 +- `Service` ClusterIP 로 두 endpoint 모두 등록 — 클라이언트 요청은 kube-proxy 라운드로빈 +- 노드 장애 또는 롤아웃 시 나머지 replica가 트래픽 흡수. NocoDB는 일시적 `Connection ended unexpectedly` 후 자동 재연결 (drop 1회, 재시작 없음) + +`ghcr.io/postgresml/pgcat:latest`. app chart 템플릿은 `affinity` / `topologySpreadConstraints` / `podDisruptionBudget` 옵션 지원 (v0.5.0+). + `db/pgcat-config` ConfigMap의 각 풀의 `shards.0.servers`는 **HAProxy 단일 백엔드만 가리켜야 함**: ```toml