GitHub Actionsで日次バックアップを組んだら、安心より先に通知メールが9通来た

日次バックアップを作れば安心できると思っていました。

実際は、初日に通知メールが9通来ました。原因はスケジュール設定ミス、権限不足、そしてバックアップ対象を欲張りすぎたことです。朝のコーヒーを飲む前に赤い失敗通知を見ると、なかなか目が覚めます。

それでも、組んでよかったです。手動バックアップを忘れる不安が、1日1回の自動確認に変わりました。

目次

何をバックアップするのか

最初に決めるべきなのは、バックアップ方法ではなく対象です。

私は最初、リポジトリ全体、生成物、ログ、キャッシュまで全部保存しようとしました。結果、差分が大きくなりすぎて、何が大事なのか分からなくなりました。

今は、設定ファイル、Markdown、軽いJSONだけに絞っています。重い生成物や一時ファイルは対象外です。

最小のworkflow

まずは、毎日1回リポジトリ内の指定フォルダをコミットする形にしました。

name: daily-backup

on:
  schedule:
    - cron: "0 18 * * *"
  workflow_dispatch:

permissions:
  contents: write

jobs:
  backup:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Create backup marker
        run: |
          mkdir -p backup
          date -u +"%Y-%m-%dT%H:%M:%SZ" > backup/last_run.txt

      - name: Commit changes
        run: |
          git config user.name "github-actions[bot]"
          git config user.email "github-actions[bot]@users.noreply.github.com"
          git add backup/last_run.txt
          if git diff --cached --quiet; then
            echo "No changes"
          else
            git commit -m "chore: daily backup"
            git push
          fi

ハマりポイントは permissions: contents: write です。これがないと、pushで落ちることがあります。

cronの時刻で1回ズレる

GitHub ActionsのcronはUTCです。

私はここで1回やらかしました。日本時間のつもりで設定したら、実行時刻が9時間ズレました。夜中に走らせたいのに、変な時間に通知が来て気づきました。

日本時間の午前3時に動かしたいなら、UTCでは前日の18時です。

schedule:
  - cron: "0 18 * * *"

時刻のズレは小さなミスですが、自動化ではかなり響きます。

9通メールが来た理由

失敗通知が増えた原因は3つでした。

1つ目は、push権限が足りないまま再実行を連打したことです。焦って3回押して、3回落ちました。

2つ目は、生成ファイルが毎回変わることです。タイムスタンプやキャッシュを含めると、毎日必ず差分が出ます。

3つ目は、失敗時のログを読まずにyamlをいじったことです。Actionsのログは長いですが、だいたい最後の20行に答えがあります。

Pythonでバックアップ対象を絞る

対象を絞りたい場合は、Pythonでコピーするほうが見通しがよいです。

from pathlib import Path
import shutil

ROOT = Path(".")
BACKUP = Path("backup/files")
TARGET_EXTENSIONS = {".md", ".json", ".toml"}


def main() -> None:
    BACKUP.mkdir(parents=True, exist_ok=True)

    for path in ROOT.rglob("*"):
        if ".git" in path.parts or "backup" in path.parts:
            continue
        if path.suffix not in TARGET_EXTENSIONS:
            continue

        dest = BACKUP / path
        dest.parent.mkdir(parents=True, exist_ok=True)
        # ハマりポイント: copy2を使うと更新時刻も残せる
        shutil.copy2(path, dest)


if __name__ == "__main__":
    main()

これなら、何を保存するかがコード上で見えます。自動化では「暗黙に全部」より「明示的に少し」のほうが安心です。

復元できるかを1回試す

バックアップで一番怖いのは、取れているつもりで復元できないことです。

私は以前、バックアップフォルダを眺めて安心していました。でも、それは安心の形をした未確認です。実際に別フォルダへ戻してみるまで、使えるかどうかは分かりません。

最低限、月に1回は復元テストをします。

mkdir restore-test
cp -r backup/files restore-test/files
find restore-test/files -maxdepth 2 -type f | head

Windows PowerShellなら、こう確認します。

New-Item -ItemType Directory -Force restore-test
Copy-Item -Recurse backup/files restore-test/files
Get-ChildItem restore-test/files -Recurse | Select-Object -First 5

ハマりポイントは、バックアップ成功通知だけで満足しないことです。復元できないバックアップは、きれいに並んだ不安です。

1日1回の保存より、月1回の復元確認のほうが大事な場面もあります。

まとめ

GitHub Actionsの日次バックアップは、作るだけなら難しくありません。

難しいのは、何を保存し、いつ動かし、失敗した時にどう気づくかです。私は9通の通知で少し疲れましたが、そのおかげで対象を絞る大事さが分かりました。

バックアップで守るべきなのは、全部のファイルではなく、失うと昨日の自分に戻れないファイルです。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

コメント

コメントする

目次