既存のDjangoプロジェクトをGoogle App Engine スタンダード環境にデプロイする

DjangoをGAE(Google App Engine) スタンダード環境にデプロイしようと調べてみると、サンプルアプリをデプロイする方法はたくさん見つかるのですが、ローカル環境で開発していた既存のDjangoプロジェクトをデプロイする方法は見当たらなかったので記事にしたいと思います。

基本のGAEの設定は、公式ドキュメント通りです。

> 参考文献
>App Engine スタンダード環境での Django の実行（Google App Engine公式ドキュメント）
>https://cloud.google.com/python/django/appengine?hl=ja

## プロジェクトの作成やSDKインストール、APIの有効化
公式ドキュメントの「始める前に」の節(https://cloud.google.com/python/django/appengine?hl=ja#header) を実行してください。
> 1. Google Cloud Platform Console でプロジェクトを作成します。
> 2. プロジェクトへの課金を有効にします。
> 3. Cloud SDK をインストールします。
> 4. Google Cloud SQL APIの有効化

## モジュールのインストール
今回は、Google Cloud SQL(MySQL)を使用するので、MySQL操作用のライブラリ `PyMySQL` をインストールします。
また、 PyMySQL は現時点ではDjango 2.2に対応していないので、2.1にします。

```bash:bash
$ pip install Django==2.1.14
$ pip install PyMySQL==0.9.3
$ pip install gunicorn

#忘れないうちに
$ pip freeze > requirements.txt
```


## Djangoプロジェクトへのソースの追加
Googleのサンプルアプリと、既存のアプリを比較すると `app.yaml` という設定ファイルが足りない事と、 `settings.py` の記載内容が違うことがわかります。
そこで、この二つを作成・編集します。

## app.yamlの追加
GAEへのデプロイに関する構成情報を `app.yaml` に記載することによりGAEが認識します。

`manage.py` と同じ階層にファイルを生成し、以下の内容を記述します。

このyamlファイルでは、GAEが `static/` ディレクトリから静的ファイルを提供することを記述しています。

```yaml:app.yaml
runtime: python37

handlers:
# This configures Google App Engine to serve the files in the app's static
# directory.
- url: /static
  static_dir: static/

# This handler routes all requests not caught above to your main app. It is
# required when static routes are defined, but can be omitted (along with
# the entire handlers section) when there are no static files defined.
- url: /.*
  script: auto
  # SSLで常時接続させる場合はコメントアウトを外す
  # secure: always


# This is imports the WSGI-compatible object of your Django app
entrypoint: gunicorn -b :$PORT [Djangoプロジェクト名].wsgi
```
### settings.pyのデータベースの設定フィールドを書き換える
今回Google Cloud SQL(MySQL)を使用する関係で、`setting.py`内のデフォルトのsqlite3の記述を以下の内容で上書きします。
記載するMySQLのユーザーネームやパスワードは、次のセクションで設定します。

```python:settings.py
# Database
# https://docs.djangoproject.com/en/2.1/ref/settings/#databases

# Install PyMySQL as mysqlclient/MySQLdb to use Django's mysqlclient adapter
# See https://docs.djangoproject.com/en/2.1/ref/databases/#mysql-db-api-drivers
# for more information
import pymysql  # noqa: 402
pymysql.install_as_MySQLdb()

# [START db_setup]
if os.getenv('GAE_APPLICATION', None):
    # Running on production App Engine, so connect to Google Cloud SQL using
    # the unix socket at /cloudsql/<your-cloudsql-connection string>
    DATABASES = {
        'default': {
            'ENGINE': 'django.db.backends.mysql',
            'HOST': '/cloudsql/[YOUR-CONNECTION-NAME]',
            'USER': '[YOUR-USERNAME]',
            'PASSWORD': '[YOUR-PASSWORD]',
            'NAME': '[YOUR-DATABASE]',
        }
    }
else:
    # Running locally so connect to either a local MySQL instance or connect to
    # Cloud SQL via the proxy. To start the proxy via command line:
    #
    #     $ cloud_sql_proxy -instances=[INSTANCE_CONNECTION_NAME]=tcp:3306
    #
    # See https://cloud.google.com/sql/docs/mysql-connect-proxy
    DATABASES = {
        'default': {
            'ENGINE': 'django.db.backends.mysql',
            'HOST': '127.0.0.1',
            'PORT': '3306',
            'NAME': '[YOUR-DATABASE]',
            'USER': '[YOUR-USERNAME]',
            'PASSWORD': '[YOUR-PASSWORD]',
        }
    }
# [END db_setup]
```
##　Google Cloud SQLの設定
Google App Engineでは、Cloud SQL Proxy を使用して Cloud SQL インスタンスと通信します。
その後、MySQLクライアントを使用してローカルからCloud SQL上のデータベースを管理コマンドを使用して設定します。  

### Cloud SQL Proxy のインストール
Cloud SQL Proxy のインストール方法はオペレーティングシステムによってコマンドが異なるので、公式ドキュメントの「Cloud SQL Proxy をインストールする」(https://cloud.google.com/python/django/appengine?refresh=1#setting_up_your_local_environment) を参考に実行してください。

## Cloud SQL インスタンスを作成する
### Cloud SQL for MySQL 第 2 世代のインスタンスを作成します。
こちらのリンクから実行できます。
https://cloud.google.com/sql/docs/mysql/create-instance?refresh=1

### Cloud SDK を使用して次のコマンドを実行します。
`[YOUR_INSTANCE_NAME]` は、Cloud SQL インスタンスの名前を表します。

```:bash
$ gcloud sql instances describe [YOUR_INSTANCE_NAME]

# こうするとわかりやすい
$ gcloud sql instances describe [YOUR_INSTANCE_NAME] | grep connectionName
```
出力の `[CONNECTION_NAME]` に示されている値を書き留めます。

`[CONNECTION_NAME]` の値は、`[PROJECT_NAME]:[REGION_NAME]:[INSTANCE_NAME] `の形式になっています。

## Cloud SQL インスタンスを初期化する
#### `[CONNECTION_NAME]` の値を使用して Cloud SQL Proxy を起動

```:LinuxとMacOS
$ ./cloud_sql_proxy -instances="[YOUR_INSTANCE_CONNECTION_NAME]"=tcp:3306
```

```:windows
cloud_sql_proxy.exe -instances="[YOUR_INSTANCE_CONNECTION_NAME]"=tcp:3306
```
`[YOUR_INSTANCE_CONNECTION_NAME]` を上記の手順で記録した `[CONNECTION_NAME]` の値に置き換えます。

ローカルでテストを行うために、このステップでローカルコンピュータから Cloud SQL インスタンスへの接続を確立します。ローカルでのアプリのテストが終了するまで、このコマンドを実行したままの状態にしておいてください。

#### Cloud SQL のユーザーとデータベースを作成
Cloud SQL インスタンスへ接続したターミナルは開いたまま、もう一つのターミナルを開いて、Cloud SQL のユーザーとデータベースを作成します。

MySQLクライアントが導入されていない場合は `brew install mysql` 等、OSに合わせてインストールしてから作業を進めてください。

```:bash
$ mysql --host 127.0.0.1 --user root --password
Enter password: [Cloud SQLインスタンス(データベース)作成時のrootパスワード]

mysql> CREATE DATABASE [DATABASE_NAME];
Query OK, 1 row affected (...

mysql> CREATE USER '[YOUR_USER_NAME]' IDENTIFIED BY '[YOUR_USER_PASSWORD]';
Query OK, 0 rows affected (...

mysql> GRANT ALL ON *.* TO '[YOUR_USER_NAME]';
Query OK, 0 rows affected (...
```

ここまでできたら、Cloud SQL のユーザーとデータベースの作成は完了ですので、 `Control + D` 等でMySQLクライアントから抜けてください。

## データベース設定を構成する
ここまでできたら、先ほど `settings.py` のデータベースの設定を変更した部分を設定したGoogle cloud SQLの設定に書き換えます。
`[YOUR-CONNECTION-NAME]` は、 `[CONNECTION_NAME]` の値。

 `[YOUR-USERNAME]` ・ `[YOUR-PASSWORD]` ・ `[YOUR-DATABASE]` を先ほどMySQLクライアントで設定した値に書き換えます。

以上で設定は完了です！

## 開発環境の起動と、アプリのデプロイ
### 開発環境の起動
ここまでくれば基本的にはいつものDjango開発と大差ありません。

あらかじめ必要なライブラリがインストールされているPython環境で、先ほどから接続している Cloud SQL Proxy から、Cloud SQL のマイグレーションを行います。

```:bash
$ python manage.py makemigrations
$ python manage.py migrate
$ python manage.py createsuperuser
$ python manage.py runserver
```
これらを実行したのち、 `http://localhost:8000/` にアクセスできましたら、開発環境へと正常にアクセスできています。
Djangoと同じく必要なライブラリが足りないとエラーになるので、適意インストールしながらすすめてください。

###  アプリのデプロイとログの確認
ここまで完了したら、アプリを実際にGAEにデプロイします。
`manage.py` と同じ階層で、 `app.yaml` のあるアプリのルートディレクトリで次のコマンドを実行するとデプロイできます。

```:bash
$ gcloud app deploy

#完了したら、次のコマンドでページを開けます
$ gcloud app browse
```

これで、ローカル環境で開発していたアプリをGAEにデプロイできたと思います。

お疲れ様でした！


# ImageFieldを使用している場合
ImageFieldを使用している場合、 Cloud SQLには画像のパスが保存され実態であるファイル自体は保存することができずエラーになってしまいます。
なので、画像をGoogle Cloud Strageにアップロードすることで解決することができます。

手順に関しては別の記事がありますのでそちらを参照してください。

**Django + Google Cloud Strageで、ImageFieldの画像をサーバでも正常に表示させる**
https://qiita.com/waka424/items/1e40e169375b807f715b