PHPでのServerlessアーキテクチャ：Brefは実際にどう動作するのか？

## この記事について

<iframe width="560" height="315" src="https://www.youtube.com/embed/r8fgR0lTV-k?si=NVgw_2Ba500hzmnt" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>

brefの作者であるMatthieu Napoli氏が
PHP UK Conferenceで話した「Serverless PHP: how does it really work? 」の動画の内容を独自に翻訳、要約した内容になります。
※ 公式の[ドキュメント](https://bref.sh/docs/how-it-works)で紹介されている動画です。

個人的に加筆したり、また不要だと思った箇所は削除しており、完全に個人用メモです。
**時間があれば動画を見ていただいた方がよいかと思います。**

※ 記事の要約、翻訳、執筆には生成AIを活用しています。

## はじめに

PHPでサーバーレス環境を構築する際に利用するツール「Bref」について詳しく説明します。特に、BrefがAWS Lambda上でどのようにPHPアプリケーションを実行しているのか、その内部の動きや利点・欠点について深掘りしていきます。また、デプロイ方法や具体的な使用例、そしてAWS Lambdaを初めて使用する方がつまずきやすいポイントについても解説します。

## サーバーレスとSESとは

近年、アプリケーションのホスティング方法として「サーバーレス」が注目されています。サーバーレス環境では、サーバーやコンテナ、Kubernetes、共有ホスティングなどを自分で管理する必要がなく、クラウドプロバイダが背後でインフラを管理してくれます。

「SES（Serverless Execution Service）」という言葉を聞いたことがあるかもしれません。これは、アプリケーションをサーバーレスで実行するサービスの一つです。サーバーレスを利用する主な理由は次の2つです。

1. **スケーラビリティの向上**：クラウドプロバイダがインフラのスケーリングを自動で行ってくれるため、トラフィックの増減に柔軟に対応できます。
2. **シンプルさ**：インフラの管理や設定が不要になるため、アプリケーションの開発に専念できます。

## PHPとAWS Lambda

PHPでサーバーレスアプリケーションを構築する際、選択肢は限られています。AWS Lambdaはサーバーレス環境を提供するサービスですが、残念ながらPHPをネイティブにサポートしていません。そのため、PHPをAWS Lambda上で実行するにはカスタムランタイムが必要です。

ここで登場するのが**Bref**です。また、Laravelを利用している場合は**Laravel Vapor**という選択肢もあります。本講演では、主にBrefを中心に説明しますが、Laravel Vaporにも共通する内容です。

## Brefとは

Brefは[Matthieu Napoli氏](https://github.com/mnapoli)が開始したオープンソースプロジェクトで、AWS Lambda上でPHPをサポートするためのツールです。当初は実験的なプロジェクトでしたが、現在では150人以上のコントリビューターによって成熟したプロジェクトとなっています。

Brefの目標は、PHP開発者が簡単にサーバーレス環境でアプリケーションをホスティングできるようにすることです。以下がBrefの特徴です。

- **オープンソース**：GitHubで公開されており、コミュニティによって活発に開発が進められています。
- **実績**：20,000以上の関数がデプロイされ、月間で100,000回以上のデプロイが行われています。
- **スケール対応**：月間で20億以上のリクエストが処理されており、さまざまな企業で利用されています。

## Brefの基本的な使い方

BrefはPHPのパッケージとして提供されており、Composerでインストールします。プロジェクト内に `serverless.yml` というYAMLファイルを作成し、設定を記述します。以下は最小限の例です。

```yaml
service: my-php-app
provider:
  name: aws
  runtime: provided.al2

functions:
  web:
    handler: public/index.php
    events:
      - httpApi: '*'
```

デプロイはコマンド一つで行えます。

```bash
serverless deploy
```

## Brefの内部の動き

### AWS Lambdaの仕組み

AWS Lambdaでリクエストが来た際の流れを説明します。

1. **マイクロVMの起動**：リクエストが来ると、AWSはマイクロVMと呼ばれる軽量な仮想マシン（実際にはFirecrackerという技術を使用）を起動します。
2. **ランタイムの準備**：Lambdaは指定されたランタイム（Node.jsやPythonなど）をマウントします。PHPの場合はカスタムランタイムを使用します。
3. **アプリケーションコードのデプロイ**：ユーザーのコード（PHPアプリケーション）はZIPファイルとしてデプロイされ、マイクロVM内に配置されます。
4. **ランタイムAPIの起動**：AWSはランタイムAPIという特殊なエンドポイントを起動します。これはLambdaが用意するもので、ランタイムとLambdaサービス間の通信に使用されます。

### BrefにおけるGoとPHP-FPMの役割

Brefはカスタムランタイムを提供するために内部で以下のコンポーネントを使用しています。

- **Go言語で書かれたエントリーポイント**：軽量で高速な実行が可能なGo言語で、ランタイムのエントリーポイント（ブートストラップ）を実装しています。
- **PHP-FPM**：PHP公式の高速なCGI実装であるPHP-FPMを使用して、PHPコードの実行を管理します。

**なぜGoとPHP-FPMが必要なのか？**

- **Go言語のエントリーポイント**：AWS Lambdaのカスタムランタイムを構築する際、初期化処理やランタイムAPIとの通信を効率的に行う必要があります。Goはコンパイル言語であり、バイナリが軽量で高速に起動できるため、カスタムランタイムのエントリーポイントとして適しています。
- **PHP-FPMの利用**：PHP-FPMは高いパフォーマンスを持つプロセスマネージャであり、リクエストごとに新しいPHPプロセスを立ち上げるオーバーヘッドを削減できます。AWS Lambdaの性質上、単一のリクエストを処理するたびに環境が初期化されるため、PHP-FPMを利用して効率的にPHPコードを実行します。

### リクエスト処理の流れ

1. **Goエントリーポイントの起動**：LambdaがマイクロVMを起動すると、まずGoで書かれたブートストラップが実行されます。
2. **ランタイムAPIとの接続**：GoブートストラップはAWSのランタイムAPIと通信し、リクエストを待ち受けます。
3. **PHP-FPMの起動**：ブートストラップはPHP-FPMをサブプロセスとして起動します。
4. **リクエストの処理**：ランタイムAPIから受け取ったリクエストをGoブートストラップが受け取り、PHP-FPMにHTTPリクエストとして送信します。
5. **PHPコードの実行**：PHP-FPMがPHPアプリケーションコードを実行し、レスポンスを生成します。
6. **レスポンスの返却**：生成されたレスポンスをGoブートストラップが受け取り、ランタイムAPIを通じてAWS Lambdaに返します。その後、Lambdaがクライアントにレスポンスを返します。

**HTTPを使用する理由**

GoブートストラップとPHP-FPM間の通信にはHTTPを使用しています。これはAWS LambdaのランタイムAPIがHTTPをベースに設計されており、さまざまな言語や環境での互換性を確保するためです。HTTPは広く使われるプロトコルであり、PHP-FPMもFastCGIやHTTPでの通信に対応しているため、相性が良いと言えます。

## AWS Lambdaの特徴とメリット・デメリット

### メリット

- **自動スケーリング**：リクエスト数に応じてLambda関数の実行環境が自動で増減するため、スケーリングを意識する必要がありません。
- **コスト効率**：実行時間に応じて課金されるため、リソースを無駄なく使用できます。特にアイドルタイムが多い場合に有利です。
- **インフラ管理の不要**：サーバーのセットアップやパッチ適用、監視などの手間が省けます。
- **高可用性**：AWSのインフラにより、高い可用性と耐障害性が担保されています。

### デメリット

- **コールドスタート**：初回のリクエスト時や一定時間アクセスがない場合、関数の起動に時間がかかることがあります（通常数百ミリ秒から1秒程度）。これがユーザー体験に影響を与える可能性があります。
- **実行時間の制限**：Lambda関数は最長で15分までしか実行できません。長時間のバッチ処理には向いていません。
- **デバッグやローカル開発の複雑さ**：サーバーレス特有の環境であるため、ローカルでの開発やデバッグが従来より複雑になる場合があります。
- **特定のPHP拡張機能の利用制限**：Lambda環境で利用可能な拡張機能が限定されており、特定の拡張を利用する場合は追加の設定が必要です。

## コールドスタートとその対策

**コールドスタートとは**

Lambda関数が初めて呼び出される際や、一定期間アイドル状態が続いた後に再度呼び出される際、実行環境を初期化する必要があります。この初期化に時間がかかる現象をコールドスタートと呼びます。具体的には以下の処理が含まれます。

- マイクロVMの起動
- ランタイムとコードのマウント・ロード
- 初期化処理の実行

**対策**

- **関数の最適化**：不要な依存関係を削除し、関数サイズを小さくすることで初期化時間を短縮できます。
- **メモリ設定の調整**：Lambda関数のメモリ割当量を増やすことで、CPUリソースも増加し、起動が速くなる場合があります。
- **プロビジョンドコンカレンシー**：AWSの機能を利用して、一定数の関数インスタンスを常にウォーム状態に保つことができます。ただし追加コストが発生します。

## Brefを使用したジョブの処理

Lambdaはイベント駆動型のサービスであり、HTTPリクエストだけでなく、様々なイベントをトリガーとして関数を実行できます。キューからのメッセージを処理するジョブワーカーを作成することも可能です。

**ジョブワーカーの設定例**

```yaml
functions:
  job_worker:
    handler: app/Jobs/MyJobHandler.php
    layers:
      - ${bref:layer.php-80}
    events:
      - sqs:
          arn: <Your SQS Queue ARN>
```

**ポイント**

- Lambdaの実行時間が15分以内であることに注意が必要です。長時間の処理は分割するか、他のサービスを検討します。
- キューメッセージのバッチサイズを1に設定することで、1メッセージごとに処理できます。

## Brefを使用するプロジェクトの適用例

- **小規模ウェブサイトやブログ**：トラフィックが少ない場合、Lambdaの無料枠内で運用でき、コストを大幅に削減できます。
- **スケールが必要なアプリケーション**：突然のトラフィック増加にも自動でスケーリングでき、ダウンタイムを防げます。
- **マイクロサービス**：アプリケーションを小さなサービスに分割し、Lambdaでそれぞれを実行することで柔軟性が向上します。

## Brefが向かないケース

- **長時間のバッチ処理**：Lambdaの実行時間制限（最大15分）があるため、長時間かかるバッチ処理には不向きです。
- **リアルタイム通信**：WebSocketなどを恒常的に維持する必要があるアプリケーションは適していません。
- **最低コストを追求する場合**：常時高負荷で動作するアプリケーションでは、従量課金のLambdaよりも固定費のサーバーの方が安価になる場合があります。

## AWSのPHPサポートについて

現時点でAWSがPHPをネイティブサポートする予定はありません。PHPの特性上、多くの設定や拡張が必要であり、Lambdaの標準ランタイムとして提供するのが難しいためです。したがって、PHPでLambdaを利用する場合は、Brefのようなカスタムランタイムを使用する必要があります。

## 開発・デプロイ・テストの方法

**ローカル開発**

- **通常のPHP開発と同様**：ローカルサーバー（`php -S` や Valetなど）を使用して開発できます。
- **ローカルでのテスト**：PHPUnitやPestを使用してユニットテストを実行できます。

**デプロイ**

- **簡単なデプロイ**：`serverless deploy` コマンド一つでデプロイできます。
- **環境変数の設定**：AWSの環境変数やシークレットマネージャーを使用して、データベースの認証情報などを安全に管理します。

**エンドツーエンドテスト**

- **クラウド上でのテスト**：実際にデプロイした環境に対してAPIリクエストを送り、アプリケーション全体の動作を確認します。
- **テスト環境のコスト**：Lambdaはリクエストがない限りコストが発生しないため、テスト環境を安価に維持できます。

## よくある質問と注意点

**Q1. リクエストが長時間かかる処理の場合、コストはどうなりますか？**

- **A1**: Lambdaは実行時間に応じて課金されます。長時間実行される処理はコストが高くなる可能性があります。必要に応じて処理を分割するか、別のサービス（例えば、ECSやEC2）を検討してください。

**Q2. 非標準のPHP拡張機能を使用できますか？**

- **A2**: Brefでは一般的な拡張機能が利用可能です。特定の拡張機能を使用したい場合は、カスタムレイヤーを作成して組み込むことができます。

**Q3. AWSでのロードバランシングはどうなっていますか？**

- **A3**: AWS Lambda自体がリクエストごとに関数を実行するため、従来のロードバランサーは不要です。リクエストが増えると自動で関数のインスタンスが増加します。

**Q4. コールドスタートの影響はユーザーにどのように現れますか？**

- **A4**: コールドスタート時には、レスポンスが遅延します（通常数百ミリ秒）。ユーザーはリクエストが遅いと感じる可能性がありますが、エラーにはなりません。

**Q5. データベースの認証情報はどのように管理しますか？**

- **A5**: AWSの環境変数やSecrets Manager、Systems Manager Parameter Storeを利用して、安全に認証情報を管理します。

## まとめ

Brefを利用することで、PHP開発者はAWS Lambda上でサーバーレスアプリケーションを簡単に構築・デプロイできます。内部ではGo言語とPHP-FPMを組み合わせることで、高速かつ効率的な実行環境を提供しています。

AWS Lambdaの特性を理解し、メリット・デメリットを把握することで、自身のプロジェクトに適したアーキテクチャを選択できます。特に、スケーラビリティやインフラ管理の簡素化を重視する場合、BrefとAWS Lambdaは有力な選択肢となるでしょう。