セクション1:クラウドネイティブアプリの設計
公式試験ガイド 042426版(2026年4月改訂) — Section 1: Designing highly scalable, secure, and reliable cloud-native applications
1.1 高性能なアプリケーションと API の設計
公式ガイドのキー項目は 15項目(プラットフォーム選定、コンテナデプロイ、地理的分散、ロードバランサ、セッションアフィニティ、キャッシュ、API、レート制限、非同期統合、リソース要件、コスト最適化、データレプリケーション、トラフィック分割、オーケストレーション)。1.1 だけでこのセクション内の半数近くを占める想定。
コンピュート 3 種の使い分け(最頻出)
Cloud Run
- 抽象度: 最高(コンテナ → HTTP 公開)
- スケール: 0 〜 数千(リクエスト数連動)
- コールドスタート: あり(min-instances で回避可)
- 最大時間: HTTP 60分 / Jobs 24h
- 課金: メモリ × CPU 時間 + リクエスト数
GKE Autopilot / Standard
- 抽象度: 中(Pod / Service / Ingress)
- スケール: Pod 単位で細かく制御
- StatefulSet: ✅ 対応
- GPU: ✅(Standard)
- 課金: Pod requests (Autopilot) / ノード VM (Standard)
Compute Engine
- 抽象度: 低(OS レベル)
- スケール: MIG で VM 単位
- カーネル制御: ✅ フル
- 永続接続: ✅(長時間接続)
- 課金: VM 時間 + ディスク + ネット
選定フローチャート(暗記推奨)
- 「Kubernetes 経験者がいる」だけで GKE を選ぶと、運用負荷(コントロールプレーン、ノードアップグレード、IAM 設計、ネットワーク)が積み上がる
- シンプルな HTTP API なら Cloud Run の方が 運用負荷も TCO も低い ことが多い
- PCD の選択肢では「Kubernetes 経験者がいない」「運用負荷を最小化」というキーワードがあれば Cloud Run が正解
Cloud Run のオートスケーリング深掘り
Cloud Run のスケーリング挙動は試験でも実務でも最頻出。公式ドキュメントから3つの主要フラグを正しく理解しましょう。
--min-instances(最小インスタンス数)
- デフォルト: 0。トラフィックがない場合は完全に 0 までスケールダウン
- 1以上に設定すると、そのインスタンス数を常時 warm に保つ(idle 状態でも CPU は割当てられる)
- コールドスタートを実質ゼロに(min-instances=1 でも)
- バックグラウンドタスク(リクエスト処理外で動くスレッド)がある場合、公式が 1以上を必須 としている
gcloud run deploy my-service \
--image=us-central1-docker.pkg.dev/PROJECT/repo/app:v1 \
--min-instances=1 \
--max-instances=100 \
--concurrency=80 \
--region=us-central1
(min-instances) × (idle CPU 単価) × 月時間 をベースに。
--max-instances(最大インスタンス数)
- 明示的にデフォルト値はないが、すべてのサービスに暗黙の上限が割当てられている
- 上限超過時のリクエストは 最大10秒 または スタートアップ時間の 3.5倍 までキュー待機。それを超えるとエラー (429 / 503)
- 「max-instances=100」と設定しても、突発的なスパイクでは 最大2倍程度(200)まで超過することがある(保証値ではない)
- 主目的:バックエンド(Cloud SQL の接続数上限、Apigee のクォータ)の保護
max_connections=100、1インスタンスで 10接続 → max-instances=10。
--concurrency(1インスタンスあたりの同時リクエスト)
- デフォルト: 80(同時に最大 80 リクエストを処理)
- I/O バウンド(DB 待ち、外部 API 待ち多め)→ 高めに(80〜1000)
- CPU バウンド(画像処理、暗号、ML 推論)→ 低めに(1〜10)
- concurrency=1 にすると、リクエストごとにインスタンスが必要 → コールドスタート頻発のリスク
| アプリ特性 | 推奨 concurrency | 理由 |
|---|---|---|
| シンプルな REST API(CRUD) | 80(デフォルト) | I/O 待ちが主、CPU 余裕 |
| 外部 API 集約(Backend-for-Frontend) | 200〜500 | I/O バウンド、CPU 軽量 |
| 画像/動画変換 | 1〜4 | CPU 重い、メモリ枯渇リスク |
| ML 推論(CPU) | 1〜2 | 1リクエストで CPU 100% 使う |
インスタンスのライフサイクル
- 起動: リクエスト到着 → コンテナ pull → アプリ起動 → ポート 8080 で listen 開始
- サービング: HTTP リクエストを受付、concurrency まで並列処理
- アイドル: リクエストが終わっても 最大 15分(GPU は10分)は idle で残る。すぐ次のリクエストが来ればコールドスタートを回避
- 終了 - グループA: 1分窓で利用率 < 10% のインスタンス群を即座にシャットダウン
- 終了 - グループB: 残りは 15分のアイドル timeout で順次シャットダウン
wait_timeout より短くしないと、サーバ側で切断 → 次リクエストで失敗する。
Cloud Run のスケーリングシーケンス
- 「メモリ使用率でスケールできる」 → ❌ Cloud Run は CPU 使用率と concurrency のみで判断。メモリ枯渇は OOM Kill になる
- 「max-instances は厳密な上限」 → ❌ 突発スパイクで2倍程度超過する可能性
- 「concurrency=1 で安定する」 → ❌ 逆にコールドスタートを頻発させ、レイテンシが悪化する
- 「Single-thread のアプリは concurrency 高くしてよい」 → ❌ CPU バウンドで遅延爆発
ロードバランサ — 5種類の使い分け
HTTP(S) Load Balancing
- L7(アプリケーション層)、HTTP/HTTPS
- グローバル:1つの Anycast IP で世界中
- Cloud CDN 連携、SSL 終端、URL Map
- Cloud Run / GKE / App Engine の 標準前段
TCP/SSL Proxy LB
- L4、TCP/SSL の終端
- グローバル、HTTP 以外の任意プロトコル
- MQTT、PostgreSQL 等のグローバル終端
Network LB
- L4、TCP/UDP(プロキシしない pass-through)
- リージョナル
- クライアント IP がそのままバックエンドに見える
Internal HTTP(S) LB
- L7、VPC 内のみ
- マイクロサービス間のロードバランシング
- Cloud Service Mesh と組み合わせ
Internal TCP/UDP LB
- L4、VPC 内のみ
- 非 HTTP(DB、Redis 等)
Cloud Run / GKE への LB 接続
クライアント
HTTP リクエスト
HTTP(S) LB
SSL 終端、URL Map、Cloud CDN
HTTP(S) LB
バックエンドサービス
Serverless NEG
Cloud Run / Cloud Functions / App Engine
HTTP(S) LB
バックエンドサービス
NEG / Instance Group
GKE Pod / Compute Engine VM
非同期 / イベント駆動 — 4種の使い分け
PCD で最頻出のひっかけポイント。各サービスの「本来の役割」をペアで対比して覚えるのが攻略の鍵。
Pub/Sub
- 目的: 1-to-N ファンアウト
- スループット: 毎秒数百万メッセージ
- 配信保証: at-least-once(Pull は Exactly-once 可)
- 順序: Ordering Keys でパーティション内順序
Cloud Tasks
- 目的: 個別タスクのキュー処理
- スケジュール: 最大 30日先まで delayed
- リトライ: 細粒度(最大回数、間隔、ジッター)
- レート制御: Queue 単位
Eventarc
- 目的: GCP リソースイベント駆動
- イベントソース: 130+(GCS, BigQuery, Audit Logs, Pub/Sub, Direct)
- 受信先: Cloud Run, GKE, Workflows
- 標準: CloudEvents 準拠
Workflows
- 目的: 多サービスのオーケストレーション
- 記述: YAML 宣言、条件分岐、並列実行
- ステート: 各ステップの結果を保持
- 呼出先: GCP API / 外部 HTTP API
判断軸チートシート(即答できるようにする)
| キーワード | 正解サービス | 誤答の罠 |
|---|---|---|
| 「複数の Subscriber にイベントを配信」「ファンアウト」 | Pub/Sub | Cloud Tasks(1対1)を選ぶ罠 |
| 「N分後に実行」「指数バックオフでリトライ」「特定キューにレート制限」 | Cloud Tasks | Pub/Sub(順序遅延の制御性低い) |
| 「GCS にファイル up されたら処理」「BigQuery にジョブ完了したら」 | Eventarc | Cloud Scheduler でポーリング(非効率) |
| 「5サービスを順番に呼び、エラー時にロールバック」 | Workflows | 各サービスに直接連鎖(複雑) |
| 「毎日 2時にバッチ起動」 | Cloud Scheduler + Pub/Sub / Cloud Run | k8s CronJob(運用負荷高) |
Pub/Sub Exactly-once delivery の詳細(深掘り)
📖 Exactly-once delivery の仕組みと制約
仕組み: メッセージ ID に基づいて Pub/Sub が「確認応答済み」を追跡し、確認応答デッドライン内に成功した場合は再配信しない。
制約:
- Pull subscription のみ対応。Push は at-least-once のみ(理由:Push は HTTP レスポンスで確認応答するが、Pub/Sub が受信したかどうか曖昧)
- スループット上限:毎秒約 1,000 メッセージ程度(at-least-once より約 1桁低い)
- publish-to-subscribe レイテンシが 大幅に増加
重複が発生しうるケース:
- パブリッシャー側で同じデータを別のメッセージ ID で送った場合(Exactly-once でも防げない)
- 確認応答デッドラインを超えた処理(タイムアウトしたメッセージは再配信)
gcloud pubsub subscriptions create my-sub \
--topic=my-topic \
--enable-exactly-once-delivery
トラフィック分割(Canary / Blue-Green / A/B)
Cloud Run のリビジョン管理 + traffic splitting は試験必須トピック。
段階的リリースの実装フロー
実装コード(コピペ可)
# Step 1: 新リビジョンを 0% でデプロイ + canary タグ
gcloud run deploy my-service \
--image=us-central1-docker.pkg.dev/PROJECT/repo/app:v2 \
--no-traffic --tag=canary --region=us-central1
# → https://canary---my-service-xxx.a.run.app でテスト可能
# Step 2: 10% に振る(タグ指定)
gcloud run services update-traffic my-service \
--to-tags=canary=10 --region=us-central1
# Step 3: 監視(Cloud Monitoring の error rate, p99 latency を観察)
# 異常があれば↓
gcloud run services update-traffic my-service \
--to-revisions=LATEST=0 --region=us-central1
# 旧リビジョンに 100% 戻る
# Step 4: 段階的に拡大
gcloud run services update-traffic my-service \
--to-tags=canary=50 --region=us-central1
gcloud run services update-traffic my-service \
--to-tags=canary=100 --region=us-central1
--no-traffic --tag=canary でデプロイ → Cloud Monitoring のアラート(error rate > 1% で 5分継続)で update-traffic --to-revisions=PREVIOUS=100 を自動実行する仕組みを組めば、無人ロールバックが実現する。
1.2 セキュアなアプリケーションの設計
認証パターン — 5シナリオ別の正解
🔑 シナリオ A:GCP サービスから GCP API を呼ぶ(同一プロジェクト内)
正解: Application Default Credentials (ADC) + サービスアカウント
Cloud Run / GKE / Cloud Functions / Compute Engine では、サービスに割り当てた SA から メタデータサーバ経由で自動取得。コード変更不要。
from google.cloud import storage
client = storage.Client() # ADC で自動認証
bucket = client.bucket("my-bucket")
避けるべき: SA Key を Cloud Run の環境変数や Secret に注入する(不要かつ漏洩リスク)
🔑 シナリオ B:GKE Pod から GCP API を呼ぶ
正解: Workload Identity(GKE Workload Identity)
Kubernetes ServiceAccount に annotation で GCP SA を紐付け、iam.workloadIdentityUser ロールで借用を許可。Pod は SA Key 配布なしで GCP API を呼べる。
apiVersion: v1
kind: ServiceAccount
metadata:
name: my-ksa
namespace: default
annotations:
iam.gke.io/gcp-service-account: my-gsa@PROJECT.iam.gserviceaccount.com
gcloud iam service-accounts add-iam-policy-binding \
my-gsa@PROJECT.iam.gserviceaccount.com \
--role=roles/iam.workloadIdentityUser \
--member="serviceAccount:PROJECT.svc.id.goog[default/my-ksa]"
🔑 シナリオ C:GCP 外部から GCP API を呼ぶ(GitHub Actions / AWS / Okta 等)
正解: Workload Identity Federation (WIF)
詳細は次の WIF 深掘り セクションへ。
🔑 シナリオ D:既存 Web アプリに Google アカウント認証を追加
正解: Identity-Aware Proxy (IAP)
HTTP(S) LB の前段で認証を強制、アプリのコード変更不要。詳細は IAP 深掘り へ。
🔑 シナリオ E:ユーザー認証付き SaaS(メール/パスワード、SAML、ソーシャル)
正解: Identity Platform(または Firebase Authentication)
- メール/パスワード、Google/Facebook/Apple/GitHub OAuth、SAML 2.0、OIDC をすべてサポート
- MFA(SMS、TOTP)対応
- JWT を発行 → クライアント → バックエンドが検証
🔑 シナリオ F(特殊):マイクロサービス間の mTLS
正解: Cloud Service Mesh(旧 Anthos Service Mesh)
PeerAuthentication: STRICT ポリシーで mTLS を強制。証明書は Mesh が自動ローテーション。
Workload Identity Federation(WIF)深掘り
2026改訂で 強化された頻出トピック。「Service Account Key を発行しない」現代的なクラウド外連携の標準。
WIF の構成手順(GitHub Actions の例)
# Pool 作成
gcloud iam workload-identity-pools create github-pool \
--location=global \
--display-name="GitHub Actions Pool"
# Provider 作成(GitHub の OIDC)
gcloud iam workload-identity-pools providers create-oidc github-provider \
--location=global \
--workload-identity-pool=github-pool \
--issuer-uri="https://token.actions.githubusercontent.com" \
--attribute-mapping="google.subject=assertion.sub,attribute.repository=assertion.repository" \
--attribute-condition="assertion.repository=='my-org/my-repo'"
# Service Account 借用許可(SA 経由パターン)
gcloud iam service-accounts add-iam-policy-binding \
my-sa@PROJECT.iam.gserviceaccount.com \
--role=roles/iam.workloadIdentityUser \
--member="principalSet://iam.googleapis.com/projects/PROJECT_NUMBER/locations/global/workloadIdentityPools/github-pool/attribute.repository/my-org/my-repo"
# またはリソースに直接 IAM(直接アクセスパターン)
gcloud projects add-iam-policy-binding PROJECT_ID \
--role=roles/run.developer \
--member="principalSet://iam.googleapis.com/projects/PROJECT_NUMBER/locations/global/workloadIdentityPools/github-pool/attribute.repository/my-org/my-repo"
# .github/workflows/deploy.yml
name: Deploy to Cloud Run
on:
push:
branches: [main]
permissions:
contents: read
id-token: write # ← OIDC トークン要求のため必須
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- id: auth
uses: google-github-actions/auth@v2
with:
workload_identity_provider: projects/PROJECT_NUMBER/locations/global/workloadIdentityPools/github-pool/providers/github-provider
service_account: my-sa@PROJECT.iam.gserviceaccount.com
- run: gcloud run deploy my-service --source=. --region=us-central1
- attribute-condition 未設定 → 他人のリポジトリから攻撃可能(Confused Deputy)
- PROJECT_NUMBER と PROJECT_ID の混同 → 動かないが症状がわかりにくい
- roles/iam.workloadIdentityUser が SA レベルで必要 → プロジェクトレベルではダメ
- 環境ごとに別 Pool を作るのが推奨。dev/staging/prod を同じ Pool で扱うと境界が緩む
- id-token: write 権限を GitHub Actions の workflow に与え忘れて 401 になる
- 監査:
gcloud iam service-accounts keys listで全 SA Key を棚卸し - 用途分類: GitHub Actions / AWS / Okta / オンプレ ごとに分類
- WIF 構築: 各環境向けに Pool/Provider を作成
- 切替: アプリ/パイプラインを WIF に切替
- SA Key 削除: 切替後、SA Key を削除し Organization Policy
constraints/iam.disableServiceAccountKeyCreationで新規発行も禁止
Secret Manager 深掘り
Secret Manager は単なる「環境変数の代替」ではなく、バージョン管理・IAM・CMEK・自動ローテーションを一体化したシークレット基盤。
Secret Manager の3つのアクセスパターン
① ランタイム取得(推奨)
- アプリ起動時に
access_secret_versionで取得 - 常に
latestまたは specific version を指定 - ローテーション時に即時反映
② Cloud Run secret-as-volume
- Cloud Run のフラグで
--update-secrets=/secrets/key=SECRET:latest - ファイルとしてマウント
- コードは
open("/secrets/key")で読むだけ
③ Cloud Run secret-as-env
- Cloud Run のフラグで
--update-secrets=KEY=SECRET:latest - 環境変数として注入
gcloud run services describe でも値は隠蔽されるが、プロセス環境変数として読めるランタイム取得の実装例
from google.cloud import secretmanager
def get_secret(secret_id: str, version: str = "latest") -> str:
client = secretmanager.SecretManagerServiceClient()
name = f"projects/PROJECT_ID/secrets/{secret_id}/versions/{version}"
response = client.access_secret_version(request={"name": name})
return response.payload.data.decode("UTF-8")
# 起動時に1回だけ取得(毎リクエストで取得しない)
DB_PASSWORD = get_secret("db-password")
API_KEY = get_secret("third-party-api-key")
- 毎リクエストで
access_secret_versionを呼ぶ → コスト・レイテンシ・API クォータ問題。起動時に1回だけ取得しメモリにキャッシュが正解 - 環境変数経由で Cloud Build からビルド時に注入 → ビルドログに残るリスク。ランタイム取得が安全
- シークレットをコードリポジトリに
encrypted/フォルダで管理 → Secret Manager に移行すべき - ローテーション戦略がない → 漏洩時のリスクが時間とともに増大。少なくとも 90日に1回のローテーションを CI で自動化
- Cloud Scheduler が定期的に Pub/Sub に publish
- Pub/Sub が Cloud Function を起動
- Cloud Function が新シークレット生成 + DB のパスワードを変更 + Secret Manager の新バージョンを
add - アプリは
latest参照なので次回起動から自動で新バージョンを使う - Secret Manager の rotation policy で次回ローテーション時刻を Pub/Sub に通知させる
Identity-Aware Proxy(IAP)深掘り
「既存のアプリにコード変更なしで認証を追加する」シナリオの定番解答。
IAP のサポート対象
- Cloud Run(HTTP(S) LB + Serverless NEG 経由)
- GKE(Ingress + BackendConfig で有効化)
- Compute Engine(VM 直接、または MIG + LB)
- App Engine(直接統合)
- オンプレミス(Identity-Aware Proxy connector)
IAM ロール
# ユーザーに IAP アクセスを許可
gcloud projects add-iam-policy-binding PROJECT_ID \
--member="user:alice@example.com" \
--role="roles/iap.httpsResourceAccessor"
# グループに付与(推奨)
gcloud projects add-iam-policy-binding PROJECT_ID \
--member="group:engineers@example.com" \
--role="roles/iap.httpsResourceAccessor"
# Service Account からの呼び出しを許可(Cloud Tasks 等から)
gcloud projects add-iam-policy-binding PROJECT_ID \
--member="serviceAccount:caller@project.iam.gserviceaccount.com" \
--role="roles/iap.httpsResourceAccessor"
バックエンドでの JWT 検証
from google.auth.transport import requests
from google.oauth2 import id_token
EXPECTED_AUDIENCE = "/projects/PROJECT_NUMBER/global/backendServices/BACKEND_ID"
def verify_iap_jwt(request):
jwt = request.headers.get("X-Goog-IAP-JWT-Assertion")
if not jwt:
raise ValueError("Missing IAP JWT")
payload = id_token.verify_token(
jwt,
requests.Request(),
audience=EXPECTED_AUDIENCE,
certs_url="https://www.gstatic.com/iap/verify/public_key",
)
return payload["email"] # 認証済みユーザーのメール
- バックエンドが LB をバイパスして直接アクセス可能だと、IAP が無意味になる。VPC Firewall で LB の Google IP からのみ許可、または Cloud Run なら
--ingress=internal-and-cloud-load-balancingを設定 - Compute Engine の同一 VM 内からのアクセスは IAP を通らない(localhost 経由)
- JWT 検証なしでヘッダのメールを信用すると、ヘッダ偽装で他人になりすませる。必ず
verify_tokenを実装 - IAP 有効化前に IAM を付与しないと自分も入れない。先に
roles/iap.httpsResourceAccessorを自分に付与
Binary Authorization 深掘り
サプライチェーン攻撃対策として 2026改訂で重要度が上昇。attestor / attestation / provenance の関係を正確に理解する。
用語の正確な意味
| 用語 | 定義 | 例え |
|---|---|---|
| Attestor | 署名する権威主体(KMS 鍵 + Container Analysis Note) | 身分証発行機関 |
| Attestation | 「このイメージは検証済み」という署名付きステートメント | 身分証カード |
| Provenance | 「誰が・どのソースから・どのビルダーで作った」の証跡(SLSA) | 製品の製造記録 |
| Note | Container Analysis の attestation スキーマ定義 | 身分証の様式定義 |
ポリシーの構成要素
# Binary Authorization Policy (gcloud で apply)
defaultAdmissionRule:
evaluationMode: REQUIRE_ATTESTATION
enforcementMode: ENFORCED_BLOCK_AND_AUDIT_LOG
requireAttestationsBy:
- projects/PROJECT_ID/attestors/prod-attestor
clusterAdmissionRules:
"us-central1.prod-cluster":
evaluationMode: REQUIRE_ATTESTATION
enforcementMode: ENFORCED_BLOCK_AND_AUDIT_LOG
requireAttestationsBy:
- projects/PROJECT_ID/attestors/prod-attestor
- projects/PROJECT_ID/attestors/vulnerability-attestor
# 開発環境は緩く
istioServiceIdentityAdmissionRules:
"us-central1.dev-cluster":
evaluationMode: ALWAYS_ALLOW
enforcementMode: ENFORCED_BLOCK_AND_AUDIT_LOG
enforcementMode の3モード
ENFORCED_BLOCK_AND_AUDIT_LOG
違反時はデプロイ拒否 + 監査ログ記録。本番推奨
DRYRUN_AUDIT_LOG_ONLY
違反でもデプロイは許可、監査ログだけ記録。導入時の検証で使う
ALWAYS_ALLOW / ALWAYS_DENY
固定挙動。テスト用、または攻撃時のロックダウン
alpha.image-policy.k8s.io/break-glass: "true" で一時的に bypass 可能(GKE)。ただし監査ログに 明確に記録されるので、運用ルールで承認フローを定義しておく。
- 導入時にいきなり ENFORCED にすると、既存ワークロードがデプロイ不能になる。必ず
DRYRUN_AUDIT_LOG_ONLYから始めて、すべての違反を解消してから ENFORCED に - attestor を作っただけでは署名されない。Cloud Build のステップで明示的に
gcloud container binauthz attestations sign-and-createを実行する必要がある - Artifact Registry のリージョン跨ぎで attestation が見つからなくなることがある(同一プロジェクト内なら問題ない)
- Cloud Run と GKE で構成方法が異なる(Cloud Run はサービス単位、GKE はクラスター単位)
1.3 データの保存とアクセス
ストレージ意思決定ツリー(最頻出)
PCD の選択肢で最も問われる「ストレージの選定」。下のツリーを声に出して即答できるレベルが必要。
各サービスの詳細比較
| サービス | 整合性 | スケール | レイテンシ | 典型単価感 | キーワード |
|---|---|---|---|---|---|
| Cloud SQL | 強(リージョナル) | 垂直 + Read Replica | 低(数 ms) | 低〜中 | MySQL / PG / SQL Server |
| AlloyDB | 強(リージョナル) | 垂直 + 列指向 Read | 低(< 数 ms) | 中〜高 | PG 互換、4倍性能、AI/ML |
| Spanner | 強(グローバル、外部一貫性) | 水平(無限) | 10〜数十 ms | 高 | グローバル、TrueTime、無停止 |
| Firestore | 強(単一文書) | 自動水平 | 低 | 従量 | ドキュメント、リアルタイム同期 |
| Bigtable | 結果整合 | 水平(ペタバイト) | ミリ秒 | 中〜高 | ワイドカラム、時系列、IoT |
| Cloud Storage | 強(書き込み後 read) | 無限 | 数十〜百 ms | 低 | BLOB、ストレージクラス |
| BigQuery | 強(クエリ) | 無限(サーバーレス) | 秒〜分 | クエリ + ストレージ | OLAP、SQL、ML |
| Memorystore | — | 垂直(HA) | サブミリ秒 | 中 | キャッシュ、Redis/Memcached |
- 「グローバル分散の RDB」→ Cloud SQL Multi-region と答える罠(リージョナル)。正解は Spanner
- 「ペタバイトの IoT 時系列」→ BigQuery と答える罠(OLAP で書き込みスループット不足)。正解は Bigtable
- 「リアルタイムチャット」→ Cloud SQL と答える罠(同期通信オーバーヘッド)。正解は Firestore
- 「PostgreSQL 互換で高性能」→ Cloud SQL と答える罠(標準性能)。正解は AlloyDB
Signed URL 深掘り
Cloud Storage への 一時的なアクセス権を URL に焼き込む機能。IAM 設定変更なしでアクセス委譲できる。
V2 vs V4 の違い
V2(旧)
- HMAC-SHA1 ベース
- 有効期限の指定が複雑
- 新規利用は非推奨
V4(推奨)
- HMAC-SHA256 ベース
- AWS S3 V4 と互換性高い
X-Goog-Algorithm,X-Goog-Credential,X-Goog-Date,X-Goog-Expires,X-Goog-SignedHeaders,X-Goog-Signature- 新規利用は V4 一択
署名に使う鍵
標準的なパターン。Cloud Storage Client Library が自動で SA の private key を使って署名。
from google.cloud import storage
from datetime import timedelta
client = storage.Client()
bucket = client.bucket("my-bucket")
blob = bucket.blob("uploads/file.bin")
url = blob.generate_signed_url(
version="v4",
expiration=timedelta(minutes=15),
method="PUT", # アップロード用
content_type="application/octet-stream",
)
# url を返す → クライアントが直接 PUT
impersonate_service_account または iam.serviceAccounts.signBlob 権限を持つ SA を介して署名する必要がある。
HMAC キーは「ユーザーアカウント」または「Service Account」に紐付くアクセスキー + 秘密鍵のペア。AWS の Access Key と概念は同じ。
- 主に AWS S3 互換クライアント(boto3 等)から GCS にアクセスする時に使う
- Signed URL の独自実装には可能だが、通常は SA 鍵パターンの方が安全
HTTP メソッド対応
- GET: ダウンロードリンク(限定公開)
- PUT: 単一アップロード(最も多用)
- POST: フォームベースアップロード(Resumable Upload の初期化等)
- DELETE: 削除権限の委譲
ユースケース
① Direct Upload
モバイルアプリから直接 GCS にアップロード(バックエンドの帯域節約)
② 限定公開ダウンロード
「24時間有効な PDF ダウンロードリンク」
③ 一時的 API 連携
パートナー企業に特定オブジェクトへの一時アクセスを付与
- 有効期限の最大は 7日(604,800 秒)。それ以上は IAM で恒久付与する
- サーバの時刻ずれで署名が「未来時刻」または「期限切れ」と扱われる。NTP 同期必須
- URL 自体が権限を持つため、URL を公開してはいけない(HTTPS で配信、ログに残さない)
- 署名する SA に対象オブジェクトへの IAM 権限がないと、URL は発行できても 403 で失敗する
- X-Goog-* ヘッダの偽装防止のため
SignedHeadersに Content-Type 等を含めるのが安全
🚨 事故ケース集(postmortem 形式)
min-instances=0 のままバックグラウンド処理を設計 → メモリ内キャッシュが消えてキャッシュヒット率激減
本番リリース後、レイテンシ p99 が 200ms → 2,000ms に悪化。Cloud SQL の負荷が 3倍に。ユーザーから「画面表示が遅い」「タイムアウトする」の苦情が殺到。
- アプリは「起動時に DB から設定をロードしメモリに保持」する設計
- min-instances=0 のため、トラフィックが少ない時間帯に全インスタンスが終了
- 新リクエストでコールドスタート → 起動毎に DB クエリ + キャッシュ再構築
- 結果、キャッシュヒット率が 95% → 30% に低下、DB が過負荷
4時間にわたって SLO 違反(可用性 99.5% → 88.2%)。Cloud SQL の追加コスト約 $300/日。エンジニアリングチーム5名が緊急対応。
- 緊急で
gcloud run services update --min-instances=3 - Cloud Monitoring でレイテンシ回復を確認(15分)
- Memorystore でアプリ外キャッシュに切り替え(恒久対応)
- 公式ガイドの「バックグラウンドタスクがある場合は min-instances ≥ 1」を遵守
- 状態は外部キャッシュ(Memorystore)に外出し
- 負荷試験で「コールドスタート時の p99」を本番条件で測定
- SLO アラート(p99 latency 閾値超過)を設定し早期検知
concurrency=80 のままで CPU 重い処理を実装 → メモリ枯渇で OOM 連鎖、サービス全停止
新機能(PDF 生成)リリース後、サービスが応答しなくなった。Cloud Logging で "Container terminated due to OOM" が連発。max-instances 上限まで増えても応答せず。
- concurrency=80(デフォルト)のまま PDF 生成(1リクエストで 300MB メモリ消費)を実装
- 1インスタンスで 80並列 = 24GB 要求だが、メモリ 1GB しか設定していない
- OOM Killer がコンテナを殺す → 別インスタンスに振り分け → そこも OOM の連鎖
- Cloud Run はメモリ使用率ではスケールしないため、いくらインスタンスが増えても解消しない
サービス全停止 45分。注文処理停止により売上機会損失 約 $50K。SLA 違反で顧客への補償発生。
- PDF 生成エンドポイントを別の Cloud Run Service に分離
- 分離したサービスを
--concurrency=1 --memory=2Giで再デプロイ - 本体サービスは元の concurrency=80 で動作確認
- CPU/メモリの重い処理は別 Cloud Run Service に分離(concurrency=1〜4)
- 負荷試験でメモリプロファイル測定、最大同時消費 × concurrency < memory limit を確認
- Cloud Run の OOM メトリクスにアラート(短期間で複数回 OOM)
- あるいはバッチ処理に切り出して Cloud Run Jobs で実行
WIF の attribute-condition 未設定 → 任意の GitHub リポジトリから GCP 本番にアクセス可能
セキュリティチームの監査で「GitHub Actions の OIDC Pool が任意のリポジトリから利用可能」と指摘。Cloud Audit Logs を調査すると、過去2週間で外部の不審な GitHub Actions(パブリックリポジトリ)から GCP の Secret Manager に複数回アクセス試行があった(成功は未確認)。
- Workload Identity Provider 作成時に
--attribute-conditionを指定しなかった - 結果、GitHub の OIDC トークンを発行できる世界中のすべてのリポジトリがこの Pool 経由で SA を借用できる状態
- 攻撃者は自分のリポジトリで GitHub Actions を実行し、その OIDC トークンで Pool に認証可能
- Confused Deputy Problem の典型例
幸い実際のアクセスは未遂で済んだが、SA に roles/secretmanager.secretAccessor が付与されていたため、悪用されれば全シークレットが漏洩していた。インシデントレスポンスとフォレンジック調査に 1週間。
- Provider に
--attribute-condition="assertion.repository=='my-org/my-repo'"を即時追加 - 過去のシークレットを全ローテーション
- Cloud Audit Logs を全期間スキャンし、不審アクセスを特定
- WIF Provider 作成時に 必ず
attribute-conditionで repo / org に制限 - 環境ごと(dev / staging / prod)に別 Pool を作成
- Terraform / IaC モジュールで Pool/Provider を作成し、attribute-condition を必須化
- Security Command Center で Workload Identity Federation の構成不備を継続監視
サービスアカウントキー JSON を GitHub Public リポジトリに誤コミット → 暗号資産マイニング攻撃
朝起きたら GCP 請求額が前日比 5倍。Compute Engine で見覚えのない VM が数十台稼働中。CPU が 100% で gpu インスタンスも複数。
- 新人エンジニアが SA Key JSON を
config/ディレクトリに置き、誤って Public リポジトリに push - GitHub 公開後 12分でクローラー bot が Key を発見(公開鍵スキャナ)
- SA に
roles/compute.adminが付与されていた → Compute Engine 起動権限 - 暗号資産マイニング目的で大量の VM を起動された
1日で約 $15,000 の不正課金。GCP の Billing Alert が遅れて発見が翌朝に。Google Cloud サポートに不正利用申告し、最終的に課金は免除されたが、対応期間中の他業務停止コストは回収不能。
- 即時: 漏洩 Key を
gcloud iam service-accounts keys deleteで無効化 - 該当 SA を一旦完全削除
- 不正な VM を停止 + ファイアウォール強化
- GitHub の git history から Key を完全消去(BFG Repo-Cleaner)
- Cloud Support に不正利用申告
- SA Key を発行しない運用に切り替え:WIF / Workload Identity / ADC を使う
- Organization Policy
constraints/iam.disableServiceAccountKeyCreationで SA Key 発行を禁止 - pre-commit hook(gitleaks / truffleHog)でシークレット混入を検知
- GitHub Push Protection(secret scanning)を有効化
- Billing Budget Alert を実額/予測の両方で設定(閾値 $100 / 日)
毎リクエストで access_secret_version を呼び続け、Quota Exhausted で 503 多発
負荷試験中に Cloud Run が 503 を返し始める。Cloud Logging に Quota exceeded: secretmanager.googleapis.com/access_requests が大量出力。
- DB パスワードを毎リクエストで Secret Manager から取得するコード
- 負荷試験で 6,000 RPS → 同時に 6,000 req/s の access_secret_version
- Secret Manager の Quota(プロジェクト全体で毎分 6,000 read)を瞬時に枯渇
- Quota exhausted → 503 でサービス全停止
負荷試験環境で発見できたため本番影響なし。だが、本番でセール時のスパイクで同じ事象を起こす可能性が判明。
- アプリ起動時に1回だけ Secret を取得 → メモリに保持
- ローテーション対応として、SIGHUP シグナルで再読込
- Secret Manager は起動時に1回だけ取得しメモリ保持
- または Cloud Run の
--update-secretsでランタイム注入(環境変数 or ボリューム) - ローテーション時はインスタンス再起動で対応
- Secret Manager のaccess quota メトリクスに監視を設定
IAP の前段 LB をバイパスして直接 Cloud Run に到達可能 → 認証回避された
ペンテスターから「IAP の保護を完全にバイパスできた」と報告。Cloud Run の *.run.app ドメインに直接アクセスすれば、IAP の認証を通らず内部 API が叩けた。
- Cloud Run の
--ingress=all(デフォルト)のまま IAP を構成 - HTTP(S) LB + IAP を前段に置いたが、Cloud Run のデフォルト
*.run.appURL は誰でもアクセス可能のまま - 「URL は公開されていないから安全」という Security through Obscurity に依存
- URL は Cloud Run の API でリストアップ可能 + Subdomain Enumeration で発見される
本番リリース前にペンテストで発見できたため実害なし。ただし本番で発生していれば内部 API が完全に晒される構成だった。
- Cloud Run を
--ingress=internal-and-cloud-load-balancingに変更 - 結果として LB 経由でなければ到達不能になる
- 追加で Cloud Run service の IAM で
roles/run.invokerを LB の SA のみに付与
- IAP を構成する Cloud Run は必ず
--ingress=internal-and-cloud-load-balancing - Cloud Run の IAM で
--no-allow-unauthenticatedも併用(多層防御) - Compute Engine の場合は VPC Firewall で LB の Google IP からのみ ingress 許可
- 定期的なペネトレーションテストで bypass 可能性を検証
連番 ID を主キーに使用 → 書き込みが1ノードに集中、Spanner の本来の性能が出ない
Spanner を導入したが、書き込み TPS が想定の 10% しか出ない。クエリの p99 が 500ms に悪化。Spanner Studio の Hot Spotter で「特定 split が 80% のトラフィックを処理」を確認。
- テーブル主キーに
BIGINT AUTO_INCREMENT的な単調増加 ID を採用 - 新規挿入が常に「最大 ID の次」 → 同一 split(ノード)に集中
- Spanner はキー範囲で自動シャーディングするが、連番 ID では分散できない
- 結果、Spanner の本来の水平スケール性能が出ない
移行プロジェクト全体の見直し。3週間の追加工数。本番リリース延期。
- 主キーを
UUID v4またはhash(timestamp + entity_id)に変更 - 必要なら
hash(...)|original_idの複合キーで「ハッシュプレフィックス」を導入 - データ移行スクリプトを書き直し再ロード
- Spanner の主キーは常に分散的な値(UUID, ハッシュプレフィックス, リバースタイムスタンプ)
- 連番 ID は禁忌(RDB から移行時の最頻出ミス)
- Spanner Studio の Hot Spotter で定期的に分散状況をチェック
- 負荷試験でホットスポット発生を検出
IoT センサーのタイムスタンプをそのまま行キーに → 書き込みが1タブレットに集中
IoT データ取り込み中、書き込みエラー RESOURCE_EXHAUSTED が発生。Bigtable の CPU が一部ノードで 100%、他は 5% という不均衡。
- 行キーを
{timestamp}のままにした("2026-05-28T12:00:00Z...") - 最新時刻への書き込みが常に「最後のタブレット」に集中
- Bigtable は行キー順でタブレット分散するため、単調増加キーは典型的ホットスポット
IoT データの 30% が書き込みエラー → デッドレターキューに溜まる。後追いで再投入したが、リアルタイム集計の遅延発生。
- 行キーを
{device_id_hash_4byte}#{reverse_timestamp}#{device_id}に変更 - 新スキーマでテーブル再作成 + データ再投入
- Key Visualizer で分散確認
- Bigtable は行キー設計が 90%。設計前に Google の
Designing your schemaガイドを必読 - タイムスタンプをキーに含める時はReverse Timestamp + Salting
- Bigtable Key Visualizer で常にホットスポット監視
- 本番投入前に実データに近い負荷で分散テスト
キャッシュ TTL 満了の瞬間に DB 殺到(Cache Stampede / Thundering Herd)
毎日 12:00 ちょうどに Cloud SQL の CPU が 100% に張り付き、5分間 API が応答停止。Cloud Logging に「DB connection timeout」が大量。
- 朝 11:00 に全データを一括キャッシュ(TTL=3600s = 1時間)
- 12:00 ちょうどに同時に全キャッシュが Expire
- その瞬間、数千の Cloud Run インスタンスが一斉に DB へ問い合わせ(Thundering Herd)
- Cloud SQL の max_connections に到達 → 新規接続拒否 → サービス停止
毎日 12:00 - 12:05 まで API が 503 を返す再発障害として 2週間継続。発見が遅れたのは「営業時間内だが利用ピーク前」の時間帯だったため。
- 各キーの TTL にランダムジッターを追加(TTL = base + random(0, 600))
- Probabilistic Early Expiration を実装(XFetch アルゴリズム)
- 緊急時用に Stale-While-Revalidate パターン(古いデータを返しつつ非同期更新)
- キャッシュ TTL は必ずジッター追加(同時 Expire を避ける)
- 大量同時 Miss 対策に Lock + Refresh パターン(1プロセスだけが DB を叩く)
- Memorystore の maxmemory-policy を
allkeys-lruに設定し、突発負荷でも安全に - 負荷試験で「TTL Expire 直後」のシナリオも含める
有効期限 7日の Signed URL を Slack で共有 → ログから抽出されて社外流出
顧客から「他社の社内資料っぽい PDF が、社外サイトに公開されている」と通報。調査すると、自社の GCS バケットへの Signed URL が GitHub の Public Gist にコピーされていた。
- 営業担当が機密 PDF を Signed URL(有効期限 7日)で生成し、Slack で取引先に共有
- 取引先がその URL を、無関係なツールの動作確認のためにテストとして GitHub Gist に貼り付け(Public)
- Signed URL はURL そのものが権限。IAM 不要でアクセス可能
- クローラ bot が Gist を発見 → ダウンロード → 公開サイトに転載
機密文書1件の社外流出。法務対応 + 顧客への謝罪。情報漏洩インシデントとして個人情報保護委員会への報告検討。
- 該当オブジェクトを Cloud Storage から削除(既存 Signed URL も無効化)
- 署名に使った SA の Key を rotation
- Cloud Logging で過去アクセスを調査、外部からの DL 元 IP を特定
- Signed URL の有効期限を最小化(数時間 → 15分など)
- 機密度の高いオブジェクトは Signed URL ではなく IAM + 期間限定アクセス(Conditional IAM)
- 共有経路を限定(社内 Drive / IAP 越しの専用ダウンロードポータル)
- Signed URL アクセスログを Cloud Logging で全捕捉、異常な IP を監視
- 機密文書には DLP(Data Loss Prevention)で透かしや分類タグ付与
セクション1 試験戦略まとめ
- ストレージ意思決定ツリーを3秒で即答できるレベル
- Cloud Run vs GKE の判断軸(ステートレス / GPU / StatefulSet / 運用負荷)を言語化
- Pub/Sub vs Cloud Tasks vs Workflows vs Eventarc の使い分けキーワード
- 認証 5シナリオ(ADC / Workload Identity / WIF / IAP / Identity Platform)
- 「Service Account Key を発行する」選択肢は ほぼ常に不正解
- 「環境変数にシークレット」「`:latest` でデプロイ」「Owner ロール」はほぼ常に不正解
- 「ステートレス」「運用負荷最小」「スケール 0」 → Cloud Run
- 「GPU」「細粒度の Pod 制御」「StatefulSet」 → GKE
- 「SA Key を発行したくない」「外部 IdP」 → WIF
- 「既存アプリにコード変更なし」「Google アカウント認証」 → IAP
- 「グローバル分散」「強整合」 → Spanner
- 「ペタバイト」「時系列」「ミリ秒」 → Bigtable
- 「未署名イメージのデプロイ阻止」 → Binary Authorization