第30章:1クリックデプロイ完成:push→自動でWeb更新 🤖🚀
この章は「卒業制作」だよ!🎉 GitHub に push したら、自動でコンテナがビルドされて、Web(Cloud Run)に反映されるところまでを“完成形”として作るよ 🐳➡️🌍✨ (Node は v24 が Active LTS / v25 が Current の最新状態を前提に進めるね)(nodejs.org)
30.0 今日つくる完成物(ゴールの絵)🗺️✨
できあがる流れはこれ👇
mainに push 🧑💻➡️📦- GitHub Actions が動く ⚙️
- Docker イメージをビルドして Artifact Registry に push 🐳📤
- Cloud Run にデプロイ(新しい Revision が作られる)🚀
- URL を開くと、新しい版が動いてる 😆🌐
ポイントはここ👇
- 毎回クリックで操作しない(push がスイッチ)🔘
- “どの版が本番か”が即答できる(タグ運用)🏷️
- 鍵(長期キー)を置かずに安全に認証(OIDC/WIF)🪪✨(GitHub)
30.1 “卒業制作の仕様”を先に決めよう(ミニ設計)✍️🧠
ここだけ先に決めると、その後ぜんぶ迷子にならないよ🧭
-
サービス名:例
my-api -
リージョン:例
asia-northeast1(東京)🗼 -
公開/非公開:とりあえず 公開(unauthenticated) でOK(個人API想定)🌍
-
エンドポイント
/: 動作確認/healthz: ヘルスチェック用 🩺
-
環境変数
PORT(Cloud Run 側が渡す)NODE_ENV=productionAPP_VERSION(デプロイした版を表示できると最高)🏷️
30.2 アプリ最終仕上げ(“本番で死なない”最低ライン)🧯✨
最低限ここができてると、本番が一気に安定するよ!
0.0.0.0で待ち受ける(コンテナ外から入れる)🌐PORT環境変数で起動できる 🔌/healthzを返す 🩺- SIGTERM を受けたらキレイに終わる(Graceful shutdown)🧯⏳
例(Express)👇
// src/server.ts
import express from "express";
const app = express();
app.get("/", (_req, res) => {
res.json({ ok: true, message: "Hello from Cloud Run!", version: process.env.APP_VERSION ?? "dev" });
});
app.get("/healthz", (_req, res) => {
res.status(200).send("ok");
});
const port = Number(process.env.PORT ?? 8080);
const host = "0.0.0.0";
const server = app.listen(port, host, () => {
console.log(`[boot] listening on http://${host}:${port}`);
});
// Graceful shutdown(Cloud Run は停止時に SIGTERM を送ることが多い)
process.on("SIGTERM", () => {
console.log("[shutdown] SIGTERM received");
server.close(() => {
console.log("[shutdown] http server closed");
process.exit(0);
});
// 念のためタイムアウト(詰まったら諦めて終わる)
setTimeout(() => {
console.log("[shutdown] forced exit");
process.exit(1);
}, 10_000).unref();
});
🤖AI(Copilot / OpenAI Codex など)に投げるなら:
- 「Express の
/healthzと SIGTERM 対応を最小改修で入れて」 - 「
APP_VERSIONをレスポンスに含めて、デプロイ版が分かるようにして」
30.3 Dockerfile を“卒業制作版”にする 🐳🧱✨
ここは「サイズ最適化」より “事故らない構成” を優先するよ!
**マルチステージ(build → runtime)**の例👇 (Node 24 系を想定)(nodejs.org)
## ---------- build ----------
FROM node:24-bookworm-slim AS build
WORKDIR /app
## 依存だけ先に入れてキャッシュを効かせる
COPY package*.json ./
RUN npm ci
## ソース投入 → ビルド
COPY . .
RUN npm run build
## ---------- runtime ----------
FROM node:24-bookworm-slim AS runtime
WORKDIR /app
ENV NODE_ENV=production
## 本番依存だけ
COPY package*.json ./
RUN npm ci --omit=dev
## build成果物だけ持ってくる
COPY --from=build /app/dist ./dist
## Cloud Run は PORT を渡してくれる
EXPOSE 8080
CMD ["node", "dist/server.js"]
.dockerignore も忘れずに(速度と安全の地味王👑):
node_modules
dist
.git
.gitignore
Dockerfile
docker-compose.yml
npm-debug.log
.env
.vscode
30.4 まずは手動で “初回デプロイ” を通す(儀式)🔥✅
いきなり自動化に突っ込むと、どこで詰まったか分からなくなる率が上がる😵 だから 最初だけ手で通すよ!
Cloud Run は コンテナイメージを取り込んで Revision として保持する仕組みがあるので、デプロイの挙動もつかみやすいよ。(GitHub Docs)
30.5 Google Cloud 側の準備(PowerShell コピペ用)☁️🛠️
以下は “置き換え式” でいくよ(YOUR_... を自分の値にするだけ)✍️
## ===== 自分の値に変更してね =====
$PROJECT_ID = "YOUR_GCP_PROJECT_ID"
$REGION = "asia-northeast1"
$SERVICE = "my-api"
$AR_REPO = "containers"
$SA_NAME = "gha-deployer"
$GITHUB_ORG = "YOUR_GITHUB_OWNER" # GitHubのユーザー名 or Organization
$GITHUB_REPO = "YOUR_GITHUB_REPO_NAME" # リポジトリ名
$FULL_REPO = "$GITHUB_ORG/$GITHUB_REPO"
## ===== 1) プロジェクト設定 =====
gcloud config set project $PROJECT_ID
## ===== 2) API有効化 =====
gcloud services enable run.googleapis.com artifactregistry.googleapis.com iam.googleapis.com iamcredentials.googleapis.com sts.googleapis.com
## ===== 3) Artifact Registry(Dockerリポジトリ)作成 =====
gcloud artifacts repositories create $AR_REPO `
--repository-format=docker `
--location=$REGION `
--description="Docker images for Cloud Run"
## ===== 4) デプロイ用サービスアカウント作成 =====
gcloud iam service-accounts create $SA_NAME `
--display-name="GitHub Actions deployer"
$SA_EMAIL = "$SA_NAME@$PROJECT_ID.iam.gserviceaccount.com"
## ===== 5) 権限付与(最低限スタート) =====
gcloud projects add-iam-policy-binding $PROJECT_ID `
--member="serviceAccount:$SA_EMAIL" `
--role="roles/run.admin"
gcloud projects add-iam-policy-binding $PROJECT_ID `
--member="serviceAccount:$SA_EMAIL" `
--role="roles/iam.serviceAccountUser"
gcloud artifacts repositories add-iam-policy-binding $AR_REPO `
--location=$REGION `
--member="serviceAccount:$SA_EMAIL" `
--role="roles/artifactregistry.writer"
30.6 GitHub Actions から安全に認証する(OIDC / Workload Identity Federation)🪪🔐✨
“長期キーをGitHubに置かない”やつね!最高にえらい👏 GitHub Actions は OIDC トークンを発行できて、それを使ってクラウド側で安全に認証できるよ。(GitHub)
Google 側では Workload Identity Pool / Provider を作るよ。 重要:Attribute Condition を必ず付けて入口を絞る(ここ超大事!)(GitHub)
## ===== 6) Workload Identity Pool 作成 =====
gcloud iam workload-identity-pools create "github" `
--project="$PROJECT_ID" `
--location="global" `
--display-name="GitHub Actions Pool"
## Pool のフルID取得
$POOL_FULL = gcloud iam workload-identity-pools describe "github" `
--project="$PROJECT_ID" `
--location="global" `
--format="value(name)"
## ===== 7) Provider 作成(ORGで入口制限) =====
gcloud iam workload-identity-pools providers create-oidc "my-repo" `
--project="$PROJECT_ID" `
--location="global" `
--workload-identity-pool="github" `
--display-name="My GitHub repo Provider" `
--attribute-mapping="google.subject=assertion.sub,attribute.actor=assertion.actor,attribute.repository=assertion.repository,attribute.repository_owner=assertion.repository_owner" `
--attribute-condition="assertion.repository_owner == '$GITHUB_ORG'" `
--issuer-uri="https://token.actions.githubusercontent.com"
## Provider のフルID取得(GitHub Actions YAMLで使う)
$PROVIDER_FULL = gcloud iam workload-identity-pools providers describe "my-repo" `
--project="$PROJECT_ID" `
--location="global" `
--workload-identity-pool="github" `
--format="value(name)"
## ===== 8) このリポジトリだけが $SA_EMAIL を使えるように紐付け =====
gcloud iam service-accounts add-iam-policy-binding $SA_EMAIL `
--project="$PROJECT_ID" `
--role="roles/iam.workloadIdentityUser" `
--member="principalSet://iam.googleapis.com/$POOL_FULL/attribute.repository/$FULL_REPO"
## 表示(あとでGitHubに登録する)
"SA_EMAIL=$SA_EMAIL"
"PROVIDER_FULL=$PROVIDER_FULL"
この作り方(issuer-uri / attribute-mapping / attribute-condition / principalSet)は公式の考え方に沿ってるよ。(GitHub)
30.7 GitHub 側の設定(Secrets/Variables)🧰🔧
GitHub のリポジトリで👇を登録する(Actions 用):
GCP_PROJECT_ID:YOUR_GCP_PROJECT_IDGCP_REGION:asia-northeast1AR_REPO:containersCLOUD_RUN_SERVICE:my-apiWIF_PROVIDER:projects/.../workloadIdentityPools/.../providers/...WIF_SERVICE_ACCOUNT:gha-deployer@...iam.gserviceaccount.com
※ WIF_PROVIDER と WIF_SERVICE_ACCOUNT は秘密じゃないけど、まとめて Secrets に入れてもOK👍
30.8 ここが本体!GitHub Actions(build → push → deploy)⚙️🐳🚀
/.github/workflows/deploy.yml を作るよ👇
google-github-actions/deploy-cloudrun@v3 は コンテナイメージを指定して Cloud Run にデプロイできる(URL も output で取れる)(GitHub)
auth@v3 は WIF で認証できるよ。(GitHub)
name: deploy-cloudrun
on:
push:
branches: ["main"]
permissions:
contents: read
id-token: write
jobs:
deploy:
runs-on: ubuntu-latest
env:
PROJECT_ID: ${{ secrets.GCP_PROJECT_ID }}
REGION: ${{ secrets.GCP_REGION }}
AR_REPO: ${{ secrets.AR_REPO }}
SERVICE: ${{ secrets.CLOUD_RUN_SERVICE }}
steps:
- uses: actions/checkout@v4
# 1) OIDC/WIFでGoogleにログイン(鍵いらない)
- id: auth
uses: google-github-actions/auth@v3
with:
project_id: ${{ secrets.GCP_PROJECT_ID }}
workload_identity_provider: ${{ secrets.WIF_PROVIDER }}
service_account: ${{ secrets.WIF_SERVICE_ACCOUNT }}
# 2) gcloudセットアップ
- uses: google-github-actions/setup-gcloud@v2
# 3) Artifact Registry へ push できるように docker login 相当を設定
- name: Configure Docker auth
run: gcloud auth configure-docker ${{ env.REGION }}-docker.pkg.dev --quiet
# 4) build & push(タグは SHA を本命にする)
- name: Build and Push
uses: docker/build-push-action@v6
with:
context: .
push: true
tags: |
${{ env.REGION }}-docker.pkg.dev/${{ env.PROJECT_ID }}/${{ env.AR_REPO }}/${{ env.SERVICE }}:${{ github.sha }}
${{ env.REGION }}-docker.pkg.dev/${{ env.PROJECT_ID }}/${{ env.AR_REPO }}/${{ env.SERVICE }}:main
# 5) Cloud Runへデプロイ(SHAタグを指定)
- id: deploy
uses: google-github-actions/deploy-cloudrun@v3
with:
service: ${{ env.SERVICE }}
region: ${{ env.REGION }}
image: ${{ env.REGION }}-docker.pkg.dev/${{ env.PROJECT_ID }}/${{ env.AR_REPO }}/${{ env.SERVICE }}:${{ github.sha }}
flags: >-
--set-env-vars=APP_VERSION=${{ github.sha }},NODE_ENV=production
# 6) 軽いスモークテスト(/healthz が返るか)
- name: Smoke test
run: curl -fsS "${{ steps.deploy.outputs.url }}/healthz"
補足(めっちゃ大事)💡
- Cloud Run は タグ(例: main)を digest に解決して使うので、本命は SHA タグにすると「どの版が本番?」が一瞬で分かるよ🏷️
deploy-cloudrunは Node 24 で動くので、もし self-hosted runner を使うなら runner バージョン要件に注意ね。(GitHub)
30.9 “公開設定”は最初だけ手でやるのが楽 👍🌍
Cloud Run の推奨として、CI/CD が **「公開/非公開をいじる」**のは避けよう、って考え方があるよ(権限事故を減らすため)(GitHub) なので最初だけ手で公開にして、以後はデプロイしても公開状態を維持…がラク!
(例:gcloud で公開にするなら IAM で allUsers に roles/run.invoker を付けるやつ)
30.10 動作確認チェックリスト(卒業判定)✅🎓✨
- Actions が通ってる ✅
- Cloud Run の URL が出る ✅
/が返る ✅/healthzが 200 ✅APP_VERSIONがgithub.shaになってる ✅(版が見える!)🏷️- Cloud Run のログに
bootが出てる ✅📜
Cloud Run は Revision 単位でイメージを保持してくれるので、「戻す」もやりやすいよ。(GitHub Docs)
30.11 つまずきTop10 😵💫🔧
-
Cloud Run で 502 / 起動しない
PORTを見てない、0.0.0.0じゃない、起動が遅すぎる…が多い
-
Artifact Registry に push できない(権限)
roles/artifactregistry.writerが足りない
-
OIDC が通らない
permissions: id-token: writeが無いと詰む(これ超多い)(GitHub)
-
WIF Provider の attribute-condition が雑で弾かれる
- ORG 名のミス、repo owner 違い
-
principalSet の repo 名が間違ってる
owner/repoの形が崩れてる
-
region-docker.pkg.devの region がズレてる- Artifact Registry のリージョンと一致させる
-
タグ
mainだけで運用して “どれが本番?”が迷子- SHA タグ本命で!🏷️
-
--set-env-varsが上書き事故- 重要な env を消してないか確認
-
Cloud Run の公開設定が意図せず変わる
- “最初だけ手で公開”が安定(GitHub)
-
自動化前にローカルで動いてない
- まずコンテナで
docker runして/healthzを叩くのが最強🧪🐳 - 「push 前にテストする」考え方も公式で推奨があるよ。(Docker Documentation)
30.12 仕上げ(テンプレ化して“毎回これ”にする)📦✨
最後に“個人開発の最強テンプレ”に寄せるなら👇が効く!
- リポジトリを Template 化(新規開発は複製で開始)🧬
READMEに「手順 3 行」だけ書く(未来の自分を救う)📝- Actions に
concurrencyを入れて多重デプロイ事故を防ぐ 🔁🛑 main直push じゃなく PR → merge にする(落ち着く)🧘
🤖AIに投げるなら:
- 「この workflow をテンプレとして使う前提で、再利用しやすい変数設計に直して」
- 「この構成で起きがちなセキュリティ事故を、優先度順に潰す提案をして」
30.13 ボーナス:Cloud Run 側で “リポジトリから継続デプロイ” もあるよ 🎁🔁
「GitHub Actions を組むのも楽しいけど、もっとマネージドに寄せたい」なら Cloud Run には リポジトリ連携で継続的デプロイのルートも用意されてるよ。(Google Cloud Documentation)
ここまで通ったら、もう「push → Web更新」は“体に入った”はず!🎓🔥 次はこのテンプレに **DB(Cloud SQL / Neon / Supabase など)**を繋いで、環境変数・Secrets・マイグレーションまで含めた“個人開発の完成形”に育てると、さらに無敵になるよ💪😆