Amazon Web Services ブログ

Docker コンテナイメージを使用した PHP Lambda 関数の構築

本投稿は AWS サーバーレス アプリケーションのシニアデベロッパーアドボケートである Benjamin Smith による寄稿です。

re:Invent 2020 で、AWS Lambda 関数をコンテナイメージとしてパッケージ化してデプロイできる機能が発表されました。AWS Lambda 関数をコンテナイメージとしてパッケージ化すると、PHP などのカスタムランタイムを実行する開発者にいくつかの注目すべき利点がもたらされます。このブログ投稿では、これらの利点について説明し、Lambda 関数の新しいコンテナイメージサポートを使用してサーバーレス PHP アプリケーションを構築する方法を示します。

概要

多くの PHP 開発者は、デプロイを容易にするために、ポータブルなアーティファクト作成としてコンテナを利用したアプリケーションの構築方法をご存知のことでしょう。アプリケーションをコンテナとしてパッケージ化すると、複数の環境で一貫した PHP バージョン、パッケージバージョン、および構成設定を維持しやすくなります。

Lambda では新しくコンテナイメージによるデプロイがサポートされるようになりました。これにより、コンテナベースの開発に精通されている方は、使い慣れたコンテナツールを使用してアプリケーションを構築できるようになります。また、お持ちのコンテナベースのアプリケーションをサーバーレス型のイベント駆動モデルに移行しやすくなるでしょう。これで、管理するインフラストラクチャがなくなり、スケーラビリティが自動化され、使った分だけの課金になるなどのメリットがもたらされます。

PHP アプリケーションをイベント駆動モデルで実装することによるメリットは、「サーバーレス LAMP スタック」シリーズで紹介しました。そこでは PHP を使用したサーバーレスアプリケーション作成の概念、方法、理由について説明しています。そこで紹介しているアーキテクチャパターンや機能上限は、デプロイ方式としてコンテナイメージと zip アーカイブ形式のどちらにも同様に適用されますが、例外として、現時点(2021/02)では以下の点で違いがあります。

Zip アーカイブ コンテナイメージ
最大パッケージサイズ 250 MB 10 GB
Lambda レイヤー サポート イメージに含める必要あり
Lambda Extensions サポート イメージに含める必要あり

コンテナイメージを使用したカスタムランタイム

PHP などのカスタムランタイム向けに、Lambda では Amazon Linux または Amazon Linux 2オペレーティングシステムを含むベースイメージを提供しています。これを拡張して、ブートストラップファイルに Lambda Runtime API を実装して独自のランタイムを含めることができます。

Lambda のコンテナイメージによるデプロイのサポートの提供以前は、カスタムランタイムは .zip 形式を使用してパッケージ化する必要がありました。このために、開発者が次のことを行う必要がありました。

  1. Lambda 実行環境と互換性のある Amazon Linux 環境をセットアップします。
  2. 依存関係にあるライブラリをインストールしたうえで、PHP の適切なバージョンをコンパイルします。
  3. コンパイルされた PHP ランタイムバイナリをブートストラップファイルとともにパッケージし、.zip として保存します。
  4. .zipをランタイムレイヤーとして公開します。
  5. ランタイムレイヤーを Lambda 関数に追加します。

新しいパッケージの追加や、PHP バージョンやモジュールの更新、依存関係ライブラリの変更などのようなカスタムランタイムへの変更があるたびに、このプロセスを繰り返す必要があります。このプロセスは時間がかかり、エラーが発生しやすい作業です。

Lambda の新しいコンテナイメージサポートを使用してカスタム PHP ランタイムを作成すると、ランタイム環境の変更を簡単に行うことができます。Dockerfile を使用すると、Amazon Linux 環境をセットアップしなくても、完全にスクリプト化された、より高速でポータブルなビルドプロセスを実行できます。

この GitHub リポジトリには、コンテナイメージとしてパッケージ化された Lambda 関数のカスタム PHP ランタイムが含まれています。次の Dockerfile は、AW​​S が提供する Amazon Linux のベースイメージを使用して以下のようなタスクを実行します:

  • システムとして必要になる Linux パッケージ(zip、curl、tar)をインストールします。
  • PHP ランタイムをダウンロードしてコンパイルします。
  • Composer の依存関係マネージャーと依存ライブラリをダウンロードしてインストールします。
  • PHP バイナリ、ブートストラップ、およびベンダーの依存ライブラリを、Lambda が読み取ることができるディレクトリに移動します。
  • コンテナのエントリポイントを設定します。
#Lambda base image Amazon Linux
FROM public.ecr.aws/lambda/provided as builder 
# Set desired PHP Version
ARG php_version="7.3.6"
RUN yum clean all && \
    yum install -y autoconf \
                bison \
                bzip2-devel \
                gcc \
                gcc-c++ \
                git \
                gzip \
                libcurl-devel \
                libxml2-devel \
                make \
                openssl-devel \
                tar \
                unzip \
                zip

# Download the PHP source, compile, and install both PHP and Composer
RUN curl -sL https://github.com/php/php-src/archive/php-${php_version}.tar.gz | tar -xvz && \
    cd php-src-php-${php_version} && \
    ./buildconf --force && \
    ./configure --prefix=/opt/php-7-bin/ --with-openssl --with-curl --with-zlib --without-pear --enable-bcmath --with-bz2 --enable-mbstring --with-mysqli && \
    make -j 5 && \
    make install && \
    /opt/php-7-bin/bin/php -v && \
    curl -sS https://getcomposer.org/installer | /opt/php-7-bin/bin/php -- --install-dir=/opt/php-7-bin/bin/ --filename=composer

# Prepare runtime files
# RUN mkdir -p /lambda-php-runtime/bin && \
    # cp /opt/php-7-bin/bin/php /lambda-php-runtime/bin/php
COPY runtime/bootstrap /lambda-php-runtime/
RUN chmod 0755 /lambda-php-runtime/bootstrap

# Install Guzzle, prepare vendor files
RUN mkdir /lambda-php-vendor && \
    cd /lambda-php-vendor && \
    /opt/php-7-bin/bin/php /opt/php-7-bin/bin/composer require guzzlehttp/guzzle

###### Create runtime image ######
FROM public.ecr.aws/lambda/provided as runtime
# Layer 1: PHP Binaries
COPY --from=builder /opt/php-7-bin /var/lang
# Layer 2: Runtime Interface Client
COPY --from=builder /lambda-php-runtime /var/runtime
# Layer 3: Vendor
COPY --from=builder /lambda-php-vendor/vendor /opt/vendor

COPY src/ /var/task/

CMD [ "index" ]

この Lambda 関数のデプロイについては、GitHub リポジトリの指示に従うとよいでしょう。

ランタイム関連のすべての命令は Dockerfile に保存されているため、カスタムランタイムの管理、更新、テストが簡単になります。Linux パッケージを追加したい場合は  yum installコマンドに追加できますし、別の PHP バージョンをインストールしたい場合は、php_version 引数を変更するだけです。コンパイルコマンドで、追加の PHP モジュールをインポートすることもできます。

完全なアプリケーションツリーは次のようになります。

project/
┣ runtime/
┃ ┗ bootstrap
┣ src/
┃ ┗ index.php
┗ Dockerfile

この例では Lambda 関数コードは、src ディレクトリの index.php という名前のファイルに保存されています。これには、Lambda 関数ハンドラ “index()” が含まれています。

ブートストラップファイルは、runtime ディレクトリにあります。これは、Lambda ランタイム API を使用して Lambda 実行環境と通信します。

ブートストラップの先頭行にある #! で始まる記述 #!/var/lang/bin/php で、Lambda が PHP 実行可能ファイルとして実行されることになります。

ブートストラップで使用されるすべての環境変数は、クラウドで実際に実行されるときに Lambda 実行環境によって設定されます。ローカルで実行する場合、Lambda Runtime Interface Emulator(RIE)がこれらの値を設定します。

Lambda RIE を使用したローカルテスト

Lambda のコンテナイメージサポートを使用すると、Lambda 関数をローカルでテストしやすくなります。前述のコンテナイメージの例は、AWS が提供する Lambda ベースイメージからビルドされています。このベースイメージには、Lambda RIE が含まれています。

これは Lambda の Runtime API および Extensions API の代替として機能します。つまり、AWS クラウド環境で実行する際に Lambda Runtime APIと同等な機能性を維持する軽量の Web サーバーとして動作し、ローカル環境で HTTP リクエストを JSON イベントに変換します。これにより、開発者は cURLDocker CLI などの使い慣れたツールを使用してローカルで関数をテストできます。

  1. Dockerビルドコマンドを使用して、カスタムランタイムイメージをビルドします。
    docker build -t phpmyfunction .
  2. Docker run コマンドを使用して、ポート9000にバインドして関数をローカルで実行します。
    docker run -p 9000:8080 phpmyfunction:latest
  3. このコマンドは、次の場所でローカルエンドポイントを起動します。
    localhost:9000/2015-03-31/functions/function/invocations
  4. curl コマンドを使用して、このエンドポイントにイベントを投稿できます。Lambda 関数に渡す引数ペイロードの指定は、-d オプションを使用します。引数の値は Runtime Interface Emulator で有効なJSON オブジェクトとして識別される必要があります。
    curl "http://localhost:9000/2015-03-31/functions/function/invocations" -d '{"queryStringParameters": {"name":"Ben"}}'
  5. 正しく実行されると、HTTP 200 ステータスが返されます。

Bref コンテナイメージを使用した Web アプリケーションの構築

Bref は、PHP 用のオープンソースのランタイム Lambda レイヤーです。 bref-fpm レイヤーを使用すると、Symfony や Laravel などの従来の PHP フレームワークでアプリケーションを構築できます。 Bref の FastCGI プロトコルの実装は、JSON 応答ではなく HTTP 応答を返します。 zip アーカイブ形式を使用して Lambda 関数をパッケージ化する場合、Bref のカスタムランタイムが Lambda レイヤーとして関数に提供されているため、これを利用できます。しかし、コンテナイメージとしての関数のパッケージでは、Lambda レイヤーの利用がサポートされていません(2021/02 現在)。そのため、Bref はレイヤー方式に加えて、多数の Docker イメージを提供してくれています。これらのイメージは、Lambda Runtime API を使用して Runtime Interface Client を形成し、Lambda 実行環境と適切に通信できます。

次の例は、bref/php-74-fpm コンテナイメージを使用する Dockerfile 構成を示しています。

# Uses PHP 74-fpm.0, as the base image
FROM bref/php-74-fpm
# download composer for dependency management
RUN curl -s https://getcomposer.org/installer | php
# install bref using composer
RUN php composer.phar require bref/bref
# copy the project files into a Location that the Lambda service can read from
COPY . /var/task
#set the function handler entry point
CMD _HANDLER=index.php /opt/bootstrap
  1. 最初の行は、bref/php-74-fpm を使用するようにベースイメージを設定しています。
  2. PHP の依存関係マネージャーである Composer がインストールされます。
  3. Composer の require コマンドを使用して、bref パッケージを composer.json ファイルに追加します。
  4. 次に、プロジェクトファイルが /var/task ディレクトリにコピーされます。ここで関数コードが実行されます。
  5. 関数ハンドラが、Bref のブートストラップファイルとともに設定されます。

このイメージをビルドして Amazon Elastic Container Registry にデプロイする手順は、どのイメージでも同じです。このブログ記事を参照ください。

まとめ

Lambda 関数の新しいコンテナイメージによるデプロイのサポートにより、開発者は最大 10GB のサイズの Lambda 関数をパッケージ化できます。コンテナイメージ形式と Dockerfile を使用すると、PHP などのカスタムランタイムを使用して関数を簡単に構築および更新できます。

開発者は、特定の言語バージョン、モジュール、およびパッケージの依存関係を含めることができます。Amazon Linux および Amazon Linux 2 ベースイメージは、開発者にランタイムをカスタマイズするための開始点を提供します。Lambda Runtime Interface Emulator を使用すると、開発者は Lambda 関数をローカルでテストするのが簡単になります。PHP 開発者は、bref-fpmなどの既存のサードパーティイメージを使用して、単一の Lambda 関数で Web アプリケーションを作成できます。

サーバーレス PHP アプリケーションを構築するにあたっての、より多くの情報はこちらで見つけることができます。

原文はこちらです。