AWS Lambda で Laravel を動かしてみた
会社のブログにも書きましたが、個人ブログにも書いておきます。
はじめに
AWS Lambda の Custom Runtime が来て、PHPが動くようになったので、
せっかくなら最近よく触っている Laravel を動かしてみようと思い立ちました。
Lambda Layer を作る
まずは、こちらのリポジトリを参考に、Laravelが動作する 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をセッションストレージに変更しました。
セッションをファイルに残すこともできますが、その場合は、前述したように、 /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 セッションストレージ化はしておらず、セッションはリクエスト毎に消えてしまいます。
最後に
今回は、Lambda の Custom Runtime を駆使して、Lambda 上で Laravel を動かしてみました。
まだベータ版ですが、Auroraサーバーレス のHTTPSエンドポイントも利用できるようになっていますので、
うまく実装すれば、Laravel on Lambda で使用するデータベースをAuroraにすることができる可能性があります。
セッションはDynamoに、ファイルはS3に、DBはAuroraにうまく逃がすことで、
真の意味で Laravel を Lambda で動作させることができると思っているので、今度挑戦してみたいと思っています。