Fusic Tech Blog

Fusion of Society, IT and Culture

AWS Lambda で Laravel を動かしてみた
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 が動作しません。
キャッシュ用のリソースを用意するのは面倒だったので、今回は全てを /tmp に書き込むようにします。

bootstrap/app.php

return $app;

の前に、下記の行を追加して storage のルートディレクトリを /tmp に上書きします。

$app-\>useStoragePath('/tmp');

また、 config/cache.phpconfig/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 するように変更しましょう。

https://<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 で動作させることができると思っているので、今度挑戦してみたいと思っています。

良いお年を!

k-masatany

k-masatany

インターネットの海で泳ぐときは、だいたいペンギンの姿をしています。