版数: 2.0  |  更新日: 2026-06-15  |  層: ③ 構築・運用

構築・運用手順書(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 を受信できる状態を目指します。

2026-06-08: WBS 1.2.3 実機検証中。開発セッション開始時は Docker Desktop の起動を忘れずに (未起動だと Cloudflare error 1033 が再発)。実機 FB の HTTP 400 対応は K-17 を参照。
2026-06-07 完了: 本手順に基づき WBS 1.1・MS-1 を達成済み。 実施記録は 開発ログ、 トラブルシュート・設計理解は Phase1 ナレッジ を参照。
方針変更の経緯は 開発ログ を参照。 手順変更(v1.1): PoC(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.comhttp://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.3FB 受信 API 疎通(localhost)test-feedback.ps1
1.1.1cloudflared を本 PC へ移行Pi 側を停止してから起動
1.1.1 / 1.2.3Tunnel 経由の外部疎通確認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_TOKENPoC の poc-api-tunnelConnector トークン(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/.envFEEDBACK_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

-RestartApidocker 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 201id が返る。画像は data/storage/ に保存される。

SEC-01 有効時(FEEDBACK_API_KEY 設定済み)の成功例: 末尾に HTTP_CODE:201。失敗時の切り分けは §8K-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)

新規作成は不要。 以下は PoC で実施済みのためスキップします。
  • Zero Trust での Tunnel 新規作成(horse-feedback 等)
  • DNS 移管(Squarespace → Cloudflare)
  • Public Hostname の初回設定
参照: PoC ナレッジ 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/.envTUNNEL_TOKENeyJ... 形式)を設定済みであることを確認し:

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 → HTTP 201

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_TOKENeyJ... 形式か確認。Tunnel ID(UUID)を入れていないか
Cloudflare error 1033 cloudflared が Up か確認(docker compose ps)。トークン・Pi 側二重コネクタを確認
localhost の curl 失敗(PowerShell) PowerShell の curlInvoke-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 だけ失敗する典型パターン。
  1. FEEDBACK_API_KEYserver/.env のみworker/.env ではない)
  2. PowerShell の $env:FEEDBACK_API_KEY が残っていないか(スクリプトは環境変数を最優先)。あれば Remove-Item Env:FEEDBACK_API_KEY
  3. server/.env 更新後は .\scripts\rotate-server-secrets.ps1 -ApiKey -RestartApi で API コンテナへ反映
詳細: K-30
警告「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 を起動します。

スコープ: 画像分類モデルのインフラ検証用スタブです。 生成 AI は使用しません。本番想定は TFLite(Android)/ CoreML(iOS)のオンデバイス推論です。

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.exampleworker/.env にコピーし、値を設定します。worker/.env.gitignore 対象 — commit しない

3.1 PAT(Personal Access Token)の発行

リポジトリの Actions Secrets ではない。 Settings → Secrets and variables → Actions は、GitHub クラウド上のワークフロー用です。 Worker dispatch 用 PAT はユーザーアカウントから発行し、ローカルの worker/.env のみに置きます(K-22)。
  1. GitHub 右上の自分のアイコンSettings(リポジトリではなくユーザー)
  2. 左下 Developer settingsPersonal access tokensTokens (classic)
  3. Generate new token (classic)
  4. Note: 例 github-actions-test-worker / Expiration: 任意(期限切れ前にローテーション — K-23
  5. Scopes: repo(Classic)。Fine-grained の場合は対象 repo へ Contents + Actions
  6. 生成された ghp_...worker/.envGITHUB_TOKEN に貼り付け
Actions Secrets 画面 — 今回の PAT 発行場所ではない
参考: リポジトリ Settings の Actions Secrets。Worker 用 PAT はここではなく Developer settings から発行。

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.jsonlatest_version が更新される
  • コンソールに version: v{N} が表示される

5. 学習完了 → GitHub Actions 起動(WBS 1.3.3)

  1. worker/.envGITHUB_TOKEN / GITHUB_REPOSITORY を設定
  2. ワークフローを main に push(初回のみ)
  3. 以下を実行:
.\scripts\run-training-stub.ps1

期待結果:

  • コンソール: status: 204repository_dispatch 成功)
  • GitHub → Actions → Android Buildmodel-updated で起動
  • ジョブログに model_version / model_path が出力される

6. 手動で Actions を試す(任意)

GitHub リポジトリの Actions タブから Android BuildRun 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 として配布します。

GitHub 画面の詳細手順(スクショ付き): GHA 操作手順(UI) — Run workflow・artifact ダウンロード・Worker dispatch の確認方法。

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)
artifact1回のビルド成果物のダウンロード置き場(ZIP 内に app-release.apk
adb installPC から Pixel 等へ APK を入れる(Phase1 Android の配布)

3. 手動ビルド(WBS 1.4.1)

画面操作の詳細は GHA 操作手順(UI)§A を参照。

  1. GitHub リポジトリ → ActionsAndroid Build
  2. Run workflow → ブランチ選択 →(任意)model_version を入力 → Run
  3. 完了後、実行結果 → Artifactsapp-release-apk-<version> をダウンロード

ZIP 内の app-release.apk が成果物です(設計上の app-release.apk)。

4. モデル同梱の仕組み(WBS 1.4.2)

ビルド前にワークフローが以下を実行します。

  1. repository_dispatchmodel_version / model_path を取得(手動時は入力または manifest.json
  2. models/<path> がリポジトリにあればそれを、なければ models/base/stub.tflite をコピー
  3. app/assets/models/ に配置して flutter build apk --release
スタブ検証では、ローカル Worker が生成した 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 → ActionsFEEDBACK_API_KEY を登録(値は server/.env と同じ)。K-22 の PAT 登録と同様の「クラウド側の金庫」
ローカルビルド(開発・検証) .\scripts\build-flutter-apk.ps1server/.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

  1. リポジトリを開く → 上部タブ Settings
  2. 左サイドバー Security and qualitySecrets and variablesActions
  3. Repository secretsNew repository secret
  4. Name: FEEDBACK_API_KEY(完全一致)
  5. Secret: server/.env= の右側だけ(下記注意)
  6. Add secret → 一覧に FEEDBACK_API_KEY が表示される
よくある誤り: Secret 欄に FEEDBACK_API_KEY=abc123...行全体を貼る。 正しくは abc123... のみ。誤ると GHA ログの length がローカルビルド(例: 43)と一致せず、実機 FB が 401 になる。 修正: Secret の Update で値のみ貼り直し → Android Build を再実行。
Actions secrets — 未登録時
図 4b-1: Settings → Secrets and variables → Actions。初回は Repository secrets が空。
New repository secret フォーム
図 4b-2: Name は FEEDBACK_API_KEY。Secret は = 以降の値のみ(スクショはマスク推奨)。
Repository secret added
図 4b-3: 登録成功(Repository secret added)。一覧に名前のみ表示、値は再表示不可。

4b-2. Secret 登録後の GHA ビルド(2-B)

  1. Actions → 左 Android Build
  2. Run workflow → Branch: main →(任意)model_version ラベル → Run workflow
  3. 成功 run を開く → ジョブ Build release APK ログに FEEDBACK_API_KEY is set (length …) があること
  4. run ページ下部 Artifacts → ZIP ダウンロード → 解凍 → app-release.apk
  5. install-apk.ps1 → 実機 FB(§4c

ダウンロードするのはモデルファイル単体ではなく APK(artifact)。 TFLite モデルはビルド時に APK 内へ同梱済み。

Android Build 実行一覧
図 4b-4: Android Build 一覧。#3 以降が Secret 登録後の手動 run。
Run workflow ダイアログ
図 4b-5: Run workflow — Branch main、任意で model_version ラベル。
Build release APK — FEEDBACK_API_KEY is set
図 4b-6: ビルドログで Secret 読み込み確認。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
SEC-01 付き release APK — 実機 FB 送信成功 HTTP 201(2-A)
図 4c-1: 2-A ローカル release APK。Tunnel 経由 送信成功 (HTTP 201)(2026-06-14)。
GHA artifact APK — 実機 FB 送信成功 HTTP 201(2-B)
図 4c-2: 2-B GHA artifact APK(同梱モデル: 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-v2 artifact 取得 — 達成
  • 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 手順 を参照してください。

2つの起動方法:
方法誰が起動するか用途
A. 手動ビルドworkflow_dispatch 人が GitHub の Actions 画面で Run workflow 初回確認・ビルド単体のテスト(WBS 1.4.1)
B. Worker dispatchrepository_dispatch PC の run-training-stub.ps1 が GitHub API を叩く 月次パイプライン想定(学習後に自動ビルド)(WBS 1.4.3)
2026-06-10 検証済み: 方法 A(手動ビルド A-2〜A-6)→ MS-2 達成(v1・Pixel 7a)。 方法 B(Worker dispatch)→ 月次通し E2E 完了(204 → Build #2 → v2 APK → 同梱モデル: 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.ymlname: Android Build
pages-build-deployment GitHub Pages 有効化時に GitHub が自動生成するワークフロー。本リポジトリの .github/workflows/ には無い(Jun 7 の Pages デプロイ履歴)
push しただけでは Android Build は走らない。 YAML に 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 が有効か確認

  1. リポジトリ → Settings → 左メニュー ActionsGeneral
  2. Actions permissions が「Allow all actions…」または組織ポリシーで許可されていること
  3. Fork や Organization リポジトリの場合、管理者承認が必要なことがあります

0.3 Worker dispatch を使う場合のみ — PAT 設定

手動ビルド(方法 A)だけ試す場合はスキップ可。方法 B には必要です。

よくある誤り: リポジトリの Settings → Secrets and variables → Actions は、 GitHub クラウド上のワークフローが使う秘密情報用です。 今回の Worker dispatch に必要な PAT はここではなく、下記のユーザー PAT を PC の worker/.env に置きます(K-22)。
  1. GitHub 右上の自分のアイコンSettings(リポジトリではなくユーザーの Settings)
  2. 左下 Developer settingsPersonal access tokensGenerate new token (classic)
  3. Classic PAT なら repo スコープ。Fine-grained なら対象リポジトリへの Contents + Actions 書き込み
  4. PC で worker/.env.exampleworker/.env にコピーし設定(commit しない):
GITHUB_TOKEN=ghp_xxxxxxxxxxxx
        GITHUB_REPOSITORY=makoto55879/github-actions-test

GITHUB_REPOSITORYowner/repo 形式(URL の github.com/owner/repo と一致)。


A. 手動ビルド(workflow_dispatch)

ゴール: GitHub クラウド上で APK をビルドし、artifact をダウンロードできる状態にする。

A-1. Actions タブを開く

  1. ブラウザでリポジトリを開く(例: github.com/makoto55879/github-actions-test
  2. 上部タブの Actions をクリック
Actions タブ — All workflows 一覧。左に Android Build が表示される
図 A-1: Actions タブ — 「All workflows」一覧。左サイドバーに Android Build が表示されていれば YAML の読み込み成功。
左に Android Build が無い場合: android-build.ymldefault ブランチ(main) に push されているか確認。 pages-build-deployment だけ見えるのは GitHub Pages 用で、別ワークフローです。

A-2. Android Build ワークフローを選ぶ

  1. 左サイドバーの Android Build をクリック
  2. 中央に「This workflow has a workflow_dispatch event trigger」等の表示があることを確認
  3. 右側(または上部)の Run workflow ボタンが表示される
Android Build 選択 — Run workflow ボタン表示
図 A-2: Android Build を選択。「This workflow has no runs yet」と Run workflow が表示されていれば OK。

A-3. Run workflow を実行

  1. Run workflow をクリック → ドロップダウンが開く
  2. Branch: main を選択(default ブランチ)
  3. Model version label (optional): 任意(例: v1)。空欄なら manifest.json の latest を使用
  4. Path under models/ (optional): 通常は空欄(リモートに無いパスは base スタブ + ラベルのみ)
  5. 緑色の Run workflow をクリック
Run workflow ダイアログ — model_version v1
図 A-3: Branch = main、Model version label に v1 を入力して Run workflow。

A-4. 実行状況を確認

  1. 一覧に新しい実行が追加される(黄色 ● = 実行中、緑 ✓ = 成功、赤 ✗ = 失敗)
  2. 実行行をクリック → ジョブ Flutter APK with bundled model → 各 step のログを確認
  3. 初回ビルドは Flutter セットアップ含め 約 7 分 程度(実測: 7m 18s)
Android Build #1 実行中 In progress
図 A-4a: 実行中(Status: In progress)。Artifacts は完了まで「-」表示。
Android Build #1 成功 7m18s Artifacts 1
図 A-4b: 成功(緑 ✓)。Summary に Artifacts: 1。Annotations の Node.js 警告は現状無視可。
All workflows — Android Build #1 成功
図 A-4c: All workflows に Android Build #1(Manually run)が追加された状態。

A-5. artifact から APK をダウンロード

  1. 実行が緑のチェックで完了したら、同じページを下へスクロール
  2. Artifacts セクションに app-release-apk-<version> が表示される
  3. 名前をクリック → ZIP が PC にダウンロードされる
  4. ZIP を解凍 → 中の app-release.apk がインストール対象
Artifacts がすぐ見えない場合: 実行中は Artifacts が「-」のまま。 成功直後もページ更新が遅れることがある。別ページへ移動して戻る、または F5 で再読み込みすると ページ最下部の Artifacts セクション(app-release-apk-v1)が表示される。
Artifacts app-release-apk-v1 21.9 MB ダウンロード
図 A-5: Artifacts セクション。右の ↓ アイコンで ZIP をダウンロード。
解凍後 app-release.apk 約47MB
図 A-5b: ZIP 解凍後の 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 — インストール時の -DeviceId3C261JEHN19182(真ん中の ID)。

adb 形式だけ見る場合:

& "$env:LOCALAPPDATA\Android\Sdk\platform-tools\adb.exe" devices
  1. Pixel 7a を USB で PC に接続
  2. 端末で USB デバッグ を有効(開発者向けオプション)
  3. 「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

インストール後の確認

  1. Pixel 7a のアプリ一覧から「馬体診断スタブ」(または horse_stub)を起動
  2. 画面上部付近に 同梱モデル: v1 (... bytes) が表示されれば GHA ビルドの同梱成功
  3. DEBUG リボンは release ビルドでは出ない(正常)
エラー例対処
adb が見つからない上記フルパスを使う、または platform-tools を PATH に追加
device unauthorizedPixel で USB デバッグ許可ダイアログを承認
INSTALL_FAILED_UPDATE_INCOMPATIBLEflutter run のデバッグ版と release APK の署名が異なる。adb uninstall com.horsediagnosis.horse_stub 後に再 install
install-apk.ps1 成功 — adb uninstall 後 Success
図 A-6a: 端末確認(flutter devices)→ install。署名不一致時は adb uninstall com.horsediagnosis.horse_stub → 再実行。
release APK — 同梱モデル v1 と FB 送信成功 HTTP 201
図 A-6b: GHA release APK 起動後。同梱モデル: v1送信成功 (HTTP 201) を確認(2026-06-10)。

B. Worker dispatch(repository_dispatch)

ゴール: PC で学習スタブ実行 → GitHub Actions が自動起動 → 新しい artifact ができる。

B-1. 前提確認

  • worker/.envGITHUB_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 で自動起動を確認

  1. GitHub → Actions → 左 Android Build
  2. 一覧に新しい実行が追加されている(Event 列に Repository dispatch / model-updated
  3. 2026-06-10 実測: Android Build #2 が dispatch 由来で起動(#1 の Manually run とは別)
  4. 手動(A)と同様、完了後に Artifacts から APK をダウンロード(v2 実行なら app-release-apk-v2
Android Build #2 — Repository dispatch で実行中
図 B-3: Worker 実行後の Android Build 一覧。#2 が Repository dispatch で起動(2026-06-10)。

B-4. artifact 取得 → 端末 install(2026-06-10 実測)

  1. Build #2 成功後、Artifacts から app-release-apk-v2 をダウンロード → ZIP 解凍 → app-release.apk
  2. 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 送信も試す。

release APK v2 — 同梱モデル v2 と FB 送信成功 HTTP 201
図 B-4b: 方法 B release APK 起動後。同梱モデル: v2 (92 bytes)送信成功 (HTTP 201) を確認(2026-06-11)。

B-5. 3回目以降(月次イメージ)

  1. 再度 .\scripts\run-training-stub.ps1v3 が生成される
  2. dispatch → 新 artifact app-release-apk-v3
  3. ダウンロード → install(署名不一致時は B-4 の uninstall 手順)

C. 方法 A と B の違い(まとめ)

項目A 手動ビルドB Worker dispatch
起動場所GitHub Web UIPC の PowerShell
学習スタブ不要run-training-stub.ps1 内で実行
GitHub API / PAT不要worker/.env 必須
YAML の on:workflow_dispatchrepository_dispatch
ビルド本体同じ android-build.yml の jobs
成果物artifact 内の app-release.apk

D. スクショの追加・更新方法

未撮影の図は docs/assets/img/gha/ に PNG を置き、本 HTML の placeholder を <img> に差し替えます。

ファイル名撮影タイミング状態
01-actions-all-workflows.pngActions タブ初回
02-android-build-selected.pngAndroid Build 選択済(2026-06-10)
03-run-workflow-dialog.pngRun workflow ドロップダウン
04-workflow-in-progress.png / 04-workflow-success.png実行中 / 成功
05-artifacts-section.pngArtifacts ダウンロード
06-apk-extracted.pngZIP 解凍後の APK
07-adb-install-success.pngadb install(uninstall 含む)済(2026-06-10)
08-app-release-fb-success.png実機:同梱モデル v1・FB 成功
09-dispatch-in-progress.pngWorker 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.ymltypes: [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.apkv2 … 直近 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/.envGit 非追跡。手動コピー必須
FB 画像data/storage/ホストフォルダ
DBDocker volume server_pgdatadocker 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)

  1. Docker Desktop インストール
  2. リポジトリ取得・server/.env 配置
  3. data/storage/ をコピー
  4. volume 復元後 docker compose up -d --build db api
  5. 旧 PC の Tunnel を止めたうえで docker compose --profile tunnel up -d cloudflared
  6. .\scripts\test-feedback.ps1 で疎通確認
同一 TUNNEL_TOKEN を二重起動しない。 旧 PC stop → 新 PC start の順序を守る。 FEEDBACK_API_KEY を移せば APK の再ビルドは通常不要。

連携方針: 外部連携 §1 環境整理