停電でラズパイのcronが止まる:毎日の通知スクリプトをGitHub Actionsの定期実行へ移す
公開
試した環境
- 実行環境
- GitHub Actions(ubuntu-latest、Python 3.12)
- 移行前
- Raspberry Pi の cron
- 通知先
- Discord(Webhook)
毎日決まった時刻にデータを取ってきて、Discordに通知する小さなPythonスクリプトを動かしています。これまでは自宅のRaspberry Piに置き、cronで毎日実行していました。
ところが、田舎では雷による停電がそれほど珍しくありません。停電すればラズパイも止まり、その日の通知は届きません。
幸い、私の家では大きな被害を受けたことはありません。それでも最近は、豪雨による水害もたびたび起きています。停電が長引いたり、機器そのものが水に浸かったりすれば、自宅に置いた機械は動かせなくなります。
ラズパイ側で無停電電源装置を用意するなどの手もありますが、そもそも「毎日1回、数十秒動けばいい」スクリプトなら、自宅の機械で動かす必要はありません。そこで、GitHub Actionsの定期実行に移しました。
GitHub Actionsの定期実行を選んだ理由
- 自宅の電源やネットワークに左右されない。停電してもGitHub側で動く
- スクリプトと同じリポジトリで管理できる。実行時刻の設定もコードと一緒に残る
- ログがGitHubの画面で見られる。失敗したら通知メールも届く
- 非公開リポジトリでも無料枠で足りる。1日1回、1分ほどの処理なら、月の無料枠(実行時間)にはまったく届かない
ワークフローの設定
リポジトリに .github/workflows/daily.yml を置きます。実際に使っているものから、スクリプト固有の部分を省いた形です。
name: Daily notify
on:
schedule:
# UTC 8:30 = 日本時間 17:30(火〜土)
- cron: "30 8 * * 2-6"
workflow_dispatch: # Actions の画面から手動で実行できるボタン
jobs:
run:
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
cache: pip
- run: pip install -r requirements.txt
- name: Run
env:
API_KEY: ${{ secrets.API_KEY }}
DISCORD_WEBHOOK_URL: ${{ secrets.DISCORD_WEBHOOK_URL }}
run: python main.py
いくつか、つまずきやすい点があります。
cronの時刻はUTC
schedule の cron は、日本時間ではなく**UTC(協定世界時)**で書きます。日本時間はUTCより9時間進んでいるので、日本時間の17:30に動かしたいなら、UTCの8:30を指定します。
曜日も同じくUTC基準です。日本時間の早朝(UTCでは前日)に動かす場合は、曜日が1日ずれるので注意が必要です。
毎時0分を避ける
GitHub Actionsの定期実行は、混み合う時間帯には開始が遅れることがあり、特に毎時0分は混みやすいとされています。数分の遅れが困らない用途でも、30 のように0分を外しておくと安心です。
手動実行のボタンを付けておく
workflow_dispatch を書いておくと、Actionsの画面に「Run workflow」ボタンが出ます。設定を変えたあと、次の定期実行を待たずに動作を確かめられるので便利です。
時間の上限を決めておく
timeout-minutes を指定しておくと、通信が固まったときなどに、実行時間を使い切るまで待ち続けるのを防げます。
APIキーはSecretsに入れる
ラズパイでは、APIキーやDiscordのWebhook URLを config.json に書いていました。このファイルはGitに入れていないので、GitHub Actionsからは読めません。
代わりに、リポジトリの「Settings」→「Secrets and variables」→「Actions」で、Secretsとして登録します。登録した値はワークフローの env: で環境変数として渡せ、ログにも伏せ字で表示されます。
設定ファイルと環境変数を両立させる
スクリプトの側は、「config.json があれば読み込み、同じ名前の環境変数があればそちらを優先する」形にしました。こうしておくと、手元では今までどおり config.json で、GitHub Actionsでは環境変数で、同じスクリプトが動きます。
import json
import os
def load_config(config_path="config.json"):
"""config.json があれば読み込み、同じ名前の環境変数があればそちらを優先する。"""
base_dir = os.path.dirname(os.path.abspath(__file__))
path = os.path.join(base_dir, config_path)
cfg = {}
if os.path.exists(path):
with open(path, "r", encoding="utf-8") as f:
cfg = json.load(f)
for key in ("API_KEY", "DISCORD_WEBHOOK_URL"):
if os.environ.get(key):
cfg[key] = os.environ[key]
return cfg
移してみて
GitHub Actionsで動くことを確かめてから、ラズパイのcronは止めました。二重に通知が届くこともなく、これで自宅の電気の具合とは関係なく、毎日の通知が届くようになりました。
ラズパイは、センサーの値を読むなど自宅に置く意味があるものに使い、時刻どおりに動けばよいだけの処理はクラウドに任せる。役割を分けたことで、停電の心配をひとつ減らせました。
雷の多い地域の方や、豪雨や水害への備えを考えている方には、自宅で動かしている定期処理を一度見直してみることをおすすめします。
まとめ
- 1日1回動けばよいスクリプトは、GitHub Actionsの
scheduleで動かせば、自宅の停電や水害の影響を受けない cronの時刻と曜日はUTC。日本時間から9時間引く- 毎時0分は混みやすいので外す。
workflow_dispatchで手動実行も付けておく - APIキーはSecretsに入れ、環境変数で渡す。スクリプトは設定ファイルと環境変数の両方に対応させると移行が楽