構築・運用手順書(Runbook)
この文書について(標準 Runbook 構成)
| 目的 | 環境の初回構築・日常運用・障害対応・移行を再現可能な手順として記述する |
|---|---|
| 想定読者 | インフラ担当、運用担当(Windows + Docker) |
| いつ読むか | サーバー起動、月次パイプライン、APK 配布、エラー発生時、PC 移設時 |
一般的な Runbook の章立てと本資料の対応:
| 標準の章 | 本資料のセクション |
|---|---|
| 前提・要件 | §1 サーバー構築 の「前提」 |
| 初回構築(Build) | §1 サーバー、§2 Worker |
| 日常運用・配布(Operate) | §2 Worker、§3 GHA・APK |
| 操作補助(UI) | §4 GHA 画面操作 |
| 移行・復旧(Migrate) | §5 PC 間移設、§1 のトラブルシュート |
Phase1 の手順を 1 ページに集約しています。設計の背景は 設計資料、用語は 参照 を参照してください。
§1 サーバー構築
WBS 1.1(サーバー環境構築)の runbook です。 Cloudflare Tunnel 経由で FastAPI へ FB を受信できる状態を目指します。
1033 が再発)。実機 FB の HTTP 400 対応は
K-17 を参照。
C:\Dev\PoC_スマート農業環境構築)で確立済みの
Named Tunnel・DNS・固定ドメインを再利用します。
新規 Tunnel 作成や DNS 移管は不要です。
作業の中心は「Windows PC 上で API を起動し、cloudflared の接続先を Raspberry Pi から本 PC へ移す」ことです。
1. 前提
- Windows 11 + Docker Desktop がインストール済み・起動していること
- PoC で構築済みの Cloudflare リソースが有効であること(下表)
- PoC 記録または Zero Trust ダッシュボードから
TUNNEL_TOKENを取得できること
1.1 再利用する PoC 資産(確立済み)
| 項目 | 値 | 状態 |
|---|---|---|
| Tunnel 名 | poc-api-tunnel |
稼働中(2026-06-07 時点で /ping 応答確認済み) |
| 公開 URL | https://api.dammy-otoko.com |
HTTPS 有効 |
| Public Hostname | api.dammy-otoko.com → http://api:8000 |
変更不要(docker-compose のサービス名 api と一致) |
| DNS 管理 | Cloudflare(dammy-otoko.com) |
移管済み(Squarespace 所有権は維持) |
| 現行 cloudflared | Windows PC 上の Docker(server-cloudflared-1) |
2026-06-07 移行完了(Pi 側は停止) |
1.2 実施順序(変更後)
| 順序 | WBS | 作業 | 備考 |
|---|---|---|---|
| ① | — | Docker Desktop 起動 | 未インストールの場合は先にインストール |
| ② | 1.1.2 | 環境変数・API + DB 起動 | ローカル疎通を先に確認 |
| ③ | 1.1.3 | FB 受信 API 疎通(localhost) | test-feedback.ps1 |
| ④ | 1.1.1 | cloudflared を本 PC へ移行 | Pi 側を停止してから起動 |
| ⑤ | 1.1.1 / 1.2.3 | Tunnel 経由の外部疎通確認 | MS-1 達成 |
2. Docker Desktop の準備
スリープ復帰後や初回セットアップ時は、タスクバーで Docker Desktop が起動していることを確認します。
docker version
docker compose version
コマンドが通れば次のステップへ進みます。
3. 環境変数の準備(WBS 1.1.2)
cd server
copy env.example .env
.env を編集します。テンプレートは server/env.example(SEC 変数含む)。
| 変数 | 設定内容 |
|---|---|
POSTGRES_PASSWORD | 任意の強めのパスワード(SEC-04。change_me 禁止。 DATABASE_URL も合わせて更新) |
TUNNEL_TOKEN | PoC の poc-api-tunnel の Connector トークン(Zero Trust → Tunnels → Configure)。eyJ... で始まる長い文字列。Tunnel ID(UUID)ではない |
FEEDBACK_API_KEY | (本番推奨 SEC-01)32 文字以上のランダム文字列。設定時はクライアントが Authorization: Bearer 必須。未設定時は警告付きで無認証可(移行用) |
MAX_UPLOAD_BYTES | (SEC-02)画像上限バイト数。既定 10485760(10 MiB) |
4. API + DB の起動(WBS 1.1.2)
cd server
docker compose up -d --build db api
起動確認(PowerShell では curl.exe 推奨):
curl.exe http://localhost:8000/api/v1/health
期待レスポンス: {"status":"ok"}
5. FB 受信 API 疎通(WBS 1.1.3・ローカル)
PowerShell スクリプトで確認(server/.env の FEEDBACK_API_KEY を自動読み込み。リポジトリ直下から実行):
cd C:\Dev\github-actions-test
.\scripts\test-feedback.ps1 -BaseUrl "https://api.dammy-otoko.com"
API キーをランダム生成して server/.env に書き込む(運用向け):
.\scripts\rotate-server-secrets.ps1 -ApiKey -RestartApi
-RestartApi は docker compose up -d --force-recreate api まで実行。DB パスワードは既存 volume がある場合は -DbPassword を慎重に。
または curl 直接:
curl -X POST http://localhost:8000/api/v1/feedback ^
-F "image=@scripts\sample.jpg" ^
-F "inference_result={\"label\":\"A\",\"score\":0.92}"
成功時: HTTP 201 と id が返る。画像は data/storage/ に保存される。
SEC-01 有効時(FEEDBACK_API_KEY 設定済み)の成功例: 末尾に HTTP_CODE:201。失敗時の切り分けは §8・K-30。
5.1 SEC-01 deny 確認(意図的に誤ったキー・認証なし)
正しいキーで 201 が出たら、わざと間違った値を流して 401 で弾かれることも確認する。
health は引き続き ok のまま(インフラは生き、POST だけ認証で拒否)。
| テスト | コマンド | 期待 |
|---|---|---|
| 誤った API キー | .\scripts\test-feedback.ps1 -BaseUrl "https://api.dammy-otoko.com" -ApiKey "wrong-key-on-purpose" |
HTTP 401、{"detail":"invalid api key"} |
| 認証ヘッダなし | curl.exe -s -w "`nHTTP_CODE:%{http_code}`n" -X POST "https://api.dammy-otoko.com/api/v1/feedback" -F "image=@scripts\sample.jpg;type=image/jpeg" -F "inference_result=<scripts\inference.json;type=application/json" |
HTTP 401、{"detail":"missing or invalid authorization header"} |
| 正しいキー(再確認) | .\scripts\test-feedback.ps1 -BaseUrl "https://api.dammy-otoko.com"(-ApiKey 省略) |
HTTP 201 |
実測記録: K-30 deny 確認(2026-06-14)。
リポジトリ全体のディレクトリと .env の対応: docs/index — 構成とシークレット。
6. Cloudflare Tunnel 移行(WBS 1.1.1)
- Zero Trust での Tunnel 新規作成(
horse-feedback等) - DNS 移管(Squarespace → Cloudflare)
- Public Hostname の初回設定
Phase 1-4 ナレッジ資料:Cloudflare Named TunnelとDNS管理の勘所.md
6.1 移行前の確認(任意)
Pi 側がまだ応答していることを確認できます(移行前は PoC の /ping が返る)。
curl https://api.dammy-otoko.com/ping
期待(移行前): {"message":"pong"}(PoC FastAPI)
6.2 Raspberry Pi 側 cloudflared の停止
同一 Tunnel に複数コネクタがあると、リクエストが Pi と本 PC に振り分けられ、意図しない応答になる可能性があります。移行前に Pi 側を停止します。
# Raspberry Pi 上で実行(SSH)。docker 権限がない場合は sudo を付ける
cd ~/poc-api # PoC の compose ディレクトリ
sudo docker compose stop cloudflared
停止対象は compose のサービス名 cloudflared(コンテナ名 poc-api 等とは異なる場合あり)。
6.3 本 PC で cloudflared 起動
server/.env に TUNNEL_TOKEN(eyJ... 形式)を設定済みであることを確認し:
cd server
docker compose --profile tunnel up -d cloudflared
cloudflared の起動コマンドは --token のみです。--url との併用は禁止(PoC でタイムアウトの原因になった設定)。
Cloudflare ダッシュボードの「Windows 向け cloudflared.exe インストール」手順は不要(本プロジェクトは Docker コンテナで運用。ダッシュボードの Docker タブまたはトークンコピーで可)。
6.4 Tunnel 経由の疎通確認
curl.exe https://api.dammy-otoko.com/api/v1/health
.\scripts\test-feedback.ps1 -BaseUrl "https://api.dammy-otoko.com"
期待(移行後):
/api/v1/health→{"status":"ok"}POST /api/v1/feedback→ HTTP201
7. 完了チェック(MS-1)
2026-06-07 達成済み。
GET /api/v1/healthがローカルで成功するPOST /api/v1/feedbackで画像・JSON が保存されるhttps://api.dammy-otoko.com経由でも同じ API が応答する- DB(feedbacks テーブル)にレコードが記録される
8. トラブルシュート
| 症状 | 確認事項 |
|---|---|
docker が見つからない / daemon 未起動 |
Docker Desktop を起動(docker version で Server 行が表示されること)。PATH 反映のためターミナルを開き直す |
Provided Tunnel token is not valid |
TUNNEL_TOKEN が eyJ... 形式か確認。Tunnel ID(UUID)を入れていないか |
Cloudflare error 1033 |
cloudflared が Up か確認(docker compose ps)。トークン・Pi 側二重コネクタを確認 |
localhost の curl 失敗(PowerShell) |
PowerShell の curl は Invoke-WebRequest の別名。curl.exe を使用 |
| api が起動しない | docker compose logs api。DB 接続待ちの場合は db の healthcheck を確認 |
| Tunnel 502 | Public Hostname の URL が http://api:8000 か確認。api コンテナが起動しているか確認 |
| Tunnel タイムアウト | cloudflared に --url が付いていないか確認。Pi 側 cloudflared が残っていないか確認 |
移行後も /ping が返る |
まだ Pi 側にルーティングされている。Pi の cloudflared を停止し、本 PC の cloudflared を再起動 |
image must be an image file(HTTP 400、実機 Flutter) |
multipart 画像の Content-Type 不足。K-17 参照 |
| 開発再開時に 1033 が再発 | Docker Desktop 未起動が典型。起動後 docker compose --profile tunnel up -d |
| 画像が保存されない | data/storage のマウント、ボリューム権限を確認 |
{"detail":"invalid api key"}(HTTP 401) |
health は ok でも POST だけ失敗する典型パターン。
|
| 警告「FEEDBACK_API_KEY looks like a placeholder」 | server/env.example のまま、または replace-with 等を含む値。rotate-server-secrets.ps1 -ApiKey -RestartApi で再生成 |
9. 参照資料(PoC)
| 資料 | パス |
|---|---|
| PoC 結果レポート | C:\Dev\PoC_スマート農業環境構築\PoC結果レポート:ホストPC〜複数クライアント通信検証(Phase 1).md |
| Named Tunnel ナレッジ | C:\Dev\PoC_スマート農業環境構築\Phase 1-4 ナレッジ資料:Cloudflare Named TunnelとDNS管理の勘所.md |
| Manus 作業記録 | RaspberryPiを用いたサーバ/クライアント構築と実現性検証 |
§2 AI Worker スタブ
WBS 1.3(AI Worker スタブ)の runbook です。
実際の学習は行わず、ベースモデルをコピーして新版本を生成し、
完了後に GitHub repository_dispatch で Actions を起動します。
1. 前提
- Windows 11 + Python 3.10 以上
- リポジトリをクローン済み(
models/base/stub.tfliteが存在すること) - 1.3.3 の検証には GitHub PAT とリポジトリ名の設定(後述)
.github/workflows/android-build.ymlがリモートに push 済み(dispatch 先ワークフロー)
2. ディレクトリ構成
models/
├── base/stub.tflite # ベースモデル(スタブ)
├── manifest.json # バージョン管理
└── versions/ # 生成物(gitignore、ローカルのみ)
worker/
├── __main__.py # CLI エントリ
├── training_stub.py # F-20 run_training_stub()
├── github_dispatch.py # F-21 trigger_github_dispatch()
└── .env.example
scripts/run-training-stub.ps1
3. 環境変数(WBS 1.3.3)
worker/.env.example を worker/.env にコピーし、値を設定します。worker/.env は .gitignore 対象 — commit しない。
3.1 PAT(Personal Access Token)の発行
worker/.env のみに置きます(K-22)。
- GitHub 右上の自分のアイコン → Settings(リポジトリではなくユーザー)
- 左下 Developer settings → Personal access tokens → Tokens (classic)
- Generate new token (classic)
- Note: 例
github-actions-test-worker/ Expiration: 任意(期限切れ前にローテーション — K-23) - Scopes:
repo(Classic)。Fine-grained の場合は対象 repo へ Contents + Actions - 生成された
ghp_...をworker/.envのGITHUB_TOKENに貼り付け
3.2 worker/.env の内容
| 変数 | 説明 |
|---|---|
GITHUB_TOKEN |
PAT(repo スコープ、または Fine-grained で Contents + Actions) |
GITHUB_REPOSITORY |
owner/repo 形式(例: your-org/github-actions-test) |
トークン未設定の場合、学習スタブのみ実行し dispatch はスキップします(ローカル検証用)。
2026-06-10 確認: PAT 設定後 run-training-stub.ps1 → dispatch 204 → Build #2 → artifact v2 → Pixel 7a install まで通し確認(1.5.2)。
4. 学習スタブのみ(WBS 1.3.1 / 1.3.2)
.\scripts\run-training-stub.ps1 -SkipDispatch
期待結果:
models/versions/v{N}/horse_model.tfliteが作成されるmodels/manifest.jsonのlatest_versionが更新される- コンソールに
version: v{N}が表示される
5. 学習完了 → GitHub Actions 起動(WBS 1.3.3)
worker/.envにGITHUB_TOKEN/GITHUB_REPOSITORYを設定- ワークフローを main に push(初回のみ)
- 以下を実行:
.\scripts\run-training-stub.ps1
期待結果:
- コンソール:
status: 204(repository_dispatch成功) - GitHub → Actions → Android Build が
model-updatedで起動 - ジョブログに
model_version/model_pathが出力される
6. 手動で Actions を試す(任意)
GitHub リポジトリの Actions タブから Android Build → Run workflow でも起動できます(workflow_dispatch)。
7. 完了チェック(WBS 1.3)
run_training_stub()でversions/v1/が生成される- 再実行で
v2にインクリメントされる trigger_github_dispatch()で HTTP 204 が返る- Actions の
android-build.ymlが dispatch で実行される
8. 次のステップ
APK ビルド・端末インストールは Phase1 GHA・APK 手順 を参照してください。
9. トラブルシュート
| 症状 | 確認事項 |
|---|---|
repository_dispatch failed (404) |
GITHUB_REPOSITORY の owner/repo が正しいか。トークンにリポジトリへのアクセス権があるか |
repository_dispatch failed (403) |
PAT スコープ不足。Classic PAT なら repo、Fine-grained なら Actions 書き込み権限 |
| Actions が起動しない | android-build.yml が default ブランチに存在するか。types: [model-updated] と event_type が一致するか |
Base model not found |
models/base/stub.tflite がリポジトリに含まれているか確認 |
§3 GHA・APK 配布
WBS 1.4(GHA Android ビルド)と 1.5.1(APK インストール)の runbook です。
android-build.yml が Flutter APK をビルドし、artifact として配布します。
1. 前提
- リポジトリの default ブランチに
.github/workflows/android-build.ymlが存在すること - GitHub → Settings → Actions が有効
- 端末配布時: PC に Android SDK platform-tools(
adb)と USB デバッグ有効な端末
2. 概念整理(初めて読む場合)
手順の前に全体像を押さえる場合は Phase1 ナレッジ K-20(GHA・artifact・APK)と K-21(月次パイプライン)を参照。 YAML の基礎は K-19。
| 用語 | 一言 |
|---|---|
| YAML をリモートに載せる | git push でワークフロー定義を GitHub に置く(載せただけでは本 YAML は自動ビルドしない) |
| 手動ビルド | GitHub クラウド上で APK をビルド(Actions → Run workflow) |
| artifact | 1回のビルド成果物のダウンロード置き場(ZIP 内に app-release.apk) |
| adb install | PC から Pixel 等へ APK を入れる(Phase1 Android の配布) |
3. 手動ビルド(WBS 1.4.1)
画面操作の詳細は GHA 操作手順(UI)§A を参照。
- GitHub リポジトリ → Actions → Android Build
- Run workflow → ブランチ選択 →(任意)
model_versionを入力 → Run - 完了後、実行結果 → Artifacts →
app-release-apk-<version>をダウンロード
ZIP 内の app-release.apk が成果物です(設計上の app-release.apk)。
4. モデル同梱の仕組み(WBS 1.4.2)
ビルド前にワークフローが以下を実行します。
repository_dispatchのmodel_version/model_pathを取得(手動時は入力またはmanifest.json)models/<path>がリポジトリにあればそれを、なければmodels/base/stub.tfliteをコピーapp/assets/models/に配置してflutter build apk --release
models/versions/ は gitignore のため
リモートビルドでは通常ベースモデル + バージョンラベル の同梱になります。
アプリ画面の「同梱モデル: vN」表示でパイプライン更新を確認できます。
4b. SEC-01 — APK に FEEDBACK_API_KEY を同梱(WBS 3.1.4 クライアント側)
サーバーで SEC-01 が有効なとき、アプリも Authorization: Bearer を送る必要がある。
Flutter はビルド時 --dart-define=FEEDBACK_API_KEY=... でキーを埋め込む(app/lib/config.dart)。
| ビルド経路 | キーの渡し方 |
|---|---|
| GitHub Actions(推奨・本番配布) | リポジトリ Settings → Secrets and variables → Actions に FEEDBACK_API_KEY を登録(値は server/.env と同じ)。K-22 の PAT 登録と同様の「クラウド側の金庫」 |
| ローカルビルド(開発・検証) | .\scripts\build-flutter-apk.ps1 — server/.env を自動読み込み |
実機デバッグ(flutter run) |
flutter run --dart-define=FEEDBACK_API_KEY=<server/.env と同じ値> |
# GitHub CLI(任意)
gh secret set FEEDBACK_API_KEY --repo OWNER/REPO
# ローカル APK(リポジトリ直下)
.\scripts\build-flutter-apk.ps1
.\scripts\install-apk.ps1 -ApkPath "app\build\app\outputs\flutter-apk\app-release.apk"
4b-1. GitHub Actions Secret 登録(画面手順・2026-06-14 証跡)
PAT(Developer settings)ではない。 リポジトリの Settings から登録する。
直接 URL: https://github.com/<owner>/<repo>/settings/secrets/actions
- リポジトリを開く → 上部タブ Settings
- 左サイドバー Security and quality → Secrets and variables → Actions
- Repository secrets の New repository secret
- Name:
FEEDBACK_API_KEY(完全一致) - Secret:
server/.envの=の右側だけ(下記注意) - Add secret → 一覧に
FEEDBACK_API_KEYが表示される
FEEDBACK_API_KEY=abc123... と行全体を貼る。
正しくは abc123... のみ。誤ると GHA ログの length がローカルビルド(例: 43)と一致せず、実機 FB が 401 になる。
修正: Secret の Update で値のみ貼り直し → Android Build を再実行。
FEEDBACK_API_KEY。Secret は = 以降の値のみ(スクショはマスク推奨)。
4b-2. Secret 登録後の GHA ビルド(2-B)
- Actions → 左 Android Build
- Run workflow → Branch:
main→(任意)model_versionラベル → Run workflow - 成功 run を開く → ジョブ Build release APK ログに
FEEDBACK_API_KEY is set (length …)があること - run ページ下部 Artifacts → ZIP ダウンロード → 解凍 →
app-release.apk install-apk.ps1→ 実機 FB(§4c)
ダウンロードするのはモデルファイル単体ではなく APK(artifact)。 TFLite モデルはビルド時に APK 内へ同梱済み。
main、任意で model_version ラベル。
FEEDBACK_API_KEY is set (length …)。length は server/.env の鍵長と一致すること。
Secret 未設定で GHA ビルドすると workflow に warning が出る(無認証 APK)。
キー変更時は server/.env 更新 → GitHub Secret 更新 → APK 再ビルド・再配布(セキュリティ設計 のローテーション方針)。
4c. 実機 FB 201 確認(SEC-01 クライアント検証)
2026-06-14: 経路 2-A(ローカル APK)で Pixel 7a 実機 FB 201 確認済み。
2026-06-15: 経路 2-B(GHA artifact + Actions Secret)で Pixel 7a 実機 FB 201 確認済み。
「実機 FB 201」とは、Pixel 等の実機アプリから Tunnel 経由で FB を送り、
アプリ画面に 送信成功 (HTTP 201) と表示されることを確認することです。
(PC の test-feedback.ps1 で 201 が出ても、APK に鍵が入っていなければ実機は 401 になります。)
| 経路 | 手順 | 成功の目安 |
|---|---|---|
| 2-A ローカル APK(Secret 不要) |
.\scripts\build-flutter-apk.ps1.\scripts\install-apk.ps1 -ApkPath "app\build\app\outputs\flutter-apk\app-release.apk" -DeviceId …署名不一致時: adb uninstall com.horsediagnosis.horse_stub 後に再 installアプリ起動 → 画像選択 → 「フィードバックを送信」 |
済(2026-06-14) |
| 2-B GHA artifact | §4b-1 Secret 登録 → §4b-2 ビルド → artifact ZIP → install → FB | 済(2026-06-15)。Secret length 43・GHA ビルド → artifact install → FB 201 |
送信成功 (HTTP 201)(2026-06-14)。
同梱モデル: v2 (Add Secrets))。Tunnel 経由 送信成功 (HTTP 201)、ID: f3119e16-6f20-41d7-915b-15807b25c8fe(2026-06-15)。
失敗時: 401 → Secret 値の誤り(FEEDBACK_API_KEY=... 行全体を貼ると length がずれる。例: 68 vs 正 43)→ Secret を = 右のみに修正 → GHA 再ビルド → 再 install。
鍵未同梱はビルドログの length 未表示・warning で判別(K-30)。
図解: K-31 全体構成図(セキュリティ拡張版)。
5. Worker 連携ビルド(WBS 1.4.3)
画面確認は GHA 操作手順(UI)§B。PAT 設定は Worker 手順 §3(K-22)。
.\scripts\run-training-stub.ps1
成功時: コンソール status: 204 → Actions に Android Build が自動起動(2026-06-10: Build #2 確認)。
6. Pixel 7a へインストール(WBS 1.5.1)
adb devices
.\scripts\install-apk.ps1 -ApkPath "C:\path\to\app-release.apk"
複数端末がある場合:
.\scripts\install-apk.ps1 -ApkPath "C:\path\to\app-release.apk" -DeviceId 3C261JEHN19182
起動後、アプリ上部付近に 同梱モデル: vN (... bytes) が表示されれば同梱成功です。
7. 完了チェック(MS-2 向け)
2026-06-10: 手動ビルド経路(workflow_dispatch)で以下を達成。詳細: GHA 操作手順(UI)。
workflow_dispatchで APK artifact が取得できる — 達成(app-release-apk-v1)- APK を adb でインストールしアプリが起動する — 達成(Pixel 7a)
- 画面に同梱モデルバージョンが表示される — 達成(
同梱モデル: v1) - release APK から Tunnel 経由 FB 送信 — 達成(HTTP 201)
- Worker dispatch 後に GHA が自動起動し新 artifact ができる — 達成(Build #2 →
app-release-apk-v2、WBS 1.4.3)
7b. 月次通しチェック(WBS 1.5.2)
2026-06-10〜11: 方法 B(Worker dispatch)通し E2E 完了(実機 v2・FB 確認済み)。
run-training-stub.ps1→ v2 生成 + dispatch 204 — 達成- Android Build #2(Repository dispatch)成功 — 達成
app-release-apk-v2artifact 取得 — 達成- Pixel 7a へ adb install — 達成(uninstall 後に再 install)
- 起動後
同梱モデル: v2 (92 bytes)表示 — 達成(2026-06-11) - Tunnel 経由 FB 送信 — 達成(HTTP 201、ID:
45cbbe07-c287-4db9-845f-dce8904bff91)
運用(PAT ローテーション・artifact 30 日・ローカル APK 保管): K-23。
8. トラブルシュート
| 症状 | 確認事項 | |
|---|---|---|
| Actions にワークフローが出ない | android-build.yml が default ブランチに push 済みか |
|
flutter build apk 失敗 |
Actions ログの Flutter / Java セットアップ。ローカルで flutter build apk が通るか |
|
| adb が見つからない | Android SDK platform-tools を PATH に追加 | |
| adb install 失敗 | INSTALL_FAILED_UPDATE_INCOMPATIBLE — 別署名の package が残存 |
adb uninstall com.horsediagnosis.horse_stub 後に install-apk.ps1 を再実行 |
| 同梱モデルが常に base | 想定どおり(versions/ はローカルのみ)。バージョンラベルは dispatch payload で更新される | |
| 実機 FB が 401(SEC-01 有効後) | APK がキー未同梱でビルドされていないか。§4b — GHA Secret または build-flutter-apk.ps1 で再ビルド |
§4 GHA 操作(UI)
本資料は、GitHub 上で Android Build ワークフローを起動し、 artifact から APK を取得するまでの画面操作を、スクショ付きで記録する runbook です。 概念整理は K-20、端末インストールは Phase1 GHA・APK 手順 を参照してください。
| 方法 | 誰が起動するか | 用途 |
|---|---|---|
A. 手動ビルド(workflow_dispatch) |
人が GitHub の Actions 画面で Run workflow | 初回確認・ビルド単体のテスト(WBS 1.4.1) |
B. Worker dispatch(repository_dispatch) |
PC の run-training-stub.ps1 が GitHub API を叩く |
月次パイプライン想定(学習後に自動ビルド)(WBS 1.4.3) |
同梱モデル: v2・Tunnel 経由 FB 成功、2026-06-11)。
0. 初回セットアップ(1回だけ)
0.0 なぜ Actions にワークフローが表示されるか
GitHub はリポジトリの default ブランチ(main) 上の
.github/workflows/*.yml を自動的に読み取り、Actions タブの左サイドバーに一覧表示します。
追加の「GitHub 側設定ボタン」は基本的に不要です(Actions が有効であることのみ)。
| 左に表示される名前 | 由来 |
|---|---|
| Android Build | あなたが push した .github/workflows/android-build.yml の name: Android Build |
| pages-build-deployment | GitHub Pages 有効化時に GitHub が自動生成するワークフロー。本リポジトリの .github/workflows/ には無い(Jun 7 の Pages デプロイ履歴) |
on: push が無いため、Run workflow または Worker dispatch で初めて実行される。
0.1 ワークフロー YAML をリモートに載せる
ローカルに .github/workflows/android-build.yml があることを確認し、
main ブランチへ push します。
git status
git add .github/workflows/android-build.yml
git commit -m "Add Android Build workflow"
git push origin main
push 後、GitHub リポジトリの Code タブで
.github/workflows/android-build.yml が見えることを確認します。
push だけではビルドは走りません(on: push が無いため)。
0.2 GitHub Actions が有効か確認
- リポジトリ → Settings → 左メニュー Actions → General
- Actions permissions が「Allow all actions…」または組織ポリシーで許可されていること
- Fork や Organization リポジトリの場合、管理者承認が必要なことがあります
0.3 Worker dispatch を使う場合のみ — PAT 設定
手動ビルド(方法 A)だけ試す場合はスキップ可。方法 B には必要です。
worker/.env に置きます(K-22)。
- GitHub 右上の自分のアイコン → Settings(リポジトリではなくユーザーの Settings)
- 左下 Developer settings → Personal access tokens → Generate new token (classic)
- Classic PAT なら
repoスコープ。Fine-grained なら対象リポジトリへの Contents + Actions 書き込み - PC で
worker/.env.exampleをworker/.envにコピーし設定(commit しない):
GITHUB_TOKEN=ghp_xxxxxxxxxxxx
GITHUB_REPOSITORY=makoto55879/github-actions-test
GITHUB_REPOSITORY は owner/repo 形式(URL の github.com/owner/repo と一致)。
A. 手動ビルド(workflow_dispatch)
A-1. Actions タブを開く
- ブラウザでリポジトリを開く(例:
github.com/makoto55879/github-actions-test) - 上部タブの Actions をクリック
android-build.yml が default ブランチ(main) に push されているか確認。
pages-build-deployment だけ見えるのは GitHub Pages 用で、別ワークフローです。
A-2. Android Build ワークフローを選ぶ
- 左サイドバーの Android Build をクリック
- 中央に「This workflow has a workflow_dispatch event trigger」等の表示があることを確認
- 右側(または上部)の Run workflow ボタンが表示される
A-3. Run workflow を実行
- Run workflow をクリック → ドロップダウンが開く
- Branch:
mainを選択(default ブランチ) - Model version label (optional): 任意(例:
v1)。空欄ならmanifest.jsonの latest を使用 - Path under models/ (optional): 通常は空欄(リモートに無いパスは base スタブ + ラベルのみ)
- 緑色の Run workflow をクリック
A-4. 実行状況を確認
- 一覧に新しい実行が追加される(黄色 ● = 実行中、緑 ✓ = 成功、赤 ✗ = 失敗)
- 実行行をクリック → ジョブ Flutter APK with bundled model → 各 step のログを確認
- 初回ビルドは Flutter セットアップ含め 約 7 分 程度(実測: 7m 18s)
A-5. artifact から APK をダウンロード
- 実行が緑のチェックで完了したら、同じページを下へスクロール
- Artifacts セクションに
app-release-apk-<version>が表示される - 名前をクリック → ZIP が PC にダウンロードされる
- ZIP を解凍 → 中の
app-release.apkがインストール対象
app-release-apk-v1)が表示される。
app-release.apk(例: ダウンロード\app-release-apk-v1\)A-6. Pixel 7a へインストール
APK は PC 上のファイルのままでは Pixel には入りません。
USB 接続 + adb install(またはプロジェクトの install-apk.ps1)で入れます。
flutter run で入れたデバッグ版とは別ビルド(release APK)です。
準備 — 端末確認
接続デバイスを名前付きで見るなら flutter devices(WBS 1.2.3 と同じ):
cd c:\Dev\github-actions-test\app
flutter devices
例: Pixel 7a (mobile) • 3C261JEHN19182 • android-arm64 — インストール時の -DeviceId は 3C261JEHN19182(真ん中の ID)。
adb 形式だけ見る場合:
& "$env:LOCALAPPDATA\Android\Sdk\platform-tools\adb.exe" devices
- Pixel 7a を USB で PC に接続
- 端末で USB デバッグ を有効(開発者向けオプション)
- 「USB デバッグを許可しますか?」→ 許可(WBS 1.2.3 と同様)
方法 1 — スクリプト(推奨)
install-apk.ps1 は Android SDK の adb.exe を自動検出します(PATH 未設定でも可)。
cd c:\Dev\github-actions-test
flutter devices # 任意: Pixel 7a と DeviceId を確認
.\scripts\install-apk.ps1 `
-ApkPath "C:\Users\wt2bp\Downloads\app-release-apk-v1\app-release.apk" `
-DeviceId 3C261JEHN19182
成功時の表示例: Success または Performing Streamed Install のあと終了コード 0。
方法 2 — adb を直接
& "$env:LOCALAPPDATA\Android\Sdk\platform-tools\adb.exe" install -r "C:\Users\wt2bp\Downloads\app-release-apk-v1\app-release.apk"
-r は上書きインストール。デバッグ版(flutter run)が残っていると署名不一致になる場合:
& "$env:LOCALAPPDATA\Android\Sdk\platform-tools\adb.exe" uninstall com.horsediagnosis.horse_stub
インストール後の確認
- Pixel 7a のアプリ一覧から「馬体診断スタブ」(または horse_stub)を起動
- 画面上部付近に
同梱モデル: v1 (... bytes)が表示されれば GHA ビルドの同梱成功 - DEBUG リボンは release ビルドでは出ない(正常)
| エラー例 | 対処 |
|---|---|
adb が見つからない | 上記フルパスを使う、または platform-tools を PATH に追加 |
device unauthorized | Pixel で USB デバッグ許可ダイアログを承認 |
INSTALL_FAILED_UPDATE_INCOMPATIBLE | flutter run のデバッグ版と release APK の署名が異なる。adb uninstall com.horsediagnosis.horse_stub 後に再 install |
flutter devices)→ install。署名不一致時は adb uninstall com.horsediagnosis.horse_stub → 再実行。
同梱モデル: v1・送信成功 (HTTP 201) を確認(2026-06-10)。B. Worker dispatch(repository_dispatch)
B-1. 前提確認
worker/.envにGITHUB_TOKEN/GITHUB_REPOSITORYが設定済み- 方法 A と同様、
android-build.ymlが main に push 済み
B-2. PC で学習スタブ + dispatch を実行
cd c:\Dev\github-actions-test
.\scripts\run-training-stub.ps1
期待されるコンソール出力(例 — 2026-06-10 実測):
[1/2] run_training_stub()
version: v2
path: models/versions/v2/horse_model.tflite
[2/2] trigger_github_dispatch()
repo: makoto55879/github-actions-test
event: model-updated
status: 204
status: 204→ dispatch 成功(GitHub は body なしで 204 を返す)- トークン未設定時は dispatch がスキップされ、学習スタブのみ実行される
学習だけ試す場合: .\scripts\run-training-stub.ps1 -SkipDispatch
B-3. GitHub Actions で自動起動を確認
- GitHub → Actions → 左 Android Build
- 一覧に新しい実行が追加されている(Event 列に
Repository dispatch/model-updated) - 2026-06-10 実測: Android Build #2 が dispatch 由来で起動(#1 の Manually run とは別)
- 手動(A)と同様、完了後に Artifacts から APK をダウンロード(v2 実行なら
app-release-apk-v2)
B-4. artifact 取得 → 端末 install(2026-06-10 実測)
- Build #2 成功後、Artifacts から
app-release-apk-v2をダウンロード → ZIP 解凍 →app-release.apk - Pixel 7a へ install(端末 ID は
flutter devicesで確認):
.\scripts\install-apk.ps1 `
-ApkPath "C:\path\to\app-release-apk-v2\app-release.apk" `
-DeviceId 3C261JEHN19182
INSTALL_FAILED_UPDATE_INCOMPATIBLE が出た場合(方法 A の v1 でも発生):
adb -s 3C261JEHN19182 uninstall com.horsediagnosis.horse_stub
.\scripts\install-apk.ps1 -ApkPath "..." -DeviceId 3C261JEHN19182
起動後、画面上部の 同梱モデル: v2 (... bytes) を確認。Tunnel 経由 FB 送信も試す。
同梱モデル: v2 (92 bytes)・送信成功 (HTTP 201) を確認(2026-06-11)。B-5. 3回目以降(月次イメージ)
- 再度
.\scripts\run-training-stub.ps1→v3が生成される - dispatch → 新 artifact
app-release-apk-v3 - ダウンロード → install(署名不一致時は B-4 の uninstall 手順)
C. 方法 A と B の違い(まとめ)
| 項目 | A 手動ビルド | B Worker dispatch |
|---|---|---|
| 起動場所 | GitHub Web UI | PC の PowerShell |
| 学習スタブ | 不要 | run-training-stub.ps1 内で実行 |
| GitHub API / PAT | 不要 | worker/.env 必須 |
YAML の on: | workflow_dispatch | repository_dispatch |
| ビルド本体 | 同じ android-build.yml の jobs | |
| 成果物 | artifact 内の app-release.apk | |
D. スクショの追加・更新方法
未撮影の図は docs/assets/img/gha/ に PNG を置き、本 HTML の placeholder を <img> に差し替えます。
| ファイル名 | 撮影タイミング | 状態 |
|---|---|---|
01-actions-all-workflows.png | Actions タブ初回 | 済 |
02-android-build-selected.png | Android Build 選択 | 済(2026-06-10) |
03-run-workflow-dialog.png | Run workflow ドロップダウン | 済 |
04-workflow-in-progress.png / 04-workflow-success.png | 実行中 / 成功 | 済 |
05-artifacts-section.png | Artifacts ダウンロード | 済 |
06-apk-extracted.png | ZIP 解凍後の APK | 済 |
07-adb-install-success.png | adb install(uninstall 含む) | 済(2026-06-10) |
08-app-release-fb-success.png | 実機:同梱モデル v1・FB 成功 | 済 |
09-dispatch-in-progress.png | Worker dispatch 後の run 一覧(#2) | 済(2026-06-10) |
10-app-release-v2.png | 実機:同梱モデル v2・FB 成功 | 済(2026-06-11) |
E. トラブルシュート
| 症状 | 確認・対処 |
|---|---|
| 左に Android Build が無い | android-build.yml が main に push 済みか。YAML 構文エラーがないか(Actions タブにエラー表示) |
| Run workflow が無い | All workflows ではなく Android Build を選択しているか |
| ビルド失敗(赤 ✗) | 失敗 step を開きログ確認。flutter build apk 行付近を重点的に |
| Artifacts が無い | 実行中は「-」表示。成功後もページ再読み込みで最下部 Artifacts が出ることがある |
dispatch 403 / 404 |
Worker 手順 §9。PAT スコープ・GITHUB_REPOSITORY を確認 |
INSTALL_FAILED_UPDATE_INCOMPATIBLE |
デバッグ版が残っている。adb uninstall com.horsediagnosis.horse_stub してから install-apk.ps1 を再実行 |
| dispatch 成功だが Actions が動かない | android-build.yml の types: [model-updated] と Worker の event_type が一致するか |
F. 運用上の注意(Phase1 Android)
詳細は K-23。要点のみ:
| 項目 | 内容 |
|---|---|
| PAT 有効期限 | 期限切れで dispatch 失敗(403 等)。期限の 1〜2 週間前に新 PAT → worker/.env 差し替え。方法 A には PAT 不要 |
| artifact 保持 | retention-days: 30。30 日後は GitHub 上から消える — 必要な APK は PC に保存 |
| ローカル APK 管理 | 例: releases/app-release-v1.apk、v2 … 直近 2〜3 世代を保持(gitignore 推奨) |
| 配布経路 | Phase1 Android は artifact → adb。TestFlight は iPhone ② 向け(K-21) |
G. 関連資料
- K-20 — GHA・artifact・APK の概念
- K-21 — 月次パイプライン全体像
- K-22 — PAT と Actions Secrets の違い
- K-23 — PAT ローテーション・artifact 保持・APK 世代管理
- Phase1 GHA・APK 手順 — モデル同梱・adb・MS-2 チェックリスト
- Runbook §2 Worker — PAT・
-SkipDispatch
§5 PC 間移設(検証 PC → 役場 PC)
WBS 3.1.1 本番移設のたたき手順です。コンテナを stop → 別 PC で up だけでは不十分です。
永続データと .env も移してください。
| 移すもの | 場所 | 備考 |
|---|---|---|
| リポジトリ | github-actions-test/ | git clone でも可 |
| 秘密設定 | server/.env, worker/.env | Git 非追跡。手動コピー必須 |
| FB 画像 | data/storage/ | ホストフォルダ |
| DB | Docker volume server_pgdata | docker volume ls で名前確認 |
| モデル | models/ | Worker スタブ用 |
5.1 旧 PC(検証機)
cd server
docker compose --profile tunnel stop cloudflared
docker compose --profile tunnel down
down -v は使わない(DB volume が消えます)。
5.2 バックアップ例
# DB volume(名前は環境により server_pgdata 等)
docker run --rm -v server_pgdata:/data -v C:\backup:/backup alpine ^
tar czf /backup/pgdata.tar.gz -C /data .
# 画像・.env も C:\backup 等へコピー
5.3 新 PC(役場 PC)
- Docker Desktop インストール
- リポジトリ取得・
server/.env配置 data/storage/をコピー- volume 復元後
docker compose up -d --build db api - 旧 PC の Tunnel を止めたうえで
docker compose --profile tunnel up -d cloudflared .\scripts\test-feedback.ps1で疎通確認
TUNNEL_TOKEN を二重起動しない。 旧 PC stop → 新 PC start の順序を守る。
FEEDBACK_API_KEY を移せば APK の再ビルドは通常不要。
連携方針: 外部連携 §1 環境整理。