Cloud Runでネットワーク可視化アプリを作った。初心者でも分かる設定のコツ
Google Cloud Runを使って、アクセス元の位置情報を地図で表示するアプリを作りました。Cloud Runの動作を理解するための学習プロジェクトです。
最初は何も分からなかった。ポート待ち受けエラーが出て、テンプレートファイルが見つからなくて、何度もデプロイに失敗した。でも、試行錯誤を重ねて、ようやく動くようになった。
この記事では、Cloud Runの基本的な設定方法から、実際に動かすまでの手順を詳しく書いています。初心者の方でも、同じようにアプリをデプロイできるように、エラーとその解決方法も含めてまとめました。
2026年1月時点の最新情報を含む: この記事には、2026年1月時点でのCloud Runの最新機能(IPv6対応、ソースからの直接デプロイ、無料枠の詳細など)も含まれています。
作ったもの
アクセスした瞬間に、その人のIPアドレスと位置情報を取得して、サイバーパンク風のUIで表示するアプリです。地図上にピンを立てて、接続元の場所を可視化します。
Cloud Runとは何か
Cloud Runは、Google Cloudが提供するサーバーレス(サーバーを意識しなくて良い)の実行環境です。コードをアップロードすると、自動的にサーバーを用意して、リクエストに応じて実行してくれます。
従来のサーバー運用と違うのは、使った分だけ課金される点です。アクセスがない時は、インスタンスが0になり、料金もかかりません。
⚠️ 重要:Cloud RunとCloud Functions(現:Cloud Run functions)の違い
ℹ️
INFO
2024年8月、Googleは「Cloud Functions(2nd gen)」を「Cloud Run functions」に名称変更・統合しました。現在、「Cloud Functions」という独立したサービスは存在しません。すべて「Cloud Run functions」としてCloud Runプラットフォーム上で動作します。
この記事で使っているのは「Cloud Run」です
Cloud Runの設定画面には「Functions」というデプロイオプションがあります。これは「Cloud Run functions」とは別物です。このオプションは、関数をエントリーポイントとして指定するCloud Runの機能の1つです。
使用していない
Cloud Run functions(2024年8月に名称変更)
用途: コンテナをデプロイしてWebアプリやAPIを実行
- Cloud Run: サーバーレスコンテナ実行基盤(この記事で使用)
- Cloud Run functions: 旧Cloud Functions(2nd gen)が統合されたもの。イベント駆動型の関数実行に特化
- Cloud Run functions(1st gen): 旧Cloud Functions(1st gen)。制限が多いが、まだサポートされている
- Cloud Runの「Functions」オプション: Cloud Runのデプロイ方法の1つ。関数をエントリーポイントとして指定(この記事で使用)
無料枠の詳細(2026年1月時点)
Cloud Runには無料枠(Always Free)があります。
- CPU: 月180,000 vCPU-秒(約50時間分)
- メモリ: 360,000 GiB-秒
- リクエスト数: 月200万リクエスト
学習用途なら、この無料枠内で十分に使えます。無料枠を超えた場合でも、従量課金なので、使った分だけ支払えば良いです。
なぜCloud Runを選んだか
サーバーサイドで高度処理を行うためには費用と学習コストが悩みの種です。普通に考えて、これ以上の出費はもう無理。月額料金がかかるし、使わない時もお金がかかる。貧乏なので、できるだけコストを抑えたかった。
Cloud Runなら、使った分だけ課金される。無料枠もあって、月に200万リクエストまで無料。学習用途なら十分すぎる。それに、サーバーの管理が不要で、コードを書くことに集中できる。
プロジェクトの構成
最終的に、1ファイル完結型の構成に落ち着きました。main.pyに全てのコードを書いて、requirements.txtで依存関係を指定するだけです。
ファイル構成
midori_new017/
├── main.py # メインのコード(81行)
└── requirements.txt # 依存ライブラリ(3行)
シンプルな構成にした理由は、エラーを減らすためです。
ファイルが多すぎると、クラウド上でのパス解決が複雑になって、テンプレートファイルが見つからなくなることがありました。
なので、なるべく単一ファイルで済むように制作をしました。HTMLコードが直に埋め込んであるのですが、分量的には適正範囲におさまっております。
コードの全体像
メインの処理は、network_monitorという関数にまとめています。Cloud Runの「Functions」デプロイオプションでは、この関数がエントリーポイント(プログラムの入り口)になります。
エントリーポイント関数とは
Cloud Runには複数のデプロイ方法があります。この記事では「Functions」オプションを使っています。
この方法では、HTTPリクエストを受け取る関数を1つ定義します。この関数が「エントリーポイント」です。
⚠️
WARNING
Cloud Runの「Functions」オプションと、「Cloud Run functions」(旧Cloud Functions)は名前が似ていますが、全く別物です。この記事ではCloud RunのFunctionsオプションを使っています。
エントリーポイント関数の役割:
- 関数名は任意(このプロジェクトでは
network_monitor)
- Cloud Runの設定画面で「関数のエントリーポイント」として指定
requestオブジェクトを引数として受け取る
- HTTPレスポンス(HTMLやJSONなど)を返す
def network_monitor(request):
# 1. アクセス元のIPアドレスを取得
user_ip = request.headers.get('X-Forwarded-For', request.remote_addr)
# 2. 位置情報を取得(ip-api.comを使用)
location_data = {}
if user_ip not in ['127.0.0.1', 'localhost', '::1']:
try:
geo_response = requests.get(f"{GEO_API_URL}{user_ip}?fields=...", timeout=5)
location_data = geo_response.json()
except Exception:
location_data = {"status": "fail"}
# 3. サーバーのリージョン情報を取得
cloud_run_region = os.environ.get('K_SERVICE_REGION', 'asia-northeast1')
# 4. HTMLテンプレートを文字列として定義
html_template = """..."""
# 5. データを埋め込んで返却
return render_template_string(html_template, ...)
この関数は、HTTPリクエストを受け取って、HTMLを返すだけのシンプルな構造です。
試行錯誤の過程
最初からうまくいったわけではありません。3つの大きなエラーに遭遇して、それぞれ解決方法を見つけました。
エラー1: ポート待ち受けエラー
最初に試したのは、普通のFlaskアプリのようにapp.run()でサーバーを起動する方法でした。
app = Flask(__name__)
@app.route('/')
def index():
return "Hello"
if __name__ == '__main__':
app.run(port=8080) # ← これがエラーの原因
Cloud Runにデプロイすると、PORT=8080というエラーが出ました。
原因を調べると、Cloud Runの「Functions」デプロイオプションでは、Flaskのサーバーを直接起動するのではなく、関数をエントリーポイントとして指定する必要があることが分かりました。
解決方法: app.run()を削除して、関数を直接返す形に変更しました。
Cloud Runには主に2つのデプロイ方法があります。
1. Cloud Runの「Functions」オプション(この記事で使用)
- 関数をエントリーポイントとして指定
- app.run()は不要
- 設定画面で「Functions」を選択し、「インライン エディタで関数を作成する」を選ぶ
2. Cloud Runサービス(コンテナ)
- 通常のFlaskアプリとして動作
- app.run(host='0.0.0.0', port=8080)が必要
- Dockerfileやコンテナイメージを使用
注意: Cloud Runの「Functions」オプションは、「Cloud Run functions」(旧Cloud Functions)とは異なります。この記事では、Cloud RunのFunctionsオプションを使った方法を説明しています。
def network_monitor(request):
# リクエストを処理してHTMLを返す
return render_template_string(html_template, ...)
Cloud Runが、この関数を自動的に呼び出してくれます。
エラー2: テンプレートファイルが見つからない
最初は、HTMLを別ファイル(index.html)に分けて、render_template()で読み込もうとしました。
return render_template('index.html', user_ip=user_ip, ...)
しかし、デプロイ後にTemplateNotFoundエラーが出ました。クラウド上でのファイルパス解決が複雑で、テンプレートファイルが見つからなかったのです。
解決方法: HTMLをmain.py内に文字列として直接書き込む方法に変更しました。
html_template = """
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>NETWORK VISUALIZER</title>
...
</head>
<body>
...
</body>
</html>
"""
return render_template_string(html_template, user_ip=user_ip, ...)
render_template_string()を使うことで、ファイルを読み込まずに、文字列から直接テンプレートを生成できます。
エラー3: 依存ライブラリのバージョン指定
最初は、requirements.txtにバージョンを指定していませんでした。
Flask
requests
beautifulsoup4
デプロイ時に、最新バージョンが自動的にインストールされるのですが、バージョンによって動作が変わる可能性があります。
解決方法: 明示的にバージョンを指定しました。
Flask==3.0.0
requests==2.31.0
beautifulsoup4==4.12.2
これで、どの環境でも同じバージョンが使われるようになりました。
Cloud Runの設定手順
Google Cloud Consoleから、Cloud Runサービスを作成する手順を詳しく説明します。
ステップ1: サービスの作成
- Google Cloud Consoleにログイン
- 「Cloud Run」を選択
- 「サービスの作成」をクリック
ステップ2: ソースコードの指定
Cloud Runの設定画面で「Functions」を選択して、「インライン エディタで関数を作成する」を選びます。
⚠️
WARNING
ここで選択する「Functions」は、Cloud Runのデプロイオプションの1つです。「Cloud Run functions」(旧Cloud Functions)とは異なります。混同しないよう注意してください。
Cloud RunのFunctionsオプションを選択。Cloud Functionsとは別サービス
ステップ3: 基本設定
サービスの名前: my-cyberpunk-app(任意の名前でOK)
リージョン: asia-northeast1(東京リージョン。近いリージョンを選ぶと、レスポンスが速い)
ランタイム: Python 3.14(2026年1月時点の最新版。Python 3.13もGA(一般提供)で利用可能)
📝
NOTE
ランタイムの選択について ランタイムには「GA(General Availability)」と「プレビュー」があります。
- GA: 本番環境でも安定して使える
- プレビュー: 将来変更される可能性がある
学習用途ならGAを選ぶのが安全です。
サービスの作成画面。Functions、Python 3.14、公開アクセスを選択
ステップ4: 認証設定
公開アクセスを許可するを選択します。これで、URLを知っている人なら誰でもアクセスできます。
認証が必要な場合は、「認証が必要」を選びますが、学習用途なら公開アクセスで問題ありません。
⚠️
WARNING
セキュリティ注意点 公開アクセスを許可すると、URLを知っている人なら誰でもアクセスできます。
APIキーや機密情報を含むアプリの場合は、「認証が必要」を選択し、IAMロール(roles/run.invoker)を適切に設定することをおすすめします。
ステップ5: スケーリング設定
自動スケーリングを選択します。
- インスタンスの最小数:
0(アクセスがない時は0円)
- インスタンスの最大数:
1(コスト抑制のため。アクセス集中を防ぐ)
最大インスタンス数を1にすることで、予期しない高額請求を防げます。
スケーリング設定。最小0、最大1、リクエストベース課金を選択
ステップ6: 課金設定
リクエストベースを選択します。リクエストの処理時にのみ課金され、アクセスがない時はCPUが制限されて、料金もかかりません。
ステップ7: コードの入力
インラインエディタに、main.pyの内容を貼り付けます。requirements.txtの内容も、別途指定する必要があります。
2026年1月時点で、ソースコードから直接デプロイする方法(gcloud run deploy --source)も利用できます。
特徴:
- Dockerfileがなくてもデプロイ可能
- Cloud BuildやBuildpacksが自動的にイメージを作成
ただし、この記事では初心者向けにインラインエディタを使った方法を説明しています。
重要な設定項目の詳細
Cloud Runのリビジョン設定画面では、詳細な設定項目を確認できます。
リビジョン設定画面。同時実行数、タイムアウト、実行環境などを設定
同時実行数
インスタンスあたりの最大同時リクエスト数: 10
1つのインスタンスで、同時に10個のリクエストを処理できます。この値を大きくすると、インスタンス数が減ってコストが抑えられますが、レスポンスが遅くなる可能性があります。
学習用途なら、デフォルトの10で問題ありません。
リクエストのタイムアウト
リクエストのタイムアウト: 300秒(5分)
1つのリクエストが処理できる最大時間です。位置情報の取得に時間がかかる場合があるので、300秒に設定しています。
通常のWebアプリなら、60秒程度で十分です。
実行環境
実行環境: デフォルト(または第1世代、第2世代)
- デフォルト: Cloud Runが適切な実行環境を自動選択
- 第1世代: コールドスタート(初回起動)が高速
- 第2世代: ネットワークファイルシステムのサポート、Linuxとの完全な互換性、CPUとネットワークパフォーマンスの向上
学習用途なら、デフォルトで問題ありません。第2世代を選ぶと、より高度な機能が使えますが、コールドスタートが少し遅くなる可能性があります。
最小インスタンス数とアイドル料金
最小インスタンス数を0より大きく設定すると、常にインスタンスが起動している状態になります。これにより、コールドスタート(初回起動)の遅延がなくなりますが、アクセスがない時でも料金がかかります。
学習用途なら、最小インスタンス数は0のままで問題ありません。無料枠を超える可能性があるので注意が必要です。
CPU割り当て
CPU割り当て: リクエスト中のみ
アクセスがない時は、CPUが割り当てられず、料金もかかりません。常時起動が必要な場合は、「常時割り当て」を選びますが、料金がかかります。
学習用途なら、「リクエスト中のみ」で十分です。
ソースコードのダウンロード
Cloud Runのソースタブでは、デプロイしたコードを確認できます。
ソースタブでmain.pyとrequirements.txtを確認。エントリーポイントはnetwork_monitor
このプロジェクトのソースコードは、以下の2つのファイルで構成されています。
main.py(81行)
メインの処理を書いたファイルです。IPアドレスの取得、位置情報の取得、HTMLの生成を行います。
import os
import requests
from flask import render_template_string
# ジオロケーションAPI
GEO_API_URL = "http://ip-api.com/json/"
def network_monitor(request):
"""
Cloud RunのFunctionsオプション用のエントリーポイント関数
注意: Cloud Functions(別サービス)とは異なります
"""
# 1. アクセス元の情報を取得
user_ip = request.headers.get('X-Forwarded-For', request.remote_addr)
user_agent = request.headers.get('User-Agent', 'N/A')
# 2. 位置情報を取得
location_data = {}
if user_ip not in ['127.0.0.1', 'localhost', '::1']:
try:
geo_response = requests.get(
f"{GEO_API_URL}{user_ip}?fields=status,message,country,countryCode,region,regionName,city,lat,lon,timezone,isp,org,as,query",
timeout=5
)
location_data = geo_response.json()
except Exception:
# エラー時は空のデータを返す(アプリがクラッシュしないように)
location_data = {"status": "fail"}
# 3. サーバー情報を取得
cloud_run_region = os.environ.get('K_SERVICE_REGION', 'asia-northeast1')
# 4. サイバーパンクHTMLを定義 (直接埋め込み)
html_template = """
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>NETWORK VISUALIZER // CYBERPUNK</title>
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" />
<style>
@import url('https://fonts.googleapis.com/css2?family=VT323&display=swap');
body { background-color: #000; color: #0f0; font-family: 'VT323', monospace; margin: 0; padding: 20px; text-shadow: 0 0 5px #0f0; }
.container { border: 2px solid #0ff; box-shadow: 0 0 20px #0ff; padding: 20px; border-radius: 10px; background: rgba(20,0,20,0.8); }
h1 { color: #f0f; text-align: center; text-transform: uppercase; letter-spacing: 5px; }
.grid { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; margin: 20px 0; }
.card { border: 1px solid #0ff; padding: 10px; background: #000; }
#map { height: 400px; width: 100%; border: 1px solid #0ff; box-shadow: 0 0 10px #0ff; }
.label { color: #ff0; }
</style>
</head>
<body>
<div class="container">
<h1>// Connection Established //</h1>
<div class="grid">
<div class="card">
<p><span class="label">IP_ADDRESS:</span> {{ user_ip }}</p>
<p><span class="label">LOCATION:</span> {{ location.city or 'UNKNOWN' }}, {{ location.country or 'UNKNOWN' }}</p>
</div>
<div class="card">
<p><span class="label">REGION:</span> {{ region }}</p>
<p><span class="label">STATUS:</span> <span style="color:red; animation: blink 1s infinite;">ACTIVE</span></p>
</div>
</div>
<div id="map"></div>
</div>
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
<script>
var lat = {{ location.lat or 35.68 }};
var lon = {{ location.lon or 139.76 }};
var map = L.map('map').setView([lat, lon], 10);
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png').addTo(map);
L.marker([lat, lon]).addTo(map).bindPopup("TARGET LOCATED").openPopup();
setInterval(() => { document.body.style.opacity = Math.random() * (1 - 0.95) + 0.95; }, 50);
</script>
</body>
</html>
"""
# 5. データを流し込んで返却
return render_template_string(
html_template,
user_ip=user_ip,
location=location_data,
region=cloud_run_region
)
requirements.txt(3行)
依存ライブラリを指定するファイルです。
Flask==3.0.0
requests==2.31.0
beautifulsoup4==4.12.2
ダウンロード方法:
- この記事の最後にある「ソースコード」セクションから、各ファイルの内容をコピー
- ローカルに
main.pyとrequirements.txtを作成
- 内容を貼り付けて保存
または、GitHubなどに公開している場合は、リポジトリからクローンできます。
デプロイ後の動作確認
デプロイが完了すると、Cloud RunからURLが発行されます。
https://my-cyberpunk-app-999880815400.asia-northeast1.run.app
このURLにアクセスすると、サイバーパンク風のUIが表示されます。
デプロイ後の動作確認。IPv6アドレス、位置情報、地図表示が正常に動作
確認できた項目:
- IPv6アドレスの表示
- 接続拠点(例:門前仲町)の特定と地図表示
- サイバーパンク風のネオンカラーデザイン
地図上にピンが表示され、接続元の位置が分かります。
Cloud Runの指標画面では、リクエスト数やレイテンシを確認できます。
指標画面。リクエスト数とレイテンシを確認
リクエスト数の詳細。8回のリクエスト、0.004/sのレート
リクエストのレイテンシ指標。レスポンス時間を確認
コスト管理のコツ
Cloud Runは使った分だけ課金されますが、設定を間違えると予期しない高額請求になる可能性があります。
推奨設定
- 最大インスタンス数:
1(アクセス集中による高額課金を物理的に防ぐ)
- CPU割り当て:
リクエスト中のみ(アクセスがない時は0円)
- 認証:
未認証を許可(URLを知っていれば誰でも見られる状態)
最大インスタンス数を1にすることで、同時に処理できるリクエスト数が制限され、予期しない高額請求を防げます。
無料枠の確認(2026年1月時点)
Cloud Runの無料枠(Always Free)は以下の通りです。
- CPU: 月180,000 vCPU-秒(約50時間分)
- メモリ: 360,000 GiB-秒
- リクエスト数: 月200万リクエスト
この無料枠を超えた場合の料金例(参考):
- 1 vCPU・512 MiB・最大同時処理数20・リクエスト平均レイテンシ400msの設定で、1,000万リクエスト/月を処理する場合、約$13.69/月(無料枠込み)
学習用途なら、無料枠内で十分に使えます。最大インスタンス数を1に設定することで、予期しない高額請求を防げます。
コスト最適化のためのRecommender機能
Google Cloudには、Cloud Runのコスト削減やセキュリティ向上を提案する「Recommender」機能があります。
提案される内容の例:
- リクエストベース請求からインスタンス常時割り当てへの変更提案
- サービスアカウントの権限最小化
- その他の最適化提案
定期的に確認すると、コストを最適化できます。
まとめ
Cloud Runを使って、ネットワーク可視化アプリを作りました。試行錯誤を重ねて、ようやく動くようになった。
学んだこと:
- Cloud Runの「Functions」オプションでは、関数をエントリーポイントとして指定する(Cloud Functionsとは別物)
- テンプレートファイルは、文字列として直接埋め込む方が確実
- 依存ライブラリのバージョンは明示的に指定する
- 最大インスタンス数を1にすることで、コストを抑制できる
初心者の方でも、この手順に従えば、同じようにアプリをデプロイできるはずです。
最新機能と今後の展開(2026年1月時点)
Cloud Runには、以下のような最新機能があります。
IPv6対応(GA)
2025年11月に、Cloud RunがIPv6対応(GA)になりました。
主な機能:
- VPCネットワークに対してIPv4・内部IPv6デュアルスタックサブネットを使用
- 外部IPv6通信も可能
注意: すべてのリージョンで即座に利用可能とは限らないので、ドキュメントで確認が必要です。
ソースからの直接デプロイ
gcloud run deploy --sourceコマンドを使えば、Dockerfileがなくても、ソースコードから直接デプロイできます。
特徴:
- Cloud BuildやBuildpacksが自動的にイメージを作成
- 対応言語:Go、Node.js、Python、Java、.NET、Ruby、PHPなど
Worker Pools(プレビュー)
複数のビルドを並列化したり、ビルド用のリソースを分けて管理できる「Worker Pools」機能がプレビューで利用可能です。大規模なプロジェクトで便利です。
よくある質問と注意点
Q: Cloud RunとCloud Functionsの違いは?
ℹ️
INFO
2024年8月に、Cloud Functions(2nd gen)は「Cloud Run functions」に名称変更・統合されました。現在、「Cloud Functions」という独立したサービスは存在しません。
各サービスの特徴:
- Cloud Run: サーバーレスコンテナ実行基盤。コンテナをデプロイしてWebアプリやAPIを実行。この記事で使用。
- Cloud Run functions: 旧Cloud Functions(2nd gen)が統合されたもの。イベント駆動型の関数実行に特化。HTTPトリガーで最大60分、イベントトリガーで最大9分。
- Cloud Run functions(1st gen): 旧Cloud Functions(1st gen)。制限が多いが、まだサポートされている。
この記事で使っているのは「Cloud Run」です
Cloud Runの設定画面にある「Functions」オプションは、Cloud Run functionsとは別物です。関数をエントリーポイントとして指定するCloud Runの機能の1つです。
Q: ランタイムの「GA」と「プレビュー」の違いは?
A: GA(General Availability)は本番環境で安定して使えるバージョンです。プレビューは新しい機能やランタイムで、将来変更される可能性があります。学習用途ならGAを選ぶのが安全です。
Q: 最小インスタンス数を1にすると無料枠を超える?
A: 最小インスタンス数を1にすると、アクセスがない時でもインスタンスが起動しているため、アイドル時間にも料金が発生します。無料枠を超える可能性があるので、学習用途なら0のままで問題ありません。
Q: 最大インスタンス数を設定しないとどうなる?
A: 最大インスタンス数を設定しないと、トラフィックが急増した時に無制限にスケールアウトする可能性があります。予期しない高額請求を防ぐため、必ず最大インスタンス数を設定することをおすすめします。
Q: IPv6で接続できない場合は?
A: DNSでAAAAレコードが返ってきても、Cloud Runのサービス自体でIPv6のインバウンドを受け付けていないことがあります。アプリのリスニングアドレスが::やデュアルスタックでない等の設定不足が原因の可能性があります。
さらに深く学ぶには
同じようなことを試している方の参考になれば嬉しいです。