Repository navigation
feat(infra): 운영 DB·오브젝트 백업 스크립트 + 일 1회 cron - #249
Merged
Merged
Conversation
점검 결과 **백업이 하나도 없었다.** systemd 타이머·crontab·덤프 파일 전부 없었고, 사용자 계정·면접 기록 90건·피드백 76건·이력서 파일이 도커 볼륨 단일 사본에만 존재했다. 배포 워크플로가 볼륨을 건드리진 않지만, infra/CLAUDE.md 가 초기화 절차로 안내하는 `docker compose down -v` 한 번이면 전부 사라진다 — 같은 문서가 그걸 안티패턴으로 경고까지 하면서 안전망은 없는 상태였다. - infra/backup/backup.sh — pg_dump -Fc + MinIO 볼륨 tar.gz, 보존 14일 - **받은 즉시 검증한다**: pg_restore --list 로 복원 가능 형식인지 + 테이블 데이터가 실제로 들어있는지 확인. 0바이트나 깨진 파일을 성공으로 세면 "백업이 있다"는 착각만 남는다(오늘 고친 버그들과 같은 함정). - MinIO tar 는 --user 로 실행. 없이 돌리면 결과물이 root 소유가 되고 보존 삭제가 조용히 실패해 아카이브가 무한정 쌓인다. - LAST_SUCCESS 마커 — cron 출력은 아무도 안 본다. 백업이 멈춘 것과 정상인 것을 구분할 유일한 단서. - 운영 서버에 설치 + crontab 등록(매일 04:00 KST) 완료, 첫 백업 생성됨. 실제 복원을 검증했다 — 임시 DB 로 받아 20개 테이블, users/sessions/messages/ feedbacks/embeddings/documents/resumes 행 수가 원본과 전부 일치. 한계: 같은 디스크라 논리적 사고만 막고 디스크·호스트 장애는 못 막는다. 원격 복제와 LAST_SUCCESS 신선도 헬스체크는 후속 과제(README 에 명시).
i3months
added a commit
to i3months/stackup
that referenced
this pull request
Oct 6, 2026
Team-StackUp#249 로 백업은 생겼지만 **실패하면 아무도 모르는** 상태였다. cron 출력은 읽는 사람이 없고 LAST_SUCCESS 를 들여다볼 사람도 없다 — 오늘 반복해서 당한 "조용히 멈춤" 패턴 그대로다. 실패 모드가 둘이고 잡는 방법이 다르다: 1) 돌았는데 실패 (pg_dump 오류·디스크 부족) → 스크립트가 Discord 알림. 팀이 이미 보는 채널이라 로그 파일보다 실제로 눈에 띈다. 2) 아예 안 돎 (cron 죽음·재부팅) → 스크립트는 못 알린다. 돌지 않았으니까. 백엔드 헬스체크가 밖에서 LAST_SUCCESS 나이를 본다(기본 36시간). - infra/backup/backup.sh: fail() + ERR trap 에서 Discord 알림. 알림 실패는 전부 삼켜 백업 결과를 덮지 않는다(curl -m 10, || true). cron 에는 환경변수가 없으므로 .env 에서 웹훅 한 줄만 꺼내 쓴다(통째 source 하면 다른 값을 덮는다). - deploy-app.yml: DISCORD_WEBHOOK_URL 을 운영 .env 에 주입(GitHub Secret 에만 있어서 서버에서는 쓸 수 없었다). - BackupHealthIndicator + /api/system/health 의 backup 컴포넌트. **전체 status 에는 반영하지 않는다** — 백업이 낡은 건 "복구 수단이 없다"이지 "면접이 안 된다"가 아니다. aggregate 에 넣으면 업타임 감시가 헛울리고 진짜 장애와 구분도 안 된다. ready 에서도 뺀다(트래픽 수용 가능 여부와 무관). - compose: 백업 디렉토리를 backend 에 읽기 전용 마운트. 검증: 실패 시 exit 1 + LAST_SUCCESS 미갱신(그래야 헬스체크가 잡는다), 웹훅이 죽어도 지연 0초, 웹훅 미설정에서도 정상 동작.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
요약
점검 결과 백업이 하나도 없었습니다. 스크립트를 추가하고 운영에 설치·검증했습니다.
발견
데이터가 단일 도커 볼륨에만 있었습니다 — PostgreSQL 13MB(사용자 5명, 세션 100건, 메시지 451건, 피드백 76건, 임베딩 57건), MinIO 246MB(이력서·음성·TTS·분석 원문).
배포 워크플로가 볼륨을 건드리진 않습니다(
up -d --build+image prune뿐). 다만infra/CLAUDE.md가 초기화 절차로docker compose down -v를 안내하고, 같은 문서가 그걸 안티패턴으로 경고까지 합니다 — 위험을 알면서 안전망이 없는 상태였습니다.변경 내용
infra/backup/backup.sh—pg_dump -Fc+ MinIO 볼륨tar.gz, 보존 14일,~/backups저장.설계에서 신경 쓴 세 가지 (전부 오늘 겪은 함정의 재발 방지입니다):
1. 받은 즉시 검증합니다.
pg_restore --list로 복원 가능한 형식인지 + 테이블 데이터가 실제로 들어 있는지 확인하고, 아니면 실패로 끝냅니다. 0바이트나 깨진 파일을 성공으로 세면 "백업이 있다"는 착각만 남습니다.2. MinIO tar 를
--user로 돌립니다. 없이 돌리면 결과물이 root 소유가 되고, 보존 삭제(find -delete)가 stackup 유저 권한으로 조용히 실패해 아카이브가 무한정 쌓입니다. 시험 실행에서 실제로 root 소유로 나오는 걸 확인하고 고쳤습니다.3.
LAST_SUCCESS마커를 남깁니다. cron 출력은 아무도 보지 않습니다. 백업이 멈춘 것과 정상인 것을 구분할 유일한 단서입니다.검증 — 실제로 복원해봤습니다
임시 DB 로 받아 원본과 대조했습니다:
복원해보지 않은 백업은 백업이 아니라고 봐서 여기까지 했습니다.
운영 반영 상태 (이미 적용됨)
~/stackup/infra/backup/backup.sh설치crontab:0 4 * * * ... >> ~/backups/backup.log 2>&1LAST_SUCCESS기록됨)이 PR 이 머지되면 deploy 의 rsync 가 같은 내용으로 덮어써 레포와 서버가 일치합니다.
같은 디스크에 저장합니다. 실수 삭제·잘못된 마이그레이션·논리적 손상은 막지만 디스크·호스트 장애는 못 막습니다. 원격 복제(S3 또는 다른 호스트 rsync)가 다음 단계입니다.
그리고 스크립트가 실패하면 cron 은 조용합니다.
LAST_SUCCESS를 보는 사람이 없으면 결국 같은 함정이라, 헬스체크에 마커 신선도를 넣는 것을 후속으로 제안합니다(/api/system/health에backup컴포넌트 추가). README 에 둘 다 명시했습니다.리뷰어 체크포인트
~/backups가 적절한지🤖 Generated with Claude Code