Jay's <Devlog />Jay's Devlog
HomeBlogAbout
Login

© 2026 Jay's <Devlog />. All Rights Reserved.

GitHubLinkedIn
Gravatar
  1. Home
  2. Blog
  3. k3s
  4. Kubernetes K3s 환경에서 PostgreSQL HA 부하 테스트: pgbench로 검증하는 성능과 오토스케일링(HPA)
ORCHESTRATION
k3s개인서버 26 min read

Kubernetes K3s 환경에서 PostgreSQL HA 부하 테스트: pgbench로 검증하는 성능과 오토스케일링(HPA)

jay

jay

2026년 1월 16일 01:07

목차

  • 1. 테스트 환경 준비 (Client Pod)
  • 2. 시나리오 1: Master 단일 성능
  • 3. 시나리오 2: Replica 읽기 분산과 트러블슈팅
    • 이슈: 무한 대기와 WAL 누락
  • 4. 시나리오 3: HPA 오토스케일링의 위력
    • 이슈: HPA가 생성이 안 된다?
    • 최종 부하 테스트 (Scale Out)
  • 5. 결론

지난 포스팅에서 K3s 위에 Bitnami Helm 차트를 이용해 PostgreSQL Master-Replica HA(고가용성) 환경을 구축했다. 구축만 해놓고 “잘 되겠지”라고 믿는 것은 엔지니어의 자세가 아니다.

이번에는 PostgreSQL의 표준 벤치마킹 도구인 pgbench를 사용하여 실제로 부하를 줘보고, 1) 읽기 분산이 제대로 되는지, 2) 오토스케일링(HPA)이 동작하여 성능이 향상되는지 검증해 본 과정을 기록한다.

이 과정에서 겪었던 WAL 로그 누락 이슈와 HPA 설정 문제 해결 과정도 함께 담았다.

1. 테스트 환경 준비 (Client Pod)

로컬 PC에서 테스트하면 네트워크 레이턴시가 포함되므로, 클러스터 내부에 테스트용 Pod를 띄워서 진행했다.

# pgbench가 포함된 임시 파드 생성 (같은 네임스페이스 권장)
kubectl run pgbench-client -n data --rm -it --image postgres:14 --restart=Never -- bash

파드 내부로 진입한 후, pgbench 실행을 위해 환경 변수를 설정한다. 여기서 주의할 점은 Service 이름이다. 같은 네임스페이스라면 짧은 이름도 되지만, 혹시 모르니 정확하게 서비스명을 확인해야 한다.

# Master(Primary) 노드 접속 정보
export PGHOST=postgresql-primary
export PGUSER=postgres
export PGPASSWORD='my-secret-password'

# 테스트용 DB 생성
createdb sbtest

2. 시나리오 1: Master 단일 성능

먼저 데이터베이스에 더미 데이터를 채워 넣어야 한다. -s 50 옵션으로 약 800MB 가량의 데이터를 생성했다.

# 데이터 초기화 (Scale Factor 50)
pgbench -i -s 50 sbtest

그다음, Master 노드의 순수 성능을 측정했다.

# 동시 접속 20, 60초 수행
pgbench -c 20 -j 4 -T 60 -r sbtest

결과: 약 707 TPS K3s 홈 서버 환경임을 고려할 때, 쓰기(Write)가 포함된 트랜잭션에서 700대 TPS는 상당히 준수한 수치다. 이것이 우리 서버의 기준점(Baseline)이 된다.

root@pgbench-client:/# pgbench -c 20 -j 4 -T 60 -r sbtest
pgbench (14.20 (Debian 14.20-1.pgdg13+1), server 17.6)
starting vacuum...end.
transaction type: <builtin: TPC-B (sort of)>
scaling factor: 50
query mode: simple
number of clients: 20
number of threads: 4
duration: 60 s
number of transactions actually processed: 42595
latency average = 28.255 ms
initial connection time = 30.840 ms
tps = 707.839182 (without initial connection time)
statement latencies in milliseconds:
         0.001  \set aid random(1, 100000 * :scale)
         0.000  \set bid random(1, 1 * :scale)
         0.000  \set tid random(1, 10 * :scale)
         0.000  \set delta random(-5000, 5000)
         1.379  BEGIN;
         4.807  UPDATE pgbench_accounts SET abalance = abalance + :delta WHERE aid = :aid;
         1.748  SELECT abalance FROM pgbench_accounts WHERE aid = :aid;
         2.045  UPDATE pgbench_tellers SET tbalance = tbalance + :delta WHERE tid = :tid;
         4.299  UPDATE pgbench_branches SET bbalance = bbalance + :delta WHERE bid = :bid;
         1.666  INSERT INTO pgbench_history (tid, bid, aid, delta, mtime) VALUES (:tid, :bid, :aid, :delta, CURRENT_TIMESTAMP);
        12.270  END;

3. 시나리오 2: Replica 읽기 분산과 트러블슈팅

이제 읽기 전용(Read-Only) 부하를 Replica로 보내 성능을 테스트할 차례다. PGHOST를 Read 서비스로 변경하고 테스트를 돌렸는데… 멈췄다.

이슈: 무한 대기와 WAL 누락

아무리 기다려도 응답이 없어서 Replica 파드의 로그를 확인해보니 충격적인 에러가 찍혀 있었다.

FATAL: could not receive data from WAL stream: ERROR: requested WAL segment 00000001...10 has already been removed

원인 분석: 데이터 초기화 단계(-i -s 50)에서 대량의 데이터를 너무 빠르게 밀어 넣는 바람에, Replica가 동기화하기도 전에 Master가 디스크 공간 확보를 위해 오래된 WAL(Write-Ahead Log) 파일을 지워버린 것이다. “형, 나 10번 파일 줘” 했는데 형이 “야, 그거 이미 지웠어”라고 하는 상황.

해결 방법: Replica가 처음부터 다시 데이터를 받아오도록(Base Backup) Pod와 PVC(디스크)를 모두 삭제하여 초기화했다.

# 1. 파드 삭제
kubectl delete pod postgresql-read-0 -n data

# 2. PVC 삭제 (핵심! 이걸 지워야 데이터가 날아가서 처음부터 다시 받아온다)
kubectl delete pvc data-postgresql-read-0 -n data

이후 Replica가 정상적으로 Running 상태가 된 것을 확인하고 재시도했다. Replica 테스트 시에는 -n (No Vacuum) 옵션이 필수다. (Replica는 읽기 전용이라 Vacuum을 수행할 수 없기 때문)

pgbench -n -c 20 -j 4 -T 60 -S -r sbtest

결과: 약 656 TPS Master(707 TPS)와 거의 대등한 읽기 성능을 보여주었다. 복제가 성능 저하 없이 잘 동작함을 확인했다.

root@pgbench-client:/# pgbench -n -c 20 -j 4 -T 60 -S -r sbtest
pgbench (14.20 (Debian 14.20-1.pgdg13+1), server 17.6)
transaction type: <builtin: select only>
scaling factor: 50
query mode: simple
number of clients: 20
number of threads: 4
duration: 60 s
number of transactions actually processed: 39442
latency average = 30.454 ms
initial connection time = 356.658 ms
tps = 656.733215 (without initial connection time)
statement latencies in milliseconds:
         0.001  \set aid random(1, 100000 * :scale)
        30.248  SELECT abalance FROM pgbench_accounts WHERE aid = :aid;

4. 시나리오 3: HPA 오토스케일링의 위력

마지막으로 가장 기대했던 Horizontal Pod Autoscaler (HPA) 테스트다. 트래픽이 몰리면 Replica 파드가 자동으로 늘어나서 성능이 좋아지는지 보고 싶었다.

이슈: HPA가 생성이 안 된다?

values.yaml에 분명 autoscaling: enabled: true를 넣었는데 kubectl get hpa를 쳐보니 아무것도 없었다.

알고 보니 내가 사용한 차트는 bitnami/postgresql-ha가 아니라 일반 bitnami/postgresql 차트였다. 일반 차트는 버전이나 설정에 따라 HPA 템플릿이 내장되어 있지 않아 설정이 무시된 것이다.

해결: 수동으로 hpa.yaml을 작성하여 적용했다. (테스트를 위해 CPU 임계치를 50%로 낮춤)

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
spec:
  scaleTargetRef:
    kind: StatefulSet
    name: postgresql-dev-read
  minReplicas: 1
  maxReplicas: 3
  metrics:
    - type: Resource
      resource:
        name: cpu
        target:
          type: Utilization
          averageUtilization: 50

최종 부하 테스트 (Scale Out)

HPA 적용 후, 더 강력한 부하(-c 40)를 5분간(-T 300) 지속했다.

pgbench -n -c 40 -j 10 -T 300 -S -r sbtest

테스트 도중 backend died 에러가 일부 발생했다. 급격한 스케일 아웃 과정에서 리소스(메모리) 부족으로 인한 OOM 혹은 커넥션 재분배 과정의 드랍으로 추정된다.
아래 사진과 같이 복제된 파드가 종료가 되는 것을 확인할 수 있다.

하지만 최종 결과는 놀라웠다.

  • 변경 전: 656 TPS (Pod 1개)
  • 변경 후: 2,424 TPS (Pod 3개)

약 3.7배의 성능 향상을 기록했다. HPA가 동작하여 파드가 3개로 늘어났고, 로드밸런서가 트래픽을 적절히 분산해주었기 때문이다.

pgbench (14.20 (Debian 14.20-1.pgdg13+1), server 17.6)
pgbench: error: client 5 aborted in command 1 (SQL) of script 0; perhaps the backend died while processing
pgbench: error: client 10 aborted in command 1 (SQL) of script 0; perhaps the backend died while processing
pgbench: error: client 20 aborted in command 1 (SQL) of script 0; perhaps the backend died while processing
pgbench: error: client 2 aborted in command 1 (SQL) of script 0; perhaps the backend died while processing
pgbench: error: client 13 aborted in command 1 (SQL) of script 0; perhaps the backend died while processing
...
transaction type: <builtin: select only>
scaling factor: 50
query mode: simple
number of clients: 40
number of threads: 10
duration: 300 s
number of transactions actually processed: 727652
latency average = 16.497 ms
initial connection time = 359.488 ms
tps = 2424.754928 (without initial connection time)
statement latencies in milliseconds:
         0.001  \set aid random(1, 100000 * :scale)
        14.276  SELECT abalance FROM pgbench_accounts WHERE aid = :aid;
pgbench: fatal: Run was aborted; the above results are incomplete.

참고: 부하 테스트가 끝난 후 CPU 사용량이 5%대로 떨어졌지만 파드는 바로 줄어들지 않았다. 이는 Kubernetes HPA의 Stabilization Window(기본 5분) 정책 때문이다. 트래픽이 잠시 줄었다고 바로 파드를 죽이면 “Flapping(널뛰기)” 현상이 발생하기 때문에, 일정 시간 동안 낮은 부하가 유지되어야 Scale In이 발생한다.

아래 사진과 같이 테스트가 마친 후에는 파드가 내려가는 것을 확인할 수 있다.

5. 결론

이번 테스트를 통해 홈 서버 K3s 환경에서도 엔터프라이즈급 HA 구성과 오토스케일링이 완벽하게 동작함을 검증했다.

  1. HA 구성: Master 장애나 부하 시에도 Replica를 통해 읽기 성능을 확장할 수 있다.
  2. 트러블슈팅: 대량 데이터 적재 시 wal_keep_size 튜닝이나 PVC 초기화가 필요함을 배웠다.
  3. HPA: 트래픽에 따라 유연하게 파드가 늘어나며, 실제 처리량(TPS)이 선형적으로 증가함을 확인했다.

이제 안심하고 이 DB 위에 서비스를 올려도 될 것 같다. 끝!


📂 [시리즈] K3s 홈 서버 DB 운영 가이드

  • [1편] K3s 환경에서 PostgreSQL Master-Replica HA 구성하기 (Helm, HostPath PV)
  • [2편] PostgreSQL HA 부하 테스트: pgbench 성능 검증과 HPA 오토스케일링

#Autoscaling#Helm#Home Lab#HPA#k3s#Kubernetes#Load Testing#pgbench#Postgresql#Troubleshooting

You might also like

[Jenkins] Docker로 띄운 Jenkins, 데이터 그대로 Kubernetes로 이관하기

#Helm#Jenkins#k3s

[Next.js + WordPress] 홈서버 배포기: ETIMEDOUT 해결부터 성능 최적화까지

#DevOps#Docker#Jenkins

[Home Server] 우분투 24.04 LTS 보안 강화 가이드: UFW 설정과 3중 방어 체계 분석

#Cloudflare#Docker#Fail2Ban

Comments (0)

Leave a Reply

Or login to comment with your account.

No comments yet. Be the first to share your thoughts!