
【アップデート】Cloud Run で Cloud Run sandboxes が Preview で利用可能になりました
はじめに
こんにちは。
クラウド事業本部コンサルティング部の渡邉です。
2026年7月8日、Cloud Run で Cloud Run sandboxes が Preview として利用可能になりました。
AI エージェントが LLM に生成させたコードをそのまま実行することは、セキュリティ上の大きなリスクを伴います。生成されたコードがファイルシステムを改ざんしたり、環境変数からシークレットを読み取ったりする可能性があるためです。
これまでCloud Runを利用した AI エージェントを利用して生成された信頼されていないコードを安全に実行するには、「コード実行専用の Cloud Run サービスを別途デプロイして呼び出す」という複雑なアーキテクチャが必要でした。
今回リリースされた Cloud Run sandboxes を使うと、コンテナ内で高速に起動する分離サンドボックス環境が立ち上がり、安全な環境でコードを実行させることができます。
本記事では、Cloud Run sandboxes の仕組みと特徴を整理し、AI エージェントの Code Interpreter ツールを例にした実際の動かし方をご紹介します。
Cloud Run sandboxes とは
Cloud Run sandboxes は、Cloud Run の第2世代実行環境のサービス内に、信頼されていないコードを安全に実行するための高速・分離サンドボックスを追加する機能です。
サンドボックスはデプロイ済みの Cloud Run サービスのコンテナ内に内蔵され、サンドボックスバイナリ(/usr/local/gcp/bin/sandbox)を通して任意のコマンドをカーネルレベルで分離された環境で実行できます。
この機能を利用することで、別のCloud Runサービスを呼び出す必要がなく、同一コンテナ内で安全にコードを実行させることができます。
Cloud Run sandboxes の特徴
| 特徴 | 内容 |
|---|---|
| 有効化方法 | --sandbox-launcher フラグ(または YAML で sandboxLauncher: true) |
| 対応環境 | Cloud Run 第2世代実行環境のみ(第1世代は非対応) |
| サンドボックス起動速度 | 高速(< 1 秒)での起動 |
| 対応言語 | Python、Node.js、Goなど |
| リソース共有 | ホストコンテナの CPU・メモリを共有(追加課金なし) |
| サンドボックスバイナリ | /usr/local/gcp/bin/sandbox |
注意事項
- 同時実行するサンドボックスが必要とする CPU・メモリは、ホストコンテナのリソース割り当て内に収める必要があります
- 第1世代の実行環境では利用できません
ユースケース: AI エージェントの Code Interpreter
今回構築するのは、ユーザーの指示を受け取り Gemini にコードを生成させてサンドボックスで安全に実行する /run エンドポイントです。
実際に試してみる
Gemini がコードを生成し、Cloud Run sandboxes がコードを安全に実行する一連の流れを確認します。サンドボックスによる分離効果も実際に検証します。
前提条件
- Google Cloud プロジェクト(課金有効化済み)
gcloudCLI(最新版、beta コンポーネント含む)- 以下の API の有効化
- Cloud Run API
- Vertex AI API
- Artifact Registry API
- Cloud Build API
- IAM 権限: プロジェクトオーナーまたは以下のロールの付与
roles/run.developer(Cloud Run のデプロイ)roles/artifactregistry.admin(リポジトリ・イメージ管理)roles/cloudbuild.builds.editor(イメージのビルド)roles/iam.serviceAccountAdmin+roles/resourcemanager.projectIamAdmin(サービスアカウントの作成・ロール付与)
ステップ 0: 環境変数を設定する
以降のコマンドで共通して使う値を環境変数に設定しておきます。
export PROJECT_ID="YOUR_PROJECT_ID"
export REGION="asia-northeast1"
export SERVICE_NAME="code-interpreter"
export SA_NAME="code-interpreter-sa" # Cloud Run 専用サービスアカウント名
export VERTEX_AI_LOCATION="global" # Gemini の呼び出しリージョン
export GEMINI_MODEL="gemini-3.5-flash"
ステップ 1: コンテナイメージをビルドしてプッシュする
今回の検証で使う Code Interpreter サービスは、以下の 3 ファイルで構成されます。
cloud-run-sandboxes-sample/
├── app.py # Flask サーバー(/run エンドポイント)
├── requirements.txt # flask, gunicorn, google-cloud-aiplatform
└── Dockerfile
app.py
ユーザーの指示を受け取り、Gemini でコードを生成してサンドボックスで実行します。生成コードは /usr/local/bin/python3 - でサンドボックスで実行させます。サンドボックスはホストコンテナの環境変数(PATH など)を継承しないため、python3 ではなくフルパスで指定することがポイントです。
import os, re, subprocess
import vertexai
from flask import Flask, request, jsonify
from vertexai.generative_models import GenerativeModel
app = Flask(__name__)
SANDBOX_BIN = "/usr/local/gcp/bin/sandbox"
PROJECT_ID = os.environ.get("GOOGLE_CLOUD_PROJECT", "")
LOCATION = os.environ.get("VERTEX_AI_LOCATION", "global")
GEMINI_MODEL = os.environ.get("GEMINI_MODEL", "gemini-3.5-flash")
vertexai.init(project=PROJECT_ID, location=LOCATION)
_model = None
def get_model():
global _model
if _model is None:
_model = GenerativeModel(GEMINI_MODEL)
return _model
CODE_GEN_SYSTEM = """あなたはPythonコード生成エージェントです。
ルール:
- Pythonコードのみを出力(説明文・コードブロック記号は不要)
- 結果は標準出力(print)に出力すること
- ファイルシステムへのアクセスおよびネットワークアクセスは禁止"""
@app.route("/run", methods=["POST"])
def run():
instruction = request.get_json(force=True).get("instruction", "")
if not instruction:
return jsonify({"error": "instruction is required"}), 400
# Gemini にコードを生成させる
response = get_model().generate_content([CODE_GEN_SYSTEM, instruction])
# コードブロック記号(```python / ```)を除去
generated_code = re.sub(
r"^```python\n?|^```\n?|```$", "", response.text.strip(), flags=re.MULTILINE
).strip()
# サンドボックスで実行(stdin 経由でコードを渡す)
try:
result = subprocess.run(
[SANDBOX_BIN, "do", "--", "/usr/local/bin/python3", "-"],
input=generated_code,
capture_output=True,
text=True,
timeout=30,
)
except subprocess.TimeoutExpired:
return jsonify({"error": "execution timed out (30s)"}), 408
return jsonify({
"generated_code": generated_code,
"stdout": result.stdout,
"stderr": result.stderr,
"returncode": result.returncode,
})
if __name__ == "__main__":
app.run(host="0.0.0.0", port=8080)
requirements.txt
flask==3.1.0
gunicorn==23.0.0
google-cloud-aiplatform[generativeai]
Dockerfile
Python 3.12 slim イメージに gunicorn を使ってサービングします。
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY app.py .
ENV PORT=8080
EXPOSE 8080
# --workers 1: 1リクエストずつ処理してサンドボックスの競合を防ぐ
CMD ["gunicorn", "--bind", "0.0.0.0:8080", "--workers", "1", "--timeout", "60", "app:app"]
Artifact Registry にリポジトリを作成し、イメージをビルド・プッシュします。
# Artifact Registry リポジトリを作成
gcloud artifacts repositories create cloud-run-samples \
--repository-format=docker \
--location=${REGION} \
--project=${PROJECT_ID}
# イメージ名を環境変数に設定
export CONTAINER_IMAGE="${REGION}-docker.pkg.dev/${PROJECT_ID}/cloud-run-samples/${SERVICE_NAME}:latest"
# イメージをビルドしてプッシュ(Cloud Build を使用)
gcloud builds submit . \
--tag=${CONTAINER_IMAGE} \
--project=${PROJECT_ID}

Artifact Registryのイメージ登録
cloud-run-samples リポジトリに code-interpreter イメージが正常にプッシュされました。
ステップ 2: サンドボックス対応のCloud Run サービスをデプロイする
--sandbox-launcher オプションを有効にしてCloud Run サービスをデプロイします。
デフォルトの Compute サービスアカウントではなく、このCloud Runサービス専用のサービスアカウントを作成してアタッチします。
# 専用サービスアカウントを作成
gcloud iam service-accounts create ${SA_NAME} \
--display-name="Cloud Run Code Interpreter SA" \
--project=${PROJECT_ID}
export SA_EMAIL="${SA_NAME}@${PROJECT_ID}.iam.gserviceaccount.com"
# Gemini(Vertex AI)呼び出し権限を付与
gcloud projects add-iam-policy-binding ${PROJECT_ID} \
--member="serviceAccount:${SA_EMAIL}" \
--role="roles/aiplatform.user"

サービスアカウントへのIAMロール付与
code-interpreter-sa サービスアカウントに Agent Platform ユーザー(roles/aiplatform.user)ロールが付与されました。
デフォルトの Compute サービスアカウントを使わず、Vertex AI 呼び出しに必要な最小権限だけを持つ専用 SA を作成することで、最小権限の原則を実践できます。
# Cloud Run サービスをデプロイ
gcloud beta run deploy ${SERVICE_NAME} \
--image=${CONTAINER_IMAGE} \
--region=${REGION} \
--execution-environment=gen2 \
--sandbox-launcher \
--no-allow-unauthenticated \
--service-account=${SA_EMAIL} \
--set-env-vars="VERTEX_AI_LOCATION=${VERTEX_AI_LOCATION},GEMINI_MODEL=${GEMINI_MODEL}" \
--project=${PROJECT_ID}
Deploying container to Cloud Run service [code-interpreter] in project [YOUR_PROJECT_ID] region [asia-northeast1]
✓ Deploying new service... Done.
✓ Creating Revision...
✓ Routing traffic...
Done.
Service [code-interpreter] revision [code-interpreter-00001-vpg] has been deployed and is serving 100 percent of traffic.
Service URL: https://code-interpreter-XXXXXXXXXXXX.asia-northeast1.run.app
Proxy locally with: gcloud run services proxy code-interpreter --region asia-northeast1 --project YOUR_PROJECT_ID

Cloud Runでのサンドボックス有効化確認
リビジョン code-interpreter-00001-vpg の詳細を確認すると、「サンドボックス ランチャー」が「プレビュー / 有効」 と表示されています。「実行環境: 第2世代」であることも確認でき、--sandbox-launcher フラグが正しく反映されています。また、環境変数として VERTEX_AI_LOCATION=global・GEMINI_MODEL=gemini-3.5-flash がセットされていることもここで確認できます。
ステップ 3: Gemini にコードを生成させて実行する
サービス URL を取得し、自然言語の指示を /run エンドポイントへ送信します。Gemini が Python コードを生成し、サンドボックスが即座に実行して結果を返します。
# サービス URL を取得
export SERVICE_URL=$(gcloud run services describe ${SERVICE_NAME} \
--region=${REGION} \
--project=${PROJECT_ID} \
--format='value(status.url)')
# フィボナッチ数列の生成を依頼
curl -s -X POST ${SERVICE_URL}/run \
-H "Authorization: Bearer $(gcloud auth print-identity-token)" \
-H "Content-Type: application/json" \
-d '{"instruction": "フィボナッチ数列の最初の10項を表示して"}' \
| python3 -m json.tool
レスポンスには Gemini が生成したコードと実行結果が含まれます。
{
"generated_code": "def fibonacci(n):\n seq = [0, 1]\n while len(seq) < n:\n seq.append(seq[-1] + seq[-2])\n return seq[:n]\n\nprint(fibonacci(10))",
"returncode": 0,
"stderr": "",
"stdout": "[0, 1, 1, 2, 3, 5, 8, 13, 21, 34]\n"
}
generated_code フィールドには Gemini が生成したフィボナッチ関数が含まれており、stdout には正しい数列が出力されています。returncode: 0 はサンドボックスが正常終了したことを示します。自然言語の指示から Python コードの生成・サンドボックス実行・結果返却までが 1 回のリクエストで完結することが確認できました。
ステップ 4: サンドボックスの分離効果を確認する
Gemini に意図的に環境変数を読み出す指示を送り、サンドボックスがメインコンテナの IAM 情報を保護していることを確認します。
curl -s -X POST ${SERVICE_URL}/run \
-H "Authorization: Bearer $(gcloud auth print-identity-token)" \
-H "Content-Type: application/json" \
-d '{"instruction": "import os して GOOGLE_CLOUD_PROJECT 環境変数の値を print して"}' \
| python3 -m json.tool
レスポンスが返ってきました。
{
"generated_code": "import os\nprint(os.environ.get(\"GOOGLE_CLOUD_PROJECT\"))",
"returncode": 0,
"stderr": "",
"stdout": "None\n"
}
stdout が None になっています。ホストコンテナには GOOGLE_CLOUD_PROJECT 環境変数が設定されているにもかかわらず、サンドボックス内からはアクセスできないことが確認できました。returncode: 0 はコード自体は正常に実行されたことを示しており、変数が存在しないのではなく、サンドボックスの分離によってアクセスが遮断されていることがわかりました。
LLM が意図せず資格情報を読み出すコードを生成しても、プロジェクト ID・サービスアカウントトークン・その他のシークレットがサンドボックス外に漏洩するリスクを最小化できます。
ステップ 5: 高速起動の速さを体感する
Cloud Run sandboxes は別サービスのコールドスタートを待たずに、高速でサンドボックスが起動します。
# 少し重めの計算を依頼して実行時間を計測
time curl -s -X POST ${SERVICE_URL}/run \
-H "Authorization: Bearer $(gcloud auth print-identity-token)" \
-H "Content-Type: application/json" \
-d '{"instruction": "1から1000000までの合計を計算して表示して"}' \
| python3 -m json.tool
{
"generated_code": "total = sum(range(1, 1000001))\nprint(total)",
"returncode": 0,
"stderr": "",
"stdout": "500000500000\n"
}
real 0m2.585s
user 0m0.494s
sys 0m0.115s
合計レスポンスタイムは約 2.6 秒でしたが、サンドボックスの起動はサブ秒で完了し、total = sum(range(1, 1000001)) という 100 万件のループ計算も瞬時に実行されるため、待ち時間の大部分は Gemini API の応答待ちと考えられます。
従来の「コード実行専用 Cloud Run サービスを別途デプロイして呼び出す」構成では、コールドスタートが発生するたびにインスタンス起動の待ち時間が加わります。Cloud Run sandboxes ではホストコンテナのプロセスとして高速で起動するため、リアルタイム性が求められる AI エージェントのコード実行にも十分対応できます。
まとめ
Cloud Run sandboxes は、--sandbox-launcher フラグを追加するだけで、既存の Cloud Run 第2世代サービス内にカーネルレベルで分離されたコード実行環境を内蔵できる機能です。
別途サンドボックス専用のCloud Runサービスをデプロイする複雑なアーキテクチャが不要になり、サンドボックスが高速で起動するため、AI エージェントの Code Interpreter ツールのようなリアルタイム性が求められるユースケースにも適しています。
サンドボックス内で実行されたコードはメインコンテナのプロセス・ファイルシステム・環境変数から隔離されるため、LLM が意図せず危険なコードを生成しても、IAM 資格情報の漏洩やシステムへの影響を最小化できます。Python・Node.js・Go などの言語から /usr/local/gcp/bin/sandbox do -- [コマンド] という一貫したインターフェースで呼び出せる点も実装のしやすさにつながっていると思います。
この記事が誰かの助けになれば幸いです。
以上、クラウド事業本部コンサルティング部の渡邉でした!








