09. 트러블슈팅과 정리

1. 기본 확인 순서
오류는 다음 순서로 확인합니다.
설정 → Event → Pod·PVC → 컨테이너 로그 → Service·접속
Web UI 오류와 DB 장애는 구분합니다. UI가 느려도 SQL 접속은 정상일 수 있습니다.
실제 환경에 맞춰 변수를 설정합니다.
export K8S_CONTEXT="$(kubectl config current-context)"
export SG_NAMESPACE="stackgres"
export SG_RELEASE="stackgres-operator"
export DB_NAMESPACE="postgres"
export DB_CLUSTER="demo-db-yaml"
export DB_SERVICE="$DB_CLUSTER"
2. 기본 진단 명령
2.1. 리소스와 Event
kubectl --context "$K8S_CONTEXT" get sgclusters \
--namespace "$DB_NAMESPACE"
kubectl --context "$K8S_CONTEXT" get pods,pvc,svc \
--namespace "$DB_NAMESPACE" \
-o wide
kubectl --context "$K8S_CONTEXT" get events \
--namespace "$DB_NAMESPACE" \
--sort-by=.metadata.creationTimestamp
2.2. Pod와 컨테이너
kubectl --context "$K8S_CONTEXT" describe pod "${DB_CLUSTER}-0" \
--namespace "$DB_NAMESPACE"
컨테이너 이름을 확인합니다.
kubectl --context "$K8S_CONTEXT" get pod "${DB_CLUSTER}-0" \
--namespace "$DB_NAMESPACE" \
-o jsonpath='{.spec.initContainers[*].name}{"\n"}{.spec.containers[*].name}{"\n"}'
문제 컨테이너의 로그를 조회합니다.
kubectl --context "$K8S_CONTEXT" logs "${DB_CLUSTER}-0" \
--namespace "$DB_NAMESPACE" \
--container <CONTAINER_NAME> \
--tail=200
재시작 전 로그는 --previous로 확인합니다.
kubectl --context "$K8S_CONTEXT" logs "${DB_CLUSTER}-0" \
--namespace "$DB_NAMESPACE" \
--container <CONTAINER_NAME> \
--previous \
--tail=200
3. Operator 설치 오류
helm status "$SG_RELEASE" \
--namespace "$SG_NAMESPACE" \
--kube-context "$K8S_CONTEXT"
kubectl --context "$K8S_CONTEXT" get deployments,pods,jobs \
--namespace "$SG_NAMESPACE"
kubectl --context "$K8S_CONTEXT" get events \
--namespace "$SG_NAMESPACE" \
--sort-by=.metadata.creationTimestamp
| 증상 | 확인 항목 |
|---|---|
| Forbidden | 설치 계정 RBAC |
| 기존 리소스 충돌 | Release·리소스 소유 관계 |
| Job 실패 | 설치 Job Pod 로그 |
| Deployment 미준비 | 배치·이미지·Readiness |
| Webhook 오류 | Operator·Service·인증서·통신 |
원인 확인 전 CRD 삭제·설치 반복을 하지 않습니다.
4. Web UI 접속 오류
4.1. 접속 경로
루트 주소에서 오류가 나면 /admin/을 확인합니다.
https://<관리 주소>/admin/
4.2. HTTP·HTTPS 불일치
다음 오류는 요청 프로토콜·대상 포트를 확인합니다.
The plain HTTP request was sent to HTTPS port
| 구성 | 확인 항목 |
|---|---|
| HTTP 백엔드 | HTTP 활성화·Ingress 대상 포트 |
| HTTPS 백엔드 | Controller의 HTTPS 백엔드 설정 |
| 외부 TLS 종료 | 백엔드 구간 프로토콜 |
kubectl --context "$K8S_CONTEXT" get svc stackgres-restapi \
--namespace "$SG_NAMESPACE" \
-o yaml
kubectl --context "$K8S_CONTEXT" get ingress \
--namespace "$SG_NAMESPACE" \
-o yaml
4.3. 반복 타임아웃
브라우저 개발자 도구의 Network에서 실패한 API 요청을 확인합니다.
- Request URL
- Status Code
- Duration
- Response
같은 시각의 REST API 로그와 비교합니다.
kubectl --context "$K8S_CONTEXT" logs \
--namespace "$SG_NAMESPACE" \
deployment/stackgres-restapi \
--all-containers=true \
--tail=200
프록시·REST API·브라우저 중 실패 구간을 확인하고 타임아웃을 조정합니다.
5. PVC·Pod Pending
5.1. PVC 확인
kubectl --context "$K8S_CONTEXT" get pvc \
--namespace "$DB_NAMESPACE"
kubectl --context "$K8S_CONTEXT" describe pvc <PVC_NAME> \
--namespace "$DB_NAMESPACE"
kubectl --context "$K8S_CONTEXT" get storageclass
kubectl --context "$K8S_CONTEXT" get pv
| 상태 | 확인 항목 |
|---|---|
| PVC Pending | StorageClass·Provisioner·용량·배치 |
| PVC Bound, Pod Pending | 노드 배치·볼륨 연결·마운트 |
| WaitForFirstConsumer | Pod 배치·볼륨 생성 조건 |
| 로컬 볼륨 | 볼륨의 노드 제약 |
5.2. VolumeBinding 충돌
다음 메시지는 PVC의 동시 변경 충돌일 수 있습니다.
the object has been modified;
please apply your changes to the latest version and try again
단일 메시지로 최종 실패를 판단하지 않습니다. 재시도 후 PVC·Pod 상태가 진행되는지 확인합니다.
PVC 삭제·resourceVersion 수정으로 해결하려 하지 않습니다.
6. 이미지·초기화 오류
6.1. ImagePullBackOff
Pod Event에서 다운로드 오류를 확인합니다.
| 오류 유형 | 확인 항목 |
|---|---|
| 이미지 없음 | 저장소 경로·태그 |
| 인증 실패 | imagePullSecrets |
| 인증서 오류 | 노드 런타임의 인증서 신뢰 |
| 연결 실패 | 노드 DNS·방화벽·프록시 |
관리 PC의 다운로드 성공이 노드의 성공을 보장하지는 않습니다.
6.2. Init Container 지연
상태·로그를 확인합니다.
kubectl --context "$K8S_CONTEXT" describe pod "${DB_CLUSTER}-0" \
--namespace "$DB_NAMESPACE"
kubectl --context "$K8S_CONTEXT" logs "${DB_CLUSTER}-0" \
--namespace "$DB_NAMESPACE" \
--container <INIT_CONTAINER_NAME> \
--tail=200
스토리지·파일 초기화·확장 패키지 중 지연 단계를 구분합니다.
7. 클러스터 생성 지연
총시간보다 단계별 지연을 확인합니다.
| 단계 | 확인 대상 |
|---|---|
| 리소스 생성 | SGCluster·Operator 로그 |
| 볼륨 준비 | PVC·스토리지 Event |
| Pod 배치 | 스케줄링 Event |
| 이미지 다운로드 | Pulling·Pulled Event |
| 초기화 | Init Container |
| DB 준비 | Patroni·PostgreSQL·Readiness |
| UI 표시 | REST API·브라우저 요청 |
UI 표시와 SQL 실행 가능 시점은 따로 기록합니다.
이미지 캐시·인스턴스 수·스토리지 조건이 다른 결과를 단순 비교하지 않습니다.
8. DB 접속 오류
8.1. Service 연결 대상
kubectl --context "$K8S_CONTEXT" get svc "$DB_SERVICE" \
--namespace "$DB_NAMESPACE" \
-o yaml
kubectl --context "$K8S_CONTEXT" get endpointslices \
--namespace "$DB_NAMESPACE" \
--selector "kubernetes.io/service-name=${DB_SERVICE}"
| 증상 | 확인 항목 |
|---|---|
| DNS 실패 | Service 이름·Namespace·도메인 |
| Timeout | 방화벽·NetworkPolicy·접속 주소 |
| Connection refused | 포트·Service 연결 대상 |
| 인증 실패 | Role·비밀번호·인증 설정 |
| 권한 오류 | Database·Schema·테이블 권한 |
| 쓰기 실패 | Primary 접속 여부 |
| TLS 오류 | DB TLS·클라이언트 설정 |
내부는 Service 포트, 외부 NodePort는 할당 포트를 사용합니다.
8.2. 역할 확인
kubectl --context "$K8S_CONTEXT" exec \
--namespace "$DB_NAMESPACE" \
"${DB_CLUSTER}-0" \
--container patroni \
-- patronictl list
SQL로도 확인합니다.
SELECT pg_is_in_recovery();
Replica에 연결됐다면 쓰기 대상 Service·현재 Primary를 확인합니다.
9. SQL Script·Extension 오류
| 증상 | 확인 항목 |
|---|---|
| SQL 미실행 | managedSql 참조·Namespace·실행 상태 |
| SQL 일부만 반영 | 실패 지점·Auto-commit |
| 앱 계정 권한 오류 | 객체 소유자·GRANT |
| Extension 없음 | 지원 패키지·파일 준비 |
| 활성화 실패 | Database·권한·의존성 |
| 확장 기능 조회 실패 | 설치 Schema·search_path |
SQL 상태를 조회합니다.
kubectl --context "$K8S_CONTEXT" get sgcluster "$DB_CLUSTER" \
--namespace "$DB_NAMESPACE" \
-o jsonpath='{.status.managedSql}{"\n"}'
대상 Database에서 확장을 확인합니다.
SELECT name, default_version, installed_version
FROM pg_available_extensions;
SELECT extname, extversion
FROM pg_extension;
파일 준비와 CREATE EXTENSION 실행은 구분합니다.
10. 진단 결과 보관
작업 디렉터리를 생성합니다.
mkdir -p diagnostics
설정·리소스 상태·Event를 저장합니다.
kubectl --context "$K8S_CONTEXT" get sgcluster "$DB_CLUSTER" \
--namespace "$DB_NAMESPACE" \
-o yaml \
> diagnostics/sgcluster.yaml
kubectl --context "$K8S_CONTEXT" get pods,pvc,svc \
--namespace "$DB_NAMESPACE" \
-o wide \
> diagnostics/resources.txt
kubectl --context "$K8S_CONTEXT" get events \
--namespace "$DB_NAMESPACE" \
--sort-by=.metadata.creationTimestamp \
> diagnostics/events.txt
공개 전 내부 주소·SQL·인증 정보를 제거합니다. Secret 전체는 수집하지 않습니다.
11. 테스트 리소스 정리
11.1. 삭제 전 확인
- Context·클러스터 이름
- 데이터 보존 필요성
- SGCluster·PVC·PV 관계와 reclaimPolicy
- 공유 설정·다른 클러스터의 참조
SGCluster 삭제는 PVC·데이터 삭제로 이어질 수 있습니다. 설정 파일은 백업이 아닙니다.
11.2. 클러스터 삭제
데이터가 필요 없는 테스트 클러스터만 삭제합니다.
kubectl --context "$K8S_CONTEXT" delete sgcluster "$DB_CLUSTER" \
--namespace "$DB_NAMESPACE"
남은 리소스를 확인합니다.
kubectl --context "$K8S_CONTEXT" get pods,pvc,svc \
--namespace "$DB_NAMESPACE"
kubectl --context "$K8S_CONTEXT" get pv
남은 PVC는 대상·보존 필요성을 확인한 뒤 삭제 여부를 판단합니다.
11.3. 관련 설정 삭제
다른 클러스터가 참조하지 않는 리소스만 삭제합니다.
kubectl --context "$K8S_CONTEXT" delete sgscript \
"${DB_CLUSTER}-app-init" \
--namespace "$DB_NAMESPACE"
kubectl --context "$K8S_CONTEXT" delete sginstanceprofile \
demo-profile \
--namespace "$DB_NAMESPACE"
실제 이름을 사용합니다. UI가 생성한 추가 리소스도 참조를 확인합니다.
11.4. Operator 제거
모든 StackGres DB의 사용 종료·정리를 확인한 뒤 진행합니다.
kubectl --context "$K8S_CONTEXT" get sgclusters \
--all-namespaces
helm uninstall "$SG_RELEASE" \
--namespace "$SG_NAMESPACE" \
--kube-context "$K8S_CONTEXT"
Operator 제거는 DB·데이터 정리를 대신하지 않습니다. 잔여 CRD·RBAC·Secret은 따로 확인하고 일괄 삭제하지 않습니다.