既存の Laravel コンテナを AWS Lambda にデプロイ

■結論

COPY --from=public.ecr.aws/awsguru/aws-lambda-adapter:0.8.3 /lambda-adapter /opt/extensions/lambda-adapter

■やりたかったこと

皆さんはLaravelアプリケーションをどのようにデプロイしていますか?

私はもともとLaravelアプリケーションをDockerコンテナ上で開発し、AWS Fargateにデプロイしていました。

しかし、たまにしかアクセスしないアプリケーションでは、サーバーが常時起動しているFargateでは費用が気になってくるものです。

対してAWS Lambdaは、アクセスがあったときのみ起動してくれます。さらにLambdaはコンテナのデプロイに対応しています。

そこで、Fargateにデプロイしているコンテナをお手軽にLambdaへ引っ越しする方法を考えました。

■今回使うもの

Laravel(PHP)をLambdaにデプロイしようと検索すると、brefを使った記事がたくさんヒットします。

が、今回は使いません。希望としては既存のコンテナを脳死でLambdaにデプロイしたいです。

今回の要件にピッタリなのがLambda Web Adapter です。これを使えば、コンテナで動くHTTPサーバーは何でもLamdbaにデプロイできます。

■手順

簡単にまとめると以下の3ステップでできます。

① Fargate (ECS) で動くようなDockerfileを用意する

今回は既にある想定です。サンプルは最後にあります。

② Lambda Web Adapterを導入する (Dockerfileに記述)

COPY --from=public.ecr.aws/awsguru/aws-lambda-adapter:0.8.3 /lambda-adapter /opt/extensions/lambda-adapter

③ Webサーバーを8080番ポートで立ち上げる

php artisan serve --host=0.0.0.0 --port=8080

ここは、実際にはCMDで呼び出すシェルファイルに記述し、コンテナ起動時にWebサーバーを立ち上げるようにしています。

また、8080番ポートを公開する設定をDockerfileに記述します。

EXPOSE 8080

なお、環境変数 "PORT" を設定すれば他のポートでも可です。

※ 今回はLaravelのコンテナを単体で動かすので、NginxなどのWebサーバーとはここでお別れします。

■メリット・デメリット

アプリケーションに手を加えることなくFargateからLambdaへの移行ができたのが最大のメリットです。

ただし、Laravelアプリケーションを一つのLambda関数に閉じ込めるので、パフォーマンスには懸念が残ります。

今回は比較的小規模なアプリケーションだったのですが、Lambdaのメモリを512MB程度まで上げることで、気にならない程度までパフォーマンスが向上しました。(なお、Lambda設定可能な最小値は128MBですが、レスポンスに2秒弱かかりました。)

Lambdaを使っている場合、コールドスタートも気になります。Laravelのアプリケーション全体を一つのLambda関数に閉じ込めているので、起動が遅くなるのと引き換えに、いずれかのパスにアクセスすれば以降はウォームスタートとなるのが特徴です。

また、今回はDBアクセスについて検証していませんが、RDBへの同時接続数は意識する必要があります。AWSのサービスでは、RDS ProxyRDS Data APIなどが使えそうです。あるいはDynamoDBなどのNoSQLへの移行も考えられますね。

■まとめ

Lambda Web Adapter使って、Laravelのコンテナを非常に簡単にLambdaへデプロイできました。

Lambdaに与えるメモリ次第では、パフォーマンス面でも問題なく動いてくれそうです。

■参考

■今回使った資材

◯修正前

  • Dockerfile
FROM php:8.1.17-fpm-alpine3.17
COPY ./docker/php/php.ini /usr/local/etc/php/

# 必要なパッケージをインストール
RUN apk add --no-cache \
  libzip-dev \
  zlib-dev \
  mariadb-client \
  unzip \
  curl \
  vim \
  && docker-php-ext-install zip pdo_mysql sockets \
  && curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/bin --filename=composer \
  && composer self-update --2 \
  && echo 'export PATH="$PATH:$HOME/.composer/vendor/bin"' >> ~/.bashrc

# PHPの設定ファイルをコピー
COPY ./docker/php/php.ini /usr/local/etc/php/conf.d/custom.ini

# srcディレクトリをコピー
COPY ./src /work/web
RUN mv /work/web/.env.aws /work/web/.env

# Laravel環境準備のコマンド
RUN cd /work/web \
  && composer install \
  && chmod -R 777 ./storage

# 起動スクリプトコピー&権限付与
COPY ./docker/php/startup.sh /usr/local/bin/
RUN chmod 777 /usr/local/bin/startup.sh
CMD ["startup.sh"]

# ワーキングディレクトリを設定
WORKDIR /work/web
  • startup.sh
#!/bin/sh

cd /work/web
php artisan migrate
/usr/local/sbin/php-fpm

◯修正後

  • Dockerfile
FROM php:8.1.17-fpm-alpine3.17
COPY ./docker/php/php.ini /usr/local/etc/php/

# 必要なパッケージをインストール
RUN apk add --no-cache \
  libzip-dev \
  zlib-dev \
  mariadb-client \
  unzip \
  curl \
  vim \
  && docker-php-ext-install zip pdo_mysql sockets \
  && curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/bin --filename=composer \
  && composer self-update --2 \
  && echo 'export PATH="$PATH:$HOME/.composer/vendor/bin"' >> ~/.bashrc

# PHPの設定ファイルをコピー
COPY ./docker/php/php.ini /usr/local/etc/php/conf.d/custom.ini

# srcディレクトリをコピー
COPY ./src /work/web
RUN mv /work/web/.env.aws.lambda /work/web/.env

# Laravel環境準備のコマンド
RUN cd /work/web \
  && composer install \
  && chmod -R 777 ./storage

# 起動スクリプトコピー&権限付与
COPY ./docker/php/startup-lambda.sh /usr/local/bin/
RUN chmod 777 /usr/local/bin/startup-lambda.sh

# Lambda Web Adapterのコピー
COPY --from=public.ecr.aws/awsguru/aws-lambda-adapter:0.8.3 /lambda-adapter /opt/extensions/lambda-adapter

EXPOSE 8080

CMD ["startup-lambda.sh"]

# ワーキングディレクトリを設定
WORKDIR /work/web
  • startup-lambda.sh
#!/bin/sh

cd /work/web
php artisan migrate
php artisan serve --host=0.0.0.0 --port=8080