Repository navigation
feat(infra): 백업 실패 알림(Discord) + 신선도 헬스체크 - #251
Merged
Merged
Conversation
#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.
왜
#249 로 백업은 생겼지만 실패하면 아무도 모르는 상태였습니다. cron 출력은 읽는 사람이 없고
LAST_SUCCESS를 들여다볼 사람도 없습니다 — 이번 점검에서 반복해서 당한 "조용히 멈춤" 패턴 그대로입니다. 백업이 있다는 사실만으로 안심하는 게 제일 위험합니다.실패 모드가 둘이고, 잡는 방법이 다릅니다
두 번째가 핵심입니다. 스크립트에만 알림을 달면 "실행은 됐지만 실패" 만 잡히고, cron 자체가 죽는 더 조용한 실패는 영원히 안 보입니다.
설계 판단 — 전체 상태는 안 건드립니다
backup컴포넌트를/api/system/health에 넣되status집계에서는 제외했습니다.{ "status": "UP", "components": { "backup": { "name": "backup", "status": "DOWN" } } }백업이 낡은 건 "복구 수단이 없다" 이지 "면접이 안 된다" 가 아닙니다. aggregate 에 넣으면 업타임 감시가 장애로 오인해 헛울리고, 진짜 장애와 구분도 안 됩니다.
/ready에서도 뺐습니다 — 트래픽 수용 가능 여부와 무관합니다.이걸 위해
ComponentSpec에informational플래그를 추가했고, 진짜 장애는 여전히 DOWN 으로 끌어내리는지 회귀 테스트로 고정했습니다.변경 내용
infra/backup/backup.sh—fail()+ERRtrap 에서 Discord 알림. 알림 실패는 전부 삼켜 백업 결과를 덮지 않습니다(curl -m 10,|| true). cron 에는 환경변수가 없으므로.env에서 웹훅 한 줄만 꺼내 씁니다(통째source하면 다른 값을 덮습니다).deploy-app.yml—DISCORD_WEBHOOK_URL을 운영.env에 주입. GitHub Secret 에만 있어서 서버에서는 쓸 수 없는 상태였습니다.BackupHealthIndicator—LAST_SUCCESS나이 확인 (backup.max-age-hours, 기본 36시간 = 일 1회 + 여유).docker-compose.yml— 백업 디렉토리를 backend 에 읽기 전용 마운트. 호스트 경로라 compose 가 소유하는 게 맞습니다(fix(infra): compose·.env.example 이 settings.py 기본값을 덮어쓰지 않게 #248 규칙).검증
스크립트 실패 경로를 서버에서 직접 확인했습니다:
LAST_SUCCESS테스트:
BackupHealthIndicatorTest5건(최근/한도초과/경계/기록없음/깨진기록),SystemHealthServiceTest+3건(백업 DOWN 이 전체를 안 내림 / 진짜 장애는 내림 / ready 에 미포함)../gradlew test실패는 Testcontainers 5개 클래스뿐(로컬 Docker 없음, CI 검증).영향 범위
/api/system/health응답에backup컴포넌트 추가 (기존 키·집계 의미 불변)DISCORD_WEBHOOK_URL(배포가 주입),backup.marker-path/backup.max-age-hours(기본값 있음)DISCORD_WEBHOOK_URL은 배포가.env에 넣어주지만,secrets.DISCORD_WEBHOOK_URL이 설정돼 있어야 합니다(PR 알림에 쓰이고 있으니 있을 겁니다). 배포 후 값이 들어갔는지 확인하겠습니다.알림이 실제로 Discord 에 도착하는지는 제가 웹훅 값을 몰라서 검증하지 못했습니다. 코드 경로(실패 시 호출·지연 없음·예외 안 새어나감)만 확인했습니다. 배포 후 일부러 실패시켜 확인하려면 말씀해 주세요.
리뷰어 체크포인트
backup을 집계에서 뺀 판단. 반대로 "백업 없으면 DOWN 이 맞다" 고 보시면 플래그만 바꾸면 됩니다.🤖 Generated with Claude Code