Google App EngineへのLaravel環境デプロイが思っていたより難しかった件


GCPでLaravelを動かすだけだと思っていたのですが、思いのほか時間がかかったのと、一つで完結するような記事がなかったので、自分へのメモと今後Google Cloud Platformをはじめてみたい方向けにこの記事を残します


<h1>始める前に知っておくこと</h1>
天下のGoogle様のサービスだけあってできることは非常に多いですが、個人で通常利用する分には<font color="DeepPink">**課金関連以外でのサポートを受けれません。**</font>そのため、基本的に技術的な問題に関してはコミュニティで質問することを推奨してます。
なので自分で調べたり、質問する力がないとすこしきついと感じました

また、詳しくは[この記事](https://qiita.com/Baisel/items/d975936328efe36534dc)で説明していますが、AppEngineでは<font color="DeepPink">**デプロイするファイルの差分が10000以上だとエラーとなる**</font>ため、回避手段もいくつかありますが、10000なんて必要なライブラリなどを入れていたらすぐに超えてしまうので、注意が必要です
<br>
<h1>ローカル環境を構築</h1>
まずはローカルにlarvelの環境を作り、正常に表示されることを確認します

```console
$ composer create-project "laravel/laravel=5.7.*" sample
$ cd sample
$ php artisan serve
```
`127.0.0.1:8000`にブラウザでアクセスしてページが表示されていればOK

<h4>app.yamlを追加</h4>
先にローカル環境に作ったプロジェクトに`app.yaml`を追加しておきます(.envとかと同じ階層でOK)
runtimeにはローカル環境で使っているPHPのバージョンを記載し、自分なら**PHP Ver7.3**なので**php73**
※現時点ではPHP7.4まで対応していますが、詳細は[こちら](https://cloud.google.com/appengine/docs/standard/php7/runtime)で確認してください　

```console:app.yaml
runtime: php73

env_variables:
  APP_KEY: YOUR_APP_KEY  ## .envのAPP_KEYを記載　ex) base64:Ts6Uz7mj0x~~~
  APP_STORAGE: /tmp
  VIEW_COMPILED_PATH: /tmp
  SESSION_DRIVER: cookie
```
↑`app.yaml`はインスタンスが崩れていたりするとデプロイ時にエラーを出力するので注意！

<h4>App Engine用にストレージパスを追記</h4>

```PHP:bootstrap/app.php
/*
|--------------------------------------------------------------------------
| Set Storage Path
|--------------------------------------------------------------------------
|
| This script allows you to override the default storage location used by
| the  application.  You may set the APP_STORAGE environment variable
| in your .env file,  if not set the default location will be used
|
*/
//この行を追記---------------------------------------------------|
$app->useStoragePath(env('APP_STORAGE', base_path() . '/storage'));

return $app;
```
<h4>不要なパッケージを削除しておく</h4>
存在するとエラーの原因になるので消しておく

```console
$ composer remove --dev beyondcode/laravel-dump-server
$ composer remove --dev facade/ignition   //ちなみにLaravel 6.0を使っているならこちらを削除
```
<br>
<h1>Google App Engineにデプロイ</h1>
まずはGoogleCloudの[コンソール](https://console.cloud.google.com/)で適当なプロジェクトを作っておきます
![asdad.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/236873/896d90eb-c7e0-c179-c276-f3a510da9a51.png)
<h4>Google Cloud SDKをインストール</h4>
ローカル環境にGoogle Cloud SDKをインストール
自分の場合はUbuntuだったのですが、ほかの環境の方は[ここ](https://cloud.google.com/sdk/docs/quickstart)を確認

```console
$ echo "deb [signed-by=/usr/share/keyrings/cloud.google.gpg] https://packages.cloud.google.com/apt cloud-sdk main" | sudo tee -a /etc/apt/sources.list.d/google-cloud-sdk.list
$ sudo apt-get install apt-transport-https ca-certificates gnupg
$ echo "deb https://packages.cloud.google.com/apt cloud-sdk main" | sudo tee -a /etc/apt/sources.list.d/google-cloud-sdk.list
$ curl https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo apt-key --keyring /usr/share/keyrings/cloud.google.gpg add -
$ sudo apt-get update && sudo apt-get install google-cloud-sdk
```
<h4>Google Cloud SDKをセットアップ</h4>
**gcloud**コマンドが使えるようになったらローカル環境のフォルダに移動し、セットアップを行います
こちらも詳細は[こちら](https://cloud.google.com/sdk/docs/quickstart#initializing_the_sdk)に乗っていますが、以下の通りです

```console
$ gcloud init
Pick configuration to use:
 [1] Re-initialize this configuration [default] with new settings 
 [2] Create a new configuration
Please enter your numeric choice:  1  //デフォルトの設定があればそれを使うので「1」入力。最初などは「2」で設定を登録しておきます
```

```console
Choose the account you would like to use to perform operations for 
this configuration:
 [1] 登録していればここにメールアドレスが表示される
 [2] Log in with a new account
Please enter your numeric choice:  1　//メールアドレスが登録されていればそれを選択し、なければ「2」を押して、この後発行されるURLで追加できます
```

```console
Pick cloud project to use: 
 [1] 存在すればgoogle cloudのプロダクト名が表示されます
 [2] 存在すればgoogle cloudのプロダクト名が表示されます
 [3] Create a new project
Please enter numeric choice or text value (must exactly match list 
item):  2　//事前に作成しておいたGoogle Cloudのプロダクトの番号を選択
```
[asia-east1]が東京、[asia-east2]が大阪リージョンなのでお好きなのを選んでください。a,b,cも任意のものを選択してください

```console
Which compute zone would you like to use as project default?
 [1] [asia-east1-a] 　
 [2] [asia-east1-b]　
 ...
 [14] Do not use default zone
 Please enter your numeric choice: 1 //使用するリージョンを決めます。ここは使いたいリージョンを選んでください
```
<h4>デプロイ</h4>

```console
$ gcloud app deploy
Do you want to continue (Y/n)?  y
```
正常ならこんな画面に行くはずです
![https___qiita-image-store.s3.ap-northeast-1.amazonaws.com_0_123511_89ad103a-cdf9-5cbd-44ee-e2677c128631.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/236873/377fa77e-cbad-82e3-908d-dfaca730ef04.png)

<h1>Cloud SQLとの接続(クラウドデータベースとの接続)</h1>
<h4>SQLインスタンスを作成する</h4>
コンソールからSQLインスタンスを作成しますが、最初はアクティブになってないので「SQL」で検索すると出てきます
![dwdw.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/236873/09a7a195-dea5-11d7-e25e-c7a525f93645.png)

自分はMysqlを選択しています
![sas.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/236873/66279108-8e25-3ca4-e10b-fdc697dbacd6.png)
インスタンスIDは任意で大丈夫ですが、Mysqlのバージョンやリージョンはローカル環境に合わせましょう
![dedewdded.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/236873/cc0c7583-34b7-6222-ff09-2fee478bd423.png)
インスタンスを作成したらダッシュボードに表示されている**接続名**が**"YOUR_CONNECTION_NAME"**になるのでメモしておきましょう
また左の**"ユーザー"**からユーザーも作成できるので、.envで使っているユーザーと同じユーザーをつくっておきます(root以外を使っている場合)
![swswsws.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/236873/15a0e333-54bc-1724-d7fa-52f085878c7b.png)
<h4>データベースを作成する</h4>
SQLインスタンスのダッシュボード左のメニューから**"データベース"**を選択してローカル環境で使っているデータベースと同じ名前のデータベースを作っておきます
※コマンドでもデータベースを作成できます(gcloud sql databases create データベース名 --instance=インスタンス名)
![dedwedwedwed.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/236873/72b9a90f-5d78-b003-b8a3-c6e1ee2d8547.png)
<h4>ローカル環境でテーブルを作成</h4>
ローカル環境で以下のコマンドを実行し、テーブルを作成しておきます

```console
$ php artisan make:auth
$ php artisan session:table
$ php artisan migrate --force
```
<h4>ローカル環境のテーブルをクラウドと同期</h4>
以下のコマンドをローカル環境で実行するとrootのパスワードが求められ、成功すると同じディレクトリに`database_table`というファイルが作成されます

```console
$ mysqldump -u root -p -h localhost ローカル環境のデータベース名 > database_table
```
プロダクトのダッシュボードで「storage」を検索して、**stogare**のページを開き、任意の名称のパケットを作成します
![ddwdw.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/236873/fd7de067-b5cd-91d8-f9d0-cf5f72c9678e.png)
作成したパケットを選択して、ローカルで作成した`database_table`をアップロードします
![fsfsfsfsfsfs.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/236873/ba65163b-80cd-17b0-0207-2205685302a7.png)
ファイルがアップデートできたらSQLインスタンスのダッシュボードに移行し、インポートを選択します
![sasswq.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/236873/6aa4fe4e-7393-a739-f9ab-aa0bf7cb21a7.png)
先ほどアップデートしたファイルを参照し、**SQL**の形式で自分の使うデータベースにインポートします
![fwfw4fe4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/236873/ad413252-c32b-7414-a442-451c76203ef3.png)

<h4>デプロイ</h4>
ローカル環境の`app.yaml`を差し替えてデプロイを行います

```console:app.yaml
runtime: php73


# この記載がないとcssとjsがnot foundになるので必須
handlers:
# Serve a directory as a static resource.
- url: /css
  static_dir: public/css
- url: /js
  static_dir: public/js


env_variables:
  ## Put production environment variables here.
  APP_KEY: YOUR_APP_KEY  ## .envのAPP_KEYを記載　ex) base64:Ts6Uz7mj0x~~~
  APP_STORAGE: /tmp
  VIEW_COMPILED_PATH: /tmp
  CACHE_DRIVER: database
  SESSION_DRIVER: database
    ## Set these environment variables according to your CloudSQL configuration.
  DB_DATABASE: YOUR_DB_DATABASE　　　　　　　　#クラウドのデータベース名
  DB_USERNAME: YOUR_DB_USERNAME　　　　　　　　#クラウドのユーザー名
  DB_PASSWORD: YOUR_DB_PASSWORD　　　　　　　　#クラウドのユーザーのパスワード
  ## for MYSQL, use DB_SOCKET:
  DB_SOCKET: "/cloudsql/YOUR_CONNECTION_NAME"　#YOUR_CONNECTION_NAMEにはメモっていたものを記載
  ## for PostgreSQL, use DB_HOST:
  # DB_HOST: "/cloudsql/YOUR_CONNECTION_NAME"　#Postgresqlの場合はこのように記載

```

```console
$ gcloud app deploy
Do you want to continue (Y/n)?  y
```
正常にデプロイできると以下のような画面になるので、ユーザーの登録とログインができることを確認してください
![dwdwdwdwdw.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/236873/307246a6-98c3-7414-7c3b-459c5dd485fb.png)




<h2>エラー集</h2>
```console
SQLSTATE[HY000] [2002] No such file or directory (SQL: select * from sessions where id = dgbEmZ9AufF9UD9kkeUxLlfeppZ1Z3erfe7UHQbHd limit 1)
```
Cloud SQLに上手く接続できていないときに発生し、原因は様々ですが自分の場合は以下の手段で解決しました

まずはMysqlで`show variables like '%sock%';`を実行すると以下のような表示が出てくると思います

```
+-----------------------------------------+-----------------------------------------+
| Variable_name                           | Value                                   |
+-----------------------------------------+-----------------------------------------+
| performance_schema_max_socket_classes   | 0                                       |
| performance_schema_max_socket_instances | 0                                       |
| socket                                  | /tmp/mysql/mysql.sock 　　　　　　       |
+-----------------------------------------+-----------------------------------------+
```

socketの値を第2引数に入れます

```php5:config\database.php
'mysql' => [
    'unix_socket' => env('DB_SOCKET','/tmp/mysql/mysql.sock'),
           ],
```

```console:app.yaml
DB_SOCKET: "/cloudsql/YOUR_CONNECTION_NAME"
```

<h2>参考文献</h2>
https://cloud.google.com/community/tutorials/run-laravel-on-appengine-standard
https://qiita.com/kiyc/items/d86da4e5753220c121b9
https://memento-mori.jp/blog/back-end/google-app-engine_on_laravel
https://note.com/kawa1228/n/n4ab34e8f63fb
http://takaya030.hatenablog.com/entry/2017/07/17/181217
https://cloud.google.com/sdk/gcloud/reference/topic/gcloudignore
https://cloud.google.com/sql/docs/mysql/connect-admin-proxy?hl=ja
https://cloud.google.com/sql/docs/mysql/import-export/importing?hl=ja#console
https://github.com/GoogleCloudPlatform/cloudsql-proxy/issues/140
http://numa0323.hatenablog.jp/entry/2019/09/18/122436
https://laboradian.com/use-php72-with-ubuntu1604/