Amazon Linux 2023にLaravel 10.x + PHP8.2 + MySQL8.0環境を構築する

# はじめに
- この記事は、[Ateam LifeDesign Advent Calendar 2023](https://qiita.com/advent-calendar/2023/ateam-life-design)の1日目記事です
- 諸事情により、Amazon Linux 2023にLaravel環境を構築することになりました
- 最新のLaravel環境を構築するのが数年ぶりでした…
    - なんだかんだ詰まりポイントがあったので、個人メモがてら書き残します
- なお、ECSやらRDSやら使わず、EC2に全乗せします
    - そのため本番用ではなく、あくまでも初心者の学習用途としてお使いください

# EC2環境の用意

### AMI
- 適当な名前を付けた上で、Amazon Linux 2023 AMIを選択
![EC2](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/199678/2a5424a5-20e9-799b-57e1-1e56098b212a.png)

### インスタンスタイプ
- とりあえず実験的に動かすなら、t2.microで十分

### キーペア
- 既に使っているキーペアがあれば、それを選択
    - なければ「新しいキーペアの作成」

### セキュリティグループ
- セキュリティグループで、普段Webサーバ用に使っているものがあればセット
    - もし無いなら、下記のように選択
        - SSHできるようにAllow SSH traffic from
            - 固定IPなら「自分のIP」を選んでおく方が安全
                - 現在のIPアドレスからしかSSHできなくなる
            - 「任意の場所」にセットすればIP変わってもログインできる一方、リスクは上がる
        - インターネットからのHTTPSトラフィックを許可
        - インターネットからのHTTPトラフィックを許可
![スクリーンショット 2023-11-25 22.43.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/199678/7303e92b-baa3-81e9-add3-e43ab0875e51.png)
- 昔はインバウンドルールを自力でセットする必要あったのに、今はEC2インスタンス立ち上げでこんな楽な設定方法あるんですね…

### 完了
- あとは特に変えずにOK
- 「インスタンスを起動」を選択

# Elastic IPを関連付け
- EC2インスタンス再起動のたびにIPアドレスが変わると面倒なので、Elastic IPを確保するため、メニューの「Elastic IP」を選択
    - 「Elastic IPアドレスを割り当てる」ボタン押下
    - 特に設定変えず、「割り当て」ボタン押下

![スクリーンショット 2023-11-25 22.54.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/199678/084eb527-3a94-36d3-896c-024bd79d66c9.png)

- 割り当てられたIPアドレスを、先程作成したEC2インスタンスに紐づける
    - さきほど確保したIPアドレスを選択
    - アクション→Elastic IPアドレスの関連付け
    - 「インスタンス」で、さっき立ち上げたEC2インスタンスを選択
        - インスタンスが立ち上がりきっていないと選択肢が出てこないので、選択肢に無い場合はしばらく待つ
![スクリーンショット 2023-11-25 22.58.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/199678/ab3f4e5e-d965-ce9c-e5c9-d95865bbf02b.png)

# EC2インスタンスに接続

### IPアドレスをコピー
- IPアドレスでsshをする
    - メニューの「インスタンス」から、さきほど立ち上げたEC2インスタンスを選択
    - パブリック IPv4 アドレスをコピー
        - ※プライベートIPではない
![スクリーンショット 2023-11-25 23.02.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/199678/8d0f5bcd-620c-cb07-e27f-043f03af63d3.png)

### ssh接続
- ターミナルでコマンドを打つ
    - `ssh ec2-user@[IPアドレス] -i [鍵のパス]`
    - 必要なものは接続先IPアドレス（コピー済み）と鍵
    - EC2インスタンス作成の時に新しいキーペアを作成したなら、それを使う
        - 既存の鍵を選択したなら、それを使う
        - もし新しいキーペアを作成した場合、パーミッションを変えないと接続できない（権限強すぎエラーが出る）
            - `chmod 0600 [ダウンロードした鍵]`で、パーミッション変更
- 無事に入れたらこんな表示になる

```
   ,     #_
   ~\_  ####_        Amazon Linux 2023
  ~~  \_#####\
  ~~     \###|
  ~~       \#/ ___   https://aws.amazon.com/linux/amazon-linux-2023
   ~~       V~' '->
    ~~~         /
      ~~._.   _/
         _/ _/
       _/m/'
```

# PHP, Apacheのインストールと起動

参考：[Amazon Linux 2023 での LAMP のインストール](https://docs.aws.amazon.com/ja_jp/AWSEC2/latest/UserGuide/ec2-lamp-amazon-linux-2023.html)

```bash
$ sudo dnf update -y
```

- 必要なものとりあえず入れる
```bash
$ sudo dnf install -y httpd wget php-fpm php-mysqli php-json php php-devel
```

この時点でPHPが使えるようになっているはず。

```bash
$  php -v
PHP 8.2.9 (cli) (built: Aug  3 2023 11:39:08) (NTS gcc x86_64)
Copyright (c) The PHP Group
Zend Engine v4.2.9, Copyright (c) Zend Technologies
    with Zend OPcache v8.2.9, Copyright (c), by Zend Technologies
```

- Apache起動
```bash
sudo systemctl start httpd
```

- Apache自動起動設定
```bash
sudo systemctl enable httpd
```

- 設定確認
```bash
$ sudo systemctl is-enabled httpd
enabled
$ sudo systemctl status httpd
● httpd.service - The Apache HTTP Server
     Loaded: loaded (/usr/lib/systemd/system/httpd.service; enabled; preset: disabled)
    Drop-In: /usr/lib/systemd/system/httpd.service.d
             └─php-fpm.conf
     Active: active (running) since ~~~
     ...
```
- activeになってたらOK

- Webブラウザで開いてみる
    - この時、http**s**ではなくhttpで開くこと！
    - AWS Consoleの「パブリック IPv4 アドレス」や「パブリック IPv4 DNS」のリンクから開くと、自動的にhttp**s**になってしまう
        - 私の場合、この初歩的なことに気付かず時間を浪費
![スクリーンショット 2023-11-25 23.22.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/199678/18eda1fd-1a9f-d4d8-d120-7e9fa0aadfc9.png)
    - とりあえず、この画面が見えたらApacheの起動には成功している
        - 繋がらない場合、EC2インスタンスのセキュリティグループで80番ポート開いているか要確認

# Composerのインストール
- LaravelはComposerを使うため、先にComposerを入れる

```bash
$ curl -sS https://getcomposer.org/installer | php
```

```
$ sudo mv composer.phar /usr/local/bin/composer
$ composer -V
Composer version 2.6.5 2023-10-06 10:11:52
```
- Composerのバージョンが表示されたらOK


# Laravelのインストール
参考：[Laravel 10.x インストール](https://readouble.com/laravel/10.x/ja/installation.html)

- パーミッション変更

```bash
$ cd /var/www
$ sudo chmod 0777 /var/www
```

```bash
$ composer create-project laravel/laravel example-app
```
- example-appは、付けたいディレクトリ名
- 特にエラー無く終わればOK

# Apacheの設定を変える
- 現状だと`/var/www/html`を見に行っているので、Laravelを見るように変える必要がある

```
$ sudo vi /etc/httpd/conf/httpd.conf
```

- 既に書かれているDocumentRootをコメントアウトし、作成したディレクトリのpublicを見るように追記
```conf:/etc/httpd/conf/httpd.conf
#DocumentRoot "/var/www/html"
DocumentRoot "/var/www/example-app/public"
```

- もう少し下にDirectoryと書かれている箇所があるので、以下内容を追記
    - これが漏れているとroutingが効かず、トップページ以外が404になる
```conf:/etc/httpd/conf/httpd.conf
<Directory "/var/www/example-app/public">
  Allowoverride All
</Directory>
```

- config弄ったのでApache再起動で反映
    - 本番ならreloadやgracefulの方が良いが、今回は新規構築なのであまり気にしない
```bash
$ sudo systemctl restart httpd
```

- ここまでの設定がうまくいっていると、Laravelのエラー画面になる
![スクリーンショット 2023-11-25 23.42.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/199678/d0b8d3fc-0e8e-3de6-e7ba-d94fccd56d5b.png)

# LaravelのPermission変更

- パーミッションを変えればOK
    - 777が本当に適切かはよく確認すること

```bash
$ sudo chmod -R 0777 storage/
```

![スクリーンショット 2023-11-25 23.47.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/199678/e686972d-d928-6a23-d2c3-e3bd59730ed7.png)

# データベースの設定を確認
- これでLaravelは動いているっぽく見える
    - しかしLaravelを入れるということはデータベースを使えてなんぼのはず
    - ただしmigrateすると落ちるはず

```bash
$ php artisan migrate

   Illuminate\Database\QueryException

  SQLSTATE[HY000] [2002] Connection refused (Connection: mysql, SQL: select * from information_schema.tables where table_schema = laravel and table_name = migrations and table_type = 'BASE TABLE')

  at vendor/laravel/framework/src/Illuminate/Database/Connection.php:822
    818▕                     $this->getName(), $query, $this->prepareBindings($bindings), $e
    819▕                 );
    820▕             }
    821▕
  ➜ 822▕             throw new QueryException(
    823▕                 $this->getName(), $query, $this->prepareBindings($bindings), $e
    824▕             );
    825▕         }
    826▕     }

      +38 vendor frames

  39  artisan:35
      Illuminate\Foundation\Console\Kernel::handle()
```

- Laravelでは`.env`ファイルでデータベース等の各種設定を行う
    - .envファイルが現状こうなっちゃっているので、MySQLのインストールと初期設定が必要

```config:/var/www/example-app/.env
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=laravel
DB_USERNAME=root
DB_PASSWORD=
```

- 一応、SQLiteを使えばMySQL入れるよりも簡単にデータベースが使える
    - が、SQLiteは機能やセキュリティ的に貧弱なこともあるため、変に詰まるよりかは最初からMySQLやPostgreSQLを入れた方が良い
    - この記事ではMySQLを入れることとする



# MySQLクライアントをインストールする
参考：[[小ネタ]Amazon Linux 2023にMySQL Clientをインストールする](https://dev.classmethod.jp/articles/install-mysql-client-to-amazon-linux-2023/)
参考：[Failed to start mysql.service: Unit mysql.service not found. → mysql-server インストール忘れ
](https://it-afi.com/linux/failed-to-start-mysql-service-unit-mysql-service-not-found-%E2%86%92-mysql-server-%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB%E5%BF%98%E3%82%8C/)

```bash
$ sudo dnf -y localinstall  https://dev.mysql.com/get/mysql80-community-release-el9-1.noarch.rpm
```

```bash
$ sudo dnf -y install mysql mysql-community-client
```

- 今回はRDSを使わず、MariaDBじゃなくMySQLをそのまま入れるので

```bash
$ sudo yum install -y mysql-server
```

- 入ったらmysql立ち上げ

```bash
$ sudo systemctl start mysqld
$ sudo systemctl status mysqld
● mysqld.service - MySQL Server
     Loaded: loaded (/usr/lib/systemd/system/mysqld.service; enabled; preset: disabled)
     Active: active (running) since ~~
     ...
```

- active (running)になってたらOK

# MySQLの初期設定

参考：[MySQLの初期パスワードのありか](https://qiita.com/RyosukeSomeya/items/c695487588f5ac28d9f4)

- MySQLは初回起動時、自動的にパスワードが設定される
- ログを見ると仮パスワードが書かれているので、まずはそれで入る

```bash
$ sudo less /var/log/mysqld.log

...
[Server] A temporary password is generated for root@localhost: [Password]
...
```

```bash
$ mysql -u root -p
Enter password:
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 9
Server version: 8.0.35

Copyright (c) 2000, 2023, Oracle and/or its affiliates.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql>
```

- 初期パスワードのまま他操作するとエラーになるので、パスワードを変えておく
    - パスワードはダミーです。もっとまともなパスワードを設定するようにしましょう
```sql
mysql> set password for root@localhost='NEW_password1234';
```

- データベースを作る

```sql
mysql> CREATE DATABASE laravel;
Query OK, 1 row affected (0.01 sec)

mysql> exit
Bye
```

# .envを編集

```conf:/var/www/example-app/.env
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=laravel
DB_USERNAME=root
DB_PASSWORD=NEW_password1234
```

これでコケずに実行完了するはず。

```bash
$ php artisan migrate

   INFO  Preparing database.

  Creating migration table ............................................................................................................... 38ms DONE

   INFO  Running migrations.

  2014_10_12_000000_create_users_table ................................................................................................... 50ms DONE
  2014_10_12_100000_create_password_reset_tokens_table ................................................................................... 59ms DONE
  2019_08_19_000000_create_failed_jobs_table ............................................................................................. 46ms DONE
  2019_12_14_000001_create_personal_access_tokens_table .................................................................................. 75ms DONE
```

# tinkerで確かめる

- LaravelはtinkerというREPLが使える
    - Ruby on Railsで言うところの`rails console`みたいなやつ

```php
> $user = new App\Models\User();
= App\Models\User {#7003}

> $user->name = 'hoge';
= "hoge"

> $user->email = 'hoge@example.com';
= "hoge@example.com"

> $user->password = 'hogefuga1234';
= "hogefuga1234"

> $user->save();
= true

> User::all();
= Illuminate\Database\Eloquent\Collection {#7217
    all: [
      App\Models\User {#7219
        id: 1,
        name: "hoge",
        email: "hoge@example.com",
        email_verified_at: null,
        #password: "...",
        #remember_token: null,
        created_at: "yyyy-mm-dd hh:ii:ss",
        updated_at: "yyyy-mm-dd hh:ii:ss",
      },
    ],
  }

>
```

- これで、無事にデータベースに値が入っていることが確認できる
- 以上です