AQ Tech Blog

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

作成者: shinichi.okimoto|2025年07月15日

はじめに

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パイプラインへの組み込みも比較的容易であると考えられるため、今後の拡張も期待できますね。

以上、沖本からでした。

参考資料