Laravelでもサーバーレス！brefを使ってLaravelアプリケーションを作成してみた

## はじめに
**Laravel初学者の皆さんは、ポートフォリオを作成した時どこにデプロイしていますか？**
AWSまで勉強して`EC2`にデプロイしている方、`Heroku`にデプロイしている方いろいろいるかと思います。

しかし、それぞれ問題点があると思います。

### EC2の問題点
EC2は、インスタンスベースの仮想サーバーなので、常に稼働させなければならないため課金が発生する。（無料利用枠が、1年間毎月750時間分与えられるので十分かもしれませんが）
設定がめんどい。
下記の記事がWordPressを構築する記事ですがやることがとても多いです。
`CloudFormation`ができるようになればもっと短縮できますがハードルが高いですよね・・

https://qiita.com/Ryo9597/items/6d96ab9fdc1f1dca8838

### Herokuの問題点
無料枠のDBの選択肢が`PostgreSQL`になる。
30分何もアクセスがないとサーバーがスリープモードに入ってしまう。

ということで今回お勧めするのがサーバーレスです。

AWSにおけるサーバーレスとは、「インスタンスベースの仮想サーバー（EC2など）を使わずにアプリケーションを開発するアーキテクチャ」を指します。

一般にシステムの運用には、プログラムを動かすためのサーバーが必要です。
そしてそのサーバーは、常に稼働していなければなりません。

しかし、サーバーレスは言葉の通りサーバーを必要としません。
課金もそのアプリが動いた時のみなのでとても安いです。
**無料利用枠には、1か月ごとに100万件のリクエストが含まれます。**
もし無料枠を超過しても、**100万件リクエストごとに20円ぽっち**です。
初学者のお財布に優しいサービスです！

https://aws.amazon.com/jp/lambda/pricing/

初学者のインフラの入り口としては最適です。
ぜひこの記事でサーバーレスの世界に触れてみてください！

### 対象読者
・Laravelを勉強中の初学者の方
・LaravelのポートフォリオをHerokuにデプロイしている方
・LaravelのポートフォリオをEC2にデプロイしている方

## `bref`とは
`bref`は、PHPアプリケーションの実行を簡単にすることを目的としています。
`bref`は、サーバーレスフレームワークを使用して、サーバーレスアプリケーションを構成およびデプロイします。

https://www.serverless.com/

## 事前準備
ここでやることは3つです。

### ①`serverless`コマンドをインストールする
```terminal:ターミナル
$ npm install -g serverless
```

### ②AWSアクセスキーを作成する
#### 1. AWSダッシュボードで、IAMに移動し、新しいユーザーを作成する

#### 2. ユーザー名を設定し、プログラムによるアクセスを有効にする
![スクリーンショット 2021-08-03 11.05.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/838372/5a292f3a-07e5-3f9c-baa3-91807d72be3e.png)

#### 3. [既存のポリシーを直接アタッチ]をクリックし、AdministratorAccessを検索して選択する
![スクリーンショット 2021-08-03 11.06.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/838372/be374731-a89d-9c1a-9827-25dd9d72e173.png)

#### 4. ユーザーの作成を完了し、生成されたAWSアクセスキーとシークレットキーをメモする

### ③次のコマンドを実行して、AWSのキーを設定する
```terminal:ターミナル
$ serverless config credentials --provider aws --key <key> --secret <secret>
```

これでサーバーレスフレームワークが使えるようになりました！

## Laravelアプリケーションの作成
### ①Laravelのインストール
```terminal:ターミナル
$ composer create-project laravel/laravel serverless-laravel --prefer-dist "6.0.*"
```

### ②ディレクトリに移動する
```terminal:ターミナル
$ cd serverless-laravel
```

### ③BrefとLaravel-Brefパッケージをインストール
```terminal:ターミナル
$ composer require bref/bref bref/laravel-bridge --update-with-dependencies
```

### ④serverless.ymlを作成
```terminal:ターミナル
$ php artisan vendor:publish --tag=serverless-config
```

### ⑤serverless.ymlを修正する
このままでは警告が出るので一部修正します。

```
Deprecation warnings:

Support for "package.include" and "package.exclude" will be removed with next major release. Please use "package.patterns" instead
More Info: https://www.serverless.com/framework/docs/deprecations/#NEW_PACKAGE_PATTERNS

Resolution of lambda version hashes was improved with better algorithm, which will be used in next major release.
Switch to it now by setting "provider.lambdaHashingVersion" to "20201221"
More Info: https://www.serverless.com/framework/docs/deprecations/#LAMBDA_HASHING_VERSION_V2
```

#### 1. "package.include" および "package.exclude"のサポートが切れるよ
"package.include" および "package.exclude"のサポートは、次のメジャーリリースで削除されます。
かわりに "package.patterns" を使用してくださいとあるので修正しましょう。

```diff_yaml:serverless-laravel/serverless.yml
# サービス名(Lambda関数の関数名が「サービス名-ステージ名-関数名」となる) 
service: laravel

provider:
  # awsにデプロイする
  name: aws
  # 東京リージョンにデプロイする
  region: ap-northeast-1
  # ステージ名
  stage: dev
  # ランタイムの設定
  runtime: provided.al2

package:
  # 指定したディレクトリを展開から除外する
- exclude:
+ patterns:
    - node_modules/**
    - public/storage
    - resources/assets/**
    - storage/**
    - tests/**

functions:
  # この関数は、Laravelのウェブサイト/APIを実行する
  web:
    # HTTPのリクエストが来たときに呼び出されるファイル
    handler: public/index.php
    # API Gatewayのタイムアウトは29秒
    timeout: 28
    # php7.4を使用する
    layers:
      - ${bref:layer.php-74-fpm}
    # API Gateway
    events:
      - httpApi: '*'
  # Lambdaでartisanのコマンドを実行することができるようにする
  artisan:
    handler: artisan
    timeout: 120
    layers:
      - ${bref:layer.php-74}
      - ${bref:layer.console}

plugins:
  # プラグインの読み込み
  - ./vendor/bref/bref
```

#### 2. ハッシュの解決はもっと良いアルゴリズムで改善されたよ
Lambdaのハッシュの解決は改善され、次のメジャーリリースで使用されます。
provider.lambdaHashingVersion "を "20201221 "に設定することで、このアルゴリズムに切り替えることができます。

```diff_yaml:serverless-laravel/serverless.yml
# サービス名(Lambda関数の関数名が「サービス名-ステージ名-関数名」となる) 
service: laravel

provider:
  # awsにデプロイする
  name: aws
  # 東京リージョンにデプロイする
  region: ap-northeast-1
  # ステージ名
  stage: dev
  # ランタイムの設定
  runtime: provided.al2
  # Lambdaバージョンのハッシュの解決(warningが出るので対応)
+ lambdaHashingVersion: 20201221

package:
  # 指定したディレクトリを展開から除外する
  patterns:
    - node_modules/**
    - public/storage
    - resources/assets/**
    - storage/**
    - tests/**

functions:
  # この関数は、Laravelのウェブサイト/APIを実行する
  web:
    # HTTPのリクエストが来たときに呼び出されるファイル
    handler: public/index.php
    # API Gatewayのタイムアウトは29秒
    timeout: 28
    # php7.4を使用する
    layers:
      - ${bref:layer.php-74-fpm}
    # API Gateway
    events:
      - httpApi: '*'
  # Lambdaでartisanのコマンドを実行することができるようにする
  artisan:
    handler: artisan
    timeout: 120
    layers:
      - ${bref:layer.php-74}
      - ${bref:layer.console}

plugins:
  # プラグインの読み込み
  - ./vendor/bref/bref
```

### ⑥デプロイする前にキャッシュをクリアする
```terminal:ターミナル
$ php artisan config:clear
```

### ⑦デプロイする
```terminal:ターミナル
$ serverless deploy

Service Information
service: laravel
stage: dev
region: ap-northeast-1
stack: laravel-dev
resources: 14
api keys:
  None
endpoints:
  ANY - https://XXXXXXXXX.execute-api.ap-northeast-1.amazonaws.com
functions:
  web: laravel-dev-web
  artisan: laravel-dev-artisan
layers:
  None
```

これで完了です。
エンドポイントが発行されるのでアクセスしましょう！

表示されればOKです。
![スクリーンショット 2021-08-03 11.31.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/838372/73d8703d-06ce-d0a8-bb5d-0f13e64c04e0.png)

APIもチェックしてみましょう。

```diff_php:serverless-laravel/routes/api.php
<?php

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;

/*
|--------------------------------------------------------------------------
| API Routes
|--------------------------------------------------------------------------
|
| Here is where you can register API routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| is assigned the "api" middleware group. Enjoy building your API!
|
*/

Route::middleware('auth:api')->get('/user', function (Request $request) {
  return $request->user();
});

+ Route::get('/', function () {
+   return "hello world!";
+ });
```

APIも正しく動くことが確認できました！

![スクリーンショット 2021-08-03 11.34.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/838372/70d7e4fa-604a-8110-6832-685df8b5ddb2.png)

## 終わりに
ほとんど公式サイトをコピペするだけでLaravelでもサーバーレスができました！
ぜひポートフォリオにサーバーレスを使ってみてください。

ここまで読んでいただきありがとうございました。