PostgreSQL Storage
PostgreSQL 스토리지 (PostgreSQL Storage)
Section titled “PostgreSQL 스토리지 (PostgreSQL Storage)”FractalOps PostgreSQL의 desired 스토리지는 Longhorn입니다. 데이터베이스 팟과 PVC는 CloudNativePG 오퍼레이터가, 볼륨 라이프사이클은 Longhorn이 소유합니다.
스토리지 토폴로지
Section titled “스토리지 토폴로지”flowchart TB cnpg["CloudNativePG 오퍼레이터"] --> cluster["Cluster\nfractalops/fractalops-postgresql"] cluster --> rw["Write service\nfractalops-postgresql-rw"] cluster --> pvc["PVC (오퍼레이터 소유)"] pvc --> sc["StorageClass\nfractalops-longhorn-cnpg-rwo\n(Retain, strict-local)"] sc --> lh["Longhorn 볼륨\ndriver.longhorn.io"] cluster --> barman["Barman Cloud plugin"] barman --> s3["SeaweedFS S3\nfractalops-runtime-backups"] snap["VolumeSnapshotClass\nfractalops-longhorn-snapshot"] -. manual recovery only .-> lh bao["OpenBao (external PVE LXC)\nruntime/postgres"] -. secret source .-> eso["External Secrets\nin-cluster projection"] eso -. app/superuser .-> cluster
정규 식별자
Section titled “정규 식별자”- 오퍼레이터: CloudNativePG
- Cluster:
fractalops/fractalops-postgresql - Write service:
fractalops/fractalops-postgresql-rw - StorageClass:
fractalops-longhorn-cnpg-rwo - VolumeSnapshotClass:
fractalops-longhorn-snapshot - ObjectStore:
fractalops/fractalops-postgresql-backup - ScheduledBackup:
fractalops/fractalops-postgresql-object-store - Driver:
driver.longhorn.io
오퍼레이터와 클러스터 부트스트랩
Section titled “오퍼레이터와 클러스터 부트스트랩”make bootstrap-fractalops-postgresql이 타깃은 platform/k8s/bootstrap_fractalops_postgresql.sh를 호출해 오퍼레이터와 클러스터를 깝니다.
클러스터 health 점검
Section titled “클러스터 health 점검”pnpm assembly:kubectl -- -n fractalops get cluster.postgresql.cnpg.io fractalops-postgresqlpnpm assembly:kubectl -- -n fractalops get pooler.postgresql.cnpg.io fractalops-postgresql-pooler-rwCluster의 상태가 healthy이고 pooler가 Ready면 정상입니다. 더 깊은 점검:
pnpm assembly:kubectl -- -n fractalops get pods -l cnpg.io/cluster=fractalops-postgresqlpnpm assembly:kubectl -- -n fractalops get pvc -l cnpg.io/cluster=fractalops-postgresql이식 가능한 백업과 복구
Section titled “이식 가능한 백업과 복구”정규 base backup은 매일 02:30 UTC에 CNPG Barman Cloud plugin이 인증된 SeaweedFS S3 버킷으로 전송하고, WAL은 연속 archive합니다. 반복 VolumeSnapshot 백업은 사용하지 않습니다. VolumeSnapshotClass는 사고 조사나 명시적 수동 복구점에만 남깁니다.
2026-08-04에는 6시간 base 주기와 30일 보존이 200Gi 미만의 로컬 object plane에 맞지
않아 SeaweedFS writable volume이 고갈됐습니다. Base 주기는 일 1회로 제한했지만 30일
보존은 삭제 승인 없이 유지합니다. ADR-0019의 off-cluster backup은 전용 자격증명, 비용
승인, 새 base 완료, 격리 restore proof가 모두 끝난 뒤에만 전환합니다.
2026-07-30 복구 증거:
- Backup
fractalops-postgresql-object-store-20260730114548-retry완료 - 격리된 96 GiB data/20 GiB WAL 클러스터가
Cluster in healthy state도달 - 복구된 Dex DB에서
offline_session=1,refresh_token=1확인 - 복구 성공 후 DB VolumeSnapshot 8개와 detached retained volume 약 92.9 GB 제거
pnpm assembly:kubectl -- -n fractalops get scheduledbackup,backuppnpm assembly:kubectl -- -n fractalops get objectstore.barmancloud.cnpg.io런타임 자격증명 (OpenBao → External Secrets)
Section titled “런타임 자격증명 (OpenBao → External Secrets)”런타임 자격증명은 External Secrets로 OpenBao에서 소싱됩니다. OpenBao는 Kubernetes 안 Postgres 소비자가 아닙니다. OpenBao는 PVE LXC의 외부 secret authority로 남고, Kubernetes 앱은 ExternalSecret로 필요한 값만 투사받습니다.
- 스코프:
runtime/postgres - App 시크릿:
fractalops-postgresql-app - Superuser 시크릿:
fractalops-postgresql-superuser - Seed actor identity는 SPIRE가 증명합니다. Seed 권한 판정은 SpiceDB가 맡고, 실제 secret value 생성/보관/회전은 OpenBao가 맡습니다. ESO는 그 결과를 Kubernetes Secret으로 투사만 합니다.
규칙 (Rules)
Section titled “규칙 (Rules)”- SQL 상태는 CloudNativePG로 모읍니다. Object/blob/file asset은 SeaweedFS/S3 계열,
Redis-compatible cache/session은
cache-planeValkey가 소유합니다. 세 부류를 섞어 Postgres PVC 감축 대상으로 착각하지 마세요. - 안정 PVC나 hostPath PV를 수동으로 만들지 마세요. CloudNativePG가 DB 팟/PVC를, Longhorn이 볼륨 라이프사이클을 소유합니다.
- PGDATA와 WAL은
WaitForFirstConsumer, Longhorn replica 1개,strict-local인fractalops-longhorn-cnpg-rwo를 사용합니다. DB 고가용성은 CNPG 동기 복제와 필수 pod anti-affinity가 맡습니다. 일반durable-rwo-v2를 쓰면 이중 복제와 원격 I/O가 생깁니다. - Postgres를 쓰는 Kubernetes 스택의 endpoint/DB/role/psql image/OpenBao key는
platform/k8s/environments/*/runtime.cue에서 생성되는 environment values가 소유합니다. 앱 chart 기본값에fractalops-postgresql-rw같은 환경 식별자를 직접 박지 마세요. pomeriumdatabroker는 더 이상pomerium-databrokerPVC를 렌더하지 않습니다. DB는 공유 CNPG의 dedicatedpomeriumdatabase/role에 붙고, Pomerium runtime config는 OpenBaoruntime/postgres:pomerium_password를 사용해 Postgres DSN을 렌더합니다.cliproxyruntime config와 OAuth auth files는 CLIProxyAPI의 PGSTORE를 통해 공유 CNPGcliproxydatabase에 저장합니다.cliproxy-authsPVC는 정본이 아니며, retained PV에서 legacy JSON을 발견하면 read-only 복구 소스로만 사용하고public.auth_storeimport 후 management API의 auth file count로 검증합니다.- 단일
supabaseApplication의 업스트림 차트는 PostgreSQL, MinIO, Storage PVC를 렌더하지 않습니다. SQL 수명주기와 로그인 역할은 CNPG가, 스키마·권한은 Backend Git migration이, Storage object bytes는 인증된 SeaweedFS S3가 소유합니다. 이전 Storage 볼륨은 일회성 orphan handoff와 read-only copy 증거에만 보존하고, 이를 유지하려고 ArgoForce=true또는Replace=true를 desired state에 추가하지 마세요. penpot,windmill,playwrightgrid의 app-local PostgreSQL subchart/PVC는 꺼져 있습니다. 각 앱의 dedicated DB/role/secret은postgres-application-databasechart가 생성하고, 앱 chart는 namespace-local Secret만 참조합니다.datahub는 shared CNPGdatahubDB/role/secret contract만 먼저 깔립니다. 현재 GMS SQL datasource는 chart 값에서 MySQL을 가리키므로,global.sql.datasource를 Postgres JDBC로 바꾸고 system-update proof를 남기기 전에는 app flip하지 않습니다.daytona는 shared CNPGdaytonaDB/role/secret을 사용합니다. Upstream chart의externalDatabase가daytona-cnpg-postgresql을 참조하고, region reconcile은 CNPG에 직접 접속합니다. Bundled PostgreSQL subchart와 PVC는 렌더링하지 않습니다.- Redis-compatible cache 목적 워크로드는 DB/PVC 규칙으로 다루지 않습니다.
cache-plane의 Valkey Operator 리소스가 소유하고, 앱은novu-valkey/glitchtip-valkey같은 호환 alias만 둡니다. - 스토리지 클래스/스냅샷 클래스 이름은 정규 식별자입니다. 환경 CUE에서 바꾸고, 클러스터에 직접 패치하지 마세요.
CNPG timeline divergence runbook
Section titled “CNPG timeline divergence runbook”fractalops-postgresql-1 같은 old primary가 pg_rewind 중 could not restore file
또는 could not find previous WAL record를 내면, 그 PVC는 최신 timeline 증거일 수
있습니다. 바로 PVC를 삭제하지 마세요.
- Longhorn
SnapshotCR로 old primary data/WAL, current primary data/WAL을 먼저 보존합니다. - current primary가 이미 쓰기 중이면 새 timeline에 추가 변경이 생깁니다. 에이전트 런이나 bulk job을 더 실행하지 말고, 어떤 timeline을 정본으로 삼을지 먼저 결정합니다.
- CloudNativePG의 일반 복구법은 out-of-sync replica PVC 삭제 후 재생성이지만, old primary에 더 최신 데이터가 있을 수 있는 사고에서는 snapshot 또는 backup 없이 삭제하지 않습니다.
*-postgresql-stable처럼 CNPG chart가 소유하지 않는 detached stable PVC는 삭제 후보입니다. 단, 삭제 전 owner chart가 없는지와 Longhorn snapshot/backup 필요 여부를 확인합니다.
Temporal은 fractalops namespace retention을 7일로 운영합니다. Temporal DB가 빠르게 커지면
retention 자체보다 workflow 폭주, open workflow 누적, archival 미구성, visibility 보관 정책을
먼저 봅니다. Primary Postgres 용량 확장은 Longhorn 예약량에 막힐 수 있으므로 scratch/cache PVC가
durable DB 확장을 밀어내지 않게 해야 합니다.
WAL low-disk recovery
Section titled “WAL low-disk recovery”2026-07-31에는 20Gi WAL PVC가 가득 차 current primary가 PostgreSQL 시작을 거부했고,
fractalops-postgresql-rw에는 not-ready endpoint만 남아 Daytona migration이 멈췄습니다.
정본은 runtime.cue의 32Gi walSize와 8GB max_slot_wal_keep_size입니다. 직접 PVC를
패치하거나 standby를 강제 승격하지 마세요. CNPG의 full-disk guard는 정상 PVC reconciliation보다
먼저 멈추므로, chart의 Sync hook이 CNPG pvcRole 라벨로 기존 PVC를 찾아 CUE 목표 크기를 먼저
적용합니다. 이후 wave 1의 Cluster가 같은 크기를 계속 소유합니다. 복구 후 RW endpoint, 두
instance의 Ready 상태, replication catch-up을 확인한 뒤 종속 Application을 다시 sync합니다.