Brefを利用してPHP-FPMアプリケーションをAWS Lambdaで動かす(Terraformコードあり)

目次
はじめに
PHPを利用したアプリケーションをAWS上でサーバレスに動かしたいと考えましたが、LambdaのランタイムではPHPがサポートされていません。
そこで、Brefを利用してLambda上でPHPを動かす方法を試しました。
この記事では、BrefとTerraformを使って、PHP-FPMで動作するWebアプリケーション(今回はシンプルなサンプルサイト)をAWS Lambda上で簡単に構築する手順を紹介します。
この記事では以下の点について説明しています。
- Brefの基本的な概念の理解。
- PHP-FPMアプリケーションをLambdaにデプロイし、ブラウザからのアクセスを試してみる。
- Brefを用いた構成をTerraformでデプロイする。
Brefとは?
Brefは、PHPアプリケーションをAWS Lambda上で簡単に実行できるように設計されたオープンソースのツールセットです。
PHPのランタイムレイヤーや、一般的なPHPアプリケーション(コンソールコマンドやWebアプリケーション、キュー処理など)をLambdaで動かすための仕組みを提供しています。
とくにPHP-FPMランタイムをサポートしているため、既存の多くのPHPフレームワーク(Laravel、Symfonyなど)や、$_GET
,$_POST
,$_SESSION
といったスーパーグローバルに依存する従来のWebアプリケーションも、比較的少ない変更でLambdaに移行できるのが大きな魅力です。
今回構築する構成
今回は以下の技術スタックを使用して、シンプルな動的Webページを作成します。
- PHP: アプリケーションコード
- Composer: PHPの依存関係管理(Brefパッケージの導入)
- Bref: PHPランタイムとAWS Lambdaとの連携
- AWS Lambda: PHPコードの実行環境(サーバレス)
- Amazon API Gateway (HTTP API): WebブラウザからのHTTPリクエストを受け付けるエンドポイント
- Terraform: 上記のAWSリソースをコードで定義し、プロビジョニングするIaC (Infrastructure as Code) ツール
作成するWebページは、アクセス時の情報を表示するシンプルなものです。
以下、構成図です。
リクエストおよびレスポンスをBrefがLambda対応の形に調整してくれるため、PHPのコードを大きく変更することなく関数の実行が可能となります。
前提条件
この記事を進めるにあたって、以下の環境が準備されていることを前提とします。
- AWSアカウントがあり、AWS CLIが設定済みであること(Terraformが認証情報を利用します)。
- Terraformがローカルマシンにインストール済みであること。
- PHPがインストール済みであること。
- Composerがインストール済みであること。
使用した主なツールとバージョン
ツール | バージョン |
---|---|
OS | macOS 15.4.1 |
PHP | 8.3.21 |
Composer | 2.8.8 |
Bref | 2.4.3 |
Terraform | 1.10.4 |
手順
それでは、実際に手を動かして構築していきましょう!
1. プロジェクトのセットアップ
まずプロジェクト用のディレクトリを作成し、その中にアプリケーションコードを配置するapp
ディレクトリを作成します。
mkdir bref-phpfpm-lambda-project
cd bref-phpfpm-lambda-project
mkdir app
最終的なディレクトリ構成は以下のようになります。
bref-phpfpm-lambda-project/
├── app/
│ ├── index.php # PHPアプリケーションコード
│ ├── composer.json # Composer設定ファイル
│ └── vendor/ # Composerでインストールされるライブラリ
├── main.tf # Terraform設定ファイル
└── lambda_function.zip # Terraformが作成するデプロイパッケージ (自動生成)
2. PHPアプリケーションの作成 (app/index.php)
app
ディレクトリ内に、index.php
というファイル名で以下のPHPコードを作成します。
内容としては、アクセスしてきたユーザーの名前(デフォルトはGuest)を表示し、実行環境情報を表示するHTMLページとなっています。$_SERVER
、$_GET
といったスーパーグローバル変数も利用しています。
<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
// リクエスト情報を取得
$requestMethod = $_SERVER['REQUEST_METHOD'] ?? 'GET';
$requestUri = $_SERVER['REQUEST_URI'] ?? '/';
$name = htmlspecialchars($_GET['name'] ?? 'Guest');
// HTMLコンテンツを生成
$pageTitle = "Bref PHP-FPM on Lambda";
$phpVersion = phpversion();
$operatingSystem = php_uname('s');
$serverSoftware = $_SERVER['SERVER_SOFTWARE'] ?? 'N/A';
$htmlBody = <<<HTML
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{$pageTitle}</title>
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
margin: 40px;
background-color: #f0f2f5;
color: #333;
line-height: 1.6;
}
.container {
background-color: #ffffff;
padding: 20px 30px;
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
max-width: 800px;
margin: auto;
}
h1 {
color: #5a29e4;
border-bottom: 2px solid #5a29e4;
padding-bottom: 10px;
}
p {
margin-bottom: 15px;
}
pre {
background-color: #e9ecef;
padding: 15px;
border-radius: 4px;
overflow-x: auto;
white-space: pre-wrap;
word-wrap: break-word;
}
.info-table {
width: 100%;
border-collapse: collapse;
margin-top: 20px;
}
.info-table th, .info-table td {
border: 1px solid #dee2e6;
padding: 8px 12px;
text-align: left;
}
.info-table th {
background-color: #e9ecef;
font-weight: bold;
}
</style>
</head>
<body>
<div class="container">
<h1>{$pageTitle}</h1>
<p>こんにちは、<strong>{$name}</strong>さん!</p>
<p>このページは、AWS Lambda上でBrefとPHP-FPMを使用して動的に生成されています。</p>
<h2>実行環境情報</h2>
<table class="info-table">
<tr>
<th>PHP Version</th>
<td>{$phpVersion}</td>
</tr>
<tr>
<th>Operating System</th>
<td>{$operatingSystem}</td>
</tr>
<tr>
<th>Server Software</th>
<td>{$serverSoftware}</td>
</tr>
<tr>
<th>Request Method</th>
<td>{$requestMethod}</td>
</tr>
<tr>
<th>Request URI</th>
<td>{$requestUri}</td>
</tr>
</table>
</div>
</body>
</html>
HTML;
// Content-Type ヘッダーを設定
header('Content-Type: text/html; charset=utf-8');
echo $htmlBody;
3. Composerの設定 (app/composer.json)
次に、app
ディレクトリ内にBrefを導入するためのcomposer.json
を作成します。
{
"require": {
"bref/bref": "^2.0"
},
"config": {
"optimize-autoloader": true,
"preferred-install": "dist"
}
}
app
ディレクトリに移動し、以下のコマンドを実行してBrefをインストールします。
cd app
composer install --prefer-dist --no-dev --optimize-autoloader
cd ..
これにより、app/vendor
ディレクトリが作成されます。
4. Terraformコードの作成 (main.tf)
プロジェクトのルートディレクトリ (bref-phpfpm-lambda-project/
) にmain.tf
というファイル名で以下のTerraformコードを作成します。
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0" # AWSプロバイダのバージョン。適宜更新してください。
}
archive = {
source = "hashicorp/archive"
version = "~> 2.2.0" # archiveプロバイダのバージョン。適宜更新してください。
}
}
}
provider "aws" {
region = "ap-northeast-1" # リージョン。ご自身の環境に合わせて変更してください。
}
# app ディレクトリ内のコードをZIPファイルにアーカイブします
data "archive_file" "lambda_zip" {
type = "zip"
source_dir = "${path.module}/app" # 'app' ディレクトリを指定
output_path = "${path.module}/lambda_function.zip"
}
# Lambda実行用のIAMロール
resource "aws_iam_role" "lambda_exec_role" {
name = "bref-web-php-fpm-lambda-role"
assume_role_policy = jsonencode({
Version = "2012-10-17",
Statement = [
{
Action = "sts:AssumeRole",
Effect = "Allow",
Principal = {
Service = "lambda.amazonaws.com"
}
}
]
})
}
# LambdaがCloudWatch Logsにログを書き込むためのポリシー
resource "aws_iam_role_policy_attachment" "lambda_logs" {
role = aws_iam_role.lambda_exec_role.name
policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
}
locals {
# Bref PHP-FPMランタイムレイヤーのARN
# Brefの公式サイト(https://runtimes.bref.sh/)で、使用するPHPバージョンとリージョンに対応した最新のFPMレイヤーARNを確認してください。
# 以下は東京リージョン(ap-northeast-1) PHP 8.3 FPM の例です (2025年5月時点)。
bref_layer_php_fpm_arn = "arn:aws:lambda:ap-northeast-1:534081306603:layer:php-83-fpm:49"
}
# Lambda関数
resource "aws_lambda_function" "php_lambda" {
function_name = "BrefPHPFpmWebSample"
handler = "index.php" # FPMの場合、PHPファイル名がハンドラとなります
role = aws_iam_role.lambda_exec_role.arn
runtime = "provided.al2" # Brefはカスタムランタイムを使用します。amazon linux2を指定します。
filename = data.archive_file.lambda_zip.output_path
source_code_hash = data.archive_file.lambda_zip.output_base64sha256
layers = [
local.bref_layer_php_fpm_arn # PHP-FPMレイヤーを指定
]
timeout = 30
memory_size = 256
}
# HTTP API Gateway
resource "aws_apigatewayv2_api" "http_api" {
name = "bref-php-fpm-http-api"
protocol_type = "HTTP"
target = aws_lambda_function.php_lambda.arn
}
# Lambda関数にAPI Gatewayからの呼び出しを許可する権限
resource "aws_lambda_permission" "api_gw_permission" {
statement_id = "AllowAPIGatewayInvoke"
action = "lambda:InvokeFunction"
function_name = aws_lambda_function.php_lambda.function_name
principal = "apigateway.amazonaws.com"
# HTTP APIの実行ARNを指定して、API Gatewayからの呼び出しを許可します
source_arn = "${aws_apigatewayv2_api.http_api.execution_arn}/*/*"
}
# API GatewayのエンドポイントURLを出力します
output "api_gateway_endpoint" {
description = "URL for the API Gateway."
value = aws_apigatewayv2_api.http_api.api_endpoint
}
ポイント:
locals { bref_layer_php_fpm_arn }
: 使用するBrefのPHP-FPMレイヤーのARNを定義します。
このARNはリージョンやPHPのバージョンによって異なるため、Bref runtime versions (レイヤーARN一覧)で最新のものを確認してください。
arm64アーキテクチャ用のレイヤーも選択できます。
5. デプロイ
ターミナルでプロジェクトのルートディレクトリ (bref-phpfpm-lambda-project/
) にいることを確認し、以下のTerraformコマンドを実行します。
Terraformの初期化(初回のみ、またはprovider変更時):
terraform init
実行計画の確認(オプション):
terraform plan
どのようなリソースが作成・変更されるかを確認できます。
AWSリソースの作成・適用:
terraform apply
確認のプロンプトが表示されたらyes
と入力します。
デプロイには数分かかることがあります。
6. 動作確認
terraform apply
が正常に完了すると、Outputs:
セクションにapi_gateway_endpoint
の値が表示されます。
Outputs:
api_gateway_endpoint = "https://xxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/"
このURLにアクセスし、ページが以下のように表示されれば成功です。
指定したBrefのLambdaレイヤーで動作しているPHPのバージョン(この例ではphp-83-fpm
レイヤーなのでPHP 8.3系)が確認できます。
さらに、URLの末尾に?name=
にクエリパラメーターを追加してアクセスしてみます。
例:https://xxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/?name=BrefTestUser
以下のようにページ内の挨拶メッセージが指定したパラメーターに変わり、Request URIにもパラメーターが表示されていることが確認できます。
まとめ
この記事では、BrefとTerraformを使ってPHP-FPMアプリケーションをAWS Lambda上で実行する基本的な手順をご紹介しました。
Brefの利用によってLambda上であることを意識することなくPHPを実行することができました。
Laravelなどのフレームワークの実行も可能ですので、PHPをサーバレスに実行する際の選択肢として考慮できるでしょう。
また、GitHub ActionsなどのCI/CDパイプラインへの組み込みも比較的容易であると考えられるため、今後の拡張も期待できますね。
以上、沖本からでした。
参考資料
アジアクエスト株式会社では一緒に働いていただける方を募集しています。
興味のある方は以下のURLを御覧ください。