k-masatany.com

  • About
  • Blog
  • Contact
  • About
  • Blog
  • Contact

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/

img

見慣れた画面が出てきました。

もし、{ \"message\": \"Missing Authentication Token\" } と表示される場合は、API Gateway のルートエンドポイントに対する GET リクエストを`先ほど作成した Lambda に proxy するように変更しましょう。

http://<API Gateway のエンドポイント>Prod/notfound

img

など、ルーティングを設定されていないエンドポイントにアクセスすると、きちんと 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's memorandum
このエントリーをTwitterで共有 このエントリーをLINEで共有 このエントリーをはてなブックマークに追加

Table of Contents

  • はじめに
  • Lambda Layer を作る
    • Laravel の 動作に必要な extension の追加
    • bootstrap の修正
    • Lambda Layer の 公開
  • SAM テンプレートを作成する
  • Lambda 関数を作る
    • Laravel プロジェクトの作成
    • 各種キャッシュを書き込めるようにする
    • セッションストレージを DynamoDBに逃がす
  • Lambda Function のデプロイ
  • 動作確認
  • おわりに

© 2021 k-masatany