Skip to content

PostGIS K3s Operations

JinmuGo edited this page Aug 19, 2026 · 3 revisions

PostGIS / k3s Operations

dev와 prod의 Momo PostgreSQL은 PostgreSQL 17 + PostGIS 3.5.7 linux/arm64 이미지로 k3s에서 실행한다. prod는 2026-08-19 최신 백업의 격리 restore 리허설을 통과한 뒤 승격했으며, 두 환경 모두 동일한 고정 GHCR digest를 사용한다.

노드 주소, SSH 계정, registry credential, DB 비밀번호, Gmail 앱 비밀번호는 이 문서에 기록하지 않는다.

운영 계약

항목 정상 조건
컨테이너 플랫폼 linux/arm64
데이터베이스 PostgreSQL 17
공간 확장 이미지에 PostGIS 3.5.7이 있고 DB에 postgis extension이 활성화됨
이미지 배포 검증된 GHCR digest 사용, latest 금지
migration 각 환경 ArgoCD Sync의 PreSync Hook으로 실행
데이터 StatefulSet을 재생성해도 기존 PVC 유지
애플리케이션 상태 /healthdatabase·postgis, /health/storagestorageup

PostGIS 이미지로 Pod를 실행하는 것과 DB에서 extension을 활성화하는 것은 별개다. 이미지 안에 extension 파일이 있어도 migration의 CREATE EXTENSION IF NOT EXISTS postgis가 실행되지 않으면 ST_DWithin 같은 공간 함수를 사용할 수 없다.

배포와 migration 확인

각 환경의 애플리케이션 Sync가 시작되면 momo-api-migration Job이 PreSync 단계에서 먼저 실행된다. TypeORM은 이미 기록된 migration을 건너뛰므로 동일 release를 다시 Sync해도 안전해야 한다.

  1. ArgoCD의 dnd-15th-3-dev 또는 dnd-15th-3-prod Application에서 최신 Sync operation을 연다.
  2. PreSync 단계의 momo-api-migrationSucceeded인지 확인한다.
  3. 실패하면 Hook 로그와 DB 연결/extension 오류를 확인하고 API rollout을 진행하지 않는다.
  4. 성공한 Hook은 HookSucceeded 정책으로 자동 삭제될 수 있다. 현재 Job 목록에 없다는 이유로 실행되지 않았다고 판단하지 말고 ArgoCD operation history를 근거로 삼는다.

read-only Kubernetes 권한이 있으면 실패한 Hook이 남아 있는 동안 다음 정보도 수집한다.

namespace=dnd-15th-3-dev # prod는 dnd-15th-3-prod
kubectl -n "$namespace" get job,pod
kubectl -n "$namespace" logs job/momo-api-migration

장애가 전파되는 방식

k3s 노드의 외부 DNS 또는 registry 접근 실패
  → PostGIS 이미지 pull 실패
  → PostgreSQL Pod ImagePullBackOff
  → migration 또는 API DB readiness 실패
  → API Pod NotReady / Service EndpointSlice 유실
  → momo-dev.jinmu.me 또는 momo.jinmu.me 503

팀원은 장애 중 StatefulSet, PVC를 삭제하거나 ArgoCD에서 강제 Sync하지 않는다. 특히 PVC 삭제는 데이터 손실로 이어질 수 있다.

팀원 진단 절차

# Pod와 최근 event
kubectl -n dnd-15th-3-dev get pods -o wide
kubectl -n dnd-15th-3-dev get events --sort-by=.lastTimestamp
kubectl -n dnd-15th-3-dev describe pod momo-postgres-0

# API Service의 Ready endpoint
kubectl -n dnd-15th-3-dev get service,endpointslice

# 외부 사용자 경로
curl -fsS https://momo-dev.jinmu.me/health

환경, 최초 발견 시각, /health 응답, Pod 상태와 노드명, 핵심 event, 전체 이미지 digest를 함께 전달한다.

인프라 관리자 진단과 복구

먼저 장애 Pod가 배치된 노드에서 DNS와 registry 접근을 분리해 검사한다.

getent hosts ghcr.io
curl -sS -o /dev/null -w '%{http_code}\n' https://ghcr.io/v2/

인증하지 않은 GHCR /v2/ 요청의 401은 registry까지 도달했다는 뜻이다. 이름 해석 실패, timeout, TLS 오류는 정상으로 간주하지 않는다. manifest unknown이나 unauthorized는 이미지 이름, 공개 범위 또는 credential을 확인하고, no matching manifest for linux/arm64는 OCI manifest의 플랫폼을 확인한다.

DNS와 registry 접근이 복구되면 Pod가 요청한 정확한 digest를 노드에 확보한다. 긴급 복구 중 다른 PostGIS 버전으로 바꾸지 않는다. 이미지가 containerd에 존재하는 것을 확인한 뒤 PostgreSQL Pod만 재생성하며 StatefulSet과 PVC는 삭제하지 않는다.

sudo k3s ctr -n k8s.io images list | grep dnd-15th-3-postgis
kubectl -n dnd-15th-3-dev delete pod momo-postgres-0
kubectl -n dnd-15th-3-dev wait --for=condition=Ready pod/momo-postgres-0 --timeout=180s
kubectl -n dnd-15th-3-dev rollout status deployment/momo-api --timeout=180s

rollback이 필요하면 API를 이전 이미지로 roll-forward한다. PostGIS DB 이미지나 PVC를 이전 PostgreSQL 이미지로 되돌리거나 삭제하지 않는다.

복구 검증

아래 검사가 모두 통과해야 해당 환경의 복구 완료로 판단한다.

# Kubernetes 상태
kubectl -n dnd-15th-3-dev get pod momo-postgres-0
kubectl -n dnd-15th-3-dev get pods -l app.kubernetes.io/name=momo-api
kubectl -n dnd-15th-3-dev get pods -l app.kubernetes.io/name=momo-place-sync-worker
kubectl -n dnd-15th-3-dev get endpointslice

# DB 내부: credential은 Secret 또는 승인된 관리 도구에서 주입
psql "$DATABASE_URL" -c "SELECT postgis_lib_version();"
psql "$DATABASE_URL" -c "SELECT count(*) FROM typeorm_migrations;"
psql "$DATABASE_URL" -c "SELECT ST_DWithin(ST_SetSRID(ST_MakePoint(127.0, 37.5), 4326)::geography, ST_SetSRID(ST_MakePoint(127.001, 37.5), 4326)::geography, 1000);"

# 사용자 경로
base_url=https://momo-dev.jinmu.me # prod는 https://momo.jinmu.me
curl -fsS "$base_url/health"
curl -fsS "$base_url/health/storage"
  • PostgreSQL, API, Worker Pod가 모두 Ready다.
  • API Service의 EndpointSlice에 Ready endpoint가 있다.
  • postgis_lib_version()3.5.7을 반환한다.
  • TypeORM migration 기록이 3개 이상이고 공간 쿼리가 true다.
  • /healthdatabase·postgis/health/storagestorage가 모두 up이다.
  • info.postgis.versiondetails.postgis.version에 설치 버전이 표시된다.

Prometheus / Gmail 알림

Alertmanager는 dnd-15th-3-dev|prod namespace의 warningcritical만 Gmail receiver로 전달한다. critical은 1시간, warning은 6시간 간격으로 재통지하며 복구 알림도 보낸다.

알림 Severity 1차 담당 대응
MomoImagePullFailure warning 인프라 관리자 노드 DNS, registry, credential, ARM64 manifest 확인
MomoPostgresUnavailable critical 인프라 관리자 StatefulSet, PVC, DB log와 event 확인
MomoApiUnavailable critical 백엔드 팀 API log/health 확인 후 인프라 문제면 escalation
MomoApiEndpointMissing critical 백엔드 팀 readiness와 EndpointSlice 확인 후 escalation
MomoMigrationJobFailed critical 백엔드 팀 ArgoCD PreSync history와 migration log 확인
MomoPostgresBackupJobFailed warning 인프라 관리자 dump/upload log, Object Storage와 credential 확인

전용 Momo Job 알림이 Gmail 대응의 기준이다. kube-prometheus-stack의 기본 KubeJobFailed는 Prometheus에서 원본 상태를 계속 보여 주지만, 전용 알림이 있는 PostgreSQL backup과 dev migration에 대해서는 중복 메일을 보내지 않는다. 그 밖의 Job과 prod migration의 기본 실패 경고는 계속 Gmail로 전달한다.

실패 Job 조사와 정리

실패 Job은 자동 TTL로 삭제하지 않고 CronJob의 failedJobsHistoryLimit: 3 범위에서 보존한다. 알림에 표시된 namespace와 Job 이름으로 상태와 Pod를 먼저 확인한다.

namespace=dnd-15th-3-dev
job=momo-postgres-backup-SCHEDULE_ID

kubectl -n "$namespace" get job "$job" -o wide
kubectl -n "$namespace" describe job "$job"
kubectl -n "$namespace" get pods -l "job-name=$job" -o wide

backup Job은 재시도로 Pod가 여러 개 생길 수 있다. 위 목록의 각 실패 Pod에 대해 dump init container와 upload container 로그를 따로 확인한다. init container가 실패하면 upload는 시작되지 않았을 수 있다.

pod=FAILED_BACKUP_POD
kubectl -n "$namespace" logs "$pod" -c dump --timestamps
kubectl -n "$namespace" logs "$pod" -c upload --timestamps

migration은 ArgoCD operation history와 남아 있는 Hook Pod의 migration 로그를 함께 확인한다.

namespace=dnd-15th-3-dev
job=momo-api-migration
kubectl -n "$namespace" get pods -l "job-name=$job" -o wide
kubectl -n "$namespace" logs job/"$job" -c migration --timestamps

환경, 실패 시각, Job/Pod 상태, 실패 container 로그, backup이면 OCI Object Storage의 최신 객체를 기록한다. 원인을 확인하고 다음 실행 또는 수동 검증이 성공한 뒤에만 해당 실패 Job을 삭제한다. CronJob, 성공 Job, StatefulSet, PVC는 삭제하지 않는다.

kubectl -n "$namespace" delete job "$job"

삭제 후 해당 KubeJobFailed가 Prometheus에서 사라지고 새로운 Momo 전용 firing 알림이 없는지 확인한다.

메일 수신자는 인프라 관리자가 운영한다. SMTP 앱 비밀번호는 monitoring namespace의 alertmanager-gmail Secret app-password 키로만 관리하고 Wiki, 평문 파일, Git 기록에 남기지 않는다. 메일 장애가 나도 애플리케이션 배포는 유지되며 Alertmanager가 재시도한다. 현재 알림은 Prometheus와 Grafana에서도 확인할 수 있다.

책임 분리와 재발 방지

주체 책임
백엔드 팀 migration, health, API/Worker 로그, 배포 증거 수집
인프라 관리자 노드 DNS, k3s/containerd, registry, PVC/backup, Alertmanager/Gmail
ArgoCD 승인된 manifest 반영, Hook history와 drift 표시
  • 노드 재부팅 후에도 GHCR DNS와 HTTPS 검사가 성공하는지 확인한다.
  • PostGIS 이미지는 CI에서 linux/arm64, PostgreSQL 17, PostGIS 3.5를 smoke test한다.
  • DB 이미지 변경은 불변 digest로 검토한다.
  • prod의 다음 DB 이미지 변경도 최신 backup/restore 리허설과 불변 digest 검증 뒤 진행한다.

Clone this wiki locally