name: DR Drill on: schedule: - cron: "0 4 * * 1" # 每周一凌晨 4 点 workflow_dispatch: # 支持手动触发 inputs: backup_file: description: '指定备份文件(可选,留空使用最新备份)' required: false default: '' no_cleanup: description: '演练后不清理测试数据库' required: false type: boolean default: false jobs: dr-drill: runs-on: ubuntu-latest timeout-minutes: 30 steps: - name: Checkout uses: actions/checkout@v4 - name: Install MySQL client run: | sudo apt-get update -qq sudo apt-get install -y -qq mysql-client - name: Prepare backup directory run: mkdir -p backups docs/dr/reports - name: Download latest backup artifact (if no backup file specified) if: github.event.inputs.backup_file == '' uses: actions/download-artifact@v3 with: name: db-backup path: backups/ continue-on-error: true - name: Run database backup (if no artifact available) if: steps.download.outcome == 'failure' || true env: DATABASE_URL: ${{ secrets.DATABASE_URL }} BACKUP_DIR: ./backups run: | if [ -z "$(ls -A backups/db_backup_*.sql.gz 2>/dev/null)" ]; then echo "No backup artifact found, creating fresh backup..." chmod +x scripts/backup-db.sh ./scripts/backup-db.sh else echo "Using existing backup artifact" fi - name: Run disaster recovery drill env: DATABASE_URL: ${{ secrets.DATABASE_URL }} BACKUP_DIR: ./backups DR_DRILL_TEST_DB: next_edu_dr_drill run: | chmod +x scripts/dr-drill.sh ARGS="" if [ -n "${{ github.event.inputs.backup_file }}" ]; then ARGS="$ARGS --backup ${{ github.event.inputs.backup_file }}" fi if [ "${{ github.event.inputs.no_cleanup }}" = "true" ]; then ARGS="$ARGS --no-cleanup" fi ./scripts/dr-drill.sh $ARGS - name: Upload drill report if: always() uses: actions/upload-artifact@v3 with: name: dr-drill-report-${{ github.run_id }} path: docs/dr/reports/ retention-days: 90 - name: Notify operations team (on failure) if: failure() env: WEBHOOK_URL: ${{ secrets.DR_NOTIFICATION_WEBHOOK }} SMTP_HOST: ${{ secrets.SMTP_HOST }} run: | echo "DR Drill failed! Notifying operations team..." # Webhook 通知(如果配置) if [ -n "$WEBHOOK_URL" ]; then curl -X POST "$WEBHOOK_URL" \ -H "Content-Type: application/json" \ -d "{ \"text\": \"⚠️ DR Drill Failed\", \"attachments\": [{ \"color\": \"danger\", \"fields\": [ {\"title\": \"Repository\", \"value\": \"${{ github.repository }}\", \"short\": true}, {\"title\": \"Run ID\", \"value\": \"${{ github.run_id }}\", \"short\": true}, {\"title\": \"Triggered By\", \"value\": \"${{ github.actor }}\", \"short\": true}, {\"title\": \"Time\", \"value\": \"$(date -u +%Y-%m-%dT%H:%M:%SZ)\", \"short\": true}, {\"title\": \"Action\", \"value\": \"Check workflow logs and report artifact\", \"short\": false} ] }] }" || echo "WARN: Webhook notification failed" else echo "INFO: DR_NOTIFICATION_WEBHOOK not set, skipping webhook notification" fi # 邮件通知(如果配置 SMTP) if [ -n "$SMTP_HOST" ]; then echo "INFO: SMTP notification would be sent (configure in production)" fi - name: Summary if: always() run: | echo "=== DR Drill Workflow Summary ===" echo "Run ID: ${{ github.run_id }}" echo "Triggered by: ${{ github.actor }}" echo "Status: ${{ job.status }}" echo "Report: Check dr-drill-report-${{ github.run_id }} artifact" echo "" if [ -f docs/dr/reports/dr_drill_*.md ]; then echo "Latest drill report:" cat docs/dr/reports/dr_drill_*.md | head -50 fi