2018. 12. 12
AWS Lambda で Laravel を動かしてみた
会社のブログにも書きましたが、個人ブログにも書いておきます。
はじめに
AWS Lambda の Custom Runtime が来て、PHPが動くようになったので、
せっかくなら最近よく触っている Laravel を動かしてみようと思い立ちました。
https://aws.amazon.com/jp/blogs/aws/new-for-aws-lambda-use-any-programming-language-and-share-common-components/
Lambda Layer を作る
まずは、こちらのリポジトリを参考に、Laravelが動作する Lambda Layer を作成していきます。
https://github.com/stackery/php-lambda-layer
Laravel の 動作に必要な extension の追加
上のリポジトリのままでは、Laravel を動作させるための拡張がいくつか不足しているので、build.sh
を書き換えて追加の拡張をLayerに追加します。
#!/bin/bash
yum install -y php71-cli php71-mbstring php71-mysqlnd php71-opcache php71-pdo php71-pgsql zip
mkdir /tmp/layer
cd /tmp/layer
cp /opt/layer/bootstrap .
cp /opt/layer/php.ini .
mkdir bin
cp /usr/bin/php bin/
mkdir lib
for lib in libncurses.so.5 libtinfo.so.5 libpcre.so.0; do
cp \"/lib64/${lib}\" lib/
done
cp /usr/lib64/libedit.so.0 lib/
mkdir php-7.1.d/
cp -a /etc/php-7.1.d/* php-7.1.d/
cp -a /etc/php-7.1.ini php-7.1.d/php.ini
cp -a /usr/lib64/php lib/
zip -r /opt/layer/php71.zip .
bootstrap の修正
起動時に実行される bootstrap
の下記の部分を、
exec(\"PHP_INI_SCAN_DIR=/opt/etc/php-7.1.d/:/var/task/php-7.1.d/ php -S localhost:8000 -c /var/task/php.ini -d extension_dir=/opt/lib/php/7.1/modules '$HANDLER'\");
各種 ini ファイルが読み込まれるように、PHP_INI_SCAN_DIR
を変更しておきます。
exec(\"PHP_INI_SCAN_DIR=/opt/etc/php-7.1.d/:/var/task/php-7.1.d/:/opt/php-7.1.d/ php -S localhost:8000 -c /var/task/php-7.1.d/php.ini -d extension_dir=/opt/lib/php/7.1/modules/ '$HANDLER'\");
修正が済んだら、 make
コマンドを実行することで、Layer の動作に必要な zip ファイルが出来上がります。
Lambda Layer の 公開
zip ファイルを、Lambda Layer に登録します。
upload.sh や publish.sh に記載されたバケット名やレイヤー名は自身の環境に合わせて適宜変更してください。
変更が正しく行われていれば、 make upload
, make publish
で Layer が登録されます。
注意点として、awscli
が最新でないと、 aws lambda
コマンドに Layer 周りのコマンドが存在しないためデプロイが失敗します。
必ず実行前に、awscli
を最新のものにしておきましょう。
SAM テンプレートを作成する
サンプルの SAM テンプレートを下記のように編集します。
AWSTemplateFormatVersion: 2010-09-09
Description: My PHP Application
Transform: AWS::Serverless-2016-10-31
Resources:
phpserver:
Type: AWS::Serverless::Function
Properties:
FunctionName: !Sub ${AWS::StackName}-Laravel
Description: Laravel on Lambda
CodeUri: src/laravel
Runtime: provided
Handler: server.php
MemorySize: 1028
Timeout: 30
Tracing: Active
Layers:
- !Sub arn:aws:lambda:${AWS::Region}:<アカウントID>:layer:<レイヤー名>:<レイヤーバージョン>
Events:
api:
Type: Api
Properties:
Path: /{proxy+}
Method: ANY
Environment:
Variables:
TZ: Asia/Tokyo
Layers
には先ほど作成した Layer の ARN を設定してください。
Lambda 関数を作る
Laravel プロジェクトの作成
template.yml
の設置してあるディレクトリ配下に、src
ディレクトリを作成し、Laravel プロジェクトを作成します。
composer
はインストール済みの想定です。
mkdir src
cd src
php composer.phar create-project --prefer-dist laravel/laravel laravel \"5.7.*\"
各種キャッシュを書き込めるようにする
基本的に Lambda のファイルシステムは読み込み専用のため、キャッシュの書き込み等が実行できず、このままでは Laravel が動作しません。
キャッシュ用のリソースを用意するのは面倒だったので、今回は全てを Lambda 上で唯一書き込みが許可されている /tmp
に書き込むようにします。
bootstrap/app.php
の
return $app;
の前に、下記の行を追加して storage のルートディレクトリを /tmp
に上書きします。
$app->useStoragePath('/tmp');
また、 config/cache.php
と config/view.php
で存在しないディレクトリを指定しているとエラーが起きるので、それぞれ /tmp
直下に書き込むように変更します。
// config/cache.php
'file' => [
'driver' => 'file',
'path' => storage_path(''),
],
// config/view.php
'compiled' => env(
'VIEW_COMPILED_PATH',
realpath(storage_path(''))
),
セッションストレージを DynamoDBに逃がす
手前味噌ですが、今回はこちらの記事のように、DynamoDBをセッションストレージに変更しました。
https://k-masatany.hatenablog.com/entry/2018/12/06/143739
セッションをファイルに残すこともできますが、その場合は、前述したように、 /tmp
配下に書き込むように設定してください。
これで最低限の設定が完了しました。
Lambda Function のデプロイ
sam
コマンドでデプロイを実行します。
sam package \\
--template-file template.yml \\
--output-template-file serverless-laravel.yml \\
--s3-bucket <デプロイ用バケット名>
sam deploy \\
--template-file serverless-laravel.yml \\
--stack-name serverless-laravel \\
--capabilities CAPABILITY_IAM
※ DynamoDB の作成や、DynamoDB に Lambda からアクセスするための IAM の設定などは今回の記事の本筋とは外れるため、各自適切に設定してください。
動作確認
問題なくデプロイが完了したら、API Gateway 経由でアクセスしてみましょう。
https://<API Gateway のエンドポイント>Prod/
見慣れた画面が出てきました。
もし、{ \"message\": \"Missing Authentication Token\" }
と表示される場合は、API Gateway のルートエンドポイントに対する GET リクエストを`先ほど作成した Lambda に proxy するように変更しましょう。
http://<API Gateway のエンドポイント>Prod/notfound
など、ルーティングを設定されていないエンドポイントにアクセスすると、きちんと 404 ページが表示されます。
ただし、svg/404.svg
などがうまく処理できておらず、読み込めませんでした。
この辺は CloudFront 等をうまく使って静的ファイルを Lambda の外に逃がしてやる必要がありそうです。
今回作成したコードは、下記のリポジトリにテンプレートとして置いているので、興味のある方は使ってみてください。
なお、下記サンプルでは DynamoDB セッションストレージ化はしておらず、セッションはリクエスト毎に消えてしまいます。
https://github.com/k-masatany/lambda-laravel-template
おわりに
今回は、Lambda の Custom Runtime を駆使して、Lambda 上で Laravel を動かしてみました。
まだベータ版ですが、Auroraサーバーレス のHTTPSエンドポイントも利用できるようになっていますので、
うまく実装すれば、Laravel on Lambda で使用するデータベースをAuroraにすることができる可能性があります。
https://docs.aws.amazon.com/ja_jp/AmazonRDS/latest/AuroraUserGuide/data-api.html
セッションはDynamoに、ファイルはS3に、DBはAuroraにうまく逃がすことで、
真の意味で Laravel を Lambda で動作させることができると思っているので、今度挑戦してみたいと思っています。