Repository navigation
feat: S3 기반 MySQL 백업 및 모니터링 파이프라인 구성 #66
Copy link
Copy link
Closed
Description
목표
Prod DB의 전체 dump와 binlog를 S3에 보관하여 목표 RPO 5분을 만족하는 백업 파이프라인을 구성합니다.
권한 관리 원칙
- 백업에 필요한 IAM Role, IAM Policy, IAM User와 권한 연결은 AWS에서 수동으로 구성합니다.
- Terraform 코드에는 IAM 권한 정의와 연결 정보를 선언하지 않습니다.
- Terraform에서는 수동으로 생성된 Instance Profile이나 역할의 이름 등 필요한 식별자만 변수로 전달해 참조합니다.
- 구체적인 권한 내용은 저장소 이슈와 코드에 노출하지 않고 별도 운영 문서에서 관리합니다.
백업 구성 배포 구조
백업 스크립트와 systemd 설정은 DB EC2에 SSM Endpoint를 추가하지 않고 다음 경로로 배포합니다.
GitHub Actions
├─ 일회용 SSH 키 생성
├─ EC2 Instance Connect로 공개 키를 DB 메타데이터에 등록
├─ API EC2를 대상으로 SSM 포트 포워딩 시작
└─ SSH 터널을 통해 DB EC2에서 설치 스크립트 실행
API EC2
└─ TCP 터널 역할만 수행
DB EC2
├─ 공개 키는 60초 후 자동 만료
└─ systemd가 mysqldump/binlog 백업을 독립 실행
- GitHub Actions Runner에만 일회용 SSH 개인 키를 생성하고 작업 종료 시 삭제합니다.
- API EC2에는 SSH 개인 키와 AWS CLI를 추가하지 않으며 DB EC2까지의 TCP 터널만 제공합니다.
- DB EC2의 SSH Host Key를 고정하여 접속 대상을 검증합니다.
- DB EC2의 22번 포트는 API EC2 Security Group에서만 접근할 수 있도록 제한합니다.
작업 내용
- GitHub Actions에서 일회용 SSH 키, EC2 Instance Connect, API EC2 SSM 포트 포워딩을 이용해 DB EC2에 백업 구성을 배포합니다.
- 백업 전용 S3 버킷 또는 prefix를 Terraform으로 구성합니다.
- S3 접근에 필요한 최소 권한 IAM Role·Policy와 EC2 권한 연결을 AWS에서 수동으로 구성합니다.
- S3 암호화, Versioning, Object Lock 적용 여부와 2주 보관 Lifecycle을 구성합니다.
- MySQL binlog 설정과 전체 dump 기준점(position 또는 GTID) 기록 방식을 구성합니다.
- 일 1회 mysqldump, 압축, 체크섬, manifest 생성 및 S3 업로드 스크립트를 작성합니다.
- binlog 생성부터 S3 업로드 완료까지 5분 이내가 되도록 아카이빙 스크립트와 systemd service/timer를 구성합니다.
- 실패 재시도와 중복 실행에 대한 멱등성을 보장합니다.
- mysqldump 실패, binlog 누락, 마지막 업로드 지연 시 Discord Webhook 알림을 전송합니다.
- EC2 자체 장애처럼 내부 알림을 보낼 수 없는 경우를 감지할 외부 freshness 모니터링을 구성합니다.
- 로컬 binlog 보관 기간(
binlog_expire_logs_seconds)을mysql_tuning.cnf에 명시적으로 선언하고 값을 정합니다.
배경과 판단 기준
현재 상태: 호스트의 로컬 binlog는 30일 보관 중이지만(binlog_expire_logs_seconds=2592000), 이 값은 MySQL 8.4 기본값이고 modules/app_stack/templates/mysql_tuning.cnf에 선언이 없습니다. 의도해서 정한 값이 아니라 손대지 않아 남은 값이며, S3 보관 정책(2주)과도 어긋납니다.
필요한 값: S3 dump가 14일에 만료되므로, 그보다 오래된 로컬 binlog는 재생을 시작할 기준점이 없어 복구에 쓸 수 없습니다.
| 용도 | 필요한 로컬 보관 |
|---|---|
| 최신 시점까지 복구 (커밋 기준 RPO 0) | 최신 dump까지 — 최대 24시간 |
| 임의 시점 복구 | 가용한 가장 오래된 dump까지 — 14일 |
| 14일을 넘는 구간 | 쓸 수 없음 (기준점 dump 없음) |
값을 정하는 기준으로 제안하는 것
- S3 dump 보관(14일) + 만료 경계 여유. 로컬 만료는 binlog 회전 시점에 평가되고 S3 Lifecycle은 비동기로 동작해 두 시점이 정확히 맞지 않습니다
- 기본값 의존을 없애는 것이 목적이므로, 값을 30일로 유지하기로 결정하더라도 명시적으로 선언합니다. MySQL 버전 상향 시 조용히 바뀔 수 있습니다
함께 정해야 하는 것: user_data가 lifecycle.ignore_changes에 있어 코드 수정만으로는 실행 중인 인스턴스에 반영되지 않습니다. mysql_tuning.cnf 변경을 어떤 경로로 반영할지 함께 정해야 합니다.
긴급도: 낮습니다. binlog 하루 증가량이 약 59KB이고 데이터 볼륨 사용률이 3%라 디스크 압박은 없습니다. 다만 ROW 포맷 binlog는 실제 행 이미지를 담으므로 보관 기간은 성능이 아니라 데이터 보관 정책의 문제입니다. 삭제된 데이터의 이력이 그 기간만큼 호스트에 남습니다.
완료 조건
- 전체 dump와 binlog가 자동으로 S3에 저장됩니다.
- dump와 binlog의 복구 기준점 및 체크섬을 확인할 수 있습니다.
- 마지막 성공 binlog 업로드가 5분을 넘기거나 파일이 누락되면 알림이 발생합니다.
- 백업 파일 보관 및 삭제가 S3 Lifecycle에 의해 관리됩니다.
- IAM 역할·정책·사용자 권한이 Terraform 코드에 포함되지 않고 수동으로 관리됩니다.
- DB EC2에 SSM Endpoint나 장기 SSH 개인 키를 두지 않고 백업 구성을 배포할 수 있습니다.
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
No labels
Activity
coderabbitai commentedon Jul 15, 2026
🔗 Related PRs
#25 - feat: IaC 코드 최신화 & tfstate에 대한 S3 백엔드화 & Github Actrion 워크플로우 생성 [closed]
#53 - feat: Prod DB EC2 리소스 추가 [merged]
#58 - refactor: DB EC2 MySQL data volume 분리 [merged]
#63 - refactor: prod RDS 리소스 제거 [merged]
📝 Issue Planner
Check the box below or use the
@coderabbitai plancommand to generate an implementation plan and prompts that you can use with your favorite coding assistant.🧪 Issue enrichment is currently in open beta.
You can configure auto-planning by selecting labels in the issue_enrichment configuration.
To disable automatic issue enrichment, add the following to your
.coderabbit.yaml:💬 Have feedback or questions? Drop into our discord!
fix: 첫 dump 판단 기준을 타이머 stamp 에서 성공 기록으로 바꾼다
fix: 최초 설치 시 복구 기준점이 되는 첫 dump 생성 (#75)
fix: 마운트 의존성을 RequiresMountsFor 로 선언한다 (#77)