既存の Laravel コンテナを AWS Lambda にデプロイ
■結論
- Lambda Web Adapter を使うと、Dockerfileに一行追加するだけで(ほぼ)動く
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サーバーを立ち上げるようにしています。
なお、環境変数 "PORT" を設定すれば他のポートでも可です。
※ 今回はLaravelのコンテナを単体で動かすので、NginxなどのWebサーバーとはここでお別れします。
■メリット・デメリット
アプリケーションに手を加えることなくFargateからLambdaへの移行ができたのが最大のメリットです。
ただし、Laravelアプリケーションを一つのLambda関数に閉じ込めるので、パフォーマンスには懸念が残ります。
今回は比較的小規模なアプリケーションだったのですが、Lambdaのメモリを512MB程度まで上げることで、気にならない程度までパフォーマンスが向上しました。(なお、Lambda設定可能な最小値は128MBですが、レスポンスに2秒弱かかりました。)
Lambdaを使っている場合、コールドスタートも気になります。Laravelのアプリケーション全体を一つのLambda関数に閉じ込めているので、起動が遅くなるのと引き換えに、いずれかのパスにアクセスすれば以降はウォームスタートとなるのが特徴です。
また、今回はDBアクセスについて検証していませんが、RDBへの同時接続数は意識する必要があります。AWSのサービスでは、RDS ProxyやRDS 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