feat: MySQL S3 백업 systemd 파이프라인 구성 - #71
Conversation
- systemd service 배포 워크플로우 작성 - validate 모드와 install 모드로 나누어 구현
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (11)
🚧 Files skipped from review as they are similar to previous changes (2)
📝 WalkthroughWalkthroughMySQL S3 백업 파이프라인을 추가한다. 공통 검증·업로드 유틸리티, dump/binlog 실행 스크립트, systemd 타이머, 원자적 설치와 롤백, AWS·SSM 원격 배포, 회귀 테스트와 운영 문서를 구성한다. ChangesMySQL 백업 파이프라인
Estimated code review effort: 5 (Critical) | ~100 minutes Sequence Diagram(s)sequenceDiagram
participant GitHubActions
participant AWS
participant DBEC2
participant BackupInstaller
participant S3
GitHubActions->>AWS: AWS 역할 설정 및 SSM 포트포워딩 시작
AWS->>DBEC2: DB SSH 연결 전달
GitHubActions->>DBEC2: SSH 호스트키 지문 확인
GitHubActions->>DBEC2: validate-remote.sh 실행 또는 설치 번들 전송
DBEC2->>BackupInstaller: install.sh 실행
BackupInstaller->>S3: 버킷 확인 및 백업 산출물 업로드
Possibly related issues
Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Terraform Plan:
|
Terraform Plan:
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: b91ea935c2
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
Actionable comments posted: 8
🧹 Nitpick comments (1)
scripts/mysql_backup/tests/run.sh (1)
147-152: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick winbinlog 객체가 manifest보다 먼저 업로드되는지 검증하세요.
현재 개수만 확인하므로 순서가 뒤집혀도 통과합니다. manifest가 먼저 노출되면 불완전한 binlog를 복구 가능 상태로 오인할 수 있습니다.
검증 추가안
assert_equals "8" "$(wc -l <"$upload_log" | tr -d ' ')" "four closed binlogs and manifests must be uploaded" + mapfile -t uploaded_keys <"$upload_log" + for ((i = 0; i < ${`#uploaded_keys`[@]}; i += 2)); do + assert_equals \ + "${uploaded_keys[i]}.manifest.json" \ + "${uploaded_keys[i + 1]}" \ + "each binlog must be uploaded immediately before its manifest" + done🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@scripts/mysql_backup/tests/run.sh` around lines 147 - 152, Update the assertions in the test around upload_log to verify upload ordering, not only the total count: confirm each closed binlog entry is recorded before its corresponding manifest entry. Preserve the existing count, persisted state, and rotation assertions while using the upload_log contents to fail when a manifest is exposed before its binlog.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.github/workflows/mysql-backup-deploy.yml:
- Around line 31-45: Pin the actions used by the checkout and AWS credential
configuration steps to their full immutable commit SHAs instead of version tags.
Replace the Session Manager plugin’s latest download with a specific versioned
artifact, obtain its AWS-provided signature, verify the downloaded package with
the official signing key before installation, and only then run dpkg.
In `@scripts/mysql_backup/bin/mysql-backup-binlog`:
- Around line 94-115: Update the S3 recovery logic around uploaded_keys and
last_uploaded to validate manifest continuity before recording progress. Iterate
through the sorted binlog manifest numbers in order, require each adjacent
number to increase by exactly one, and set last_uploaded only to the final entry
in the contiguous sequence; stop before any gap so missing binlogs are not
treated as uploaded. Preserve the existing state reset and recovery message
behavior.
In `@scripts/mysql_backup/bin/mysql-backup-dump`:
- Around line 91-93: 백업 정리 순서를 조정하여 완료된 작업 포인터인 CURRENT_JOB_FILE을 staging 디렉터리인
JOB_DIR보다 먼저 삭제하세요. rm -f "$CURRENT_JOB_FILE"을 rm -rf "$JOB_DIR" 앞에 배치하고, 성공 상태
기록은 기존처럼 정리 작업 이후에 수행하세요.
In `@scripts/mysql_backup/install.sh`:
- Line 47: Separate the command substitution from the readonly declaration for
both SOURCE_DIR at scripts/mysql_backup/install.sh:4-4 and TRANSACTION_DIR at
scripts/mysql_backup/install.sh:47-47: assign each value first, then declare the
variable readonly on the following line so mktemp or other command failures
remain visible to set -e.
In `@scripts/mysql_backup/README.md`:
- Around line 10-17: Update the “배포 전 GitHub 설정” section in README.md to
document the required Repository Secret AWS_ROLE_ARN, including that it must
contain the AWS IAM role ARN used by the workflow. Keep the existing Repository
Variables guidance unchanged.
In `@scripts/mysql_backup/tests/run.sh`:
- Around line 282-295: Remove the previous manifest capture after copying it to
first_manifest and before the retry invocation of mysql-backup-dump. Update the
test flow around manifest_capture so the second run must recreate the capture,
ensuring cmp validates that the retry regenerated and reuploaded the manifest.
- Around line 4-5: run.sh의 PROJECT_DIR와 TEST_ROOT 선언을 readonly 선언과 값 할당으로 분리하세요.
먼저 readonly PROJECT_DIR/TEST_ROOT를 선언한 뒤 각 명령 치환을 별도 할당문으로 실행해 cd 또는 mktemp의 실패
상태가 호출자에게 전달되도록 하세요.
In `@scripts/mysql_backup/validate-remote.sh`:
- Around line 31-36: Update the pre-validation query in the
mysql-backup-validate workflow to evaluate the returned MySQL settings instead
of only checking whether the docker exec succeeds. Apply the same validation
conditions used by mysql-backup-validate for binlog enabled, binlog format,
server_id, sync_binlog, and innodb_flush_log_at_trx_commit, and fail validation
when any requirement is not met.
---
Nitpick comments:
In `@scripts/mysql_backup/tests/run.sh`:
- Around line 147-152: Update the assertions in the test around upload_log to
verify upload ordering, not only the total count: confirm each closed binlog
entry is recorded before its corresponding manifest entry. Preserve the existing
count, persisted state, and rotation assertions while using the upload_log
contents to fail when a manifest is exposed before its binlog.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 75a09b7a-f531-42b8-b099-88ff56d600f4
📒 Files selected for processing (14)
.github/workflows/mysql-backup-deploy.ymlmodules/app_stack/scripts/mysql_setup.sh.tftplscripts/mysql_backup/README.mdscripts/mysql_backup/bin/mysql-backup-binlogscripts/mysql_backup/bin/mysql-backup-dumpscripts/mysql_backup/bin/mysql-backup-validatescripts/mysql_backup/install.shscripts/mysql_backup/lib/backup-common.shscripts/mysql_backup/systemd/mysql-backup-binlog.servicescripts/mysql_backup/systemd/mysql-backup-binlog.timerscripts/mysql_backup/systemd/mysql-backup-dump.servicescripts/mysql_backup/systemd/mysql-backup-dump.timerscripts/mysql_backup/tests/run.shscripts/mysql_backup/validate-remote.sh
| @@ -0,0 +1,382 @@ | |||
| #!/usr/bin/env bash | |||
There was a problem hiding this comment.
혹시 해당 스크립트는 로컬에서만 실행해야 하나요 ? 워크플로우 등에서 동작하도록 하면 좋을 거 같습니다 !
There was a problem hiding this comment.
좋은 것 같습니다! CI로써 실행하기보다는 원래 명시적 실행 방식을 유지하도록 work_dispatch로 구성하겠습니다!
There was a problem hiding this comment.
반영했습니다. 자동 CI로 실행되지는 않도록 workflow_dispatch 방식의 MySQL Backup Test 워크플로우를 추가했습니다. AWS 권한이나 운영 환경 접근 없이 백업 스크립트 단위 테스트만 수동으로 실행됩니다!
| @@ -0,0 +1,208 @@ | |||
| #!/usr/bin/env bash | |||
There was a problem hiding this comment.
git에 기록된 파일 모드가 100644입니다. 워크플로우가 이 스크립트를 직접 실행하고(mysql-backup-deploy.yml:227) tar가 모드를 그대로 보존하므로, install 모드는 Permission denied로 즉시 실패합니다.
chmod +x scripts/mysql_backup/install.sh방어적으로 워크플로우 쪽도 bash "$install_dir/install.sh" ...로 호출하면 확실합니다.
(bin/mysql-backup-binlog도 혼자 644인데, 이쪽은 install -m 755로 정규화되니 동작에는 영향 없습니다. 일관성 차원에서만 참고해주세요.)
There was a problem hiding this comment.
반영했습니다! install.sh의 파일 모드를 100755로 변경했고, 파일 모드가 달라지는 상황에도 실행할 수 있도록 배포 워크플로우에서도 bash install.sh 형태로 호출하도록 보완했습니다. mysql-backup-binlog는 설치 과정에서 755로 정규화되므로 별도로 변경하지 않았습니다.
|
|
||
| jobs: | ||
| deploy: | ||
| runs-on: ubuntu-latest |
There was a problem hiding this comment.
workflow_dispatch는 이 파일이 존재하는 아무 브랜치에서나 실행됩니다. 지금은 environment:도 브랜치 조건도 없어서, write 권한이 있으면 feature 브랜치에서 install.sh를 수정한 뒤 dispatch → prod DB에 root로 임의 코드 실행이 가능합니다. 리뷰·승인 경로를 전혀 거치지 않습니다.
jobs:
deploy:
runs-on: ubuntu-latest
environment: prod-db # required reviewers 지정
if: github.ref == 'refs/heads/main' || inputs.mode == 'validate'추가로 secrets.AWS_ROLE_ARN 역할의 trust policy sub 조건이 repo:...:ref:refs/heads/main으로 제한돼 있는지 확인 부탁드립니다. IAM은 코드로 확인이 안 돼서요. 제한돼 있지 않다면 위 게이트가 실질적인 유일한 방어선입니다.
There was a problem hiding this comment.
지적해 주신 feature branch 기반 운영 실행 문제는 반영하겠습니다. 다만 validate 모드도 선택한 브랜치의 스크립트를 운영 서버에서 sudo로 실행하므로 main || validate 조건만으로는 충분하지 않은 것으로 확인했습니다. 두 모드 모두 main 브랜치에서만 실행되도록 제한하고, prod-db Environment의 승인 및 배포 브랜치 정책을 함께 적용하겠습니다. Environment 적용에 따라 IAM OIDC 신뢰 정책의 subject도 수동으로 점검하겠습니다!
There was a problem hiding this comment.
OIDC와 연결된 Role의 trust policy 확인 결과, repo:...:ref:refs/heads/main로 정상적으로 반영되어 있습니다!
There was a problem hiding this comment.
반영했습니다! validate, install 모두 main 브랜치에서만 실행되도록 제한했고, prod-db Environment의 승인 절차를 거치도록 설정했습니다. Environment에는 required reviewer, self-review 차단, main 배포 브랜치 제한이 적용되어 있습니다.
| done | ||
| install -d -m 700 "$JOB_DIR" | ||
|
|
||
| if [[ ! -s "$DUMP_FILE" ]]; then |
There was a problem hiding this comment.
실패한 dump가 다음 날 성공으로 종료됩니다.
dump-current 포인터와 staging/dump-<job_id>가 실패 시 보존되고, 재실행하면 [[ ! -s "$DUMP_FILE" ]]가 false라 덤프를 다시 뜨지 않고 기존 파일을 업로드합니다. 그런데 재시도 주기가 타이머와 같은 하루입니다.
| 시각 | 동작 | 결과 |
|---|---|---|
| D일 03:00 | dump 성공 → manifest 업로드 실패 | exit 1, 포인터·staging 보존 |
| D+1일 03:00 | job_id를 그대로 재사용, 재덤프 없음 |
D일 데이터를 D일 키 프리픽스에 업로드 |
| " | exit 0 |
D+1일 백업은 존재하지 않음 |
created_at이 job_id에서 유도되니 타임스탬프 자체가 거짓말을 하지는 않지만, 종료 코드는 성공이고 해당 날짜 백업은 없습니다.
제안:
job_id나이 상한 (예: 6시간 초과면 포인터·staging 폐기하고 새 job 시작)- 하루를 기다리지 않는 당일 재시도 경로 —
Type=oneshot은Restart=를 못 쓰니OnFailure=로 짧은 간격 타이머를 킥하거나ExecStart내부 제한 재시도
There was a problem hiding this comment.
좋은 제안 감사합니다 ㅎㅎ 바로 반영했습니다~ 실패 시 10분 간격으로 최대 3회 재시도하고, 완성된 dump가 남아 있으면 dump를 다시 생성하지 않고 업로드만 재시도하도록 구성했습니다.
Type=oneshot에서도 Restart=on-failure는 사용할 수 있어 해당 방식으로 구성했습니다. 작업 생성 후 6시간이 지나면 기존 staging과 포인터를 폐기하고 새 dump를 생성하므로 다음 날 이전 데이터를 성공 처리하는 문제도 방지했습니다.
|
|
||
| docker exec "$MYSQL_CONTAINER" sh -lc \ | ||
| 'MYSQL_PWD="$MYSQL_ROOT_PASSWORD" exec mysqldump -uroot --single-transaction --quick --source-data=2 --routines --events --triggers --hex-blob --set-gtid-purged=OFF --no-tablespaces "$1"' \ | ||
| sh "$MYSQL_DATABASE" | gzip -1 >"$partial_dump" |
There was a problem hiding this comment.
dump 시작 전 여유 공간 검사가 없습니다. 공간 검사(db_bytes * 2 + 256MB)는 bin/mysql-backup-validate:43-52에만 있고 설치 시점에 한 번 돕니다.
그런데 staging이 MySQL datadir와 같은 EBS입니다(backup-common.sh의 BACKUP_ROOT, MYSQL_DATA_DIR 모두 /mnt/mysql-data). 데이터가 커진 뒤 03:00에 이 gzip이 볼륨을 채우면 MySQL이 쓰기 실패로 죽습니다. 백업이 프로덕션을 죽이는 실패 모드라 머지 전에 막는 게 좋겠습니다.
- 이 스크립트 시작부에 validate와 동일한 검사를 넣고, 부족하면 덤프를 시작하지 않고 실패 처리
- 여유가 되면 staging 전용 볼륨 분리
(gzip 스트림을 aws s3 cp -로 바로 흘리는 방법도 있지만 재시도 멱등성이 깨지니 공간 검사 쪽을 권합니다.)
There was a problem hiding this comment.
반영했습니다~ 새 dump를 생성하기 직전에 DB 크기의 2배와 256MiB를 기준으로 여유 공간을 검사하고, 부족하면 mysqldump를 실행하지 않고 실패하도록 변경했습니다.
이미 dump 생성이 끝난 상태에서 S3 업로드만 재시도하는 경우에는 추가 디스크 공간을 사용하지 않으므로 공간 검사를 다시 수행하지 않도록 했습니다.
관련 이슈
다음 작업: 백업 실패 감지 후 내부 알림 API를 거쳐 Discord로 전달하는 경로를 구성합니다.
작업 내용
특이 사항
리뷰 반영 사항
1.2.835.0버전 경로에서 패키지와 서명을 함께 내려받고, 패키지 버전1.2.835.0-1, AWS 공개 키 fingerprint, GPG 서명이 모두 일치할 때만 설치하는 fail-closed 방식으로 변경했습니다.리뷰 요구사항 (선택)
Summary by CodeRabbit
새 기능
안정성 개선
문서 및 테스트