Hosting Laravel application on Lambda with Custom Runtime.

Wed, 12 Dec 2018 13:19:41


Introduction

Hello everyone.

As AWS Lambda's Custom Runtime came and PHP started working. https://aws.amazon.com/jp/blogs/aws/new-for-aws-lambda-use-any-programming-language-and-share-common-components/

So, I will to move Laravel which often touch recently.


Make Lambda Layer

First of all, with reference to this repository, we will create a Lambda Layer on which Laravel operates.

https://github.com/stackery/php-lambda-layer

Add extension necessary for Laravel's behavior

In the above repository, there are some extensions for running Laravel, so rewrite build.sh and add extra extensions to the 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 .

Fixing bootstrap

Change PHP_INI_SCAN_DIR so that various ini files can be loaded. Change points is the following part of bootstrap that executed at startup.

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'");
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'");

When you have finished modifying it, you can execute the make command to create the zip file necessary for Layer's operation.

Release of Lambda Layer

Register the zip file in Lambda Layer. Please change the bucket name and layer name stated in upload.sh and publish.sh as appropriate according to your own environment.

Run make upload and make publish. Layer will be registered, If changes are done correctly.

As a reminder, if awscli is not up to date, the deployment fails because aws lambda command is not have commands related to Lambda Layer. Be sure to make awscli the latest before executing.

Creating a SAM template

Edit the sample SAM template as follows. Please set the ARN of the Layer you created earlier in Layers.

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

Make the Lambda function

Creating a Laravel Project

Create a src directory under the directory wheretemplate.yml is set up and create a Laravel project. composer is an installed assumption.

mkdir src
cd src
    
php composer.phar create-project --prefer-dist laravel/laravel laravel "5.7.\*"

Enable to write various caches

Basically, Lambda's file system is read-only, so it is not possible to write cache and other tempolary files. And Laravel will not work as it is. It was troublesome to prepare a resource for caching, so I will write everything in /tmp this time.

In bootstrap/app.php, Add the line below and override the storage root directory to /tmp.

$app->useStoragePath('/tmp'); # add this line
return $app;

Also, if you specify directories that do not exist in config/cache.php andconfig/view.php, errors will occur, so change them so that they are written directly under /tmp.

// config/cache.php
   
'file' => [
    'driver' => 'file',
    'path' => storage_path(''),
],
// config/view.php
   
'compiled' => env(
    'VIEW_COMPILED_PATH',
    realpath(storage_path(''))
),

Relieve session storage to DynamoDB

This time, as in this article, I changed DynamoDB to session storage.

Use DynamoDB as a Laravel session storage.

You can also leave the session in the file, but in that case, please set it to write under /tmp as described above. This completes the minimum setting.

Deploy the Lambda Function

Run the deploy with the sam command.

sam package \n    --template-file template.yml \n    --output-template-file serverless-laravel.yml \n    --s3-bucket <bucket for deploy>
   
sam deploy \n    --template-file serverless-laravel.yml \n    --stack-name serverless-laravel \n    --capabilities CAPABILITY_IAM

Since creating DynamoDB and setting up IAM to access DynamoDB from Lambda are out of the line of this article, please set them appropriately.

Operation confirmation

After successful deployment, let's access via API Gateway.

https://<Endpoint of API Gateway>/

image

A familiar screen appeared.

If {" message ":" Missing Authentication Token "} is displayed, let's change the GET request to the root endpoint of API Gateway to proxy to the Lambda created earlier.

https://<Endpoint of API Gateway>/notfound

image

If I access an endpoint that is not routed, will be displayed 404 pages properly. However, svg/404.svg etc could not be processed well and could not loaded. It seems necessary to use CloudFront etc., to escape the static file outside Lambda.

The code created this time is put as a template in the following repository, so please use it if you are interested. In the sample below, DynamoDB sessions are not stored and sessions disappear for each request.

https://github.com/k-masatany/lambda-laravel-template


Finally

This time, I tried running Laravel on Lambda, making full use of Lambda's Custom Runtime.

Although it is still in beta, since Aurora serverless HTTPS endpoints are also available. If successfully implemented, there is a possibility that the database used by Laravel on Lambda can be Aurora.

Sessions to Dynamo, Files to S3, DB to Aurora. I think that Laravel can be operated in Lambda in a true sense, so I wount like to try this time.

Have a great new year!