AppEngineフレキシブル環境でのDjango実行チュートリアルでつまづいたのでその備忘録

## はじめに

この記事は [ちゅらデータ Advent Calendar 2021 21日目](https://qiita.com/advent-calendar/2021/churadata)の記事となります。~~遅刻したけど @kueda_cs が22日と23日の記事をフライングしたので実質セーフですね！！！！！（アウト）~~

## この記事の内容

実務において Google App Engine の フレキシブル環境を使ってDjangoアプリを構築する機会があり、進めていく中で[公式ガイド](https://cloud.google.com/python/django/flexible-environment?hl=ja)を参考にしたのですが、ハマるハマる。

そこで記事を参考にするにもこちらは3年前の記事で、この通りには行かなかったので、私もしくは同じガイドでハマっている誰かの役にたってくれるといいと思いここにまとめるものとします。基本的にコードのハマるところを抑えつつ、前提的なところはカットして進めていきます。

この記事でまとめたドキュメントには書いていないポイント
- [Secret Manager に シークレット値を保存する](https://qiita.com/Intel0tw5727/items/f8f66986e59412d29977#secret-manager-%E3%81%AB-%E3%82%B7%E3%83%BC%E3%82%AF%E3%83%AC%E3%83%83%E3%83%88%E5%80%A4%E3%82%92%E4%BF%9D%E5%AD%98%E3%81%99%E3%82%8B)
- [アプリを App Engine フレキシブル環境にデプロイする](https://qiita.com/Intel0tw5727/items/f8f66986e59412d29977#%E3%82%A2%E3%83%97%E3%83%AA%E3%82%92-app-engine-%E3%83%95%E3%83%AC%E3%82%AD%E3%82%B7%E3%83%96%E3%83%AB%E7%92%B0%E5%A2%83%E3%81%AB%E3%83%87%E3%83%97%E3%83%AD%E3%82%A4%E3%81%99%E3%82%8B)

↓偉大なる先人
[Google App Engine フレキシブル環境でのDjangoの実行チュートリアルでハマった](https://qiita.com/kai_kou/items/0039e5258fc8577c4093)

## 「始まる前に」

プロジェクトを作りましょう。課金を有効にしましょう。ガイドで使うCloud SQL Admin API, Secret Manager, and Cloud Build API を有効にしましょう。

## 「環境を準備する」

クローンしてPythonの仮想環境をつくります。ここまではきっと大丈夫なはず。Cloud SQL Proxyもダウンロードして実行権限を与えましょう。

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

Cloud SQLを立ち上げます。HA構成にするとどちゃくそ高くなるので注意。もし他のDBサーバ立ち上げる場合は、接続できるようにドキュメント外で設定する必要があるので注意。Cloud Storageもちゃちゃっと立ち上げる。

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

**ここにつまづきポイントがあるので注意**

Secret Manager にシークレットとしてDjango環境ファイルを作成するとあるのですが、後ほど実施するローカルサーバからのアクセスの際にうまくいきません。

具体的には

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

```shell
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
```

のこの部分。内容をこのままSecretManagerに登録するのですが、そのままやると echo コマンドによってローカルではエラーが発生してしまいます（クラウド環境では大丈夫）。なので .env には下記内容で登録します。

```shell:.env
DATABASE_URL=postgres://DATABASE_USERNAME:DATABASE_PASSWORD@//cloudsql/PROJECT_ID:REGION:INSTANCE_NAME/DATABASE_NAME
GS_BUCKET_NAME=PROJECT_ID_MEDIA_BUCKET
SECRET_KEY=$(cat /dev/urandom | LC_ALL=C tr -dc '[:alpha:]'| fold -w 50 | head -n1)
```

手順最後にある「ローカル設定のオーバーライドを防ぐため、ローカル ファイルを削除します。」を無視します。でないとローカルからテストする場合にinvalid lineになってしまいうまく読み込まれません。

**なぜ上記現象が発生するのか**

djangoを構成するファイルの中に `mysite/setttings.py` というファイルがあり、この中のGoogle Cloudサービスを使う箇所を見てみましょう。

```python:mysite/settings.py
# [END_EXCLUDE]
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))
```

6行目以降の内容について、該当するプロジェクトからSecretManagerを参照して、`SETTINGS_NAME`に登録されている変数を取りに行きます。このときに事前に設定したdjango_settingsというSecretなデータを参照しているというわけです。もし `dev/stg/prd` という形で環境を分けるなら、SecretManagerにわけて登録しそれぞれ参照先が選べるようになるとスマートです。

上記設定が終わればシークレットへのアクセスを設定します（ローカル実行の場合は影響しない）。

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

ターミナルタブを2つ立ち上げ、1つはcloud sql auth プロキシの起動に、もう一つはDjangoの起動に使いましょう。DBにCloud SQLを使わなかった人は、正しくサーバーが選択されているか、ポートは正しく選択できているか確認しましょう。

## 「Django 管理コンソールを使用する」

adminユーザを作るだけなのでこれもOK。

## 「アプリを App Engine フレキシブル環境にデプロイする」

ドキュメントに則ってデプロイします。瞬間的ではありますが全世界に公開することになるので、気になる方はファイアウォールルールから自宅のIPのみ接続できるようにしましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/186010/647a250e-cdef-1c81-5713-ae2e4b0f7a1a.png)

## おわりに

いかがでしたでしょうか。ドキュメント通りうまく行かないということはありがちですが、意外と英単語並べて調べてみると解決できる方法は多いように思います。チュートリアルが常に新しいとは限らないので、こういう記事を残していきつつ誰かの役にたってくれるといいと思います。