brefphpを使って簡単にLaravelをサーバレス環境で動かす

# はじめに

ここではbrefについてと、実際の使い方や使ってみた所感などをまとめてみようと思います。

## [brefphp](https://bref.sh/) とは？

PHPアプリケーションをAWS Lambdaに簡単にデプロイするための [Composer](https://getcomposer.org/) パッケージです。
コマンド1つでサーバーからアプリケーションのデプロイまで行えます。

# :information_source: 使い方

## Step :one: serverless のインストール

bref は serverless コマンドを使用してAWS環境にデプロイを行います。
以下のコマンドでインストールしましょう。

npmの場合

```
npm install -g serverless
```

yarnの場合

```
yarn global add serverless
```

追加したら以下のコマンドでちゃんと実行できるか確認しましょう

```
$ serverless --version
Framework Core: 2.4.0
Plugin: 4.0.4
SDK: 2.3.2
Components: 3.2.1
```

## Step :two: Laravelプロジェクトの作成 [[公式](https://readouble.com/laravel/8.x/ja/installation.html)]

今回はLaravelで使うため、プロジェクトを作成します。

Laravelインストーラーがない場合はインストールします

```
composer global require laravel/installer
```

プロジェクトを作成します。

```
laravel new brefphp-test
```

## Step :three: brefphp パッケージのインストール [[公式](https://bref.sh/docs/frameworks/laravel.html)]

作成したLaravelプロジェクトのディレクトリに移動して、
brefphpをインストールします。

```
composer require bref/bref bref/laravel-bridge
```

デフォルトのサーバ構成設定ファイルを作成します。

```
php artisan vendor:publish --tag=serverless-config
```

## Step :four: デプロイの準備

### 「アクセスキーID」と「シークレットアクセスキー」の作成
デプロイ前に、デプロイ先のサーバ(AWS)のアクセスキーとシークレットキーを使用します。
IAMのユーザー追加画面でテスト用ユーザーを作りましょう。

1. ユーザー名を決めて、アクセスの種類の「プログラムによるアクセス」をチェックします。
![WS000000.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/157186/948f05e2-acd6-6756-663b-c5cc6cf02b84.jpeg)

2. アクセス権限の設定です。「既存のポリシーを直接アタッチ」を選択して「AdministratorAccess」を選択します。
 名前の通り最強権限なので、発行されるキーは絶対に外部に漏らさないでください！
![WS000001.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/157186/12399135-2314-31e0-99d4-1679e492ac14.jpeg)

3. タグはよしなに追加します。（自分は何もしません）
![WS000002.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/157186/9ee7406a-d64b-7ed0-6c0c-f281b6de36ce.jpeg)

4. ユーザーの追加完了です。
  「アクセスキーID」と「シークレットアクセスキー」をメモしておいてください。
![WS000004.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/157186/dc37e43d-83ea-3a71-3bfe-56c482348e86.jpeg)

### プロファイルの追加

メモした「アクセスキーID」と「シークレットアクセスキー」をプロファイルに登録します。

```
$ aws configure --profile <プロファイル名>

AWS Access Key ID [None]: <アクセスキーID>
AWS Secret Access Key [None]: <シークレットアクセスキー>
Default region name [None]:
Default output format [None]:
```

すると、 `~/.aws/credentials` (Windowsの場合は `%USERPROFILE%/.aws/credentials`) にプロファイルが登録されます。

```text:/.aws/credentials
[<プロファイル名>]
aws_access_key_id = <アクセスキーID>
aws_secret_access_key = <シークレットアクセスキー>
```

## Step :five: デプロイ

以下のコマンドでデプロイします。
`<プロファイル名>`は、先程(Step4)で登録した名前を指定してください。

```
$ serverless deploy -v --aws-profile=<プロファイル名>

Serverless: Packaging service...
Serverless: Excluding development dependencies...
Serverless: Creating Stack...
Serverless: Checking Stack create progress...
～～～～～～～[色々ログ出てきて]～～～～～～～
Serverless: Stack update finished...
Service Information
service: laravel
stage: dev
region: us-east-1
stack: laravel-dev
resources: 15
api keys:
  None
endpoints:
  ANY - https://xxxxxxxxxx.execute-api.us-east-1.amazonaws.com/dev
  ANY - https://xxxxxxxxxx.execute-api.us-east-1.amazonaws.com/dev/{proxy+}
functions:
  web: laravel-dev-web
  artisan: laravel-dev-artisan
layers:
  None

Stack Outputs
WebLambdaFunctionQualifiedArn: arn:aws:lambda:us-east-1:000000000000:function:laravel-dev-web:2
ArtisanLambdaFunctionQualifiedArn: arn:aws:lambda:us-east-1:000000000000:function:lara![WS000000.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/157186/369830d6-c723-975d-2b78-c94add355649.jpeg)
vel-dev-artisan:2
ServiceEndpoint: https://xxxxxxxxxx.execute-api.us-east-1.amazonaws.com/dev
ServerlessDeploymentBucketName: laravel-dev-serverlessdeploymentbucket-o2rnhi5yd9v6
```

生成されたエンドポイントにアクセスして、LaravelのWelcomeページが出てくれば成功です
![Laravel.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/157186/8b225835-5947-4f5f-5884-27c42217f099.png)

AWS Lambdaの関数にも追加されています。
![WS000000.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/157186/141dc4f9-d47d-42ca-639c-6fbb1e0f7e26.jpeg)

## Step :six: 環境の削除

`$ serverless remove` コマンドで環境を削除できます。

```
$ serverless deploy -v --aws-profile=<プロファイル名>

Serverless: Getting all objects in S3 bucket...
Serverless: Removing objects in S3 bucket...
Serverless: Removing Stack...
Serverless: Checking Stack removal progress...
～～～～～～～[色々ログ出てきて]～～～～～～～
Serverless: Stack removal finished...
Serverless: Stack removal finished...
```

# :desktop: ローカル環境の構築

Dockerでローカル環境を構築します。
すでにローカル用のイメージは用意されているので、すぐに立ち上げることができます。

Laravelプロジェクトと同じディレクトリに `docker-compose.yml` の作成

```yml:docker-compose.yml
version: "3.5"

services:
    web:
        image: bref/fpm-dev-gateway
        ports:
            - '8000:80'
        volumes:
            - .:/var/task
        links:
            - php
        environment:
            HANDLER: public/index.php
            DOCUMENT_ROOT: public
    php:
        image: bref/php-74-fpm-dev
        volumes:
            - .:/var/task:ro
```

`docker-compose up -d` でコンテナを起動します。[^1]

`http://localhost:8000` にアクセスするとLaravelのWelcomeページが表示されます。

# :moneybag: 料金について

ローカル環境には料金はかかりません。
AWSにデプロイした際に以下のサービスを利用するため、料金が発生します。

1. [Lambda](https://aws.amazon.com/jp/lambda/pricing/)
2. [API Gateway](https://aws.amazon.com/jp/api-gateway/pricing/)
3. [S3](https://aws.amazon.com/jp/s3/pricing/)
4. [CloudFormation](https://aws.amazon.com/jp/cloudformation/pricing/)

# 所感

個人開発で使って便利だったので、会社のプロジェクトでも導入してみました。
LambdaやAPI Gatewayによるいろいろな制限があったりして、思うように行かなかった部分もありますが、なんとかサービスもリリースしているような状態です。

EC2に比べてLambdaやAPI Gatewayの料金は基本的にトラフィック依存なので、
アクセス数のそんなにないサービスでは、かなりのコスト削減になりました。

### :warning: 発生した問題とその対策

実際にプロジェクトに導入した際に発生した問題と、それについての対策を記述します。
もし似たような問題が発生した場合は参考になればと思います。

#### POSTで10MBを超えるファイルをアップロードできない

API GatewayのPOSTリクエストサイズの最大が10MBのため、大容量のファイルをPOSTできません。
最終的にS3にアップロードする予定だったので、S3で[Presigned URL](https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/dev/PresignedUrlUploadObject.html)[^2]を発行して、それに対しアップロードするようにしました。

#### 6MBを超えるデータを返せない

またもやAPI Gatewayの制約により、レスポンスの最大は6MBです。
（まぁ、6MBなんて超えることなんて稀ですが…）
こちらも一度S3にアップロードして[Presigned URL](https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/dev/PresignedUrlUploadObject.html)を生成し、
フロントエンドから直接S3のURLを参照するようにしました。

#### APIのタイムアウトの最長が29秒

API Gatewayの制約上、29秒でタイムアウトしてしまいます。
なので、重い処理は[Amazon SQS](https://aws.amazon.com/jp/sqs/)に投げて処理するようにしました。

#### クーロンの最大時間が15分

バックグラウンドのクーロン処理は結局Lambda上で行うため、
Lambdaの制約上15分が限界です。

今の所、15分を超えるようなクーロンはないですが、
発生する場合は[Amazon SQS](https://aws.amazon.com/jp/sqs/)で分割して実行するか、
別途専用のサーバを立てる必要がありそうです。

#### ログの stack が使用できない

Brefのプロバイダにて、`stack` は強制的に `stderr` に置き換えられている。
`config/logging.php` に独自設定を追加するか、
プロバイダを読み込まないようにする対応が必要。


# PR

brefの[Slackコミュニティ](https://brefworkspace.slack.com/join/shared_invite/enQtNTcwMjU2NTcxNjAxLTIxYmM2MmRjMDkzYjdjYTNkMmE5NGI3YTcyZjc2ZGRjNTFmNjFmYzk5NWQ1YmVhMDkwNzExNzhjZThkZWM0ODE#/)に日本語チャンネルができました。
一緒にbrefについて語りましょう😆

[^1]: 初回のみイメージをビルドするため時間がかかります。
[^2]: 一時的に有効なURLを発行して直接S3にアップロードできるURL