GAE Flexでphp + phalcon + CloudSQLの環境を構築する


[Google Cloud Platform その２ Advent Calendar 2018](https://qiita.com/advent-calendar/2018/gcp2)の13日目の投稿です

次案件ではAWSではなくGCP（Google Cloud Platform）を使うことになり、
GAE(Google App Engine)で環境を構築してます。

前提条件として
GCPのGAEAPIの有効化とCloudSQLにDBを作成していることとします。
ここら辺は特にハマる場所もないかとおもいます。


# フォルダ構成

# 新構成（抜粋）
```
app.yaml
composer.json
cron.yaml
nginx-app.conf
php.ini
source/
└ public/
 └ info.php
vendor/
└ autoloader.php
```

# app.yaml

```yaml:app.yaml
env: flex

# サービス名
service: "default"

# ランタイムPHP（デフォルトは7.1が指定される）
runtime: php

# unix_socketソケット
beta_settings:
  cloud_sql_instances: [project_name]:asia-northeast1:[mysqlインスタンス名]

# ドキュメントルート
runtime_config:
  document_root: source/public
  enable_stackdriver_integration: true  //stack_driverの許可

# ビルド必要のないファイルがある場合は指定
skip_files:

# 並列処理の許可
threadsafe: true

# 環境変数
env_variables:
  MYSQL_USER: '****'
  MYSQL_PASSWORD: '********'
  MYSQL_DSN: '/cloudsql/[project_name]:asia-northeast1:[mysqlインスタンス名]'
```

※変更箇所は適宜書き換えてください

# compose.json

```json:composer.json
{
    "require": {
        "php": "7.0.*",
        "google/cloud-logging": "^1.11",
        "google/cloud-error-reporting": "^0.10.0",
        "ext-yaml": "*"
    }
}
```

phalconは現在php7.0でしか対応されていないため、バージョン指定します
yamlを使いたいのでextensionを読み込ませます

# nginx-app.conf

```nginx:nginx-app.conf
    location / {
        try_files $uri $uri/ /index.php?_url=$uri&$args;
    }

    location ~ \.php {
        fastcgi_pass localhost:9000;
        fastcgi_index /index.php;

        include fastcgi_params;
        fastcgi_split_path_info       ^(.+\.php)(/.+)$;
        fastcgi_param PATH_INFO       $fastcgi_path_info;
        fastcgi_param PATH_TRANSLATED $document_root$fastcgi_path_info;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    }

    location ~ /\.ht {
        deny all;
    }
```

# php.ini

```ini:php.ini
extension=phalcon.so
```

これでphalconextensionが使えるようになります。

# info.php

```php:public/info.php
<?php
phpinfo(); 
```

# 反映

```
$ gcloud app deploy
```

# phpinfoの確認

https://[project_name].appspot.com/info.php
にアクセスしてみましょう

![181203-0001.png](https://qiita-image-store.s3.amazonaws.com/0/42874/13600e0d-3eef-ee29-2155-0f1ed2ab088f.png)
![181203-0002.png](https://qiita-image-store.s3.amazonaws.com/0/42874/bf4a9310-16fd-5709-8354-8a687f8b063b.png)
![181203-0003.png](https://qiita-image-store.s3.amazonaws.com/0/42874/b3307b61-f6a1-043d-89a5-0c14d6af7284.png)


# ハマりやすい場所

## DB接続時にNo such file or directoryって言われる

app.yamlに記載されている

```
MYSQL_DSN: '/cloudsql/[project_name]:asia-northeast1:[mysqlインスタンス名]'
```

を接続情報として記載していますが、そもそも作られたコンテナに/cloudsql/[project_name]:asia-northeast1:[mysqlインスタンス名]ファイルが存在しなくてはお話になりません

このファイルが存在しているかを確認します

コンソール上からインスタンスに入ります
![181203-0004-2.png](https://qiita-image-store.s3.amazonaws.com/0/42874/c9c88f29-ed59-e53a-4f7b-3545eea31f67.png)

![181203-0005.png](https://qiita-image-store.s3.amazonaws.com/0/42874/369368f0-d7bb-1772-b36a-1e972e24ca7a.png)

VMへのSSH接続を押すとインスタンスにはいれます

### コンテナに入る

プロセスを確認して見るとNAMESに記載されている「gaeapp」と言うコンテナ名だとわかります

```
$ docker ps
CONTAINER ID        IMAGE                                                                                                                           COMMAND                  CREATED             STATUS              PORTS                                                                  NAMES
6a6888acd69a        asia.gcr.io/[project_name]/appengine/default.20181127t164715@sha256:9da1cd92a7ed1b616bd76c700a67406483877a12c0884e4a007f6f872759fc05   "/usr/bin/supervis..."   6 days ago          Up 6 days           172.17.0.1:8080->8080/tcp                                              gaeapp
a55365a7197c        gcr.io/google-appengine/cloud-sql-proxy                                                                                         "/cloud_sql_proxy ..."   6 days ago          Up 6 days                                                                                  cloudsql
42609bb8968f        gcr.io/google-appengine/api-proxy                                                                                               "/proxy"                 6 days ago          Up 6 days                                                                                  api
d12fd39d166e        gcr.io/google-appengine/nginx-proxy                                                                                             "/var/lib/nginx/bi..."   6 days ago          Up 6 days           8080/tcp, 0.0.0.0:8443->8443/tcp, 8090/tcp, 0.0.0.0:10402->10402/tcp   nginx_proxy
9e6594b862d8        gcr.io/google-appengine/iap-watcher                                                                                             "./start_iap_watch..."   6 days ago          Up 6 days                                                                                  iap_watcher
71b8c45c76e0        gcr.io/google-appengine/fluentd-logger                                                                                          "/opt/google-fluen..."   6 days ago          Up 6 days                                                                                  fluentd_logger
```

gaeappコンテナ入り対象ファイルを確認します

```
$ docker exec -it gaeapp /bin/bash
root@6a6888acd69a:/app# cd /cloudsql/
root@6a6888acd69a:/cloudsql# ls -la
total 8
drwxr-xr-x 2 root root 4096 Nov 27 07:52 .
drwxr-xr-x 1 root root 4096 Nov 27 07:52 ..
srwxrwxrwx 1 root root    0 Nov 27 07:52 [project_name]:asia-northeast1:[DBインスタンス名]
root@6a6888acd69a:/cloudsql# 
```

これがない場合はAPIが有効化になっていない可能性が高いので
https://console.cloud.google.com/apis/api/sqladmin.googleapis.com/overview?project=raharu-project-3&authuser=0&duration=PT1H
で有効化されているか確認してください

## php_network_getaddresses: getaddrinfo failed: Name or service not knownと言われる

この場合はFW側から生成している接続形式がおかしい場合があります

エラーを見ると

```
#0 [internal function]: PDO->__construct('mysql:host=mysq...', 'root', '*******', Array)
```

となっており、接続形式がまちがっています
mysql:host=mysq...省略されていますが、hostでは繋げません
mysql:unix_socket=/cloudsql/[project_name]:asia-northeast1:[DBインスタンス名]になるようにソースを変更します
(この形式になるようになんとかする全FW共通)

### Phalconの場合

```php:apps/config/services.php
$di->set('base_master', function () use ($config) {
    return new DbAdapter(array(
        'unix_socket' => $config->mysql_base_master->host,
        'username' => $config->mysql_base_master->username,
        'password' => $config->mysql_base_master->password,
        'dbname' => $config->mysql_base_master->dbname,
    ));
});
```

```php:/app/config/config.php
    'mysql_base_master' => array(
        'adapter'     => 'Mysql',
        'host'        => getenv('MYSQL_DSN'),
        'username'    => getenv('MYSQL_USER'),
        'password'    => getenv('MYSQL_PASSWORD'),
        'dbname'      => '[db_name]',
        'charset'     => 'utf8',
    ),
```

自分の場合は環境差分を考えて、DbAdapterのkey部分も設定ファイルに落とし込んでいます

# YAMLエクステンションの有効化について

composerのインストールを行う際にext-yamlはローカル環境ではyamlを先にインストールしておかないとextensionを引っ張ってこれずにインストールエラーになります、そのためあらかじめローカル環境でpeclからyamlをインストールしておく必要があります。（プロジェクトにyamlファイルを使わない場合は必要ありません）

# Phalconのバージョンについて

現在提供されているphalconのバージョンは3.0.4です、
このバージョンのZephirにはExceptionを２重にスローされてしまうバグがあり、
PhalconのBeforeExptionでエラーハンドリングをした場合に問題が発生してしまい、
自分はここでかなり苦戦しました、一旦の解決だけを見るのであればpublic/index.phpでキャッチしたExceptionをごにょごよすればできますが、マルチモジュール構成にしている場合はちょっと困ったことになります。

Phalcon >= 3.2以上の対応が待たれます

# まとめ

GAE Flexは複数のデプロイ方式にも対応されており、マシンスペックアップやスケーリングも容易にできます。
設定もapp.yamlに統一されており、本番環境で使用するにも十分に耐えうる環境だと思います。

インフラエンジニアがいない小さな会社や個人開発の場合非常に強い味方になってくれるはずです。

今後ともGCPもといGAEの発展にさらに期待しています

# 参考
https://github.com/phalcon/cphalcon/issues/12154