GitHub Actions¶
GitHub Actions を使えば、例えば main ブランチに push したら、まず pytest を実行して、PASS
したら 自分のローカルネットワークにあるサーバーでデプロイする、っていう CI(Continuous Integrity)/CD(Continuous Deployment) パイプラインを構築できる。
必要なのは、ワークフローファイルを .github/workflows/my-actions.yml (ファイル名はなんでもいい)のように作成しておいて、 GitHub にソースコードと一緒にあげること。
ワークフローファイルのサンプル¶
name: CI/CD pipeline
on:
push:
branches: [ "main" ]
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v6
- name: Install uv
uses: astral-sh/setup-uv@v8.0.0
with:
enable-cache: true
cache-dependency-glob: "uv.lock"
- name: Install dependencies
run: uv sync --frozen
- name: Run tests
run: uv run pytest
deploy:
needs: test
runs-on: self-hosted
steps:
- name: Checkout code
uses: actions/checkout@v6
- name: Install dependencies
run: uv sync --frozen
- name: Restart service
run: sudo systemctl restart fastapi-app
テストが成功したときだけデプロイする
deploy に needs: test をつけてるので、 test ジョブが成功したときだけ deploy ジョブが走る。
pytest が失敗したらデプロイされない: cf. Defining prerequisite jobs (needs)
test ジョブ(ubuntu-latest)¶
runs-on: ubuntu-latest— GitHub がホストするサーバーで pytest を実行する。actions/checkout@v6— リポジトリのコードを GitHub サーバーにコピーする。astral-sh/setup-uv@v8.0.0— uv をインストールする。enable-cacheとcache-dependency-glob: "uv.lock"で、uv.lockが変わらない限り依存をキャッシュして高速化する。uv sync --frozen—uv.lockの内容そのままで依存をインストールする。--frozenをつけるとuv.lockを更新せず、ロックとpyproject.tomlがズレてたらエラーにする。 CI ではロックを勝手に書き換えてほしくないので--frozenにしておく方がいいか。uv run pytest— テストの実行: cf. pytest
deploy ジョブ(self-hosted)¶
デプロイ先のサーバーに self-hosted runner をセットアップしておくと、そのサーバー上でジョブが走る。ここではコードを取得して uv で同期した後で、 systemd で FastAPI のサービスを再起動する。
runs-on: self-hosted— 自前のサーバー(self-hosted runner)で実行する。 runner がチェックアウトした場所で、そのまま依存の同期とサービス再起動をやる。uv sync --frozen— デプロイ先でも同じロックファイルから依存をそろえる。sudo systemctl restart fastapi-app— FastAPI を動かしてる systemd サービスを再起動して、新しいコードを反映する。
Note
このサンプルは、デプロイ先サーバーに self-hosted runner と FastAPI の systemd サービス、 sudoers が用意されてる前提。セットアップ手順は self-hosted runner のセットアップ を参照。
self-hosted runner のセットアップ¶
cf. Adding self-hosted runners
デプロイ先のサーバー(ここでは RockyLinux、ユーザーは alice)に runner をインストールする。 GitHub リポジトリの Settings → Actions → Runners → New self-hosted runner → Linux を選ぶと、ダウンロードと config.sh での登録( token 付き)のコマンドが表示されるので、それをサーバーで順に実行する。
alice は wheel グループ(sudo できる)前提
この手順の後半に出てくる sudo( svc.sh install など)は、 alice が wheel グループに入っていて sudo できることが前提。
グループ追加は今開いているセッションには即反映されないので、 usermod のあとはログインし直す( id で wheel が出れば反映済み)。
-
ダウンロードして展開する。 URL / バージョン / token は GitHub の画面に出てくるものを使う。
-
config.shで runner を登録する。いくつか聞かれるけど、基本そのまま Enter でいい。 -
./run.shで起動すると、 GitHub を監視してるような状態(Listening for Jobs)になる。$ ./run.sh √ Connected to GitHub Current runner version: '2.333.1' 2026-04-20 02:02:44Z: Listening for Jobsこの状態で
mainに push すると、ジョブが自動で走る。 -
./run.shはターミナルを閉じると止まるので、常時Listening for Jobsを維持するにはサービスとして登録する。Note
svc.sh installは、 runner のファイルを持ってるユーザー(ここではalice)で動くサービスとして登録される。なのでジョブもaliceで走り、チェックアウト先は/home/alice/actions-runner/_work/my-fastapi-app/my-fastapi-appのようになる。<runnerインストール先>/_work/は、config.shの work フォルダ設定(デフォルト_work)で決まる。- リポジトリ名が 2 回繰り返される部分は、
GITHUB_WORKSPACEを参照
runner のセットアップができたら、 FastAPI 本体を systemd サービスとして登録する。 runner と同じユーザー( alice )で動かすシステムサービス設定例。
-
systemd の設定
WorkingDirectoryを runner のチェックアウト先(<runnerインストール先>/_work/<リポジトリ名>/<リポジトリ名>)にしておけば、デプロイステップのuv syncと再起動がそのまま反映される(コピー処理は不要)。/etc/systemd/system/fastapi-app.service[Unit] Description=FastAPI App After=network.target [Service] User=alice Group=alice WorkingDirectory=/home/alice/actions-runner/_work/my-fastapi-app/my-fastapi-app ExecStart=/home/alice/.local/bin/uv run gunicorn -c gunicorn_config.py app.main:app Restart=always RestartSec=5 [Install] WantedBy=multi-user.target -
sudoers の設定
aliceユーザーがパスワードなしでsudo systemctl restart fastapi-appを実行できるようにしておく。 -
Gunicorn の設定
gunicorn_config.py を作成しておく。
SSH 鍵と Deploy Key の登録¶
self-hosted runner は GitHub がくれるトークンでチェックアウトするけど、デプロイ先サーバーから自分で git pull したりプライベートリポジトリにアクセスするには、 SSH 鍵を作って GitHub に Deploy Key として登録しておくのが楽。 Deploy Key は「1つのリポジトリだけにアクセスできる SSH 公開鍵」で、アカウント全体に効く個人の SSH 鍵より権限を絞れる: cf. Managing deploy keys
-
デプロイ先サーバーで SSH 鍵( ed25519 )を生成する。自動化で使うならパスフレーズは空でいい: cf. Generating a new SSH key
-
公開鍵の中身をコピーしておく。
-
GitHub リポジトリの Settings → Deploy keys → Add deploy key を開いて、コピーした公開鍵を貼る。 push もさせたいとき以外は Allow write access は off のままでいい。
-
リモート URL を HTTPS から SSH に切り替えて、接続を確認する。
ssh -T git@github.comでHi <your-account>/my-fastapi-app!と返ってくれば、その鍵でリポジトリにアクセスできてる。$ cd my-fastapi-app/ $ git remote -v origin https://github.com/<your-account>/my-fastapi-app.git (fetch) origin https://github.com/<your-account>/my-fastapi-app.git (push) $ git remote set-url origin git@github.com:<your-account>/my-fastapi-app.git $ ssh -T git@github.com Hi <your-account>/my-fastapi-app! You've successfully authenticated, but GitHub does not provide shell access. $ git remote -v origin git@github.com:<your-account>/my-fastapi-app.git (fetch) origin git@github.com:<your-account>/my-fastapi-app.git (push)
Secrets¶
デプロイにトークンなどの秘密情報が必要なら、リポジトリの Settings → Secrets and variables → Actions に登録して、ワークフローから ${{ secrets.NAME }} で参照する: cf. Using secrets in GitHub Actions