9.3 vip-manager와 PgBouncer
HAProxy가 애플리케이션과 데이터베이스 사이에 중계 계층을 세우는 방식이라면, 이 절의 두 도구는 접속 지점 자체를 클러스터 상태에 맞춰 움직인다. vip-manager는 IP 주소를 leader 노드로 옮기고, confd와 PgBouncer 조합은 pooler의 목적지를 다시 쓴다. 어느 쪽이든 애플리케이션의 접속 문자열은 완전히 고정된다.
vip-manager
vip-manager는 CYBERTEC이 관리하는 별도 프로젝트(cybertec-postgresql/vip-manager)로, Patroni 4.1.4 공식 문서에는 언급이 없다. 그럼에도 Patroni와의 결합이 이 도구의 존재 이유인데, REST API를 폴링하는 대신 DCS의 Patroni leader key를 직접 watch한다는 점이 특징이다.
동작은 단순하다. 각 노드에서 데몬으로 실행되며, trigger-key로 지정한 DCS 키의 값을 감시한다. 이 키는 Patroni의 leader key여야 하므로 <namespace>/<scope>/leader 형식과 일치해야 한다(기본값은 /service/pgcluster/leader). 키 값이 trigger-value로 지정한 자기 멤버 이름과 같으면 로컬 네트워크 인터페이스에 VIP를 등록하고, 다르면 해제한다. failover로 leader key 값이 바뀌면 구 leader 노드의 vip-manager는 VIP를 내리고 새 leader 노드의 vip-manager가 올린다.
flowchart TD
DCS["DCS leader key"]
DCS -->|watch| VM1["노드1 vip-manager"]
DCS -->|watch| VM2["노드2 vip-manager"]
VM1 -->|값 = 자기 이름| ADD["VIP 등록"]
VM2 -->|값 ≠ 자기 이름| DEL["VIP 해제"]
설정 파일에는 감시 대상과 VIP 정보를 적는다.
trigger-key: "/service/batman/leader"
trigger-value: "postgresql0"
ip: 10.0.0.100
netmask: 24
interface: eth0
manager-type: basic감시 백엔드로는 etcd(기본), Consul, Patroni REST API를 지원한다. manager-type은 VIP를 올리는 방법의 분기로, 인터페이스에 IP를 직접 얹는 basic 외에 hetzner 같은 클라우드 API 호출 방식이 있다. 인터페이스에 IP를 얹는 방식은 일반적으로 같은 L2 세그먼트 안에서만 유효하므로, 클라우드나 라우팅 분리 환경에서는 이런 별도 타입이 필요해진다.
trigger-key는 자기 클러스터의 namespace와 scope에 맞춰야 한다. 기본값 /service/pgcluster/leader가 실제 leader key 경로와 다르면 존재하지 않는 키를 감시하게 되어 VIP가 어느 노드에도 붙지 않는다. 또한 vip-manager는 Patroni 공식 문서가 다루지 않는 외부 프로젝트이므로, Patroni나 DCS 버전을 올릴 때 호환 여부를 별도로 확인하는 편이 안전하다.PgBouncer와 confd
9.2에서 confd가 haproxy.cfg를 동적 생성했듯, 저장소 extras/confd/에는 PgBouncer용 템플릿도 들어 있다. pgbouncer.toml은 prefix /service/batman 아래에서 /members/와 /leader 키를 watch하고, 변화가 생기면 pgbouncer.tmpl로 설정을 다시 만든 뒤 systemctl reload pgbouncer를 실행한다.
템플릿의 논리는 한 줄로 요약된다. DCS /leader 키의 값(leader 멤버 이름)으로 /members/<leader> 키를 찾아, 그 접속 정보로 PgBouncer [databases] 섹션을 재작성한다.
[databases]
* = host=10.0.0.11 port=5432 pool_size=10failover가 일어나면 /leader 값이 바뀌고, confd가 이 파일을 새 primary 주소로 다시 쓴 뒤 reload한다. 애플리케이션은 처음부터 끝까지 PgBouncer 주소만 바라본다. 이미 연결 풀링 때문에 PgBouncer를 쓰고 있는 환경이라면 라우팅 컴포넌트가 하나도 늘지 않는 구성이다.
proxy_address와 proxy_url
노드마다 PgBouncer를 옆에 붙이는 구성을 위해 Patroni 쪽에도 지원 설정이 있다. patroni.yml의 postgresql.proxy_address는 PostgreSQL 옆에서 도는 connection pool(예: PgBouncer)에 접근하는 IP와 포트를 지정하며, 이 값은 DCS member key에 proxy_url로 기록된다.
postgresql:
listen: 0.0.0.0:5432
connect_address: 10.0.0.11:5432
proxy_address: 10.0.0.11:6432member key를 읽어 라우팅하는 도구가 PostgreSQL 직결 주소(conn_url) 대신 pooler 주소(proxy_url)를 쓸 수 있게 하는 통로다.
조합 아키텍처
vip-manager와 노드별 PgBouncer를 조합하면 “고정 IP 하나 + 연결 풀링"이 동시에 잡힌다. 애플리케이션은 VIP의 6432 포트로 접속하고, VIP는 항상 leader 노드에 있으며, 그 노드의 PgBouncer가 로컬 PostgreSQL로 연결을 풀링한다.
flowchart TD
APP["애플리케이션"] --> VIP["VIP :6432"]
VIP --> PB1
subgraph N1["노드1 (leader)"]
VM1["vip-manager"]
PB1["PgBouncer"]
PG1["PostgreSQL"]
PB1 --> PG1
end
subgraph N2["노드2 (replica)"]
VM2["vip-manager"]
PB2["PgBouncer"]
PG2["PostgreSQL"]
PB2 --> PG2
end
DCS["DCS leader key"]
VM1 -.watch.- DCS
VM2 -.watch.- DCS
failover 시 움직이는 것은 VIP뿐이다. PgBouncer는 각 노드에서 자기 로컬 PostgreSQL만 가리키므로 재설정할 것이 없고, confd 없이도 조합이 성립한다. 반대로 PgBouncer를 중앙에 한 대 두는 구성이라면 위의 confd 템플릿 방식으로 목적지를 갱신하는 쪽이 맞다.
정리
- vip-manager는 DCS leader key를 직접 watch해서 VIP를 등록/해제하는 외부 프로젝트다.
trigger-key는<namespace>/<scope>/leader형식에 맞춰야 하고, 기본값을 그대로 쓰면 동작하지 않는다. - confd의
pgbouncer.tmpl은/leader키 변화를 감지해[databases]섹션을 새 primary로 재작성하고 PgBouncer를 reload한다. postgresql.proxy_address를 설정하면 member key에proxy_url이 기록되어, 라우팅 도구가 PostgreSQL 대신 옆의 pooler를 목적지로 삼는 구성이 가능하다.- VIP + 노드별 PgBouncer 조합은 접속 지점 고정과 풀링을 동시에 얻으면서 failover 시 움직이는 부품을 VIP 하나로 줄인다.