콘텐츠로 이동






08. HA 구성과 장애 전환 검증

1. 검증 범위

단일 인스턴스를 3개로 확장하고 복제·장애 복구를 확인합니다.

항목 검증 내용
인스턴스 Primary 1개·Replica 2개
배치 노드 분산
복제 Replica 상태·복제 지연
장애 Primary Pod 삭제
연결 Primary Service 경유 쓰기 재개
복귀 삭제한 인스턴스의 역할·복제 상태

Pod 삭제는 노드 장애·네트워크 단절과 다릅니다. 이번 결과를 전체 HA 검증으로 간주하지 않습니다.

백업·복구는 제외합니다.

2. 사전 확인

2.1. 변수 설정

export K8S_CONTEXT="$(kubectl config current-context)"
export DB_NAMESPACE="postgres"
export DB_CLUSTER="demo-db-yaml"
export DB_SERVICE="$DB_CLUSTER"

클러스터·노드를 확인합니다.

kubectl --context "$K8S_CONTEXT" get sgcluster "$DB_CLUSTER" \
  --namespace "$DB_NAMESPACE"

kubectl --context "$K8S_CONTEXT" get nodes -o wide

2.2. 테스트 조건

  • 추가 인스턴스의 CPU·메모리·볼륨 확보
  • Replica 준비 후 장애 발생
  • 쓰기는 Primary Service 사용
  • 애플리케이션 재접속은 별도 검증

3개 인스턴스를 분산하려면 배치 조건을 충족하는 노드도 3개 필요합니다.

3. 인스턴스 확장

SGCluster YAML의 인스턴스 수를 변경합니다.

spec:
  instances: 3

기존 버전·스토리지·설정 참조는 유지합니다.

kubectl --context "$K8S_CONTEXT" apply \
  -f 02-cluster.yaml

UI에서는 클러스터 편집 화면에서 변경합니다. 관리 YAML이 있으면 함께 반영합니다.

Pod·PVC를 확인합니다.

kubectl --context "$K8S_CONTEXT" get pods \
  --namespace "$DB_NAMESPACE" \
  -o wide

kubectl --context "$K8S_CONTEXT" get pvc \
  --namespace "$DB_NAMESPACE"

기본 Pod 이름 예시입니다.

demo-db-yaml-0
demo-db-yaml-1
demo-db-yaml-2

Pod 번호로 Primary를 판단하지 않습니다.

4. 노드 분산과 복제 확인

4.1. 노드 분산

Pod의 NODE를 확인합니다.

kubectl --context "$K8S_CONTEXT" get pods \
  --namespace "$DB_NAMESPACE" \
  -o wide

같은 노드의 인스턴스는 노드 장애 시 함께 영향을 받습니다.

StackGres의 production 프로파일은 분산용 anti-affinity를 자동 구성합니다. 실제 프로파일·Pod 배치 조건을 확인합니다.

kubectl --context "$K8S_CONTEXT" get sgcluster "$DB_CLUSTER" \
  --namespace "$DB_NAMESPACE" \
  -o jsonpath='{.spec.profile}{"\n"}'

kubectl --context "$K8S_CONTEXT" get pod "${DB_CLUSTER}-0" \
  --namespace "$DB_NAMESPACE" \
  -o yaml

인스턴스 확장과 분산 배치는 별도로 확인합니다.

4.2. Patroni 상태

준비된 Pod에서 실행합니다.

kubectl --context "$K8S_CONTEXT" exec \
  --namespace "$DB_NAMESPACE" \
  "${DB_CLUSTER}-0" \
  --container patroni \
  -- patronictl list
항목 확인 내용
Role Leader 1개·Replica
State 실행·복제 상태
Lag 복제 지연
Timeline 장애 전후 타임라인

현재 Leader를 기록합니다.

export PRIMARY_POD="<현재-Leader-Pod>"

Replica가 비정상이거나 지연이 크면 장애 테스트를 중단합니다.

5. 쓰기 테스트 준비

5.1. 테이블 생성

06번 문서의 방법으로 appuser·appdb에 접속합니다.

CREATE TABLE app.ha_check (
    id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
    recorded_at timestamptz NOT NULL DEFAULT clock_timestamp(),
    message text NOT NULL
);

INSERT INTO app.ha_check (message)
VALUES ('before failure');

SELECT * FROM app.ha_check;

5.2. 반복 접속과 쓰기

클러스터 내부의 별도 클라이언트에서 실행합니다. psql과 DB 접근 경로가 필요합니다.

export PGHOST="demo-db-yaml.postgres"
export PGPORT="5432"
export PGDATABASE="appdb"
export PGUSER="appuser"
export PGCONNECT_TIMEOUT="3"

read -r -s -p "DB password: " PGPASSWORD
printf '\n'
export PGPASSWORD

외부 PC는 PGHOST·PGPORT를 실제 NodePort 또는 LoadBalancer 접속값으로 바꿉니다.

while true; do
  printf '%s ' "$(date -Is)"

  if psql -X -w \
    -v ON_ERROR_STOP=1 \
    -c "INSERT INTO app.ha_check (message) VALUES ('connection probe');" \
    >/dev/null 2>&1; then
    echo "WRITE_OK"
  else
    echo "WRITE_FAIL"
  fi

  sleep 1
done

매번 새 연결을 생성합니다. 기존 연결·애플리케이션 커넥션 풀 복구는 검증하지 않습니다.

6. Primary Pod 삭제

6.1. 대상 재확인

삭제 직전에 Leader를 확인합니다.

kubectl --context "$K8S_CONTEXT" exec \
  --namespace "$DB_NAMESPACE" \
  "${DB_CLUSTER}-0" \
  --container patroni \
  -- patronictl list

현재 Leader와 PRIMARY_POD가 일치하는지 확인합니다.

printf '삭제 대상: %s\n' "$PRIMARY_POD"

6.2. 장애 발생

다른 터미널에서 실행합니다.

date -Is

kubectl --context "$K8S_CONTEXT" delete pod "$PRIMARY_POD" \
  --namespace "$DB_NAMESPACE"

강제 삭제 옵션은 사용하지 않습니다.

Pod 상태와 반복 쓰기 결과를 관찰합니다.

kubectl --context "$K8S_CONTEXT" get pods \
  --namespace "$DB_NAMESPACE" \
  -o wide \
  --watch

6.3. 결과 해석

결과 의미
다른 Pod가 Leader Replica 승격·역할 전환
기존 Pod가 다시 Leader 빠른 재생성으로 기존 역할 유지 가능

리더 잠금 만료 전에 재생성되면 기존 Primary가 유지될 수 있습니다. Pod 삭제가 반드시 Replica 승격으로 이어지지는 않습니다.

7. 복구 상태 확인

7.1. 역할과 복제

준비된 Pod에서 조회합니다.

kubectl --context "$K8S_CONTEXT" exec \
  --namespace "$DB_NAMESPACE" \
  <READY_POD> \
  --container patroni \
  -- patronictl list

다음을 확인합니다.

  • Leader 1개
  • 쓰기 재개
  • 삭제한 Pod 재생성
  • Replica 상태·지연 정상화

Leader가 바뀌었다면 이전 Primary의 Replica 복귀를 확인합니다.

7.2. 데이터 확인

Primary Service로 접속합니다.

SELECT * FROM app.ha_check
ORDER BY id DESC
LIMIT 10;

INSERT INTO app.ha_check (message)
VALUES ('after recovery');

조회 성공만으로 무손실을 증명하지 않습니다. 복제 모드·장애 시점의 쓰기 결과를 함께 평가합니다.

7.3. 연결 회복 시간

반복 쓰기 로그에서 시점을 기록합니다.

시점 기록 내용
Pod 삭제 시작 명령 실행 시간
최초 WRITE_FAIL 연결·쓰기 실패
WRITE_OK 재개 연결·쓰기 회복
Replica 정상화 복제 상태 회복

테스트 간격·연결 제한 시간이 포함되므로 정밀한 장애 전환 시간으로 표현하지 않습니다.

실패가 없으면 “이번 샘플링에서 실패 미관찰”로 기록합니다.

8. 애플리케이션 재접속 확인

반복 psql과 실제 애플리케이션 검증을 구분합니다.

항목 확인 내용
기존 연결 끊긴 연결 폐기
커넥션 풀 새 연결 확보
재시도 중복 쓰기 방지
트랜잭션 실패·결과 불명 처리
접속 주소 Primary Service 사용

장애 전환 성공과 애플리케이션 연결 회복은 별도로 확인합니다.

9. 결과 기록과 정리

9.1. 검증 기록

항목 실제 결과
인스턴스 수
노드 분산
장애 전 Leader
장애 후 Leader
역할 전환 여부
쓰기 실패 구간
기존 인스턴스 복귀
애플리케이션 재접속

Pod 재시작과 Replica 승격을 구분해 기록합니다.

9.2. 테스트 정리

반복 쓰기를 Ctrl+C로 중단합니다.

unset PGPASSWORD

테스트 테이블을 삭제합니다.

DROP TABLE app.ha_check;

인스턴스 축소·PVC 삭제 전 역할과 볼륨 처리 정책을 확인합니다.