【虎の巻】DjangoをGAEにデプロイする方法をまとめました!

Posted date at 2024-06-23

GoogleCloud

title.png

 DjangoGAE(GoogleAppEngine)にデプロイする方法を記載します。林の備忘録とこれからデプロイされる人への情報提供を目的とします。

 Djangoの用途はAPI構築です。Node.jsと同様kintoneをはじめその他DBとの連携します。

 現在社内展開しているNode.js+GoogleCloudRunの代替として使用します。


🚀今回記事にするバックエンドアプリの構成

 前回記事📒にしたアプリのバックエンドになります。テキストデータをCloudSQL(MySQL)に保存し、画像データをCloudStorageに保存する構成にしています。kintoneの場合は、CloudSQL(MySQL)とCloudStorageがkintoneに置き換わります。

 

Django-GAE.png


 

🚀参考URL

 本記事は手順は以下のサイトの記事から必要な個所をピックアップして記載しています。具体的な手順はリンク先を参照してください。


 

🚀手順1:GAEにDjangoをデプロイする

🐡0.Djangoを使用したアプリの開発(ローカル)

 ご自身の手でAPIを構築します。参考動画を挙げます。林はすべて受講済ですので、質問がある方は聞いてください。

  講師 Zin nux さん、再生時間 5.5時間、林がかけた時間:10.5時間

  講師 Zin nux さん、再生時間 5時間、林がかけた時間:20時間(内デプロイ10時間)

  講師 Zin nux さん、再生時間 8.5時間、林がかけた時間:29.6時間(内デプロイ+記事作成:16時間)


🐡1.GAEの作成(GoogleCloud)

 GoogleCloud(旧GCP)にプロジェクトを作成し、GoogleAppEngineを作成します。

リージョンは「asia-notheast1」(東京)とし、言語は「python」とします。環境は「標準」とします。なお、リージョンは近い方がレイテンシ(応答速度)が高速です。

 「GCPing」📒から確認できます。


🐡2.googleCloudSDKのインストール(ローカル)

  まず、GoogleCloudからSDKインストーラをダウンロードしPowrShellから以下のコマンドを実行します。

(New-Object Net.WebClient).DownloadFile("https://dl.google.com/dl/cloudsdk/channels/rapid/GoogleCloudSDKInstaller.exe", "$env:Temp\GoogleCloudSDKInstaller.exe") & $env:Temp\GoogleCloudSDKInstaller.exe

 インストールが完了したら、初期設定を行います。初期設定ついては、こちらのサイト📒を参考に行います。


🐡3.app.yamlの作成とgiunicorn/のインストール(ローカル)

 Djangoプロジェクトのルートディレクトリに「app.yaml」を作成します。GAEでDjangoを動かすための定義ファイルになります。runtimeとentrypointを開発環境に併せて変更します。

 以下は、pythonのバージョンが「11」、設定用ディレクトリ(ファイル)が「/sns/wsgi.py」の場合です。

#インストールされているバージョンによって末尾の数字調整 runtime: python311 # 下記は 設定用ディレクトリ.wsgi:application のように記述 entrypoint: gunicorn -b :$PORT sns.wsgi:application # 下記はhttpでアクセスがあった際、httpsにリダイレクトするための記述 handlers: - url: /static static_dir: static/ - url: .* secure: always script: auto

 また、上記で「gunicorn」を使用しているため、プロジェクトにgunicornをインストールします。AnacondaNavigatorを起動し、使用している仮想環境のターミナルから以下のコマンドを実行します。

 AnacondaNavigatorを知らない場合は、[基礎編]React Hooks + Django REST Framework API でフルスタックWeb開発📒を見て学習して下さい。

(venv)$ pip install gunicorn

 gunicornのインストール後、GAE側に必要なライブラリを教えるためのファイル「requirements.txt」を以下のコマンドで生成します。このコマンドは、新しいライブラリをプロジェクトにインストールしたら都度実行します。

(venv)$ pip freeze > requirements.txt

🐡4.setting.pyの変更とGAEへデプロイ(ローカル)

 setting.pyの該当箇所を以下の通り変更します。

 〇変更前

ALLOWED_HOSTS = []

 〇変更後

if os.getenv('GAE_APPLICATION', None): # 本番環境 DEBUG = False ALLOWED_HOSTS = ['xxxxxx'] else: # 開発環境 DEBUG = True ALLOWED_HOSTS = ['*']

 本番環境のALLOWED_HOSTは一度デプロイしてからURLを取得して設定します。

 以下のコマンドでデプロイします。デプロイ時には、静的ファイルを集約するコマンドを併せて実行します。

python manage.py collectstatic

 上記のコマンドを実行することにより、デプロイコマンドを実行した際に、静的ファイルを適切にGoogleCloudStorage上に配置することができます。ですので以下の通り、デプロイコマンドとセットで実行します。

[プロジェクトID]にはGoogleCloudのコンソールのダッシュボードに表示されているプロジェクトIDを指定します。

python manage.py collectstatic gcloud app deploy --project=[プロジェクトID]

 コマンドを実行すると以下の表示がされます。

descriptor: [C:\Users\Syota_Hayashi\Desktop\dev\python\drf_api_sns\app.yaml] source: [C:\Users\Syota_Hayashi\Desktop\dev\python\drf_api_sns] target project: [xxxxxxx] target service: [default] target version: [20240622t143858] target url: [https://xxxx.an.r.appspot.com] target service account: [xxxx@appspot.gserviceaccount.com]

 **target url:の「xxxx.an.r.appspot.com」**がホスト名になりますので、以下の通り設定し、再度デプロイコマンドを実行します。

if os.getenv('GAE_APPLICATION', None): # 本番環境 DEBUG = False ALLOWED_HOSTS = ['**apply-for-overtime.an.r.appspot.com**'] else: # 開発環境 DEBUG = True ALLOWED_HOSTS = ['*']

 デプロイ完了後以下のコマンドで本番環境が立ち上がることを確認します。

gcloud app browse --project=[プロジェクトID]

🐡補足:デプロイ時requirement.txtのpillowでエラーとなった場合

 pillowが以下のようにローカルを指定していた場合、手動で書き換えます。

 〇変更前

asgiref==3.8.1 Django==5.0.6 django-cors-headers==3.4.0 djangorestframework==3.15.1 gunicorn==22.0.0 packaging==24.1 pillow @ file:///C:/b/abs_95ls_qh4c2/croot/pillow_1714398870615/work←ここを変更 sqlparse==0.5.0 tzdata==2024.1

 〇変更後

asgiref==3.8.1 Django==5.0.6 django-cors-headers==3.4.0 djangorestframework==3.15.1 gunicorn==22.0.0 packaging==24.1 Pillow==9.5.0 ←バージョンは実行時点で新しいものに変更 sqlparse==0.5.0 tzdata==2024.1

🐡補足:502BadGateWayとなる場合

 以下のコマンドを実行し、エラーを調査します。ChagGPTに聞くのも手です。

gcloud app logs tail -s default --project=apply-for-overtime

 これまであったのは、setting.pyのタイムゾーンの設定値によるエラーがありました。

#×:Error!! TIME_ZONE = 'ASIA/TOKYO' #〇:Sucess!! TIME_ZONE = 'Asia/Tokyo'

 デプロイ時は、キャッシュ無しのオプションを使用します。それでも前の状態を持つ場合は、’America/NewYork’にしてデプロイした後、’Asia/Tokyo'としてデプロイします。

gcloud app deploy --no-cache --project=[プロジェクトID]

 

🚀手順2:GCS(GoogleCloudStorage)の設定

 デプロイコマンド実行により、GCSへの静的ファイルの展開までされました。静的ファイルとは(AppEngineで実行するpythonライブラリや画像、動画などのメディアファイルが該当します)。デプロイが成功すれば、フロントからの画像保存もGCSに保存されるようになります。

 以降の手順は、静的ファイルへのアクセス件の設定を行います。

🐡バケットの手動作成(GoogleCloud)

 こちらのサイト📒の手順で、バケットに権限を設定します。バケットがない場合はバケットを手動で作成します。

権限は、手順1で確認した、「target service account」に対して「Storageオブジェクト閲覧者」、「Storageオブジェクト管理者」を付与します。

descriptor: [C:\Users\Syota_Hayashi\Desktop\dev\python\drf_api_sns\app.yaml] source: [C:\Users\Syota_Hayashi\Desktop\dev\python\drf_api_sns] target project: [xxxxxxx] target service: [default] target version: [20240622t143858] target url: [https://xxxx.an.r.appspot.com] target service account: [xxxx@appspot.gserviceaccount.com]#←こちらがサービスアカウント名

 権限設定に使用するサービスアカウントは「gcloud app deploy」コマンドで表示される以下の情報の「target service account」に該当するものを使用します。

descriptor: [C:\Users\Syota_Hayashi\Desktop\dev\python\drf_api_sns\app.yaml] source: [C:\Users\Syota_Hayashi\Desktop\dev\python\drf_api_sns] target project: [apply-for-overtime] target service: [default] target version: [20240622t143858] target url: [https://apply-for-overtime.an.r.appspot.com] target service account: [apply-for-overtime@appspot.gserviceaccount.com]←これを使用する

🐡サービスアカウントキーの発行

 上記のサービスアカウントにたいして、サービスアカウントキーを発行します。サービスアカウントキーの発行手順については、こちらのサイトのとおり行います。

 ※サービスアカウントキーの取り扱いには十分注意してください。gitignoreに設定し、GitHubにアップロードしないようにしましょう。


🐡setting.pyの追記(ローカル)

 GCSに関する記述を追加します。また、静的ファイル、メディアファイルの設定を確認し必要に応じて以下の通り変更します。サービスアカウントキーをプロジェクトのルートディレクトリに配置し、参照します。

from google.oauth2 import service_account#サービスアカウントによる認証に必要 INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'rest_framework', 'rest_framework.authtoken', 'core.apps.CoreConfig', 'api_user.apps.ApiUserConfig', 'api_dm.apps.ApiDmConfig', 'corsheaders', 'storages',#←追加します ] #GCS設定 GS_BUCKET_NAME = 'dyango-media-files-sns-app'#バケットの名前を指定する DEFAULT_FILE_STORAGE = 'storages.backends.gcloud.GoogleCloudStorage' #サービスアカウントキー GS_CREDENTIALS = service_account.Credentials.from_service_account_file( os.path.join(BASE_DIR, "xxxx.json") ) #静的ファイルのフォルダ指定 STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles') STATIC_URL = '/static/' #メディア(画像、動画)ファイルのフォルダ指定 MEDIA_ROOT = os.path.join(BASE_DIR, '_media')#←プロジェクトのフォルダ名が[_madia]の場合 #MEDIA_URL = 'https://storage.googleapis.com/{}/'.format(GS_BUCKET_NAME) MEDIA_URL = 'https://storage.googleapis.com/{}/media/'.format(GS_BUCKET_NAME)

🐡GCSライブラリのインストール(ローカル)

 AnacondaNavigatorを起動し、使用している仮想環境のターミナルを起動して以下のコマンドを実行します。

pip install django-storages pip install google-cloud-storage

🐡デプロイの実行(ローカル)

  以下のコマンドを実行します。

pip freeze > requirements.txt ←ライブラリをインストールしたため python manage.py collectstatic gcloud app deploy --no-cache --project=[プロジェクトID]

 バケットがないというエラーが出た場合は以下の通り実行し、バケット名を指定します。

gcloud app deploy --project=[プロジェクトID] --bucket=gs:[バケット名]

 実行後、バケットを確認し、ファイルが格納されていることを確認します。


 

🚀手順3:CloudSQLの設定

 テキストデータをCloudSQLに保存できるように設定します。データベースはMySQLを使用します。

🐡SQLAdminAPIの有効化(GoogleCloud)

 こちらをクリックし、SQLAdminAPIを有効化します。

cloudAdmin.png


🐡Cloud SQL Proxyのインストール(ローカル)

 https://dl.google.com/cloudsql/cloud_sql_proxy_x64.exe

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

🐡Cloud SQL インスタンスの生成(GoogleCloud)

 プロジェクトからCloudSQLを選択し、インスタンスを生成します。

db-instance.png

 以下のようにCPUや、メモリ、ディスク、バックアップの時間など詳細に設定できます。

 留意点はリソースを最小限にすること、リージョンを「asia-north-east1」とすること、IPをパブリックIPにすること、パスワードを控えておくことです。

sql-params1.png

sql-params2.png


🐡ユーザとデータベースの作成

 コンソールから、ユーザとデータベースを新規作成します。ユーザ名、データベース名、パスワードを控えておきます。

users.png

create-database.png

 


🐡setting.pyの変更

 CloudSQLへの接続ができるように設定を変更します。既存のSQLiteの設定はコメントアウトか削除します。

 〇変更前

if os.getenv('GAE_APPLICATION', None): # 本番環境 DEBUG = False ALLOWED_HOSTS = ['apply-for-overtime.an.r.appspot.com'] else: # 開発環境 DEBUG = True ALLOWED_HOSTS = ['*'] #---- DATABASES = { 'default': { 'ENGINE': 'django.db.backends.sqlite3', 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), } }

 〇変更後

if os.getenv('GAE_APPLICATION', None): # GAE本番環境 DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'HOST': '/cloudsql/[YOUR_INSTANCE_CONNECTION_NAME]', 'USER': '[YOUR-USERNAME]', 'PASSWORD': '[YOUR-PASSWORD]', 'NAME': '[YOUR-DATABASE]', } } else: # 開発環境 # 事前に./cloud_sql_proxyを実行してプロキシ経由でアクセスできるようにする必要がある。 DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'HOST': '127.0.0.1', 'PORT': '3306', 'USER': '[YOUR-USERNAME]', 'PASSWORD': '[YOUR-PASSWORD]', 'NAME': '[YOUR-DATABASE]', } } #---- #DATABASES = { # 'default': { # 'ENGINE': 'django.db.backends.sqlite3', # 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), # } # }

 [YOUR-USERNAME]、[YOUR-PASSWORD]、[YOUR-DATABASE]はこれまで、CloudSQLで設定したものを指定します([YOUR-DATABASE]はインスタンス名ではなくデータベース名を指定します)。

[YOUR_INSTANCE_CONNECTION_NAME]はダッシュボードの以下の項目を指定します。

connection-name.png


 

🚀手順4:本番環境での確認

 CloudSQLの設定が環境したら、いよいよ本番のCloudSQLにテーブルを作成します。本手順が終わり次第デプロイ完了です。

🐡mysqlclientのインストール

 AnacondaNavigatorを起動し、使用している仮想環境のターミナルから以下のコマンドを実行します。

pip install mysqlclient

 ライブラリのインストールを反映させるために、以下のコマンドを実行します。

pip freeze > requirements.txt

🐡開発環境からCloudSQLのデータベースを起動し、データベースを移行する

 先ほどダウンロードした「cloud_sql_proxy.exe」をプロジェクトのルートディレクトリに移動し、以下のコマンド実行します。

./cloud_sql_proxy -instances="[YOUR_INSTANCE_CONNECTION_NAME]"=tcp:3306

 成功すると、接続状態になるので、ターミナルをもう一つ開き、以下のコマンドを実行してデータベースを作成します。

python manage.py makemigrations python manage.py migrate

 以上でテーブル作成完了です。

 ここで、データベースへのアクセスを確認するための管理者ユーザを作成します。

python manage.py createsuperuser

 管理者ユーザを作成したら、「http://127.0.0.1:8000/admin」にアクセスして、管理者ユーザでログインします。

ログインできれば、ローカルのDjango環境を使用して、CloudSQLのデータベースを参照できたことになります。

 次に、デプロイを実行して、GAE環境でCloudSQLのデータベースにログインできるか確認します。

gcloud app deploy --project=[project-id] gcloud app browse --project=[project-id]

 「http://[target-url]/admin」にアクセスして、管理者ユーザでログインします。ログインが成功できればデプロイは完了です。

 


🐡GAE環境(本番環境)でのみログインした時ServerError(500)となるとき

 管理画面のCSSの読み込みに失敗している可能性があります。ログエクスプローラをみて、404エラーが発生している場合、こちらのサイト📒の5管理場面のCSSを行います。それでもダメな場合はsetting.pyの内容を確認します。これまで以下のケースでSeverErrorとなったことがあります。

  • ・DATABASEがSQLiteを指定する記述が残っていた。
  • ・ALLOWED_HOSTSが間違っていた。

🐡フロントエンドから画像が見えないとき

 GCSから、今回Djangoをデプロイしたバケットを選択し、「権限」⇒「新しいプリンシパル」に「AllUser」を追加しロールに「Strageオブジェクト閲覧者」を設定する。


 

🚀手順4:フロントエンドの設定

 フロントエンドのAPIで指定するURLに以下の情報の中の「target url」に記載されている文字列を指定します。

descriptor: [C:\Users\Syota_Hayashi\Desktop\dev\python\drf_api_sns\app.yaml] source: [C:\Users\Syota_Hayashi\Desktop\dev\python\drf_api_sns] target project: [apply-for-overtime] target service: [default] target version: [20240622t143858] target url: [https://apply-for-overtime.an.r.appspot.com]←これを使用する target service account: [apply-for-overtime@appspot.gserviceaccount.com]

 

🚀まとめ

 いかがだったでしょうか。私自身が苦労したことと、性質上デプロイについて取り扱っている動画コンテンツが見つけられなかったため記事にしました。

 アプリを考えるのも作るものも時間と労力を要しますが、デプロイ(環境構築)もまた最後の難関であり、ハマると中々抜け出せません。私は、記事を書きながらの作業でしたが、前回と違う部分でハマってしまい、15時間かかりました。しかし、1度できてしまえばある程度手順化できる作業なのでアプリ開発よりは素段落楽だと思います(アプリ開発と比べて想像力・思考力を要さない)。

 困った時はこの記事を参考にしてください。次回Node.jsでデプロイするときに、Node.jsの手順についても記事を作成します。

←ホームに戻る