얼마전에 DB 서버 2대에 patroni를 이용하여 이중화를 했다.
patroni는 서버를 모니터하면서 master 와 slave 를 관리해준다.
이제는 이 서버에 적절히 분배해 주면서 앞단에서 로드밸런서 역할도 하고 쿼리 종류에 따라 Write 는 master 로 Read 는 두 서버를 적절히 분산을... 하기위해 pgpool 을 적용했다.
쿼리를 분석해서 자동 분기를 해주는 솔루션은 pgpool 이 거의 유일하다고 한다.
개발자가 WAS 의 소스코드 레벨에서 ORM 등을 사용하여 두개의 역할을 나눠서 알아서 나눠서 호출하게 할수는 있지만 이미 만들어진 WAS서버에서 다시 고쳐서 쓰기는 번거롭다.
자동으로 해주는 솔루션을 찾다보니 pgpool 을 이용하게 되었다.
일전에 했다가..별로여서 안쓰고 있었는데 학습차원에서 다시 했고 k3s 에 적용했다.
결론적으로 굉장히 많은 시행 착오를 겪었다. 오늘하루를 다 썼는데...
그 기록을 남기려고 한다.
결론적으로 k3s 서버에 아래 두개의 파일을 만들었다.
귀찮아서 몇개를 합치긴 했다.
drwxr-xr-x 2 root root 4096 Dec 26 10:46 ./
drwxr-xr-x 7 root root 4096 Dec 26 02:38 ../
-rw-r--r-- 1 root root 5471 Dec 26 10:46 deployment.yaml
-rw-r--r-- 1 root root 378 Dec 26 06:33 service.yaml
root@k3s-node1:/k8s/apps/pgpool#
먼저 디플로이
root@k3s-node1:/k8s/apps/pgpool# cat deployment.yaml
# ---------------------------------------------------------
# 1. ConfigMap 정의 (pool_hba.conf 설정)
# ---------------------------------------------------------
apiVersion: v1
kind: ConfigMap
metadata:
name: pgpool-hba-config
namespace: default
data:
pool_hba.conf: |
# 로컬 접속 허용
local all all trust
# 외부 접속 허용 (md5 인증 사용)
host all all 0.0.0.0/0 md5
---
apiVersion: v1
kind: Secret
metadata:
name: postgres-secrets
namespace: default
type: Opaque
stringData:
postgres-password: "비번" # 실제 비밀번호로 변경
replicator-password: "비번" # 복제용 유저
---
apiVersion: v1
kind: Secret
metadata:
name: pgpool-passwd
namespace: default
type: Opaque
stringData:
pool_passwd: |
postgres:md~~~ # DB에 저장된 사용자의 암호화된 값
rep_user:md~~~ # DB에 저장된 사용자의 암호화된 값
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: pgpool
namespace: default
spec:
replicas: 1
selector:
matchLabels:
app: pgpool
template:
metadata:
labels:
app: pgpool
spec:
# initContainer로 Secret 파일을 쓰기 가능한 공간(/config-work)으로 복사
initContainers:
- name: setup-config
image: busybox
# pool_passwd는 읽어야 하고, pool_hba.conf는 entrypoint가 수정해야 하므로 쓰기 권한도 필요함
command: ['sh', '-c', 'cp /config-src/* /config-work/ && chmod 666 /config-work/*']
volumeMounts:
- name: pool-passwd-vol
mountPath: /config-src/pool_passwd
subPath: pool_passwd
- name: pool-hba-vol
mountPath: /config-src/pool_hba.conf
subPath: pool_hba.conf
- name: pgpool-config-work
mountPath: /config-work
containers:
- name: pgpool
image: docker.io/pgpool/pgpool:4.4.3
volumeMounts:
# 복사된 쓰기 가능한 볼륨을 마운트
- mountPath: /opt/pgpool-II/etc/pool_passwd
name: pgpool-config-work
subPath: pool_passwd
- mountPath: /opt/pgpool-II/etc/pool_hba.conf
name: pgpool-config-work
subPath: pool_hba.conf
env:
- name: PGPOOL_PARAMS_BACKEND_HOSTNAME0
value: "10.34.1.111"
- name: PGPOOL_PARAMS_BACKEND_PORT0
value: "5432"
- name: PGPOOL_PARAMS_BACKEND_WEIGHT0
value: "1"
- name: PGPOOL_PARAMS_BACKEND_FLAG0
value: "ALLOW_TO_FAILOVER"
- name: PGPOOL_PARAMS_BACKEND_HOSTNAME1
value: "10.34.1.112"
- name: PGPOOL_PARAMS_BACKEND_PORT1
value: "5432"
- name: PGPOOL_PARAMS_BACKEND_WEIGHT1
value: "1"
- name: PGPOOL_PARAMS_BACKEND_FLAG1
value: "ALLOW_TO_FAILOVER"
# ... (기타 설정) ...
- name: PGPOOL_PARAMS_NUM_INIT_CHILDREN
value: "100"
- name: PGPOOL_PARAMS_MAX_POOL
value: "4"
- name: PGPOOL_PARAMS_LOAD_BALANCE_MODE
value: "on"
- name: PGPOOL_PARAMS_SR_CHECK_PERIOD
value: "10"
- name: PGPOOL_PARAMS_SR_CHECK_USER
value: "rep_user"
- name: PGPOOL_PARAMS_SR_CHECK_PASSWORD
valueFrom:
secretKeyRef:
name: postgres-secrets
key: replicator-password
- name: PGPOOL_PARAMS_SR_CHECK_DATABASE
value: "postgres"
- name: PGPOOL_PARAMS_HEALTH_CHECK_PERIOD
value: "10"
- name: PGPOOL_PARAMS_HEALTH_CHECK_USER
value: "postgres"
- name: PGPOOL_PARAMS_HEALTH_CHECK_PASSWORD
valueFrom:
secretKeyRef:
name: postgres-secrets
key: postgres-password
- name: PGPOOL_PARAMS_HEALTH_CHECK_DATABASE
value: "postgres"
# [해결책 2] 인증 방식을 다시 SCRAM으로 변경 (백엔드와 통일)
- name: PGPOOL_PARAMS_ENABLE_POOL_HBA
value: "on"
- name: PGPOOL_PARAMS_PWD_ENC_METHOD
value: "md5"
- name: PGPOOL_PARAMS_POOL_PASSWD
value: "/opt/pgpool-II/etc/pool_passwd"
- name: PGPOOL_PARAMS_PORT
value: "5432"
ports:
- name: postgresql
containerPort: 5432
- name: pcp
containerPort: 9898
# Liveness/Readiness 등 기존 설정 유지...
livenessProbe:
tcpSocket:
port: 5432
initialDelaySeconds: 60
readinessProbe:
tcpSocket:
port: 5432
initialDelaySeconds: 30
volumes:
# [해결책 1] 작업을 위한 빈 볼륨 생성
- name: pgpool-config-work
emptyDir: {}
# 원본 Secret 연결
- name: pool-passwd-vol
secret:
secretName: pgpool-passwd
- name: pool-hba-vol
configMap:
name: pgpool-hba-config
다음은 k3s 서비스
root@k3s-node1:/k8s/apps/pgpool# cat service.yaml
apiVersion: v1
kind: Service
metadata:
name: pgpool-lb
namespace: default
annotations:
metallb.universe.tf/allow-shared-ip: "shared-lb"
spec:
type: LoadBalancer
selector:
app: pgpool
ports:
- name: postgresql
port: 5432
targetPort: 5432
protocol: TCP
- name: pcp
port: 9898
targetPort: 9898
protocol: TCP
sessionAffinity: None
service 에서 shared-lb 는 하나의 아이피로 포트만 다르게 여러 서비스를 하기 위해서 사용중이다.
metallb 를 이용하여 vip를 사용했는데 계속 해서 ip를 늘려갈수는 없으니까...
사내에서 여유있는 ip 대역을 가지고 있다면 상관 없겠지만 클라우드 상에서 아이피를 늘리것은 다 돈이다.
그래서 최대한 아이피를 안 늘리는 상황으로 만들어 보았다.
결과적으로 위와 같이 해서 vip 10.34.1.150 5432 포트로 연결 성공 하였다.
하나하나 설명은 나도 모르니 패스...
가장 애를 먹었던 부분은 인증부분이다.
client -> pgpool -> db서버
이렇게 인증을 한다고 하는데
client -> pgpool 을 통과한것 같은데 계속 해서 backend (DB를 말한다.) 에 인증이 안된다는 오류를 오늘 하루종일 보았다.
GPT 에 질문을 몇번을 했는지 모르겠다.
SELECT concat(usename, ':', passwd) FROM pg_shadow WHERE usename IN ('postgres', 'rep_user');
이런식으로 DB에서 패스워드를 추출하여 상단 deployment.yaml 에 셋팅을 하게 되는데
postgres:SCRAM-SHA-256$4096:w1r3Sp4OL+LMrDdcp~~~
rep_user:SCRAM-SHA-256$4096:ngIDzkc8gUxBd6/vE~~~
처음에는 이런식으로 값이 나왔다. SCRAM 인증방식이고
두대의 서버 pg_hba 설정 파일에도
# IPv4 local connections:
host all all 127.0.0.1/32 scram-sha-256
# IPv6 local connections:
host all all ::1/128 scram-sha-256
# Allow replication connections from localhost, by a user with the
# replication privilege.
local replication all peer
host replication all 127.0.0.1/32 scram-sha-256
host replication all ::1/128 scram-sha-256
이렇게 되어 있었다.
그런데 아무리 해도 안되더라...
그래서 인증방식을 md5 로 바꿨다.
-- 1. 현재 세션의 암호화 방식을 md5로 강제 설정
SET password_encryption = 'md5';
-- 2. postgres 유저 비밀번호 재설정 (다시 MD5로 해시됨)
-- (기존 비밀번호 '비번'를 그대로 입력)
ALTER USER postgres WITH PASSWORD '비번';
-- 3. rep_user 유저 비밀번호 재설정
ALTER USER rep_user WITH PASSWORD '비번';
-- 4. 확인 (rolpassword가 'md5...' 로 시작해야 성공)
SELECT rolname, rolpassword FROM pg_authid WHERE rolname IN ('postgres', 'rep_user');
그러면 실제 값도 바뀌어져 있다.
postgres:md5~~
rep_user:md5~~
그리고 최종적으로 상단의 설정으로 바꿨다.
처음엔 ... 이렇게 해도 안되던데... db서버를 모두 재부팅 해봤는데 적용되었다.
gpt 물어보면 재부팅을 안하고 리로드만 해도 된다고 하는데 리로드를 몇번이나 해봤는지 모르겠다.

이렇게 2개의 서버와 pgpool 연결된 1개의 서버를 확인할수 있다.
그러면 로드밸런스가 잘 되는지 확인 해볼까?
for i in {1..10}; do
kubectl exec deployment/pgpool -- sh -c "export PGPASSWORD='비번'; psql -h 127.0.0.1 -U postgres -d postgres -c 'SELECT inet_server_addr(), pg_is_in_recovery();'"
sleep 1
done
노드1번에서 이렇게 해봤더니 10번이 모두 1번 서버로 나온다... 뭔가 이상하다.
root@k3s-node1:/k8s/apps/pgpool# kubectl exec -it deployment/pgpool -- psql -h 127.0.0.1 -U postgres -c "show pool_nodes;"
Defaulted container "pgpool" out of: pgpool, setup-config (init)
Password for user postgres:
node_id | hostname | port | status | pg_status | lb_weight | role | pg_role | select_cnt | load_balance_node | replication_delay | replication_state | replication_sync_state | last_status_change
---------+-------------+------+--------+-----------+-----------+---------+---------+------------+-------------------+-------------------+-------------------+------------------------+---------------------
0 | 10.34.1.111 | 5432 | up | up | 0.500000 | primary | primary | 312 | false | 0 | | | 2025-12-26 10:52:34
1 | 10.34.1.112 | 5432 | up | up | 0.500000 | standby | standby | 72 | true | 0 | | | 2025-12-26 10:52:34
(2 rows)
root@k3s-node1:/k8s/apps/pgpool#
둘다 up 이고 모두 정상이라고 GPT는 말하긴 한다...
그런데 계속 한쪽으로만 나온다.
검색을 해보니
PGPOOL_PARAMS_STATEMENT_LEVEL_LOAD_BALANCE 이 옵션을 켜라고 한다.
해봤다...
정말 두 서버를 왔다갔다 하더라...
그런데 좀 더 검색을 해보니 pgpool 이 한번 접속한 사용자는 동일하게 한쪽으로 보낸다고 한다.
세션방식이라서..근데 위 옵션을 ON으로 하면 요청 올때마다 로드밸런싱을 한다고 하네..
결국 나 혼자 하니까 그랬던 것..
'DB > Postgresql' 카테고리의 다른 글
| (연습) postgresql 이중화 (Failover) (0) | 2025.12.07 |
|---|---|
| (연습) postgreSQL 이중화 (consul, patroni) (0) | 2025.12.07 |
| pgpool 4.1.4 auto fail-over, fail-back (2) | 2021.06.20 |
| pgpool II 4.1.4 load balance (0) | 2021.06.19 |
| postgresql replication 수동 fail-over / fail-back (0) | 2021.06.19 |
