Brefが進化してたのでサーバーレスLaravel環境作ってみた


はじめに

この記事は、PHP Advent Calendar 2019 の16日目の記事です。
PHPerKaigi 2019で『サーバーレスPHP』というお話をさせていただいたのですが、その際に紹介した『Bref』が進化してたので紹介したいと思います。

Brefとは

BrefとはAWS Lambda上でPHPを動作させるためのもろもろの設定を楽にしてくれるCLIです。Brefを利用するためには、AWSアカウント、aws-cliserverless frameworkが必要となります。

いままでのBrefとの変更点

相性の良いサービス

PHPerKaigiでは『DBの絡むものには向いていない』と発表したのですが、現在の公式ドキュメントには


※公式ドキュメントより

上記のように、DBの絡むサービスが相性が良いものに変わっていました。
以前はLambdaからVPCに接続する時のコールドスタートがネックとなるので、相性が悪いサービスとしてDBが絡むものが挙げられてましたが、Lambdaの機能改善により、かなり早くDBにアクセスできるようになりました。よって、RDSを使ったサービスであれば実用に耐えうるものになったようです。

インストール

春先のBrefはこうでした。

composer require mnapoli/bref

が、今のBrefはこう変わっています。

composer require bref/bref

プロジェクト名がわかりやすくなってますね。1点注意ですが、新しいBrefはPHP7.2以上必須です。PHP7.1以下の方はPHPのバージョンアップして再度インストールしてください。

設定ファイル

BrefはデプロイにCloud Fomationを利用するため、Cloud Fomation用の設定ファイルを用意する必要がありました。
例えばこんな感じ。

template.yml
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31

Globals:
  Function:
    Environment:
      Variables:
        APP_ENV: prod

Resources:
  Website:
    Type: AWS::Serverless::Function
    Properties:
      FunctionName: 'bref-symfony-website'
      CodeUri: .
      Handler: public/index.php
      Timeout: 30 # in seconds (API Gateway has a timeout of 30 seconds)
      MemorySize: 1024
      Runtime: provided
      Layers:
        - 'arn:aws:lambda:ap-northeast-1:209497400698:layer:php-73-fpm:1'
      Events:
        HttpRoot:
          Type: Api
          Properties:
            Path: /
            Method: ANY
        HttpSubPaths:
          Type: Api
          Properties:
            Path: /{proxy+}
            Method: ANY

  Console:
    Type: AWS::Serverless::Function
    Properties:
      FunctionName: 'bref-symfony-console'
      CodeUri: .
      Handler: bin/console
      Timeout: 120 # in seconds
      Runtime: provided
      Layers:
        - 'arn:aws:lambda:ap-northeast-1:209497400698:layer:php-73:1' # PHP
        - 'arn:aws:lambda:ap-northeast-1:209497400698:layer:console:1' # The "console" layer

Outputs:
  DemoApi:
    Description: 'URL of our function in the *Prod* environment'
    Value: !Sub 'https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/'

これは、PHPerKaigiで紹介したBrefを利用して、サーバーレスなSymfony環境を用意するためのCloud Fomation用設定ファイルです。
これでもサンプルがドキュメントに用意してあたので、そんなに問題なく用意できましたが、ぱっと見わかりにくい印象です。
これが、新しいBrefだとこんな感じになります。

serverless.yml
service: bref-symfony-website

provider:
    name: aws
    region: ap-northeast-1
    runtime: provided
    environment:
        # Symfony environment variables
        APP_ENV: prod

plugins:
    - ./vendor/bref/bref

package:
    exclude:
        - node_modules/**
        - tests/**

functions:
    website:
        handler: public/index.php
        timeout: 28 # in seconds (API Gateway has a timeout of 29 seconds)
        layers:
            - ${bref:layer.php-73-fpm}
        events:
            -   http: 'ANY /'
            -   http: 'ANY /{proxy+}'
    console:
        handler: bin/console
        timeout: 120 # in seconds
        layers:
            - ${bref:layer.php-73} # PHP
            - ${bref:layer.console} # The "console" layer

まず、template.ymlというファイル名からserverless.ymlというファイル名に変わり、Cloud Fomation用の設定ファイルでなくserverlessの設定ファイルを用意する形に変更になりました。Cloud Fomationよりもシンプルになったので、かなり書きやすくなったと思います。また、前回同様、公式サイトのドキュメントにサンプルがあるので、それを利用すれば、さくっと設定ファイルが用意できます。

デプロイ

春先では、デプロイは以下のコマンドを入力する必要がありました。

sam package --output-template-file .stack.yaml --s3-bucket [S3バケット名]

sam deploy --template-file .stack.yaml --capabilities CAPABILITY_IAM --stack-name [スタック名]

sam packageで、プロジェクトをS3にアップロードし、sam deployでLambdaにデプロイ、API Gatewayに設定していました。
これが新しいBrefでは

serverless deploy

serverlessのコマンド1つでデプロイできるようになりました。書いてて薄々きづいてきましたが、Brefの進化は、Serverless Frameworkの進化が大きく影響を与えているようです。

Laravel環境をつくってみる。

では、Laravelの環境をセットアップしてみましょう。上記の通りですが、作業をまとめてみると非常にシンプルなのがよくわかります。

インストール

コマンドでインストールしていきます。

composer create-project laravel/laravel serverless-laravel --prefer-dist
cd serverless-laravel
composer require bref/bref
vendor/bin/bref init

 What kind of lambda do you want to create? (you will be able to add more functions later by editing `serverless.yml`) [PHP function]:
  [0] PHP function
  [1] HTTP application
  [2] Console application
 > 1 # どれを選んでもあとで設定を変えるので特に問題ないですが、HTTP applicationを選びました。

Creating serverless.yml
Creating index.php


 [OK] Project initialized and ready to test or deploy.                                                                  


自動生成されたプロジェクト直下のindex.phpは削除しても問題ありません。

serverless設定

そして、serverlessの設定を行います。

serverless.yml
service: serverless-laravel

provider:
  name: aws
  region: ap-northeast-1
  runtime: provided
  environment:
    # Laravel environment variables
    APP_STORAGE: '/tmp'

plugins:
  - ./vendor/bref/bref

functions:
  website:
    handler: public/index.php
    timeout: 28 # in seconds (API Gateway has a timeout of 29 seconds)
    layers:
      - ${bref:layer.php-73-fpm}
    events:
      -   http: 'ANY /'
      -   http: 'ANY /{proxy+}'
  artisan:
    handler: artisan
    timeout: 120 # in seconds
    layers:
      - ${bref:layer.php-73} # PHP
      - ${bref:layer.console} # The "console" layer

regionですが、us-east-1からap-northeast-1に変更しています。この設定はwebsiteとartisanを用意しています。websiteはHTTPアクセス用の設定、artisanはコンソール(後述)の設定です。

Laravel微調整

Lambdaのカスタムランタイム上で動作しますが、/tmpディレクトリをのぞきファイルを保存することができません。そこで、少しLaravelを調整します。
以下のとおり、ファイルを修正します。

bootstrap.php
$app = new Illuminate\Foundation\Application(
    $_ENV['APP_BASE_PATH'] ?? dirname(__DIR__)
);

+ $app->useStoragePath($_ENV['APP_STORAGE'] ?? $app->storagePath());
.env
+ VIEW_COMPILED_PATH=/tmp/storage/framework/views


+ SESSION_DRIVER=array
- SESSION_DRIVER=file


+ LOG_CHANNEL=stderr
- LOG_CHANNEL=stack
app/Providers/AppServiceProvider.php
    public function boot()
    {
+        if (! is_dir(config('view.compiled'))) {
+            mkdir(config('view.compiled'), 0755, true);
        }
    }

デプロイ

ここまで設定したら、デプロイするだけです。

serverless deploy

Serverless: Packaging service...
Serverless: Excluding development dependencies...
Serverless: Creating Stack...
Serverless: Checking Stack create progress...
.... 中略 ....
Serverless: Stack update finished...
Service Information
service: serverless-laravel
stage: dev
region: ap-northeast-1
stack: serverless-laravel-dev
resources: 15
api keys:
  None
endpoints:
  ANY - https://**********.execute-api.ap-northeast-1.amazonaws.com/dev
  ANY - https://**********.execute-api.ap-northeast-1.amazonaws.com/dev/{proxy+}
functions:
  website: serverless-laravel-dev-website
  artisan: serverless-laravel-dev-artisan
layers:
  None
Serverless: Run the "serverless" command to setup monitoring, troubleshooting and testing.

endpointsに記載されてるURLにアクセスしてみましょう。

無事、表示されました。
コンソールも実行できるか確認してみましょう。

vendor/bin/bref cli --region ap-northeast-1  serverless-laravel-dev-artisan -- inspire
Simplicity is the essence of happiness. - Cedric Bledsoe

こちらも動きました。コンソールの設定をすることで、ローカル環境からLambda上のLaravel(artisan)を実行します。serverless.ymlの設定でregionをap-northeast-1に変更したので、bref cliの実行オプションに--region ap-northeast-1を付与します。付与しないとエラーとなるので注意しましょう。

まとめ

PHPerKaigiから約半年ですが、春先よりも随分と設定がシンプルになった印象がしました。前よりも環境が準備しやすいので試しやすく、いろいろなサーバーレスPHPの実例が出てくるような気がします。
来年もPHPerKaigiに参加する予定なので、なにか実例ありましたら是非当日おしえてくださいー!