Google Cloud Run デプロイ手順 - horihorikeitaa/suumo-scaper GitHub Wiki
Google Cloud Run へのデプロイ手順
このドキュメントでは、SUUMO Scrapper アプリケーションを Google Cloud Run にデプロイするための詳細な手順を説明します。
目次
準備作業
1. Google Cloud SDK のインストール
MacOS の場合:
brew install --cask google-cloud-sdk
インストール確認:
gcloud --version
2. Google Cloud へのログイン
gcloud auth login
3. プロジェクト ID の確認と設定
プロジェクト一覧を表示:
gcloud projects list
プロジェクトの設定:
# プロジェクトIDを設定
gcloud config set project suumo-scraper-460206
4. 必要な API の有効化
# 必要なAPIを有効化
gcloud services enable cloudbuild.googleapis.com run.googleapis.com artifactregistry.googleapis.com secretmanager.googleapis.com
認証情報の管理
Google Sheets にアクセスするための認証情報を安全に管理します。
1. Secret Manager での認証情報の管理
# Secret Managerにシークレットを作成
gcloud secrets create suumo-scraper-credentials \
--replication-policy="automatic"
# 認証情報JSONファイルの内容をシークレットの新しいバージョンとして追加
gcloud secrets versions add suumo-scraper-credentials \
--data-file="suumo-scraper-460206-6734b711c3fa.json"
2. サービスアカウントの作成と権限設定
# サービスアカウントを作成
gcloud iam service-accounts create suumo-scraper-sa \
--display-name="SUUMO Scraper Service Account"
# プロジェクトIDを変数に設定
PROJECT_ID="suumo-scraper-460206"
# サービスアカウントに必要な権限を付与
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:suumo-scraper-sa@$PROJECT_ID.iam.gserviceaccount.com" \
--role="roles/run.invoker"
# シークレットへのアクセス権を付与
gcloud secrets add-iam-policy-binding suumo-scraper-credentials \
--member="serviceAccount:suumo-scraper-sa@$PROJECT_ID.iam.gserviceaccount.com" \
--role="roles/secretmanager.secretAccessor"
アプリケーションの設定
1. Cloud Functions 用のエントリーポイントの作成
src/suumo_scraper/cloud_function.py
ファイルを作成し、以下の内容を追加:
import json
import functions_framework
from flask import jsonify
from src.suumo_scraper.main import update_suumo_sheet
from src.suumo_scraper import config
@functions_framework.http
def suumo_scraper(request):
"""
Google Cloud Functionsのエントリーポイント
HTTPリクエストを受け取り、SUUMOスクレイピングを実行する
GASからリクエストが送信されることを想定
"""
# CORSヘッダーを設定
headers = {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'POST, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type',
'Access-Control-Max-Age': '3600'
}
# OPTIONSリクエスト(プリフライトリクエスト)への対応
if request.method == 'OPTIONS':
return ('', 204, headers)
# POSTリクエスト以外は拒否
if request.method != 'POST':
return (jsonify({'error': 'Method not allowed'}), 405, headers)
try:
# リクエストのJSONデータを取得
request_json = request.get_json(silent=True)
if not request_json:
return (jsonify({'error': 'No JSON data provided'}), 400, headers)
# パラメータの取得
mode = request_json.get('mode', config.MODE_NEW_ONLY)
url = request_json.get('url', None)
# モードのバリデーション
if mode not in [config.MODE_NEW_ONLY, config.MODE_FULL_UPDATE]:
return (jsonify({'error': f'Invalid mode: {mode}'}), 400, headers)
# スクレイピング実行
result = update_suumo_sheet(update_mode=mode, new_url=url)
# 結果を返す
return (jsonify(result), 200, headers)
except Exception as e:
# エラーハンドリング
error_response = {
'status': 'error',
'error_message': str(e)
}
return (jsonify(error_response), 500, headers)
2. 認証情報の取得部分の修正
src/suumo_scraper/sheets/connection.py
ファイルの setup_sheet_connection
関数を修正:
def setup_sheet_connection():
"""
Google Spreadsheetsに接続するための関数
ローカルファイルまたは環境変数から認証情報を取得
"""
try:
# 環境変数から認証情報を取得する方法を追加
if os.environ.get('GOOGLE_APPLICATION_CREDENTIALS_JSON'):
# 環境変数からJSON文字列を取得してJSONオブジェクトに変換
creds_json = json.loads(os.environ['GOOGLE_APPLICATION_CREDENTIALS_JSON'])
creds = Credentials.from_service_account_info(creds_json, scopes=config.SCOPES)
logging.info("環境変数から認証情報を取得しました")
elif os.path.exists(config.CREDS_FILE_PATH):
# ローカルファイルから認証情報を取得
creds = Credentials.from_service_account_file(
config.CREDS_FILE_PATH, scopes=config.SCOPES
)
logging.info(f"ファイルから認証情報を取得しました: {config.CREDS_FILE_PATH}")
else:
# デフォルトの認証情報(Cloud Run上で使用するアプリケーションデフォルト認証情報)
try:
# このパスはCloud Runでマウントされた認証情報
creds = Credentials.from_service_account_file(
"/secrets/credentials.json", scopes=config.SCOPES
)
logging.info("Cloud Runのマウントされた認証情報を使用します")
except FileNotFoundError:
# アプリケーションデフォルト認証情報を試す
import google.auth
creds, _ = google.auth.default(scopes=config.SCOPES)
logging.info("アプリケーションデフォルト認証情報を使用します")
3. Dockerfile の作成
プロジェクトルートに以下の内容で Dockerfile
を作成:
FROM python:3.11-slim
WORKDIR /app
# アプリケーションコードとセットアップをコピー
COPY src/ ./src/
COPY setup.py ./
# setup.pyから依存関係をインストール
RUN pip install --no-cache-dir -e .
# functions-frameworkを明示的にインストール
RUN pip install --no-cache-dir functions-framework
# 環境変数の設定
ENV PYTHONPATH=/app
ENV PORT=8080
# Cloud Functionsのエントリーポイントを指定
CMD exec functions-framework --target=suumo_scraper --source=src/suumo_scraper/cloud_function.py --port=$PORT
4. .dockerignore ファイルの作成
認証情報などをコンテナに含めないよう、.dockerignore
ファイルを作成:
# 認証情報ファイルを除外
*.json
!package.json
!patterns.json
# Git関連
.git/
.gitignore
# Python関連
__pycache__/
*.py[cod]
*$py.class
*.so
.Python
env/
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
.installed.cfg
*.egg
# テスト関連
tests/
.pytest_cache/
# VSCode設定
.vscode/
# データディレクトリ
data/
# その他
*.log
*.bak
.DS_Store
コンテナのビルドとデプロイ
1. コンテナのビルド
# プロジェクトIDを変数に設定
PROJECT_ID="suumo-scraper-460206"
# コンテナをビルド
gcloud builds submit --tag gcr.io/$PROJECT_ID/suumo-scraper
2. Cloud Run へのデプロイ
# Cloud Runにデプロイ
gcloud run deploy suumo-scraper \
--image gcr.io/$PROJECT_ID/suumo-scraper \
--platform managed \
--region asia-northeast1 \
--service-account="suumo-scraper-sa@$PROJECT_ID.iam.gserviceaccount.com" \
--allow-unauthenticated \
--set-secrets="/secrets/credentials.json=suumo-scraper-credentials:latest"
デプロイ完了後、サービス URL が表示されます。この URL を控えておいてください。
3. デプロイの確認
# デプロイされたサービスの確認
gcloud run services list
# デプロイされたサービスの詳細を確認
gcloud run services describe suumo-scraper
Google Apps Script との連携
Google Apps スクリプトから、Cloud Run 上のアプリケーションを呼び出します。
GAS スクリプトの例
function callSuumoScraper(mode) {
// Cloud RunのURL(デプロイ時に表示されたURL)
const url = "https://suumo-scraper-xxx-an.a.run.app"; // ← 実際のURLに置き換える
const options = {
method: "post",
contentType: "application/json",
payload: JSON.stringify({
mode: mode, // 'new_only' または 'full_update'
}),
muteHttpExceptions: true,
};
try {
const response = UrlFetchApp.fetch(url, options);
const result = JSON.parse(response.getContentText());
return result;
} catch (e) {
console.error("エラーが発生しました: " + e.toString());
return { status: "error", error_message: e.toString() };
}
}
// 新規物件追加ボタン用の関数
function addNewProperties() {
return callSuumoScraper("new_only");
}
// 全物件更新ボタン用の関数
function updateAllProperties() {
return callSuumoScraper("full_update");
}
トラブルシューティング
サービスアカウントが存在しないエラー
エラーメッセージ:
ERROR: Service account XXX does not exist.
対応策:
- サービスアカウントを作成
- 必要な権限を付与
Pipenv でのインストールエラー
エラーメッセージ:
ERROR:: --system is intended to be used for Pipfile installation, not installation of specific packages. Aborting.
対応策:
- Dockerfile を修正し、setup.py を使用して依存関係をインストール
- または、pipenv コマンドを修正
認証情報に関するエラー
対応策:
- Secret Manager に認証情報が正しく保存されているか確認
- サービスアカウントに適切な権限が与えられているか確認
- マウントパスが正しいか確認