Laravel+mixのAWS Elastic Beanstalk環境を自動ビルド設定する

# Laravel+mixのAWS Elastic Beanstalk環境を自動ビルド設定する

# はじめに
AWSのElasticBeanstalk環境をカスタマイズするように.ebextensionsを使用して設定するという方法があります。
毎回のデプロイが自動になるためデプロイ作業の時間が短縮でき、ミスも少なくなります。

# 動機
今回、フロントをLaravel Mixでアセットビルドとコンパイルし、設定ファイルにコードを数行追加するだけでセットアップするため、アプリケーションのアップロード時にwebpackビルドを実行したいと考えました。

しかし、Elastic Beanstalkのアプリケーション環境は、PHPプラットフォームを採用しますが、`composer install`は自動で実行されるものの、nodejsはインストールされておらず、`npm install`や`npm run dev`なども自動では実行されません。

そこで、webpackビルドを行うために必要なnodejsのインストール〜webpackビルドの実行までを行えるように、Elastic Beanstalk環境をカスタマイズすることにしました。

本来であれば、CodeCommit/CodeBuild/CodeDeployをCodePipelineで繋げてデプロイを自動化するのが理想なのですが、開発しつつデプロイを繰り返すという様な手返しの良さを実現したく、.ebextensionsを使用して組み込む事にしました。

## Laravel Mixを使う理由
Laravel Mixは、webpackのAPIラッパーです。
文法は洗練されていて簡単に使用できます。
バージョン管理、ホットリロード、アセットビルドとコンパイルは、設定ファイルにコードを数行追加するだけでセットアップできます。

# アプリケーション環境
- PHP 8.0 running on 64bit Amazon Linux 2/3.3.14 (PHPプラットフォーム)
- Composer version 2.3.7
- nodejs v14.19.3
- npm 8.12.2

# 事前準備
- Laravelプロジェクト導入（もちろん終わっている前提）
- ElasticBeanstalk環境の初期化（もちろん終わっている前提）

# 作業
ついでに、Laravelやcomposerの環境設定もきっちりやります。
1. composer環境
    1. composerの最新版をインストール
    1. composerのオプション設定
1. webpackビルドを行うために必要な作業
    1. nodejsのインストール
    1. npmの最新版をインストール
    1. laravelプロジェクトのクリーンアップ
    1. npm install
    1. npm run prod(dev)

# Linux サーバーでのソフトウェアのカスタマイズ
設定ファイルは、アプリケーションが実行している Linux サーバーに影響する次のキーをサポートしています。
詳しくは、[公式ドキュメント](https://docs.aws.amazon.com/ja_jp/elasticbeanstalk/latest/dg/customize-containers-ec2.html)を確認して下さい。

# .ebextensionsの各種ファイル
プロジェクトのの下に「.ebextensions」というフォルダを作成して、設定ファイル作成します。
カテゴリ毎に、ファイルを分けても構いませんが、設定ファイルはアルファベット順に実行されます。

```
PROJECT_ROOT/.ebextensions/01_setup.config
```

たとえば、.ebextensions/01run.config は .ebextensions/02do.config の前に実行します。

詳しくは、[公式ドキュメント](https://docs.aws.amazon.com/ja_jp/elasticbeanstalk/latest/dg/command-options.html#configuration-options-precedence)を確認して下さい。


#### 今回使うのは、コマンドとコンテナコマンドの２つ

- [コマンド](https://docs.aws.amazon.com/ja_jp/elasticbeanstalk/latest/dg/customize-containers-ec2.html#linux-commands)
commands キーを使用すると、EC2 インスタンスでコマンドを実行できます。このコマンドは、アプリケーションとウェブサーバーがセットアップされ、アプリケーションバージョンファイルが抽出される前に実行されます。
指定されたコマンドはルートユーザーとして実行され、名前のアルファベット順に処理されます。デフォルトでは、コマンドはルートディレクトリで実行します。別のディレクトリからコマンドを実行するには、cwd オプションを使用します。

- [コンテナコマンド](https://docs.aws.amazon.com/ja_jp/elasticbeanstalk/latest/dg/customize-containers-ec2.html#linux-container-commands)
container_commands キーを使用して、アプリケーションのソースコードに影響するコマンドを実行できます。コンテナコマンドは、アプリケーションおよびウェブサーバーが設定され、アプリケーションバージョンアーカイブが抽出された後、アプリケーションバージョンがデプロイされる前に実行されます。コンテナ以外のコマンドと他のカスタマイズオペレーションは、アプリケーションソースコードが抽出される前に実行されます。
指定されたコマンドはルートユーザーとして実行され、名前のアルファベット順に処理されます。コンテナコマンドは、ソースコードがアプリケーションサーバーにデプロイされる前に抽出されたステージングディレクトリから実行されます。コンテナコマンドを使用してステージングディレクトリにあるソースコードに対して行うすべての変更は、ソースが最終的な場所にデプロイされる際に含まれます。
leader_only を使用して、1 つのインスタンスでコマンドを実行するか、テストコマンドが test と評価される場合のみコマンドを実行するよう true を設定できます。リーダー専用コンテナコマンドは、環境の作成およびデプロイ中のみ実行されます。他のコマンドやサーバーカスタマイズオペレーションは、インスタンスがプロビジョニングまたは更新されるたびに実行されます。リーダー専用コンテナコマンドは、AMI ID やインスタンスタイプの変更などの起動設定の変更によって実行されることはありません。

# 今回の設定ファイルの内容
このバージョンのPHPプラットフォームでは、
/var/app/staging/ – ここで、アプリケーションのソースコードがデプロイ中に処理され
次に
/var/app/current/ – ここで、アプリケーションのソースコードが処理後に実行されます。
最終的には、/var/www/htmlは、/var/app/currentのシンボリックリンクになっていました。

```.ebextensions/01_setup.config
commands:
  # composerの最新版をインストール
  01_updateComposer:
    command: export COMPOSER_HOME=/root && /usr/bin/composer.phar self-update

  # nodejsのリポジトリをインストール
  10_getNodeRepo:
    command: "curl --silent --location https://rpm.nodesource.com/setup_14.x | bash -"

  # nodejsをインストール
  11_installNode:
    command: "yum install -y nodejs"

  # npmの最新版をインストール
  12_updateNpm:
    command: "npm install npm -g"

# コンテナコマンドが実行されるカレントディレクトリは、/var/app/stagingです。
container_commands:
  # laravelプロジェクトのクリーンアップ
  20_config_clear:
    command: "php artisan config:clear"
    leader_only: true

  21_cache_clear:
    command: "php artisan cache:clear"

  # Viewをキャッシュ
  # tailwindcssをビルドしたい場合は、これを行っておかないと全てのスタイルがビルドされません。
  # 使用しているスタイルのみをcssへ追加しようとしますが、直接bladeファイルを捜索しているのではなく
　　　　# view cacheから探す様になっているようです。
  22_view_cache:
    command: "php artisan view:cache"

  # npm installで、node_modulesディレクトリやnpm run prod(dev)での成果物を書き込む権限を
  # 一旦、ec2-userに持たせます。
  30_permissions:
    # fix permissions on the Laravel app folder
    command: 'chown -R ec2-user:ec2-user .'

  31_npm_install_command:
    command: 'sudo -u ec2-user npm install'

  32_npm_run_command:
    # tailwindもbuildされます。
    command: 'sudo -u ec2-user npm run prod'

  # 規定のユーザに戻します。
  33_permissions:
    # fix permissions on the Laravel app folder
    command: 'chown -R webapp:webapp .'
```

Containerコマンドとして実行できないものはshファイルにします。

# 実行
あとは、いつも通りzipに含めて、アップロードすれば実行されます。

ちなみに、コマンド系のログは`/var/log/cfn-init-cmd.log`で確認する事ができます。

```
[ec2-user@ip-xxx-xxx-xxx-xxx ~]$ ls -l /var/log/
total 1328
drwx------ 3 root    root        17 Jun 18 03:19 amazon
-rw------- 1 root    root      9950 Jun 18 11:31 boot.log
-rw------- 1 root    utmp      9216 Jun 18 11:50 btmp
-rw-r--r-- 1 root    root      9741 Jun 18 11:55 cfn-hup.log
-rw-r--r-- 1 root    root     19501 Jun 18 03:22 cfn-init-cmd.log
-rw-r--r-- 1 root    root      2496 Jun 18 03:22 cfn-init.log
-rw-r--r-- 1 root    root    687822 Jun 18 11:55 cfn-wire.log
drwxr-x--- 2 chrony  chrony       6 Jun 18 03:19 chrony
-rw-r--r-- 1 root    root     95892 Jun 18 03:19 cloud-init.log
-rw-r----- 1 root    root      2527 Jun 18 03:19 cloud-init-output.log
-rw------- 1 root    root     24458 Jun 18 11:50 cron
-rw-r--r-- 1 root    root     26946 Jun 18 03:19 dmesg
-rw-r--r-- 1 root    root         0 Jun 18 03:19 eb-cfn-init-call.log
-rw-r--r-- 1 root    root     51175 Jun 18 03:19 eb-cfn-init.log
-rw-r--r-- 1 root    root         0 Jun 18 04:01 eb-engine.log
-rw-r--r-- 1 root    root         0 Jun 18 03:20 eb-hooks.log
-rw-r--r-- 1 root    root     73863 Jun 18 11:50 eb-publish.log
drwxr-xr-x 3 healthd healthd     39 Jun 18 03:21 healthd
drwxr-xr-x 3 root    root        21 Jun 18 03:19 httpd
-rw------- 1 root    root       208 Jun 18 03:19 maillog
-rw------- 1 root    root    205130 Jun 18 11:54 messages
drwxr-xr-x 4 nginx   nginx       71 Jun 18 03:22 nginx
drwxr-xr-x 2 webapp  webapp      23 Jun 18 03:22 php-fpm
drwxr-xr-x 2 root    root        40 Jun 18 04:01 rotated
drwxr-xr-x 2 root    root        43 Jun 18 08:53 sa
-rw------- 1 root    root     14557 Jun 18 11:51 secure
-rw-rw-r-- 1 root    utmp      3072 Jun 18 11:51 wtmp
drwxr-xr-x 2 xray    xray         6 Mar 29 15:21 xray
-rw------- 1 root    root        63 Jun 18 03:20 yum.log
```

以上です。

# 補足
以下のコマンドを実行するようにしていますが、先に`composer install`が実行され`vender`ディレクトリ以下に`autoload.php`などが作成されている必要があります。

[出典バンドルに依存関係を含める](https://docs.aws.amazon.com/ja_jp/elasticbeanstalk/latest/dg/php-configuration-composer.html)によると

Elastic Beanstalk がインスタンスで vendor フォルダを検出した場合、composer.json ファイルは (存在する場合でも) 無視されます。アプリケーションは vendor フォルダから依存関係を使用します。

と記載がありましたので、アップロードするzipに最初からvendorが含まれていると、`composer install`が省略されてしまうようですので、注意が必要ですね・・・

```
php artisan config:clear
```