Pgpool-II 아키텍처 및 핵심 역할
Pgpool-II는 PostgreSQL 데이터베이스 클러스터의 프록시 및 미들웨어 계층에서 작동하는 고급 연결 관리 도구입니다. 클라이언트와 백엔드 데이터베이스 노드 사이에서 PostgreSQL 프론트엔드/백엔드 프로토콜을 중계하며, 애플리케이션 입장에서는 단일 PostgreSQL 인스턴스로 인식됩니다. 이러한 투명성 덕분에 기존 애플리케이션의 소스 코드 수정 없이도 클러스터링 기능을 활용할 수 있습니다.
주요 기술적 기능
- 고가용성(HA) 및 자동 장애 조치: 다중 노드 구성을 통해 단일 장애 지점(SPOF)을 제거합니다. 백엔드 노드 장애 시 자동으로 풀에서 제외하며, Watchdog 모듈을 통해 Pgpool-II 프로세스 자체의 활성/대기 구성과 가상 IP(VIP) 페일오버를 관리하여 스플릿 브레인(Split-brain) 현상을 방지합니다.
- 쿼리 라우팅 및 로드 밸런싱: 읽기 전용 쿼리(SELECT)는 여러 스탠바이 노드로 분산 처리하여 처리량을 극대화합니다. 쓰기 쿼리(INSERT, UPDATE, DELETE)는 스트리밍 복제 모드에서는 Primary 노드로, 네이티브 복제 모드에서는 모든 노드로 전달됩니다.
- 연결 풀링(Connection Pooling): 클라이언트 연결을 캐싱하고 재사용하여 TCP 핸드셰이크 및 PostgreSQL 백엔드 프로세스 포킹 오버헤드를 크게 줄여줍니다.
- 인메모리 쿼리 캐시: 반복되는 SELECT 쿼리의 결과를 메모리에 저장하여, 파싱 및 실행 계획 수립 단계를 건너뛰고 즉시 결과를 반환합니다.
- 온라인 복구 및 노드 확장: 서비스 중단 없이 신규 스탠바이 노드를 추가하거나 장애 노드를 재동기화할 수 있는 기능을 제공합니다.
- 연결 대기열 관리: PostgreSQL의
max_connections한계에 도달했을 때 즉시 오류를 반환하는 대신, 연결을 대기열에 추가하여 시스템 과부하를 방지합니다.
지원 환경 및 호환성
Pgpool-II는 Linux 및 FreeBSD를 비롯한 대부분의 UNIX 계열 운영체제를 지원하며, Windows 환경은 공식적으로 지원하지 않습니다. 백엔드 PostgreSQL 서버는 버전 7.4 이상을 지원하지만, 모든 노드가 동일한 메이저 버전을 사용해야 합니다. 또한, SSL 지원 여부, 블록 크기 등 컴파일 옵션이 노드 간에 상이할 경우 예기치 않은 동작이 발생할 수 있으므로 동일한 빌드 옵션을 유지하는 것이 권장됩니다.
시스템 제약 사항 및 기술적 한계
Pgpool-II를 프로덕션 환경에 도입할 때는 다음과 같은 아키텍처적 제약 사항을 반드시 고려해야 합니다.
백엔드 제어 함수 및 로드 밸런싱 예외
pg_terminate_backend() 함수를 사용하여 백엔드 프로세스를 강제 종료하면, PostgreSQL이 이를 전체 서버 셧다운 시그널로 해석하여 의도치 않은 장애 조치(Failover)를 트리거할 수 있습니다. 최신 버전에서는 프로세스 ID가 상수일 때 이를 완화하는 로직이 추가되었으나, Extended Query 프로토콜 환경에서는 여전히 사용에 주의해야 합니다.
또한, 단일 문자열에 여러 SQL 명령이 포함된 멀티 스테이트먼트(Multi-statement) 쿼리는 로드 밸런싱 대상에서 제외되어 항상 Primary 노드로 라우팅됩니다.
인증 및 접근 제어 구성
네이티브 복제 및 스트리밍 복제 모드에서 md5 및 scram-sha-256 인증을 지원하려면 pool_passwd 파일을 구성해야 합니다. 다음은 인증을 위한 해시 등록 프로세스입니다.
# 데이터베이스 OS 사용자로 로그인 후 해시 생성
pg_md5 --md5auth --username=db_admin_user secure_password_123
# 생성된 pool_passwd 파일 포맷 예시
# db_admin_user:md5a1b2c3d4e5f6g7h8i9j0
위 명령어는 pool_passwd 파일을 자동으로 생성하거나 업데이트합니다. 이후 pool_hba.conf에 해당 인증 방식에 대한 접근 제어 규칙을 추가하고, PostgreSQL 측의 사용자 자격 증명과 정확히 일치하는지 확인한 후 pgpool reload를 수행해야 합니다.
임시 테이블 및 세션 컨텍스트 관리
네이티브 복제 모드에서 임시 테이블(Temporary Table)에 대한 DDL 및 DML은 Primary 노드에서만 실행됩니다. 만약 SELECT 쿼리에서 임시 테이블 이름이 문자열 리터럴로 동적 바인딩되면 Pgpool-II가 이를 감지하지 못해 로드 밸런싱이 발생하고 '테이블을 찾을 수 없음' 오류가 발생할 수 있습니다.
-- 회피 전략: SQL 힌트 주석을 사용하여 Primary 노드로 강제 라우팅
/*NO LOAD BALANCE*/
SELECT * FROM pg_catalog.pg_class WHERE relname = 'temp_session_data';
PostgreSQL 8.2 이하 버전을 사용하는 경우, 연결 풀링으로 인해 세션이 유지되므로 임시 테이블이 자동으로 정리되지 않습니다. 이를 방지하려면 트랜잭션 블록 내에서 ON COMMIT DROP 옵션을 사용하거나 명시적으로 DROP TABLE을 수행해야 합니다.
시퀀스 및 시간 기반 함수의 복제 일관성
random(), nextval()과 같은 비결정적 함수나 컨텍스트 종속 함수는 다중 노드 간 데이터 일관성을 보장하기 어렵습니다. CURRENT_TIMESTAMP 또는 now() 함수가 기본값으로 설정된 열의 경우, Pgpool-II는 쿼리 실행 시 이를 Primary 노드의 상수 값으로 치환하여 복제 불일치를 방지합니다.
-- 원본 쿼리
INSERT INTO event_logs (event_type) VALUES ('user_login');
-- Pgpool-II 내부 재작성 후 백엔드 전달 쿼리
INSERT INTO event_logs (event_type, created_at) VALUES ('user_login', '2023-10-25 14:30:00.123456+09');
단, 열의 데이터 타입이 시간 관련 타입이 아니거나, INSERT INTO ... SELECT 구문을 사용하는 경우 쿼리 재작성 로직이 적용되지 않아 복제 오류가 발생할 수 있습니다.
파라미터 상태(ParameterStatus) 및 세션 변수 동기화
클라이언트 연결 시 PostgreSQL이 반환하는 ParameterStatus 값(예: in_hot_standby)은 노드마다 다를 수 있습니다. Pgpool-II는 Primary 노드의 값을 클라이언트에 반환하도록 설계되었습니다. 세션 변수를 변경할 때 set_config() 함수 대신 SET 명령어를 사용해야 모든 노드에 설정이 전파됩니다. 만약 set_config()를 반드시 사용해야 한다면, 해당 세션에 대해서는 로드 밸런싱을 비활성화하여 트래픽이 Primary 노드로만 향하도록 구성해야 합니다.
명령어 및 문서 표기 규칙
기술 문서 및 CLI 인터페이스에서 사용되는 문법적 규칙은 다음과 같습니다.
- 선택적 인수: 대괄호
[ ]로 표기합니다. - 상호 배타적 선택: 중괄호
{ }와 파이프|를 사용하여 여러 옵션 중 하나를 선택해야 함을 나타냅니다. - 반복 요소: 줄임표
...는 앞선 요소가 여러 번 반복될 수 있음을 의미합니다. - 프롬프트: 셸 명령은
$로, psql 인터랙티브 쿼리는=>로 구분하여 표기합니다.