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

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ページは、アクセス時の情報を表示するシンプルなものです。

    以下、構成図です。

    202507_bref_01

    リクエストおよびレスポンスを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系)が確認できます。

    202507_bref_02

    さらに、URLの末尾に?name=にクエリパラメーターを追加してアクセスしてみます。

    例:https://xxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/?name=BrefTestUser

    以下のようにページ内の挨拶メッセージが指定したパラメーターに変わり、Request URIにもパラメーターが表示されていることが確認できます。

    202507_bref_03

    まとめ

    この記事では、BrefとTerraformを使ってPHP-FPMアプリケーションをAWS Lambda上で実行する基本的な手順をご紹介しました。
    Brefの利用によってLambda上であることを意識することなくPHPを実行することができました。

    Laravelなどのフレームワークの実行も可能ですので、PHPをサーバレスに実行する際の選択肢として考慮できるでしょう。

    また、GitHub ActionsなどのCI/CDパイプラインへの組み込みも比較的容易であると考えられるため、今後の拡張も期待できますね。

    以上、沖本からでした。

    参考資料

    アジアクエスト株式会社では一緒に働いていただける方を募集しています。
    興味のある方は以下のURLを御覧ください。