【Laravel12】Brefを使ってサーバーレスアプリケーションを作成する

先日Laravel12が発表されたので、Laravelのスターターキットで作成したSPAをBrefを使ってAWS Lambdaにデプロイする流れをまとめてみます。

# 1. Brefとは
https://bref.sh

Brefは、**AWS Lambda上でPHPアプリケーションを実行するためのオープンソースツール**です。Lambdaはサーバーレス環境であり、従来のサーバー管理を不要にしますが、PHPはそのままではLambda上で動作しません。そこでBrefは、カスタムランタイムを提供し、LaravelのようなフルスタックフレームワークもLambda上で動かせるように工夫されています。

### Brefを使用したLambdaのリクエスト ~ レスポンス生成までの流れ

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/676792/69751c25-f207-4848-bb7a-0164e02916c0.png)
[引用：PHP-FPM runtime for AWS Lambda
](https://bref.sh/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ffpm-runtime.2bbecfff.png&w=3840&q=75)

#### 1. クライアントからのHTTPリクエスト
ユーザーが アプリケーション にアクセスすると、そのリクエストは Amazon API Gateway へ送られます。

#### 2. API Gatewayによるルーティング
API Gatewayは、設定されたエンドポイント（URLパスやメソッドなど）に基づいて、AWS Lambda 関数（Brefを用いたPHPランタイム）を呼び出します。

#### 3. Lambdaの起動とBrefの処理
Lambda関数が呼び出されると、`event`（API Gatewayから渡されるリクエストデータ）と`context`（Lambdaの実行コンテキスト情報）がLambdaランタイムに渡されます。
Bref はカスタムランタイムとして、Lambdaのライフサイクルに合わせて起動します。具体的には、**bootstrap と呼ばれるバイナリがイベントを受け取り、FastCGI プロトコルを通じて PHP-FPM に処理を委ねます。**

#### 4. PHP-FPMでのリクエスト処理
PHP-FPMがFastCGIリクエストを受け取り、index.php（Laravelの場合は public/index.php）をエントリーポイントとしてアプリケーションを実行します。
ここでLaravelやその他のWebフレームワークの処理が行われ、最終的にレスポンスが生成されます。

#### 5. レスポンスの返却
PHP-FPMが生成したレスポンスをFastCGIレスポンスとしてBref に返却します。
**BrefはそのレスポンスをLambdaのフォーマット（JSON形式など）に変換し、LambdaがAPI Gatewayに応答します。**
API GatewayはレスポンスをHTTPレスポンスに変換し、クライアントへ返却します。

### なぜBrefを使うのか
- コスト削減: まずなんと言っても安いです。EC2やホスティングサービスを使うよりも圧倒的にコストを削減できます。
- スケーラビリティ: 自動でスケールアウト・スケールインするため、アクセス増加時にも柔軟に対応可能です。
- 運用の簡素化: サーバー管理の負荷が大幅に軽減され、開発に専念できます。

# 2. 開発環境の構築

必要なツール・前提条件
- PHP 8.3
- Laravel 12
- AWS CLI
- Serverless Framework v3
- Bref

#### AWS CLIのインストール
AWS CLIをインストールし、以下のコマンドで認証情報を設定します。

```
aws configure
```

#### Serverless Frameworkのインストール
npmを利用してServerless Frameworkをグローバルにインストールします。
※Serverless Frameworkはv4まで出ていますが、動作が非常に不安定ということで[Bref公式から非推奨とされています](https://bref.sh/docs/setup#:~:text=Bref%20is%20compatible%20with%20Serverless%20Framework%20v3%20and%20v4.%20Note%20that%20Serverless%20Framework%20v4%20has%20proven%20to%20be%20very%20unstable%20and%20is%20no%20longer%20open%2Dsource.%20We%20recommend%20using%20v3%20for%20the%20best%20experience.)。そのため、今回はv3を使用します。

```
npm install -g serverless@3
```
#### Laravelプロジェクトの作成
今回は[Laravel12のスターターキット](https://readouble.com/laravel/12.x/ja/starter-kits.html)を利用してプロジェクトを作成します。

- まず、Composer経由でLaravelインストーラをインストールします。
```
composer global require laravel/installer
```

- 次に、LaravelインストーラCLIを使用して、新しいLaravelアプリケーションを作成します。
```
laravel new my-app
```
この時、使用するフロントエンドの種類を聞かれますが、今回は**React**を選択します。

- 後はJSパッケージをインストール&ビルドして、開発環境を立ち上げれば完了です。

```
cd my-app
npm install && npm run build
composer run dev
```

### Brefのセットアップ
LaravelプロジェクトにBrefを導入し、インフラストラクチャの設定を行います。

Laravelプロジェクトのルートディレクトリで、Composerを使ってBrefをインストールします。

```
composer require bref/bref
```

次に、プロジェクトのルートディレクトリに`serverless.yml`ファイルを作成し、Lambda用の設定を記述します。

```
service: laravel-react

provider:
    name: aws
    region: ap-northeast-1 # 東京リージョンを指定

package:
    patterns:
        - '!node_modules/**'
        - '!public/storage'
        - '!storage/**'
        - '!tests/**'
        - '!database/*.sqlite'

functions:

    web:
        handler: public/index.php
        runtime: php-83-fpm
        timeout: 28 
        events:
            - httpApi:
                path: /{proxy+} 
                method: ANY
    artisan:
        handler: artisan
        runtime: php-83-console
        timeout: 720

plugins:
  - ./vendor/bref/bref
  - serverless-lift

constructs:
    website:
        type: server-side-website
        assets:
            '/build/assets/*': public/build/assets/
            '/favicon.ico': public/favicon.ico
            '/robots.txt': public/robots.txt
```
<details><summary>各セグメントの解説</summary>

- `service`
```
service: laravel-react
```
Serverless Frameworkにおけるプロジェクト名を定義します。ここでは「laravel-react」という名前のサービスとしてデプロイされます。この名前はCloudFormationスタックやS3バケットのプレフィックスなどにも使われます。

- `provider`
```
provider:
    name: aws
    region: ap-northeast-1 # 東京リージョンを指定
```
デプロイ先のクラウドプロバイダーとリージョンを指定します。awsを使い、東京リージョン（ap-northeast-1）で各リソースが作成されます。

- `package`

```
package:
    patterns:
        - '!node_modules/**'
        - '!public/storage'
        - '!storage/**'
        - '!tests/**'
        - '!database/*.sqlite'
```
デプロイに含めないファイルやフォルダを定義しています。不要なファイルを除外することで、デプロイパッケージのサイズを削減します。

`node_modules/**`：Laravelでは不要

`public/storage`：アップロードファイル（S3などで管理する想定）

`storage/**`：ログやキャッシュなど実行時のみ必要なもの

`tests/**`：テストコード

`database/*.sqlite`：ローカル環境で使うSQLiteファイル

- `functions`
```
functions:

    web:
        handler: public/index.php
        runtime: php-83-fpm
        timeout: 28 
        events:
            - httpApi:
                path: /{proxy+} 
                method: ANY
```

web関数は、HTTPリクエストを処理するLaravelのメインエントリポイントです。

`handler`: public/index.php をLambdaのエントリポイントに指定

`runtime`: php-83-fpm（BrefによるPHP 8.3 + FPM環境）

`timeout`: 最大28秒

`events`: HTTP API Gatewayで全パス・全メソッドに対応

```
    artisan:
        handler: artisan
        runtime: php-83-console
        timeout: 720
```

artisan関数は、LaravelのCLIコマンド（バッチ処理やマイグレーションなど）用です。

`handler`: artisan コマンドをエントリポイントに

`runtime`: php-83-console（CLI用のBrefランタイム）

`timeout`: 長時間処理に備えて最大値の720秒を指定

- `plugins`
```
plugins:
  - ./vendor/bref/bref
  - serverless-lift
```

使用するServerless Frameworkのプラグインを指定します。

`bref`: PHP用のLambdaランタイムを提供

`serverless-lift`: サーバーサイドWebアプリや静的ファイル配信のための簡潔な構成を可能にするプラグイン

- `constructs`

```
constructs:
    website:
        type: server-side-website
        assets:
            '/build/assets/*': public/build/assets/
            '/favicon.ico': public/favicon.ico
            '/robots.txt': public/robots.txt
```

serverless-liftを使った、S3 + CloudFront構成での静的アセットの配信設定です。

- `type`: server-side-website：サーバーサイドアプリ（Laravelなど）に最適化された構成

- `assets`: 指定したファイル群をS3にアップロードしてCDN経由で配信


</details>


# 3. アプリケーションのデプロイ
最後に、ローカルでの動作確認から、AWS Lambdaへのデプロイ、そしてデプロイ後の動作確認までの手順を見ていきます。

### ローカルでの動作確認
まず、開発環境でLaravelアプリケーションが正しく動作するかを確認します。

```
 composer run dev
```

ブラウザで http://localhost:8000 にアクセスし、初期画面が表示されるかチェックしてください。

### デプロイ
Serverless Frameworkを用いてデプロイします。プロジェクトルートで以下のコマンドを実行してください。

```
serverless deploy
```

デプロイが成功すると、出力にAPIエンドポイントのURLが表示されます。

### デプロイ後の動作確認
デプロイ後に表示されたAPIエンドポイントにアクセスし、アプリケーションが正しく動作するか確認します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/676792/b9709e17-756b-4605-845f-cbeb5b9e1323.png)
無事アクセスできました！
# 終わりに
AWSのコンソールをほぼ一切触らずに、CLIからサーバーレスアプリケーションがコマンドを数回打つだけで完成できました。