P1 1차 방어. 호스트 cron이 매일 one-shot 컨테이너로 prod DB를 R2에 백업. (원본: infra/backup/)
42.127.251.21에 배포·검증 완료. 매일 03:00 KST
pg_dump(custom) → age 암호화 → R2 ktaxi-backups/daily/ 업로드 → 최신 14개 보존.| R2 인증 | rclone/S3 키 대신 Cloudflare API 토큰(Bearer) + R2 REST API. 키쌍 발급 불필요 |
|---|---|
| DB 접속 | 운영 pg_hba가 127.0.0.1 trust + 비번 로테이션됨 → 컨테이너를 --network container:ktaxi-postgres로 postgres netns에 붙여 127.0.0.1 trust 접속(비번 보관 불필요) |
| 스케줄 | 상시 사이드카 대신 호스트 cron이 docker run --rm 트리거 → 단순·견고 |
| 암호화 | age 공개키로 암호화. 개인키는 서버에 두지 않음(재해 시 복구 위해 오프사이트 보관) |
scp infra/backup/backup.sh infra/backup/Dockerfile root@<server>:/opt/ktaxi/backup/
ssh root@<server> 'cd /opt/ktaxi/backup && docker build -t ktaxi-backup .'
PGHOST=127.0.0.1
PGPORT=5432
PGUSER=ktaxi
PGDATABASE=ktaxi
R2_ACCOUNT_ID=eafb6baf7a01357c80a79a2aac693f80
R2_API_TOKEN=<Cloudflare R2 Read&Write 토큰>
R2_BUCKET=ktaxi-backups
BACKUP_AGE_RECIPIENT=<백업 age 공개키>
RETENTION_COUNT=14
TZ=Asia/Seoul
scp infra/backup/ktaxi-backup.cron root@<server>:/etc/cron.d/ktaxi-backup
ssh root@<server> 'chmod 644 /etc/cron.d/ktaxi-backup; touch /var/log/ktaxi-backup.log'
docker run --rm --network container:ktaxi-postgres --env-file /opt/ktaxi/backup/backup.env ktaxi-backup
# 1) R2 최신 백업 다운로드
API=https://api.cloudflare.com/client/v4/accounts/$ACCT/r2/buckets/ktaxi-backups/objects
LATEST=$(curl -fsS "$API?prefix=daily/" -H "Authorization: Bearer $TOKEN" | jq -r '.result|sort_by(.key)|last.key')
curl -fsS "$API/$LATEST" -H "Authorization: Bearer $TOKEN" -o /tmp/latest.age
# 2) 복호화 (오프사이트 개인키)
age -d -i <age-key.txt> /tmp/latest.age > /tmp/latest.dump
# 3) 스크래치 DB로 리허설 (운영 DB 직접 복원 금지)
docker exec ktaxi-postgres sh -c 'createdb -U ktaxi ktaxi_restore_test'
docker cp /tmp/latest.dump ktaxi-postgres:/tmp/latest.dump
docker exec ktaxi-postgres pg_restore -U ktaxi -d ktaxi_restore_test --no-owner --no-acl /tmp/latest.dump
# 행수 대조 후 dropdb ktaxi_restore_test
상세는 인프라 이전 가이드 Part A 와 동일.
| 로그 | tail -f /var/log/ktaxi-backup.log |
|---|---|
| 보존 | R2 daily/ 최신 RETENTION_COUNT(14)개만, 나머지 자동 삭제 |
| 개인키 | 오프사이트 보관 필수(서버에 없음). 분실 시 백업 복호화 불가 |
| RPO 한계 | 일 1회 = 최대 24h. R2 단일 PUT ~300MB — 초과 시 S3 멀티파트 전환 |