コンテンツにスキップするには Enter キーを押してください

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

良いお年を!

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

コメントする

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です