mcpField
D
django-cloud-sql-postgres

django-cloud-sql-postgres

github.com/jezwebクラウド・DevOps1.1k272 回インストール3 週間前に更新
リポジトリ

django-cloud-sql-postgres について

django-cloud-sql-postgres は、Django を Google Cloud SQL for PostgreSQL と App Engine で運用するための設定ガイドです。ローカルと本番の接続、マイグレーション、Gunicorn、一般的な問題の対処方法を開発者向けにまとめています。

主な機能

  1. 01Django と PostgreSQL の設定
  2. 02App Engine の Unix ソケット接続
  3. 03ローカルの Cloud SQL Auth Proxy
  4. 04データベース移行とデプロイ手順
  5. 05Gunicorn と静的ファイルの設定
  6. 06接続に関する一般的な問題の調査

活用シーン

  1. 01Django アプリを App Engine にデプロイする
  2. 02プロキシ経由でローカルから Cloud SQL に接続する
  3. 03本番データベースの認証情報を設定する
  4. 04接続上限や移行失敗を調査する

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/jezweb/claude-skills --skill django-cloud-sql-postgres

インストール後、Claude Code で/django-cloud-sql-postgresを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

README.md
.claude-plugin/
plugin.json
references/
cloud-sql-proxy-setup.md
iam-authentication.md
migrations-in-production.md
rules/
django-cloud-sql-postgres.md
templates/
app.yaml
requirements.txt
settings_cloudsql.py

詳細

Django on Google Cloud SQL PostgreSQL

ステータス:本番環境対応済み 最終更新日:2026-01-24 依存関係:なし 最新バージョン:[email protected]、[email protected]、[email protected]、[email protected]


クイックスタート(10 分)

1. 依存関係をインストールする
pip install Django psycopg2-binary gunicorn

Cloud SQL Python Connector を使用する場合(ローカル開発に推奨):

pip install "cloud-sql-python-connector[pg8000]"

重要な理由:

  • psycopg2-binary は Django 用の PostgreSQL アダプターです
  • App Engine Standard(Python 3.10 以降)には gunicorn が必要です
  • Cloud SQL Python Connector を使用すると、SSH トンネルなしで安全に接続できます
2. Django の設定を構成する

settings.py(Unix ソケットを使用する本番環境):

import os

# Detect App Engine environment
IS_APP_ENGINE = os.getenv('GAE_APPLICATION', None)

if IS_APP_ENGINE:
    # Production: Connect via Unix socket
    DATABASES = {
        'default': {
            'ENGINE': 'django.db.backends.postgresql',
            'NAME': os.environ['DB_NAME'],
            'USER': os.environ['DB_USER'],
            'PASSWORD': os.environ['DB_PASSWORD'],
            'HOST': f"/cloudsql/{os.environ['CLOUD_SQL_CONNECTION_NAME']}",
            'PORT': '',  # Empty for Unix socket
        }
    }
else:
    # Local development: Connect via Cloud SQL Proxy
    DATABASES = {
        'default': {
            'ENGINE': 'django.db.backends.postgresql',
            'NAME': os.environ.get('DB_NAME', 'mydb'),
            'USER': os.environ.get('DB_USER', 'postgres'),
            'PASSWORD': os.environ.get('DB_PASSWORD', ''),
            'HOST': '127.0.0.1',
            'PORT': '5432',
        }
    }

重要:

  • App Engine は /cloudsql/PROJECT:REGION:INSTANCE の Unix ソケット 経由で接続します
  • ローカル開発では、127.0.0.1:5432 で Cloud SQL Auth Proxy を実行する必要があります
  • 接続文字列をハードコードせず、環境変数を使用してください
3. app.yaml を作成する
runtime: python310
entrypoint: gunicorn -b :$PORT myproject.wsgi:application

env_variables:
  DB_NAME: "mydb"
  DB_USER: "postgres"
  CLOUD_SQL_CONNECTION_NAME: "project-id:region:instance-name"

# Cloud SQL connection
beta_settings:
  cloud_sql_instances: "project-id:region:instance-name"

handlers:
  - url: /static
    static_dir: static/
  - url: /.*
    script: auto
    secure: always

重要:

  • beta_settings.cloud_sql_instances によって /cloudsql/... の Unix ソケットが有効になります
  • DB_PASSWORD は app.yaml ではなく、gcloud app deploy または Secret Manager を使用して設定してください

6 ステップのセットアップ手順

ステップ 1:Cloud SQL インスタンスを作成する
# Create PostgreSQL instance
gcloud sql instances create myinstance \
  --database-version=POSTGRES_15 \
  --tier=db-f1-micro \
  --region=us-central1

# Create database
gcloud sql databases create mydb --instance=myinstance

# Create user
gcloud sql users create postgres \
  --instance=myinstance \
  --password=YOUR_SECURE_PASSWORD

要点:

  • 最高の互換性を得るには、POSTGRES_15 以降を使用してください
  • db-f1-micro は開発環境で最も安価です(月額 $7~10)。本番環境では db-g1-small 以上を使用してください
  • 接続名を控えておいてください:PROJECT_ID:REGION:INSTANCE_NAME

ステップ 2:Django プロジェクトを構成する

requirements.txt:

Django>=5.1,<6.0
psycopg2-binary>=2.9.9
gunicorn>=23.0.0
whitenoise>=6.7.0

settings.py への追加項目:

import os

# Security settings for production
DEBUG = os.environ.get('DEBUG', 'False') == 'True'
ALLOWED_HOSTS = [
    '.appspot.com',
    '.run.app',
    'localhost',
    '127.0.0.1',
]

# Static files with WhiteNoise
STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, 'static')
MIDDLEWARE.insert(1, 'whitenoise.middleware.WhiteNoiseMiddleware')
STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage'

# Database connection pooling
DATABASES['default']['CONN_MAX_AGE'] = 60  # Keep connections open for 60 seconds

これらの設定が必要な理由:

  • CONN_MAX_AGE=60 により接続のオーバーヘッドを削減できます(Cloud SQL には接続数の上限があります)
  • WhiteNoise を使うと、Cloud Storage なしで静的ファイルを配信できます
  • App Engine では ALLOWED_HOSTS に .appspot.com を含める必要があります

ステップ 3:Cloud SQL Proxy を使用してローカル開発環境をセットアップする

Cloud SQL Auth Proxy をインストールする:

# macOS
brew install cloud-sql-proxy

# Linux
curl -o cloud-sql-proxy https://storage.googleapis.com/cloud-sql-connectors/cloud-sql-proxy/v2.14.1/cloud-sql-proxy.linux.amd64
chmod +x cloud-sql-proxy

プロキシを実行する:

# Authenticate first
gcloud auth application-default login

# Start proxy (runs on 127.0.0.1:5432)
./cloud-sql-proxy PROJECT_ID:REGION:INSTANCE_NAME

# Or with specific port
./cloud-sql-proxy PROJECT_ID:REGION:INSTANCE_NAME --port=5432

ローカル開発用の環境変数を設定する:

export DB_NAME=mydb
export DB_USER=postgres
export DB_PASSWORD=your_password
export DEBUG=True

要点:

  • プロキシによって Cloud SQL への安全なトンネルが作成されます
  • IP アドレスを許可リストに追加する必要はありません
  • パスワード認証と IAM 認証のどちらでも使用できます

ステップ 4:マイグレーションを実行する
# Local (with proxy running)
python manage.py migrate

# Verify connection
python manage.py dbshell

本番環境のマイグレーション(Cloud Build 経由、またはプロキシを使ったローカル環境で実行):

# Option 1: Run locally with proxy
./cloud-sql-proxy PROJECT:REGION:INSTANCE &
python manage.py migrate

# Option 2: Use Cloud Build (recommended)
# See references/cloud-build-migrations.md

ステップ 5:Gunicorn を構成する

gunicorn.conf.py(任意。詳細な調整に使用):

import multiprocessing

# Workers
workers = 2  # App Engine Standard limits this
threads = 4
worker_class = 'gthread'

# Timeout (App Engine has 60s limit for standard, 3600s for flexible)
timeout = 55

# Logging
accesslog = '-'
errorlog = '-'
loglevel = 'info'

# Bind (App Engine sets $PORT)
bind = f"0.0.0.0:{os.environ.get('PORT', '8080')}"

app.yaml の entrypoint オプション:

# Simple (recommended for most cases)
entrypoint: gunicorn -b :$PORT myproject.wsgi:application

# With config file
entrypoint: gunicorn -c gunicorn.conf.py myproject.wsgi:application

# With workers and timeout
entrypoint: gunicorn -b :$PORT -w 2 -t 55 myproject.wsgi:application

要点:

  • App Engine Standard ではワーカー数に上限があります(F1:1 ワーカー、F2:2 ワーカー)
  • I/O バウンドの Django アプリには gthread ワーカークラスを使用してください
  • App Engine にリクエストを終了されないよう、タイムアウトを 60 秒未満に設定してください

ステップ 6:App Engine にデプロイする
# Collect static files
python manage.py collectstatic --noinput

# Deploy
gcloud app deploy app.yaml

# Set DB password via environment
gcloud app deploy --set-env-vars="DB_PASSWORD=your_secure_password"

# View logs
gcloud app logs tail -s default

デプロイを確認する:

# Open in browser
gcloud app browse

# Check database connection
gcloud app logs read --service=default | grep -i database

重要なルール(Django + Cloud SQL)

必ず実施すること:

  • App Engine では /cloudsql/PROJECT:REGION:INSTANCE の Unix ソケット パスを使用する
  • Unix ソケット接続では PORT=''(空文字列)を設定する
  • ローカル開発では Cloud SQL Auth Proxy を使用する
  • 接続プーリングのために CONN_MAX_AGE を設定する(30~60 秒)
  • データベースの認証情報には 環境変数 を使用する
  • 本番環境のパスワードには Secret Manager を使用する

絶対にしないこと:

  • データベースのパスワードを app.yaml に記載する(デプロイ時に Secret Manager または環境変数を使用する)
  • App Engine で HOST='localhost' を使用する(Unix ソケットパスを使用する必要があります)
  • app.yaml の beta_settings.cloud_sql_instances を設定し忘れる
  • CONN_MAX_AGE=None(無制限)を設定する(接続プールを使い果たす可能性があります)
  • Cloud SQL で SSL を無効にする(デフォルトで強制されています)

既知の問題の予防

このスキルでは、文書化されている 12 件の問題を予防します:

Issue #1:OperationalError - No such file or directory

エラー:django.db.utils.OperationalError: could not connect to server: No such file or directory 参照元:https://cloud.google.com/sql/docs/postgres/connect-app-engine-standard 発生する理由:app.yaml に beta_settings.cloud_sql_instances がないため、App Engine が Unix ソケットを見つけられません。 予防策:app.yaml に必ず beta_settings.cloud_sql_instances: "PROJECT:REGION:INSTANCE" を含めてください。

Issue #2:ローカル開発時の接続拒否

エラー:django.db.utils.OperationalError: connection refused または could not connect to server: Connection refused 参照元:https://cloud.google.com/sql/docs/postgres/connect-auth-proxy 発生する理由:Cloud SQL Auth Proxy が起動していないか、誤ったポートにバインドされています。 予防策:Django をローカルで実行する前に cloud-sql-proxy PROJECT:REGION:INSTANCE を起動してください。ポート 5432 で実行されていることを確認してください。

Issue #3:FATAL: password authentication failed

エラー:FATAL: password authentication failed for user "postgres" 参照元:https://cloud.google.com/sql/docs/postgres/create-manage-users 発生する理由:環境変数に設定されたパスワードが間違っているか、Cloud SQL インスタンスにユーザーが存在しません。 予防策:gcloud sql users list --instance=INSTANCE でパスワードを確認してください。必要に応じてリセットします:gcloud sql users set-password postgres --instance=INSTANCE --password=NEW_PASSWORD

Issue #4:接続数の上限超過

エラー:FATAL: too many connections for role "postgres" または remaining connection slots are reserved 参照元:https://cloud.google.com/sql/docs/postgres/quotas#connection_limits 発生する理由:各リクエストでプーリングなしに新しい接続を開くため、25~100 件の接続上限に達します。 予防策:Django の設定で CONN_MAX_AGE = 60 を設定してください。トラフィックが多い場合は、PgBouncer または django-db-connection-pool を使用してください。

Issue #5:App Engine のリクエストタイムアウト

エラー:DeadlineExceededError または 60 秒後にリクエストが終了します。 参照元:https://cloud.google.com/appengine/docs/standard/python3/how-requests-are-handled 発生する理由:データベースクエリまたはマイグレーションに、App Engine の 60 秒の上限を超える時間がかかります。 予防策:Gunicorn のタイムアウトを 55 秒に設定してください。長時間実行されるタスクには、Cloud Tasks または Cloud Run Jobs を使用してください。

Issue #6:本番環境で静的ファイルが 404 になる

エラー:静的ファイルが 404 を返し、CSS/JS が読み込まれません。 参照元:https://cloud.google.com/appengine/docs/standard/serving-static-files 発生する理由:app.yaml に static/ ハンドラーがないか、デプロイ前に collectstatic が実行されていません。 予防策:デプロイ前に python manage.py collectstatic --noinput を実行してください。app.yaml に静的ファイル用のハンドラーを含めてください。

Issue #7:CSRF 検証失敗

エラー:Forbidden (403) CSRF verification failed 参照元:CSRF に関する Django ドキュメント 発生する理由:appspot.com ドメインに対して CSRF_TRUSTED_ORIGINS が設定されていません。 予防策:settings.py に CSRF_TRUSTED_ORIGINS = ['https://*.appspot.com'] を追加してください。

Issue #8:デプロイ後にデータベースが見つからない

エラー:django.db.utils.OperationalError: FATAL: database "mydb" does not exist 参照元:https://cloud.google.com/sql/docs/postgres/create-manage-databases 発生する理由:Cloud SQL にデータベースは存在しますが、DB_NAME 環境変数が間違っています。 予防策:gcloud sql databases list --instance=INSTANCE でデータベース名を確認してください。DB_NAME が完全に一致していることを確認してください。

Issue #9:IAM 認証失敗

エラー:FATAL: Cloud SQL IAM user authentication failed 参照元:https://cloud.google.com/sql/docs/postgres/iam-logins 発生する理由:App Engine のサービスアカウントに roles/cloudsql.instanceUser ロールが付与されていません。 予防策:ロールを付与します:gcloud projects add-iam-policy-binding PROJECT --member="serviceAccount:[email protected]" --role="roles/cloudsql.instanceUser"

Issue #10:本番環境でマイグレーションが失敗する

エラー:マイグレーションがタイムアウトする、またはデプロイ中に実行できない 出典:https://cloud.google.com/sql/docs/postgres/connect-build 原因:App Engine のデプロイにはマイグレーションの手順がなく、エントリーポイントで実行するとタイムアウトします 予防策:Cloud Build を使用してマイグレーションを別途実行するか、デプロイ前に Cloud SQL Proxy を使ってローカルで実行します。

Issue #11:SECRET_KEY の露出

エラー:SECRET_KEY がハードコードされていることに関するセキュリティ警告 出典:Django のデプロイチェックリスト 原因:SECRET_KEY が環境変数や Secret Manager ではなく、settings.py に記述されています 予防策:SECRET_KEY = os.environ.get('SECRET_KEY') を使用し、gcloud app deploy --set-env-vars または Secret Manager で設定します。

Issue #12:コールドスタート時のデータベースタイムアウト

エラー:アイドル期間後の最初のリクエストがタイムアウトする 出典:https://cloud.google.com/appengine/docs/standard/how-instances-are-managed 原因:App Engine インスタンスのコールドスタートに加え、Cloud SQL の有効化に時間がかかります(「オンデマンド」有効化を使用している場合) 予防策:App Engine のウォームアップリクエストを使用するか、Cloud SQL インスタンスを「常時オン」にします(コストが増加します)。app_engine_apis: true を設定し、/_ah/warmup ハンドラーを追加します。


設定ファイルのリファレンス

settings.py(本番環境向けの完全な例)
import os
from pathlib import Path

BASE_DIR = Path(__file__).resolve().parent.parent

# Security
SECRET_KEY = os.environ.get('SECRET_KEY', 'dev-key-change-in-production')
DEBUG = os.environ.get('DEBUG', 'False') == 'True'
ALLOWED_HOSTS = [
    '.appspot.com',
    '.run.app',
    'localhost',
    '127.0.0.1',
]

# CSRF for App Engine
CSRF_TRUSTED_ORIGINS = [
    'https://*.appspot.com',
    'https://*.run.app',
]

# Application definition
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    # Your apps here
]

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'whitenoise.middleware.WhiteNoiseMiddleware',  # Static files
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

ROOT_URLCONF = 'myproject.urls'

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [BASE_DIR / 'templates'],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

WSGI_APPLICATION = 'myproject.wsgi.application'

# Database
IS_APP_ENGINE = os.getenv('GAE_APPLICATION', None)

if IS_APP_ENGINE:
    DATABASES = {
        'default': {
            'ENGINE': 'django.db.backends.postgresql',
            'NAME': os.environ['DB_NAME'],
            'USER': os.environ['DB_USER'],
            'PASSWORD': os.environ['DB_PASSWORD'],
            'HOST': f"/cloudsql/{os.environ['CLOUD_SQL_CONNECTION_NAME']}",
            'PORT': '',
            'CONN_MAX_AGE': 60,
            'OPTIONS': {
                'connect_timeout': 10,
            },
        }
    }
else:
    DATABASES = {
        'default': {
            'ENGINE': 'django.db.backends.postgresql',
            'NAME': os.environ.get('DB_NAME', 'mydb'),
            'USER': os.environ.get('DB_USER', 'postgres'),
            'PASSWORD': os.environ.get('DB_PASSWORD', ''),
            'HOST': os.environ.get('DB_HOST', '127.0.0.1'),
            'PORT': os.environ.get('DB_PORT', '5432'),
            'CONN_MAX_AGE': 60,
        }
    }

# Static files
STATIC_URL = '/static/'
STATIC_ROOT = BASE_DIR / 'static'
STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage'

# Default primary key field type
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'

# Logging
LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'handlers': {
        'console': {
            'class': 'logging.StreamHandler',
        },
    },
    'root': {
        'handlers': ['console'],
        'level': 'INFO',
    },
    'loggers': {
        'django': {
            'handlers': ['console'],
            'level': os.getenv('DJANGO_LOG_LEVEL', 'INFO'),
            'propagate': False,
        },
    },
}
app.yaml(完全な例)
runtime: python310
entrypoint: gunicorn -b :$PORT -w 2 -t 55 --threads 4 myproject.wsgi:application

instance_class: F2

env_variables:
  DB_NAME: "mydb"
  DB_USER: "postgres"
  CLOUD_SQL_CONNECTION_NAME: "project-id:us-central1:instance-name"
  DEBUG: "False"

beta_settings:
  cloud_sql_instances: "project-id:us-central1:instance-name"

handlers:
  - url: /static
    static_dir: static/
    secure: always

  - url: /.*
    script: auto
    secure: always

automatic_scaling:
  min_instances: 0
  max_instances: 2
  target_cpu_utilization: 0.65

これらの設定の理由:

  • F2 インスタンスクラスでは、Gunicorn ワーカーを 2 つ実行できます
  • min_instances: 0 により、アイドル時のコストを節約できます
  • target_cpu_utilization: 0.65 により、過負荷になる前にスケーリングします
requirements.txt
Django>=5.1,<6.0
psycopg2-binary>=2.9.9
gunicorn>=23.0.0
whitenoise>=6.7.0

よく使われるパターン

パターン 1:環境に応じたデータベース設定
import os

def get_database_config():
    """Return database config based on environment."""
    is_production = os.getenv('GAE_APPLICATION', None)

    if is_production:
        return {
            'ENGINE': 'django.db.backends.postgresql',
            'NAME': os.environ['DB_NAME'],
            'USER': os.environ['DB_USER'],
            'PASSWORD': os.environ['DB_PASSWORD'],
            'HOST': f"/cloudsql/{os.environ['CLOUD_SQL_CONNECTION_NAME']}",
            'PORT': '',
            'CONN_MAX_AGE': 60,
        }
    else:
        return {
            'ENGINE': 'django.db.backends.postgresql',
            'NAME': os.environ.get('DB_NAME', 'mydb'),
            'USER': os.environ.get('DB_USER', 'postgres'),
            'PASSWORD': os.environ.get('DB_PASSWORD', ''),
            'HOST': '127.0.0.1',
            'PORT': '5432',
            'CONN_MAX_AGE': 60,
        }

DATABASES = {'default': get_database_config()}

使用する場面:ローカル環境と本番環境の整合性が必要な標準的な Django プロジェクト

パターン 2:Secret Manager との統合
from google.cloud import secretmanager

def get_secret(secret_id, version_id='latest'):
    """Retrieve secret from Google Secret Manager."""
    client = secretmanager.SecretManagerServiceClient()
    project_id = os.environ.get('GOOGLE_CLOUD_PROJECT')
    name = f"projects/{project_id}/secrets/{secret_id}/versions/{version_id}"
    response = client.access_secret_version(request={"name": name})
    return response.payload.data.decode('UTF-8')

# Usage in settings.py
if os.getenv('GAE_APPLICATION'):
    SECRET_KEY = get_secret('django-secret-key')
    DB_PASSWORD = get_secret('db-password')

使用する場面:適切なシークレット管理が必要な本番環境へのデプロイ

パターン 3:Cloud SQL Python Connector(Proxy の代替)
# For local development without Cloud SQL Auth Proxy
from google.cloud.sql.connector import Connector

def get_db_connection():
    connector = Connector()
    return connector.connect(
        "project:region:instance",
        "pg8000",
        user="postgres",
        password=os.environ["DB_PASSWORD"],
        db="mydb",
    )

# In settings.py for local dev (requires pg8000 driver)
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'NAME': 'mydb',
        'USER': 'postgres',
        'OPTIONS': {
            'get_conn': get_db_connection,
        },
    }
}

使用する場面:Cloud SQL Auth Proxy をインストールできない場合のローカル開発

パターン 4:データベースの検証を含むヘルスチェックエンドポイント
# views.py
from django.http import JsonResponse
from django.db import connection

def health_check(request):
    """Health check endpoint for App Engine."""
    try:
        with connection.cursor() as cursor:
            cursor.execute("SELECT 1")
        return JsonResponse({
            'status': 'healthy',
            'database': 'connected',
        })
    except Exception as e:
        return JsonResponse({
            'status': 'unhealthy',
            'database': str(e),
        }, status=503)

# urls.py
urlpatterns = [
    path('_ah/health', health_check, name='health_check'),
]

使用する場面:ロードバランサーのヘルスチェック、データベース接続の監視


同梱リソースの使用

テンプレート(templates/)
  • templates/settings_snippet.py - データベース設定をコピーして貼り付けられます
  • templates/app.yaml - App Engine の完全な設定
  • templates/requirements.txt - 本番環境用の依存関係
リファレンス(references/)
  • references/cloud-sql-proxy-setup.md - Proxy のインストールと使用方法の詳細
  • references/iam-authentication.md - IAM ベースの認証(パスワード不要)
  • references/secret-manager.md - シークレットを適切に保存する方法
  • references/migrations-in-production.md - マイグレーションを安全に実行する方法

Claude がこれらを読み込むタイミング:

  • ローカル接続に問題がある場合は、cloud-sql-proxy-setup.md を読み込みます
  • パスワード不要の認証を設定する場合は、iam-authentication.md を読み込みます
  • 初回の本番環境へのデプロイ前に、migrations-in-production.md を読み込みます

高度なトピック

IAM データベース認証

パスワード認証の代わりに、サービス間認証に IAM を使用します。

# Enable IAM authentication on instance
gcloud sql instances patch INSTANCE --database-flags cloudsql.iam_authentication=on

# Create IAM user
gcloud sql users create [email protected] \
  --instance=INSTANCE \
  --type=CLOUD_IAM_SERVICE_ACCOUNT

# Grant connect permission
gcloud projects add-iam-policy-binding PROJECT \
  --member="serviceAccount:[email protected]" \
  --role="roles/cloudsql.instanceUser"

IAM 認証用の Django 設定:

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'NAME': os.environ['DB_NAME'],
        'USER': f"{os.environ['SERVICE_ACCOUNT']}@{os.environ['PROJECT_ID']}.iam",
        'HOST': f"/cloudsql/{os.environ['CLOUD_SQL_CONNECTION_NAME']}",
        'PORT': '',
        # No PASSWORD needed with IAM auth
    }
}
PgBouncer による接続プーリング

トラフィックの多いアプリケーションでは、Cloud Run に PgBouncer をデプロイします。

# Cloud Run service for PgBouncer
# See references/pgbouncer-setup.md for full configuration

PgBouncer を使用する理由:

  • Cloud SQL の接続数には上限があります(ティアに応じて 25~4000)
  • Django の CONN_MAX_AGE は役立ちますが、プロセス間で接続をプーリングすることはできません
  • PgBouncer は真の接続プーリングを提供します
マイグレーションを安全に実行する

方法 1:Cloud Build(推奨)

# cloudbuild.yaml
steps:
  - name: 'gcr.io/cloud-builders/gcloud'
    args: ['sql', 'connect', 'INSTANCE', '--quiet']

  - name: 'python:3.10'
    entrypoint: 'bash'
    args:
      - '-c'
      - |
        pip install -r requirements.txt
        python manage.py migrate --noinput
    env:
      - 'DB_NAME=mydb'
      - 'DB_USER=postgres'
      - 'DB_HOST=/cloudsql/PROJECT:REGION:INSTANCE'
    secretEnv: ['DB_PASSWORD']

availableSecrets:
  secretManager:
    - versionName: projects/PROJECT/secrets/db-password/versions/latest
      env: 'DB_PASSWORD'

方法 2:Proxy を使ってローカルで実行(簡単)

./cloud-sql-proxy PROJECT:REGION:INSTANCE &
python manage.py migrate

依存関係

必須:

  • Django>=5.1 - Web フレームワーク
  • psycopg2-binary>=2.9.9 - PostgreSQL アダプター
  • gunicorn>=23.0.0 - App Engine 用 WSGI サーバー

推奨:

  • whitenoise>=6.7.0 - 静的ファイルの配信
  • python-dotenv>=1.0.0 - ローカル環境変数

任意:

  • google-cloud-secret-manager>=2.20.0 - Secret Manager との統合
  • cloud-sql-python-connector[pg8000]>=1.12.0 - Python ネイティブの Cloud SQL コネクター
  • django-db-connection-pool>=1.2.5 - コネクションプーリング(CONN_MAX_AGE の代替)

公式ドキュメント


パッケージのバージョン(2026-01-24 確認済み)

{
  "dependencies": {
    "Django": ">=5.1,<6.0",
    "psycopg2-binary": ">=2.9.9",
    "gunicorn": ">=23.0.0",
    "whitenoise": ">=6.7.0"
  },
  "optional": {
    "google-cloud-secret-manager": ">=2.20.0",
    "cloud-sql-python-connector": ">=1.12.0"
  }
}

本番環境の例

このスキルは、App Engine での本番 Django デプロイを基にしています:

  • ユースケース: マルチテナント SaaS アプリケーション
  • トラフィック: 1 日あたり 10K 件以上のリクエスト
  • データベース: Cloud SQL PostgreSQL(db-g1-small、25 接続)
  • エラー: 0 件(既知の 12 件の問題をすべて防止)
  • 検証項目: Unix ソケット接続、コネクションプーリング、静的ファイル、CSRF

トラブルシューティング

問題:ソケットに対して No such file or directory が発生する

解決方法:

  1. app.yaml の beta_settings.cloud_sql_instances を確認します
  2. 接続名の形式を確認します:PROJECT:REGION:INSTANCE
  3. Cloud SQL インスタンスが存在することを確認します:gcloud sql instances list
問題:ローカルでは接続できるが、App Engine では失敗する

解決方法:

  1. HOST が 127.0.0.1 ではなく、Unix ソケットのパスを使用していることを確認します
  2. app.yaml に環境変数が設定されていることを確認します
  3. App Engine サービスアカウントに Cloud SQL Client ロールがあることを確認します
問題:デプロイ中にマイグレーションがタイムアウトする

解決方法:

  1. app.yaml のエントリーポイントでマイグレーションを実行しないでください
  2. Cloud Build を使用するか、デプロイ前にプロキシ経由でローカル実行します
  3. 大規模なマイグレーションの場合は、一時的に Cloud SQL のティアを引き上げます
問題:デプロイ後に静的ファイルが 404 になる

解決方法:

  1. デプロイ前に python manage.py collectstatic --noinput を実行します
  2. app.yaml の static/ ハンドラーが正しいディレクトリを指していることを確認します
  3. MIDDLEWARE リストに WhiteNoise が含まれていることを確認します

セットアップ完了チェックリスト

  • Cloud SQL PostgreSQL インスタンスを作成済み
  • Cloud SQL でデータベースとユーザーを作成済み
  • Cloud SQL Auth Proxy をローカルにインストール済み
  • Django の設定で Unix ソケット(本番環境)と localhost(開発環境)を指定済み
  • app.yaml に beta_settings.cloud_sql_instances を設定済み
  • コネクションプーリング用に CONN_MAX_AGE を設定済み
  • 環境変数(DB_NAME、DB_USER など)を設定済み
  • DB_PASSWORD を安全に保存済み(app.yaml には記載しない)
  • 静的ファイルを収集し、ハンドラーを設定済み
  • CSRF_TRUSTED_ORIGINS に *.appspot.com を含める
  • 初回デプロイ前にマイグレーションを実行済み(プロキシ経由でローカル実行)
  • gcloud app deploy でデプロイ済み
  • 本番環境のログでデータベース接続を確認済み

ご質問や問題がありますか?

  1. 上記のトラブルシューティングのセクションを確認してください
  2. すべての環境変数が正しく設定されていることを確認してください
  3. 公式ドキュメントを確認してください:https://cloud.google.com/python/django/appengine
  4. Cloud SQL インスタンスが稼働していることを確認してください:gcloud sql instances list

最終確認日:2026-01-24 | スキルのバージョン:1.0.0

コメント