Cloud Run 環境での Django の実行

Django のような Cloud Run にステートフル アプリケーションをにデプロイすると、サービスが連携して統合プロジェクトを形成します。

このチュートリアルは、Django ウェブ開発の知識があることを前提としています。Django 開発を初めて使用する場合は、続行する前に最初の Django アプリを作成するを実施することをおすすめします。

このチュートリアルでは Django について具体的に説明しますが、このデプロイ プロセスは WagtailDjango CMS などの他の Django ベースのフレームワークでも使用できます。

このチュートリアルでは Django 4 を使用します。Django 4 には Python 3.8 以降が必要です。

目標

このチュートリアルの内容は次のとおりです。

  • Cloud SQL データベースを作成して接続する。
  • Secret Manager のシークレット値を作成して使用する。
  • Django アプリを Cloud Run にデプロイする。

  • Cloud Storage で静的ファイルをホストする。

  • Cloud Build を使用してデプロイを自動化する。

料金

このドキュメントでは、Google Cloud の次の課金対象のコンポーネントを使用します。

料金計算ツールを使うと、予想使用量に基づいて費用の見積もりを生成できます。 新しい Google Cloud ユーザーは無料トライアルをご利用いただける場合があります。

始める前に

  1. Google Cloud アカウントにログインします。Google Cloud を初めて使用する場合は、アカウントを作成して、実際のシナリオでの Google プロダクトのパフォーマンスを評価してください。新規のお客様には、ワークロードの実行、テスト、デプロイができる無料クレジット $300 分を差し上げます。
  2. Google Cloud Console の [プロジェクト セレクタ] ページで、Google Cloud プロジェクトを選択または作成します。

    プロジェクト セレクタに移動

  3. Google Cloud プロジェクトで課金が有効になっていることを確認します

  4. Cloud Run, Cloud SQL, Cloud Build, Secret Manager, and Compute Engine API を有効にします。

    API を有効にする

  5. Google Cloud CLI をインストールします。
  6. gcloud CLI を初期化するには:

    gcloud init
  7. Google Cloud Console の [プロジェクト セレクタ] ページで、Google Cloud プロジェクトを選択または作成します。

    プロジェクト セレクタに移動

  8. Google Cloud プロジェクトで課金が有効になっていることを確認します

  9. Cloud Run, Cloud SQL, Cloud Build, Secret Manager, and Compute Engine API を有効にします。

    API を有効にする

  10. Google Cloud CLI をインストールします。
  11. gcloud CLI を初期化するには:

    gcloud init
  12. このチュートリアルで使用するアカウントに十分な権限が付与されていることを確認してください。

環境を準備する

サンプルアプリのクローンを作成する

Django サンプルアプリのコードは、GitHub の GoogleCloudPlatform/python-docs-samples リポジトリにあります。

  1. ZIP ファイルとしてサンプルをダウンロードして展開するか、ローカルマシンにリポジトリのクローンを作成します。

    git clone https://github.com/GoogleCloudPlatform/python-docs-samples.git
    
  2. サンプルコードのあるディレクトリに移動します。

    Linux / macOS

    cd python-docs-samples/run/django
    

    Windows

    cd python-docs-samples\run\django
    

Python の設定を確認する

このチュートリアルでは、Python を使用してサンプル アプリケーションをマシン上で実行します。サンプルコードでは依存関係もインストールする必要があります。

詳細については、Python 開発環境ガイドをご覧ください。

  1. Python のバージョンが 3.7 以降であることを確認します。

     python -V
    

    Python 3.7.3 以上が表示される必要があります。

  2. Python 仮想環境を作成し、依存関係をインストールします。

    Linux / macOS

    python -m venv venv
    source venv/bin/activate
    pip install --upgrade pip
    pip install -r requirements.txt
    

    Windows

    python -m venv env
    venv\scripts\activate
    pip install --upgrade pip
    pip install -r requirements.txt
    

ローカルマシンから Cloud SQL Auth プロキシをダウンロードして Cloud SQL に接続する

デプロイされたアプリは、Cloud Run 環境に組み込まれた Cloud SQL Auth プロキシを使用して Cloud SQL インスタンスと通信します。ただし、アプリをローカルでテストするには、プロキシのローカルコピーを開発環境にインストールして使用する必要があります。詳しくは、Cloud SQL Auth プロキシガイドをご覧ください。

Cloud SQL Auth プロキシは、Cloud SQL API を使用して SQL インスタンスとやり取りします。これを行うには、gcloud でアプリケーションの認証を行う必要があります。

  1. API の認証情報を取得して認証します。

    gcloud auth application-default login
    
  2. Cloud SQL Auth プロキシをダウンロードしてローカルマシンにインストールします。

    Linux 64 ビット

    1. Cloud SQL Auth Proxy をダウンロードします。
      wget https://dl.google.com/cloudsql/cloud_sql_proxy.linux.amd64 -O cloud_sql_proxy
      
    2. Cloud SQL Auth Proxy を動作可能にします。
      chmod +x cloud_sql_proxy
      

    Linux 32 ビット

    1. Cloud SQL Auth Proxy をダウンロードします。
      wget https://dl.google.com/cloudsql/cloud_sql_proxy.linux.386 -O cloud_sql_proxy
      
    2. wget コマンドが見つからない場合は、sudo apt-get install wget を実行してダウンロード コマンドを繰り返します。
    3. Cloud SQL Auth Proxy を動作可能にします。
      chmod +x cloud_sql_proxy
      

    macOS 64 ビット

    1. Cloud SQL Auth Proxy をダウンロードします。
      curl -o cloud_sql_proxy https://dl.google.com/cloudsql/cloud_sql_proxy.darwin.amd64
      
    2. Cloud SQL Auth Proxy を動作可能にします。
      chmod +x cloud_sql_proxy
      

    macOS 32 ビット

    1. Cloud SQL Auth Proxy をダウンロードします。
      curl -o cloud_sql_proxy https://dl.google.com/cloudsql/cloud_sql_proxy.darwin.386
      
    2. Cloud SQL Auth Proxy を動作可能にします。
      chmod +x cloud_sql_proxy
      

    Mac M1

    1. Cloud SQL Auth Proxy をダウンロードします。
        curl -o cloud_sql_proxy https://dl.google.com/cloudsql/cloud_sql_proxy.darwin.arm64
        
    2. Cloud SQL Auth Proxy を動作可能にします。
        chmod +x cloud_sql_proxy
        

    Windows 64 ビット

    https://dl.google.com/cloudsql/cloud_sql_proxy_x64.exe を右クリックして [名前を付けてリンク先を保存] を選択し、Cloud SQL Auth Proxy をダウンロードします。ファイル名を cloud_sql_proxy.exe に変更します。

    Windows 32 ビット

    https://dl.google.com/cloudsql/cloud_sql_proxy_x86.exe を右クリックして [名前を付けてリンク先を保存] を選択し、Cloud SQL Auth Proxy をダウンロードします。ファイル名を cloud_sql_proxy.exe に変更します。

    Cloud SQL Auth Proxy Docker イメージ

    便宜上、Cloud SQL Auth プロキシを含む複数のコンテナ イメージは、GitHub で Cloud SQL Auth プロキシ リポジトリから入手できます。次のコマンドを使用して、最新のイメージをローカルマシンに Docker で pull できます。
    docker pull gcr.io/cloudsql-docker/gce-proxy:1.30.1
    

    その他の OS

    ここに記載されていないその他のオペレーティング システムの場合は、ソースから Cloud SQL Auth Proxy をコンパイルできます。

    ダウンロード先は、PATH の場所やホーム ディレクトリなど、一般的な場所に移動できます。これを行う場合は、チュートリアルの後半で Cloud SQL Auth プロキシを起動する際の cloud_sql_proxy コマンド使用時に、選択したロケーションを必ず参照してください。

バッキング サービスを作成する

このチュートリアルでは、さまざまな Google Cloud サービスを使用して、デプロイ済みの Django プロジェクトをサポートするデータベース、メディア ストレージ、シークレット ストレージを準備します。これらのサービスは、特定のリージョンにデプロイされます。サービス間の効率を高めるために、すべてのサービスを同じリージョンにデプロイすることをおすすめします。最も近いリージョンの詳細については、リージョン別に提供されるプロダクトをご覧ください。

このチュートリアルでは、Cloud Run 環境で統合された静的アセット ホスティング メカニズムを使用します。

Cloud SQL for PostgreSQL インスタンスを設定する

Django は正式に複数のリレーショナル データベースに対応していますが、PostgreSQL に最も対応しています。PostgreSQL は Cloud SQL でサポートされているため、このチュートリアルではそのようなタイプのデータベースを使用します。

次のセクションでは、アプリ用の PostgreSQL インスタンス、データベース、データベース ユーザーの作成について説明します。

  1. PostgreSQL インスタンスを作成します。

    Console

    1. Cloud Console で、[Cloud SQL インスタンス] ページに移動します。

      Cloud SQL インスタンス ページに移動

    2. [インスタンスを作成] をクリックします。

    3. [PostgreSQL] をクリックします。

    4. [インスタンス ID] フィールドに「INSTANCE_NAME」と入力します。

    5. postgres ユーザーのパスワードを入力します。

    6. 他のフィールドはデフォルト値を使用します。

    7. [作成] をクリックします。

    インスタンスの作成と使用準備が完了するまでに数分かかります。

    gcloud

    • PostgreSQL インスタンスを作成します。

      gcloud sql instances create INSTANCE_NAME \
          --project PROJECT_ID \
          --database-version POSTGRES_13 \
          --tier db-f1-micro \
          --region REGION
      

    以下を置き換えます。

    インスタンスの作成と使用準備が完了するまでに数分かかります。

  2. 作成したインスタンス内に、データベースを作成します。

    Console

    1. インスタンス ページで、[データベース] タブに移動します。
    2. [データベースを作成] をクリックします。
    3. [データベース名] ダイアログで「DATABASE_NAME」と入力します。
    4. [作成] をクリックします。

    gcloud

    • 最近作成したインスタンス内にデータベースを作成します。

      gcloud sql databases create DATABASE_NAME \
          --instance INSTANCE_NAME
      

      DATABASE_NAME を、このインスタンス内のデータベースの名前に置き換えます。

  3. データベース ユーザーを作成するには:

    Console

    1. インスタンス ページで、[ユーザー] タブに移動します。
    2. [ユーザー アカウントを追加] をクリックします。
    3. [ユーザー アカウントをインスタンスに追加] ダイアログの「組み込み認証」で次の操作を行います。
    4. DATABASE_USERNAME というユーザー名を入力します。
    5. DATABASE_PASSWORD というパスワードを入力します。
    6. [追加] をクリックします。

    gcloud

    • 最近作成したインスタンス内にデータベース ユーザーを作成します。

      gcloud sql users create DATABASE_USERNAME \
          --instance INSTANCE_NAME \
          --password DATABASE_PASSWORD
      

      PASSWORD を安全なパスワードに置き換えます。

Cloud Storage バケットを設定する

Django に含まれている静的アセットやユーザーがアップロードしたメディアを、Cloud Storage を使用する可用性の高いオブジェクト ストレージに保存できます。django-storages[google] パッケージは、Django とこのストレージ バックエンドとのインタラクションを処理します。

Console

  1. Google Cloud コンソールで、Cloud Storage の [バケット] ページに移動します。

    [バケット] ページに移動

  2. [バケットを作成] をクリックします。
  3. [バケットの作成] ページでユーザーのバケット情報を入力します。次のステップに進むには、[続行] をクリックします。
    • [バケットに名前を付ける] で、バケット名の要件を満たす名前を入力します。
    • [ロケーション] で、次のように選択します。 MEDIA_BUCKET
    • [データのデフォルトのストレージ クラスを選択する] で、次を選択します。 Standard.
    • [オブジェクトへのアクセスを制御する方法を選択する] で [アクセス制御] オプションを選択します。
    • [詳細設定(省略可)] には、暗号化メソッド保持ポリシー、またはバケットラベルを指定します。
  4. [作成] をクリックします。

gcloud

gsutil コマンドライン ツールは、gcloud CLI のインストールの一部としてインストールされます。

  • Cloud Storage バケットを作成します。

    gsutil mb -l REGION gs://PROJECT_ID_MEDIA_BUCKET
    

    MEDIA_BUCKET は、メディア バケットの接尾辞に置き換えます。プロジェクト ID と組み合わされることで、一意のバケット名になります。

Secret Manager にシークレット値を保存する

バッキング サービスが構成されたので、Django はこれらのサービスに関する情報を必要とします。このチュートリアルでは、これらの値を Django のソースコードに直接入力せず、Secret Manager を使用してこの情報を安全に保存します。

Cloud Run と Cloud Build は、それぞれのサービス アカウントを使用してシークレットとやり取りします。サービス アカウントは、プロジェクト番号を含むメールアドレスで識別されます。

Secret Manager シークレットとして Django 環境ファイルを作成する

Django の起動に必要な設定を、保護された env ファイルに保存します。サンプルアプリは、Secret Manager API を使用してシークレット値を取得し、django-environ パッケージを使用して Django 環境に値を読み込みます。シークレットは、Cloud Run と Cloud Build からアクセスできるように構成されています。

  1. .env という名前のファイルを作成し、データベースの接続文字列、メディア バケット名、新しい SECRET_KEY 値を定義します。

    echo DATABASE_URL=postgres://DATABASE_USERNAME:DATABASE_PASSWORD@//cloudsql/PROJECT_ID:REGION:INSTANCE_NAME/DATABASE_NAME > .env
    echo GS_BUCKET_NAME=PROJECT_ID_MEDIA_BUCKET >> .env
    echo SECRET_KEY=$(cat /dev/urandom | LC_ALL=C tr -dc '[:alpha:]'| fold -w 50 | head -n1) >> .env
    
  2. シークレットを Secret Manager に保存します。

    Console

    1. Cloud Console で、[シークレット マネージャー] ページに移動します。

      シークレット マネージャー ページに移動

    2. [シークレットの作成] をクリックします。

    3. [名前] フィールドに「django_settings」と入力します。

    4. [シークレットの値] ダイアログで、.env ファイルの内容を貼り付けます。

    5. [シークレットの作成] をクリックします。

    6. [django_settings の詳細] で、プロジェクト番号をメモします。

      projects/PROJECTNUM/secrets/django_settings
      
    7. ローカル設定のオーバーライドを防ぐため、ローカル ファイルを削除します。

    gcloud

    1. 新しいシークレット django_settings.env ファイルの値で作成します。

      gcloud secrets create django_settings --data-file .env
      
    2. シークレットの作成を確認するには、次のことを確認します。

      gcloud secrets describe django_settings
      
      gcloud secrets versions access latest --secret django_settings
      
    3. プロジェクト番号(PROJECTNUM)の値を取得します。

      export PROJECTNUM=$(gcloud projects describe PROJECT_ID --format='value(projectNumber)')
      
    4. ローカル設定のオーバーライドを防ぐため、ローカル ファイルを削除します。

      rm .env
      
  3. シークレットへのアクセスを設定します。

    Console

    1. [権限] タブをクリックします。
    2. [Add] をクリックします。
    3. [新しいメンバー] フィールドに「PROJECTNUM-compute@developer.gserviceaccount.com」と入力し、Enter を押します。
    4. [新しいメンバー] フィールドに「PROJECTNUM@cloudbuild.gserviceaccount.com」と入力し、Enter を押します。
    5. [ロール] プルダウン メニューで [Secret Manager のシークレット アクセサー] を選択します。
    6. [保存] をクリックします。

    gcloud

    1. Cloud Run サービス アカウントにシークレットへのアクセス権を付与します。

      gcloud secrets add-iam-policy-binding django_settings \
          --member serviceAccount:PROJECTNUM-compute@developer.gserviceaccount.com \
          --role roles/secretmanager.secretAccessor
      
    2. Cloud Build サービス アカウントにシークレットへのアクセス権を付与します。

      gcloud secrets add-iam-policy-binding django_settings \
          --member serviceAccount:PROJECTNUM@cloudbuild.gserviceaccount.com \
          --role roles/secretmanager.secretAccessor
      

      出力で、bindings が 2 つのサービス アカウントをメンバーとしてリストしていることを確認します。

Django の管理者パスワード用のシークレットを作成する

Django 管理ユーザーは通常、インタラクティブ管理コマンド createsuperuser を実行して作成されます。

このチュートリアルでは、データ移行を使用して管理ユーザーを作成し、Secret Manager から管理パスワードを取得します。

Console

  1. Cloud Console で、[シークレット マネージャー] ページに移動します。
  2. [シークレットの作成] をクリックします。

  3. [名前] フィールドに「superuser_password」と入力します。

  4. [シークレット値] フィールドにランダムな一意のパスワードを入力します。

  5. [シークレットの作成] をクリックします。

  6. [superuser_password の詳細] で、プロジェクト番号(projects/PROJECTNUM/secrets/superuser_password)をメモしておきます。

  7. [権限] タブをクリックします。

  8. [Add] をクリックします。

  9. [新しいメンバー] フィールドに「PROJECTNUM@cloudbuild.gserviceaccount.com」と入力し、Enter を押します。

  10. [ロール] プルダウン メニューで [Secret Manager のシークレット アクセサー] を選択します。

  11. [保存] をクリックします。

gcloud

  1. ランダムに生成されたパスワードから新しい Secret superuser_password を作成します。

    echo -n "$(cat /dev/urandom | LC_ALL=C tr -dc '[:alpha:]'| fold -w 30 | head -n1)" | gcloud secrets create superuser_password --data-file -
    
  2. Cloud Build にシークレットへのアクセス権を付与します。

    gcloud secrets add-iam-policy-binding superuser_password \
        --member serviceAccount:PROJECTNUM@cloudbuild.gserviceaccount.com \
        --role roles/secretmanager.secretAccessor
    

    出力で、bindings が Cloud Build のみをメンバーとしてリストしていることを確認します。

Cloud Build に Cloud SQL へのアクセス権を付与する

Cloud Build でデータベースの移行を適用するには、Cloud Build に Cloud SQL へのアクセス権を付与する必要があります。

Console

  1. Cloud Console で、[Identity and Access Management] ページに移動します。

    [Identity and Access Management] ページに移動

  2. PROJECTNUM@cloudbuild.gserviceaccount.com のエントリを編集するには、 [編集] をクリックします。

  3. [別のロールを追加] をクリックします。

  4. [ロールを選択] ダイアログで、[Cloud SQL クライアント] を選択します。

  5. [保存] をクリックします。

gcloud

  1. Cloud Build に Cloud SQL へのアクセス権を付与します。

    gcloud projects add-iam-policy-binding PROJECT_ID \
        --member serviceAccount:PROJECTNUM@cloudbuild.gserviceaccount.com \
        --role roles/cloudsql.client
    

ローカル コンピュータでアプリを実行する

バッキング サービスを設定したら、パソコン上でアプリを実行できます。この設定により、ローカルでの開発とデータベース移行の適用が可能になります。データベース移行は Cloud Build にも適用されますが、makemigrations を行うには、このローカル設定が必要です。

  1. 別のターミナルで Cloud SQL Auth プロキシを起動します。

    Linux / macOS

    ./cloud_sql_proxy -instances="PROJECT_ID:REGION:INSTANCE_NAME"=tcp:5432
    

    Windows

    cloud_sql_proxy.exe -instances="PROJECT_ID:REGION:INSTANCE_NAME"=tcp:5432
    

    このステップで、ローカル パソコンから Cloud SQL インスタンスへのローカルテスト用接続が確立されます。ローカルでのアプリのテストが終了するまで、Cloud SQL Auth プロキシ を実行したままにしてください。このプロセスを別のターミナルで実行すると、このプロセスの実行中も作業を継続できます。

  2. 新しいターミナルで、プロジェクト ID をローカルに設定します(Secret Manager API で使用します)。

    Linux / macOS

      export GOOGLE_CLOUD_PROJECT=PROJECT_ID
    

    Windows

      set GOOGLE_CLOUD_PROJECT=PROJECT_ID
    
  3. Cloud SQL Auth プロキシを使用していることを示す環境変数を設定します(この値はコードで認識できます)。

    Linux / macOS

      export USE_CLOUD_SQL_AUTH_PROXY=true
    

    Windows

      set USE_CLOUD_SQL_AUTH_PROXY=true
    
  4. Django の移行を実行してモデルとアセットを設定します。

    python manage.py makemigrations
    python manage.py makemigrations polls
    python manage.py migrate
    python manage.py collectstatic
    
  5. Django ウェブサーバーを起動します。

    python manage.py runserver
    
  6. ブラウザで、http://localhost:8000 にアクセスします。

    「Hello, world. You're at the polls index.」というテキストを含む簡単なウェブページが表示されます。コンピュータで実行されている Django ウェブサーバーは、サンプルアプリのページを配信します。

  7. Ctrl/Cmd+C キーを押して、ローカル ウェブサーバーを停止します。

Cloud Run にアプリをデプロイする

バッキング サービスを設定できたので、Cloud Run サービスをデプロイできるようになりました。

  1. 提供された cloudmigrate.yaml を使用して、Cloud Build を使用してイメージをビルドし、データベースの移行を実行して、静的アセットにデータを入力します。

    gcloud builds submit --config cloudmigrate.yaml \
        --substitutions _INSTANCE_NAME=INSTANCE_NAME,_REGION=REGION
    

    最初のビルドが完了するまで数分かかります。

  2. ビルドが成功したら、Cloud Run サービスを初めてデプロイし、サービス リージョン、ベースイメージ、接続された Cloud SQL インスタンスを設定します。

    gcloud run deploy polls-service \
        --platform managed \
        --region REGION \
        --image gcr.io/PROJECT_ID/polls-service \
        --add-cloudsql-instances PROJECT_ID:REGION:INSTANCE_NAME \
        --allow-unauthenticated
    

    サービスの URL とともに、デプロイが成功したことを示す出力が表示されます。

    Service [polls-service] revision [polls-service-00001-tug] has been deployed
    and is serving 100 percent of traffic at https://polls-service-<hash>-uc.a.run.app
    
  3. サービス URL がわかったので、次にこの値を環境変数に設定するようにサービスを更新します。

    SERVICE_URL=$(gcloud run services describe polls-service --platform managed \
        --region REGION --format "value(status.url)")
    
    gcloud run services update polls-service \
        --platform managed \
        --region REGION \
        --set-env-vars CLOUDRUN_SERVICE_URL=$SERVICE_URL
    
  4. デプロイされたサービスを確認するには、サービスの URL に移動します。

  5. Django 管理にログインするには、URL に /admin を加え、ユーザー名 admin と前に設定したパスワードでログインします。

アプリケーションを更新する

最初のプロビジョニングとデプロイの手順は複雑でしたが、更新はより簡単なプロセスです。

  1. Cloud Build のビルドと移行スクリプトを実行します。

    gcloud builds submit --config cloudmigrate.yaml \
        --substitutions _INSTANCE_NAME=INSTANCE_NAME,_REGION=REGION
    
  2. リージョンとイメージのみを指定して、サービスをデプロイします。

    gcloud run deploy polls-service \
        --platform managed \
        --region REGION \
        --image gcr.io/PROJECT_ID/polls-service
    

本番環境用の構成

これで Django のデプロイが機能するようになりましたが、アプリケーションが本番環境に対応できるよう、追加の手順を行う必要があります。

デバッグを無効にする

mysite/settings.pyDEBUG 変数が False に設定されていることを確認します。これにより、ユーザーに詳細なエラーページが表示されなくなり、設定に関する情報が漏洩を防ぎます。

データベース ユーザーの権限を制限する

Cloud SQLを使用して作成されたすべてのユーザーには、cloudsqlsuperuser ロールに関連付けられた特権(CREATEROLECREATEDB、および LOGIN)があります。

Django データベース ユーザーにこれらの権限が付与されないようにするには、PostgreSQL でユーザーを手動で作成します。psql インタラクティブ ターミナルをインストールするか、このツールがプリインストールされている Cloud Shell を使用する必要があります。

Console

  1. Google Cloud コンソールで、「Cloud Shell をアクティブにする」をクリックします。

    Cloud Shell をアクティブにする

  2. Cloud Shell で、組み込みターミナルを使用して INSTANCE_NAME インスタンスに接続します。

    gcloud sql connect INSTANCE_NAME --user postgres
    
  3. postgres ユーザー パスワードを入力します。

    現在、psql を使用しています。postgres=> プロンプトが表示されます。

  4. ユーザーを作成します。

    CREATE USER DATABASE_USERNAME WITH PASSWORD 'DATABASE_PASSWORD';
    

    PASSWORD は、ランダムな一意のパスワードに置き換えます。

  5. 新しいデータベースに対する完全な権限を新しいユーザーに付与します。

    GRANT ALL PRIVILEGES ON DATABASE DATABASE_NAME TO DATABASE_USERNAME;
    
  6. psql を終了します。

    \q
    

gcloud

  1. SQL インスタンスへの接続を開始します。

    gcloud sql connect INSTANCE_NAME --user postgres
    

    INSTANCE_NAME は、作成した Cloud SQL インスタンスに置き換えます。

  2. postgres ユーザー パスワードを入力します。

    現在、psql を使用しています。postgres=> プロンプトが表示されます。

  3. ユーザーを作成します。

    CREATE USER DATABASE_USERNAME WITH PASSWORD 'DATABASE_PASSWORD';
    
  4. 新しいデータベースに対する完全な権限を新しいユーザーに付与します。

    GRANT ALL PRIVILEGES ON DATABASE DATABASE_NAME TO DATABASE_USERNAME;
    
  5. psql を終了します。

    \q
    

最小限の権限の設定

デフォルトでは、このサービスは、デフォルトのコンピューティング サービス アカウントでデプロイされます。ただし、デフォルトのサービス アカウントを使用すると、付与される権限が多くなりすぎることがあります。制限を強める必要がある場合は、独自のサービス アカウントを作成して、サービスに必要な権限のみを割り当てる必要があります。必要な権限は、特定のサービスにより使用されるリソースに応じて、サービスごとに異なります。

このサービスに必要な最小限のプロジェクト ロールは次のとおりです。

  • Cloud Run 起動元
  • Cloud SQL クライアント
  • メディア バケットの Storage 管理者
  • Django 設定シークレットの Secret Manager Accessor。(Django 管理シークレットへのアクセスは、サービス自体には必要ありません。)

必要な権限を持つサービス アカウントを作成し、サービスに割り当てるには、次のコマンドを実行します。

  1. gcloud CLI で、必要なロールを持つサービス アカウントを作成します

    gcloud iam service-accounts create polls-service-account
    SERVICE_ACCOUNT=polls-service-account@PROJECT_ID.iam.gserviceaccount.com
    
    # Cloud Run Invoker
    gcloud projects add-iam-policy-binding PROJECT_ID \
        --member serviceAccount:${SERVICE_ACCOUNT} \
        --role roles/run.invoker
    
    # Cloud SQL Client
    gcloud projects add-iam-policy-binding PROJECT_ID \
        --member serviceAccount:${SERVICE_ACCOUNT} \
        --role roles/cloudsql.client
    
    # Storage Admin, on the media bucket
    gsutil iam ch \
        serviceAccount:${SERVICE_ACCOUNT}:roles/storage.objectAdmin \
        gs://MEDIA_BUCKET
    
    # Secret Accessor, on the Django settings secret.
    gcloud secrets add-iam-policy-binding django_settings \
        --member serviceAccount:${SERVICE_ACCOUNT} \
        --role roles/secretmanager.secretAccessor
    
  2. サービスを新しいサービス アカウントに関連付けてデプロイします。

    gcloud run services update polls-service \
        --platform managed \
        --region REGION \
        --service-account ${SERVICE_ACCOUNT}
    

コードを理解する

サンプル アプリケーション

Django サンプルアプリは、Django の標準ツールを使用して作成されています。次のコマンドは、プロジェクトとアンケート アプリを作成します。

django-admin startproject mysite
python manage.py startapp polls

ベースビュー、モデル、ルート構成は、最初の Django アプリを作成するパート 1およびパート 2)からコピーされました。

Secret Manager のシークレット

settings.py ファイルには、Secret Manager Python API を使用して指定されたシークレットの最新バージョンを取得し、(django-environ を使用して)その環境に pull するコードが含まれています。

# SECURITY WARNING: don't run with debug turned on in production!
# Change this to "False" when you are ready for production
env = environ.Env(DEBUG=(bool, True))
env_file = os.path.join(BASE_DIR, ".env")

# Attempt to load the Project ID into the environment, safely failing on error.
try:
    _, os.environ["GOOGLE_CLOUD_PROJECT"] = google.auth.default()
except google.auth.exceptions.DefaultCredentialsError:
    pass

if os.path.isfile(env_file):
    # Use a local secret file, if provided

    env.read_env(env_file)
# ...
elif os.environ.get("GOOGLE_CLOUD_PROJECT", None):
    # Pull secrets from Secret Manager
    project_id = os.environ.get("GOOGLE_CLOUD_PROJECT")

    client = secretmanager.SecretManagerServiceClient()
    settings_name = os.environ.get("SETTINGS_NAME", "django_settings")
    name = f"projects/{project_id}/secrets/{settings_name}/versions/latest"
    payload = client.access_secret_version(name=name).payload.data.decode("UTF-8")

    env.read_env(io.StringIO(payload))
else:
    raise Exception("No local .env or GOOGLE_CLOUD_PROJECT detected. No secrets found.")

シークレットは、構成する必要のあるさまざまなシークレットの数を減らすために、複数のシークレット値を格納するために使用されます。 superuser_passwordコマンドラインから直接作成することもできますが、この方法ではなくファイルベースの方法が使用されました。コマンドラインから生成したとしたら、head -c を使用してランダムに生成された文字列の長さを慎重に決定し、ファイルの最後に改行文字がないことを確認する必要がありました(これは、パスワードを Django 管理に入力する際の問題の原因になります)。

CSRF の構成

Django には、クロスサイト リクエスト フォージェリ(CSRF)に対する保護機能が組み込まれています。Django 4.0 以降では、この機能の動作方法が変更されました。そのため、ホストされている URL を Django に指示して、データを送信するユーザーを最適に保護できるようにすることが重要です。

アプリの URL を環境変数として settings.py ファイルで指定します。これは、Django で関連する設定に使用する値です。

# SECURITY WARNING: It's recommended that you use this when
# running in production. The URL will be known once you first deploy
# to Cloud Run. This code takes the URL and converts it to both these settings formats.
CLOUDRUN_SERVICE_URL = env("CLOUDRUN_SERVICE_URL", default=None)
if CLOUDRUN_SERVICE_URL:
    ALLOWED_HOSTS = [urlparse(CLOUDRUN_SERVICE_URL).netloc]
    CSRF_TRUSTED_ORIGINS = [CLOUDRUN_SERVICE_URL]
    SECURE_SSL_REDIRECT = True
    SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https")
else:
    ALLOWED_HOSTS = ["*"]

ローカル シークレットのオーバーライド

ローカルのファイル システムで .env ファイルが見つかった場合は、Secret Manager の値の代わりに使用されます。ローカルで .env ファイルを作成すると、ローカルテスト(SQLite データベースに対するローカル開発やその他のローカル設定など)に役立ちます。

データベースへの接続

settings.pyファイルには、SQL データベースの構成が含まれています。USE_CLOUD_SQL_AUTH_PROXY を設定した場合、DATABASES 設定が Cloud SQL Auth プロキシの使用を推測するように変更されます。

# Use django-environ to parse the connection string
DATABASES = {"default": env.db()}

# If the flag as been set, configure to use proxy
if os.getenv("USE_CLOUD_SQL_AUTH_PROXY", None):
    DATABASES["default"]["HOST"] = "127.0.0.1"
    DATABASES["default"]["PORT"] = 5432

クラウドに保存された静的

また、settings.py ファイルは、django-storages を使用して Cloud Storage メディア バケットをプロジェクトに直接統合します。

# Define static storage via django-storages[google]
GS_BUCKET_NAME = env("GS_BUCKET_NAME")
STATIC_URL = "/static/"
DEFAULT_FILE_STORAGE = "storages.backends.gcloud.GoogleCloudStorage"
STATICFILES_STORAGE = "storages.backends.gcloud.GoogleCloudStorage"
GS_DEFAULT_ACL = "publicRead"

Cloud Build による自動化

cloudmigrate.yaml ファイルは、一般的なイメージのビルドステップ(コンテナ イメージを作成してコンテナ レジストリに push する)だけでなく、Django の migrate コマンドと collectstatic コマンドも実行します。これにはデータベースへのアクセスが必要です。これは、app-engine-exec-wrapperCloud SQL Auth プロキシのヘルパー)を使用して実行されます。

steps:
  - id: "build image"
    name: "gcr.io/cloud-builders/docker"
    args: ["build", "-t", "gcr.io/${PROJECT_ID}/${_SERVICE_NAME}", "."]

  - id: "push image"
    name: "gcr.io/cloud-builders/docker"
    args: ["push", "gcr.io/${PROJECT_ID}/${_SERVICE_NAME}"]

  - id: "apply migrations"
    name: "gcr.io/google-appengine/exec-wrapper"
    args:
      [
        "-i",
        "gcr.io/$PROJECT_ID/${_SERVICE_NAME}",
        "-s",
        "${PROJECT_ID}:${_REGION}:${_INSTANCE_NAME}",
        "-e",
        "SETTINGS_NAME=${_SECRET_SETTINGS_NAME}",
        "--",
        "python",
        "manage.py",
        "migrate",
      ]

  - id: "collect static"
    name: "gcr.io/google-appengine/exec-wrapper"
    args:
      [
        "-i",
        "gcr.io/$PROJECT_ID/${_SERVICE_NAME}",
        "-s",
        "${PROJECT_ID}:${_REGION}:${_INSTANCE_NAME}",
        "-e",
        "SETTINGS_NAME=${_SECRET_SETTINGS_NAME}",
        "--",
        "python",
        "manage.py",
        "collectstatic",
        "--verbosity",
        "2",
        "--no-input",
      ]

substitutions:
  _INSTANCE_NAME: django-instance
  _REGION: us-central1
  _SERVICE_NAME: polls-service
  _SECRET_SETTINGS_NAME: django_settings

images:
  - "gcr.io/${PROJECT_ID}/${_SERVICE_NAME}"

この構成では代入変数が使用されます。ファイルの値を直接変更すると、移行時に --substitutions フラグを省略できます。

この構成では、既存の移行のみが適用されます。「ローカルでアプリを実行する」で定義された Cloud SQL Auth プロキシ メソッドを使用して、移行をローカルに作成する必要があります。 このテンプレートは、必要に応じて、他の manage.py コマンドを実行するように拡張できます。

2 つのコマンドを実行せずに 1 つの構成にデプロイを含めるように Cloud Build の構成を拡張するには、Cloud Build を使用した git による継続的デプロイをご覧ください。説明されているように、これには IAM の変更が必要です。

データ移行によるスーパーユーザーの作成

Django 管理コマンド createsuperuser はインタラクティブにのみ実行できます。つまり、ユーザーがプロンプトに従い情報を入力する場合です。このコマンドを Cloud SQL Proxy で使用し、ローカルの Docker セットアップ内でコマンドを実行できますが、別の方法は、データ移行としてスーパーユーザーを作成することです。

import os

from django.contrib.auth.models import User
from django.db import migrations
from django.db.backends.postgresql.schema import DatabaseSchemaEditor
from django.db.migrations.state import StateApps

import google.auth
from google.cloud import secretmanager

def createsuperuser(apps: StateApps, schema_editor: DatabaseSchemaEditor) -> None:
    """
    Dynamically create an admin user as part of a migration
    Password is pulled from Secret Manger (previously created as part of tutorial)
    """
    if os.getenv("TRAMPOLINE_CI", None):
        # We are in CI, so just create a placeholder user for unit testing.
        admin_password = "test"
    else:
        client = secretmanager.SecretManagerServiceClient()

        # Get project value for identifying current context
        _, project = google.auth.default()

        # Retrieve the previously stored admin password
        PASSWORD_NAME = os.environ.get("PASSWORD_NAME", "superuser_password")
        name = f"projects/{project}/secrets/{PASSWORD_NAME}/versions/latest"
        admin_password = client.access_secret_version(name=name).payload.data.decode(
            "UTF-8"
        )

    # Create a new user using acquired password, stripping any accidentally stored newline characters
    User.objects.create_superuser("admin", password=admin_password.strip())

class Migration(migrations.Migration):

    initial = True
    dependencies = []
    operations = [migrations.RunPython(createsuperuser)]

クリーンアップ

このチュートリアルで使用したリソースについて、Google Cloud アカウントに課金されないようにするには、リソースを含むプロジェクトを削除するか、プロジェクトを維持して個々のリソースを削除します。

プロジェクトの削除

  1. Google Cloud コンソールで、[リソースの管理] ページに移動します。

    [リソースの管理] に移動

  2. プロジェクト リストで、削除するプロジェクトを選択し、[削除] をクリックします。
  3. ダイアログでプロジェクト ID を入力し、[シャットダウン] をクリックしてプロジェクトを削除します。

次のステップ