Files
obsidian/infra/postgresql-ha.md
Heimdall eadd6ab991 infra/apisix: K3s 내부 etcd 복귀 + destructive sync 가이드 + CRD 전환 기록
apisix.md:
- 서울 APISIX etcd: 외부 통합 → K3s 내부 apisix-etcd StatefulSet (2026-04-08)
- routes/ssls는 ApisixRoute/ApisixTls CRD로 전환 (etcd 직접 PUT 금지)
- destructive sync 동작과 객체별 관리 방법 정리
- helm values nesting 버그 회귀 방지 노트
- plugin_metadata, global_rules는 GatewayProxy CR로 관리
- ApisixRoute 예시 추가

postgresql-ha.md:
- APISIX 서울이 외부 통합 etcd 사용 안 함을 반영
- 통합 etcd는 Patroni DCS + osaka APISIX 전용
- /apisix/seoul stale prefix 삭제 절차 추가
2026-04-08 09:25:24 +00:00

174 lines
6.8 KiB
Markdown

---
title: PostgreSQL HA (Patroni + etcd)
updated: 2026-04-06
tags: [infra, postgresql, patroni, etcd, ha]
---
## 개요
PostgreSQL 3노드 HA 클러스터. Patroni가 자동 failover를 관리하고, etcd를 DCS(Distributed Consensus Store)로 사용.
K3s의 kine 데이터스토어로 사용 중. Supabase Free tier에서 로컬로 이전 완료 (2026-04-05).
## PostgreSQL 클러스터
| 노드 | 호스트 | IP | 역할 |
|------|--------|-----|------|
| postgres-1 | incus-hp2 | 10.100.2.5 | Replica 또는 Leader |
| postgres-2 | incus-kr1 | 10.100.3.185 | Replica 또는 Leader |
| postgres-3 | incus-kr2 | 10.100.1.83 | Replica 또는 Leader |
- PostgreSQL 17.9, Patroni 4.1.0
- Patroni 설정: `/etc/patroni.yml`
- Patroni 서비스: `/etc/systemd/system/patroni.service` (ExecStart: `/opt/patroni/bin/patroni /etc/patroni.yml`)
- 클러스터 이름: `nocodb-cluster`
- 레플리케이션: async streaming
- Patroni REST API: 각 노드 8008 포트
- 컨테이너 메모리 제한: 2GiB (limits.memory)
- shared_buffers: 512MB, effective_cache_size: 1536MB, work_mem: 8MB, maintenance_work_mem: 128MB, wal_buffers: 16MB
### DB 목록
| DB | 용도 |
|----|------|
| kine | K3s 데이터스토어 (kine) |
| nocodb | NocoDB |
| n8n | n8n 워크플로 |
| outline | Outline 위키 |
### Patroni 명령어
```bash
# 클러스터 상태 확인
incus exec postgres-1 -- /opt/patroni/bin/patronictl -c /etc/patroni.yml list
# 수동 switchover
incus exec postgres-1 -- /opt/patroni/bin/patronictl -c /etc/patroni.yml switchover
# Replica reinitialize
incus exec postgres-1 -- /opt/patroni/bin/patronictl -c /etc/patroni.yml reinit nocodb-cluster postgres-3 --force
```
## etcd 클러스터 (Patroni DCS)
| 노드 | 위치 | IP | 방식 |
|------|------|-----|------|
| etcd-nas | Synology NAS (서울) | 192.168.9.100 | Docker (`quay.io/coreos/etcd:v3.5.21`) |
| etcd-hp2 | Incus 컨테이너 hp2 (서울) | 10.100.2.214 | Alpine + `apk add etcd` (v3.5.16) |
| etcd-jp1 | Incus 컨테이너 jp1 (도쿄) | 10.253.101.233 | Alpine + `apk add etcd` (v3.5.16) |
- NAS: 데이터 `/volume1/docker/etcd/data`, `--restart=always`
- hp2/jp1: openrc 서비스 (`/etc/init.d/etcd`), `command_background=true`
- Patroni etcd namespace: `/patroni`
### etcd 확인 명령어
```bash
# 클러스터 멤버 확인
incus exec postgres-1 -- etcdctl --endpoints=http://192.168.9.100:2379 member list -w table
# 엔드포인트 상태
incus exec postgres-1 -- etcdctl --endpoints=http://192.168.9.100:2379,http://10.100.2.214:2379,http://10.253.101.233:2379 endpoint status -w table
```
### etcd에 저장된 데이터
| prefix | 용도 |
|--------|------|
| `/patroni` | Patroni DCS (Leader election, 설정) |
| `/apisix/osaka` | APISIX 오사카 라우팅 설정 |
| `/apisix/tokyo` | APISIX sandbox-tokyo 라우팅 설정 (2026-04-08 NixOS 전환 후 미사용, 데이터는 보존) |
| `/apisix/seoul` | APISIX 서울 K3s 라우팅 설정 |
## K3s kine 연결
K3s → HAProxy(OpenWrt 192.168.9.1:5432) → Patroni Leader PostgreSQL
### K3s config
```yaml
# /etc/rancher/k3s/config.yaml (kr1, kr2)
datastore-endpoint: "postgres://kine:kine@192.168.9.1:5432/kine"
```
### HAProxy (OpenWrt)
`/etc/haproxy.cfg`에 PostgreSQL backend 설정. Patroni REST API(`/primary` 엔드포인트)로 Leader를 자동 감지.
```
frontend ft_postgres
bind :5432
default_backend bk_postgres_primary
backend bk_postgres_primary
option httpchk GET /primary
http-check expect status 200
default-server inter 3s fall 3 rise 2 on-marked-down shutdown-sessions
server postgres-1 10.100.2.5:5432 check port 8008
server postgres-2 10.100.3.185:5432 check port 8008
server postgres-3 10.100.1.83:5432 check port 8008
```
- Patroni failover 시 HAProxy가 자동으로 새 Leader를 감지 (~3초)
- K3s config 변경 없이 Leader 전환 대응
## 애플리케이션 접속 경로
NocoDB, n8n 등 K3s 내부 애플리케이션은 **pgcat**(연결 풀링)을 통해 PostgreSQL에 접속.
```
nocodb/n8n → pgcat (db.svc.cluster.local:6432) → HAProxy 192.168.9.1:5432 → Patroni Leader
```
`db/pgcat-config` ConfigMap의 각 풀의 `shards.0.servers`**HAProxy 단일 백엔드만 가리켜야 함** (2026-04-08 변경):
```toml
[pools.nocodb.shards.0]
database = "nocodb"
servers = [["192.168.9.1", 5432, "primary"]]
[pools.n8n.shards.0]
database = "n8n"
servers = [["192.168.9.1", 5432, "primary"]]
```
⚠️ **하지 말 것**: pgcat에 Patroni 노드 IP(10.100.2.5/3.185/1.83)를 직접 박지 말 것. Patroni failover가 발생하면 pgcat는 옛 primary를 계속 가리키게 되어 nocodb/n8n이 read-only 에러 발생.
pgcat는 풀링 전용으로만 쓰고, leader 탐지는 OpenWrt HAProxy에 위임. `query_parser_enabled = false` 설정 (read/write splitting 비활성).
### 2026-04-08 사고 기록
Patroni failover 발생 → pgcat가 옛 primary IP(`10.100.2.5`)를 hardcoded 참조 → nocodb 마이그레이션 시 `cannot execute UPDATE in a read-only transaction` 에러로 4시간 가량 CrashLoopBackOff. n8n은 마이그레이션이 없어서 표면화되지는 않았으나 동일한 잠재 문제 존재. 위의 단일 백엔드 구조로 변경하여 항구 해결.
## APISIX etcd 사용 현황
| 사이트 | etcd | prefix | 비고 |
|--------|------|--------|------|
| osaka | 통합 클러스터 (192.168.9.100, ...) | `/apisix/osaka` | Docker APISIX (waf-apisix), [[apisix#오사카-apisix-osaka]] |
| sandbox-tokyo | (미가동) | `/apisix/tokyo` | 2026-04-08 NixOS 전환으로 APISIX 자체 폐기, etcd 데이터만 보존 |
| 서울 K3s | **K3s 내부 apisix-etcd StatefulSet** (apisix.apisix.svc:2379) | `/apisix` | 2026-04-08 외부 통합에서 K3s 내부로 복귀 |
### 2026-04-06 → 2026-04-08 변경 이력
1. **2026-04-06**: 서울 K3s APISIX의 K3s 내부 apisix-etcd StatefulSet을 삭제하고 외부 통합 etcd로 이전 (`/apisix/seoul` prefix). 통합 운영 + 컴포넌트 수 절감 의도.
2. **2026-04-08**: 다시 K3s 내부로 복귀. Patroni DCS와 같은 etcd 클러스터를 공유할 때 장애 전파 위험(Patroni 이슈 → APISIX 라우팅 영향)이 비직관적이라 격리. 외부 통합 etcd의 `/apisix/seoul/*` 20개 키 삭제 완료. **현재 외부 통합 etcd는 Patroni DCS + osaka APISIX 전용.**
### Stale prefix 삭제 명령 (참고)
```bash
# HTTP API v3 사용 (heimdall에서 직접 호출)
KEY=$(echo -n "/apisix/seoul/" | base64)
RANGE_END=$(echo -n "/apisix/seoul0" | base64)
curl -s -X POST "http://192.168.9.100:2379/v3/kv/deleterange" \
-H "Content-Type: application/json" \
-d "{\"key\":\"$KEY\",\"range_end\":\"$RANGE_END\"}"
```
## 관련 문서
- [[nas-storage]] — NAS StorageClass, Jumbo Frame 설정
- [[storage-plan]] — NVMe NAS + iSCSI 기획
- [[backup]] — 백업 파이프라인
- [[infra-hosts]] — 서버 목록
- [[k3s-migration]] — K3s 마이그레이션 기록