Laravel sail を使って Laravel9 の環境構築を行う

## はじめに

業務で新しいプロジェクトに入ったところ、どうやらLaravelの環境構築には `Laravel Sail` が使われているらしいので、 `Laravel Sail` での環境構築を自分でやってみたいと思った。

 `Laravel9` も使ったことがないので、この機に `Laravel9` の環境構築を `Laravel sail`で行ってみる。

※私は `Mac`でこの記事の作業を行いました。`Windows` の場合は、 事前に `WSL2` と`Docker desktop`をインストールしておく必要があります。

参考記事：[Laravel Sailで開発環境構築](https://chigusa-web.com/blog/laravel-sail/)

## Laravel のプロジェクトを作成する

### プロジェクト作成

```bash
$ curl -s https://laravel.build/laravel-sail-example | bash

# laravel-sail-exampleの部分は任意のプロジェクト名
```

出力結果の最後には、以下の通り `Laravel sail` での起動方法が書かれている。

```bash
Thank you! We hope you build something incredible. 
Dive in with: cd laravel-sail-example && ./vendor/bin/sail up
```

### 初期状態のdocker-compose.yml

作成したプロジェクトをVSCodeで開いて見てみる。

すると初期状態で`docker-compose.yml`が以下の内容で作成されている。

```bash
# For more information: https://laravel.com/docs/sail
version: '3'
services:
    laravel.test:
        build:
            context: ./vendor/laravel/sail/runtimes/8.1
            dockerfile: Dockerfile
            args:
                WWWGROUP: '${WWWGROUP}'
        image: sail-8.1/app
        extra_hosts:
            - 'host.docker.internal:host-gateway'
        ports:
            - '${APP_PORT:-80}:80'
        environment:
            WWWUSER: '${WWWUSER}'
            LARAVEL_SAIL: 1
            XDEBUG_MODE: '${SAIL_XDEBUG_MODE:-off}'
            XDEBUG_CONFIG: '${SAIL_XDEBUG_CONFIG:-client_host=host.docker.internal}'
        volumes:
            - '.:/var/www/html'
        networks:
            - sail
        depends_on:
            - mysql
            - redis
            - meilisearch
            - selenium
    mysql:
        image: 'mysql/mysql-server:8.0'
        ports:
            - '${FORWARD_DB_PORT:-3306}:3306'
        environment:
            MYSQL_ROOT_PASSWORD: '${DB_PASSWORD}'
            MYSQL_ROOT_HOST: "%"
            MYSQL_DATABASE: '${DB_DATABASE}'
            MYSQL_USER: '${DB_USERNAME}'
            MYSQL_PASSWORD: '${DB_PASSWORD}'
            MYSQL_ALLOW_EMPTY_PASSWORD: 1
        volumes:
            - 'sail-mysql:/var/lib/mysql'
        networks:
            - sail
        healthcheck:
            test: ["CMD", "mysqladmin", "ping", "-p${DB_PASSWORD}"]
            retries: 3
            timeout: 5s
    redis:
        image: 'redis:alpine'
        ports:
            - '${FORWARD_REDIS_PORT:-6379}:6379'
        volumes:
            - 'sail-redis:/data'
        networks:
            - sail
        healthcheck:
            test: ["CMD", "redis-cli", "ping"]
            retries: 3
            timeout: 5s
    meilisearch:
        image: 'getmeili/meilisearch:latest'
        ports:
            - '${FORWARD_MEILISEARCH_PORT:-7700}:7700'
        volumes:
            - 'sail-meilisearch:/data.ms'
        networks:
            - sail
        healthcheck:
            test: ["CMD", "wget", "--no-verbose", "--spider",  "http://localhost:7700/health"]
            retries: 3
            timeout: 5s
    mailhog:
        image: 'mailhog/mailhog:latest'
        ports:
            - '${FORWARD_MAILHOG_PORT:-1025}:1025'
            - '${FORWARD_MAILHOG_DASHBOARD_PORT:-8025}:8025'
        networks:
            - sail
    selenium:
        image: 'selenium/standalone-chrome'
        volumes:
            - '/dev/shm:/dev/shm'
        networks:
            - sail
networks:
    sail:
        driver: bridge
volumes:
    sail-mysql:
        driver: local
    sail-redis:
        driver: local
    sail-meilisearch:
        driver: local
```

なるほど。 `Laravel sail` では上記の `docker-compose.yml` をもとにDockerを起動するのか。

コンテナは以下の6つあるらしい。

・laravel.test
・mysql
・redis
・meilisearch
・mailhog
・mailhog

### Sailを起動する

指示に従い、下記コマンドを実行する。

※初回時はDockerイメージのビルドのために時間がかかる。

```bash
$ cd laravel-sail-example
$ ./vendor/bin/sail up
```

イメージがビルドされた後にコンテナが起動され、ターミナル には下記のように出力される。

```bash
Creating laravel-sail-example_meilisearch_1 ... done
Creating laravel-sail-example_mysql_1       ... done
Creating laravel-sail-example_mailhog_1      ... done
Creating laravel-sail-example_selenium_1    ... done
Creating laravel-sail-example_redis_1       ... done
Creating laravel-sail-example_laravel.test_1 ... done
```

Docker desktopでもコンテナが起動していることが確認できる。
![スクリーンショット 2022-04-03 6.31.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1208065/8514633b-8eed-5bd3-e734-3036cfabab70.png)

### Laravelの画面にアクセスしてみる

 `$ ./vendor/bin/sail up` を実行したターミナルの出力を見ると、下記のような一行があったので、ブラウザで `[http://0.0.0.0:80](http://0.0.0.0:80)` へアクセスしてみる。

```bash
laravel.test_1  | Starting Laravel development server: http://0.0.0.0:80
```

すると画像の通りLaravelの画面表示が確認できた。
![スクリーンショット 2022-04-03 6.49.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1208065/6454e710-532d-d911-57ac-053167c5045d.png)

### 既存のLaravel プロジェクトにLaravel Sailを導入する場合

既存のプロジェクトに `Laravel Sail` を導入する場合は、下記コマンドを実行する。

```bash
$ composer require laravel/sail --dev
$ php artisan sail:install

// 　Sailを起動
$ ./vendor/bin/sail up
```

## Sailコマンドを使う

### エイリアスの設定

デフォルトでSailコマンドは`vendor/bin/sail`スクリプトを使用して起動するので、これを簡単に実行できるようにするためエイリアスを設定する。

公式に書かれている通り、下記コマンドを実行する。

```bash
$ alias sail='[ -f sail ] && bash sail || bash vendor/bin/sail'
```

これにより、

```bash
$ sail up
```

とするだけでSailを起動できるようになった。

※これ以降の説明は、このエイリアスを設定済みの前提で行う。

Sailを停止するときは `Ctrl + C` を押す。

### PHPのバージョン確認と変更方法

下記コマンドを実行してみると、ローカル環境とSailの環境では別のバージョンのPHPがインストールされていることがわかる。

```bash
# ローカルのPHPのバージョン確認
$ php -v
PHP 8.1.4 (cli) (built: Mar 18 2022 09:44:47) (NTS)

# Sailの環境のPHPのバージョン確認
$ sail php -v
PHP 8.1.3 (cli) (built: Feb 21 2022 14:48:58) (NTS)
```

PHPのバージョンは、 `docker-compose.yml` の以下の部分で指定している。

```bash
services:
    laravel.test:
        build:
            context: ./vendor/laravel/sail/runtimes/8.1    // この行
```

初期状態では `./vendor/laravel/sail/runtimes/8.1` を読み込んでいるようなので、該当のファイルを確認してみる。
![スクリーンショット 2022-04-03 6.58.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1208065/ccef3246-7579-9ced-4afe-03c46ac66add.png)


`8.1` 配下の `Dockerfile`を確認してみると、PHPのバージョン `8.1`がインストールされていた。

8.1の他に8.0と7.4が入っているので、これらのバージョンであれば簡単に変更できるということになる。

なのでもしPHPのバージョンを変えたい場合は、以下のように `docker-compose.yml`を書き換える。

```bash
build:
    context: ./vendor/laravel/sail/runtimes/8.0    // 書き換える
```

そして再度イメージのビルドとコンテナ起動を行えば変更が反映する。

```bash
$ sail build --no-cache
$ sail up
```

### Node/npm/yarnコマンドの実行

Npde,npm,yarnも初期状態でインストールされているので、sailコマンドで実行することができる。

```bash
$ sail node -v
v16.14.2

$ sail npm -v
8.6.0

$ sail yarn -v
1.22.18
```

### artisanコマンドの実行

ローカルで `artisan` コマンドを使う場合は

```bash
$ php artisan
```

だったが、Sail上で `artisan` コマンドを使う場合は

```bash
$ sail artisan
```

で実行する。

### **アプリケーションコンテナへの接続**

下記コマンドでアプリケーションコンテナに入り、操作することもできる。

```bash
$ sail shell
sail@f6b02398bbe2:/var/www/html$ ls
README.md  bootstrap      config              lang          public     storage  webpack.mix.js
app        composer.json  database            package.json  resources  tests
artisan    composer.lock  docker-compose.yml  phpunit.xml   routes     vendor
```

### PHPUnitのテストを実行する

 `PHPUnit`の実行も `sail` コマンドで行うことができる。

```bash
$ sail test

   PASS  Tests\Unit\ExampleTest
  ✓ that true is true

   PASS  Tests\Feature\ExampleTest
  ✓ the application returns a successful response

  Tests:  2 passed
  Time:   0.95s
```

## DBの準備・MySQLコンテナについて

### DB（mysqlコンテナ）への接続設定

初期状態で`.env` には以下の通り記述されており、 `mysql` のコンテナに接続する設定がされている。

```bash
DB_CONNECTION=mysql
DB_HOST=mysql
DB_PORT=3306
DB_DATABASE=laravel_sail_example    // 作成したプロジェクト名
DB_USERNAME=sail
DB_PASSWORD=password
```

### mysqlへログインする

初期状態の `.env`に書かれているユーザーを使って `mysql` にログインし、データベースを確認してみる。

```bash
$ sail exec mysql mysql -u sail -p
Enter password:   // passwordと入力

Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 77
Server version: 8.0.28 MySQL Community Server - GPL

Copyright (c) 2000, 2022, 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> show databases;
+----------------------+
| Database             |
+----------------------+
| information_schema   |
| laravel_sail_example |
+----------------------+
2 rows in set (0.01 sec)
```

※ `sail` コマンドでやっていることは `docker-compose` と同じなので、下記コマンドでも同じことを行える。

```bash
$ docker-compose exec mysql mysql -u root -p
Enter password:
```

### テーブル作成（マイグレーション実行）

DBへ接続できていることが分かったので、マイグレーションを実行してテーブル作成を行う。

```bash
$ sail artisan migrate
Migration table created successfully.
Migrating: 2014_10_12_000000_create_users_table
Migrated:  2014_10_12_000000_create_users_table (63.62ms)
Migrating: 2014_10_12_100000_create_password_resets_table
Migrated:  2014_10_12_100000_create_password_resets_table (42.02ms)
Migrating: 2019_08_19_000000_create_failed_jobs_table
Migrated:  2019_08_19_000000_create_failed_jobs_table (44.24ms)
Migrating: 2019_12_14_000001_create_personal_access_tokens_table
Migrated:  2019_12_14_000001_create_personal_access_tokens_table (65.76ms)
```

## **Sailのカスタマイズ**

SailはDockerなので、コンテナやイメージを自由にカスタマイズすることができる。

このコマンドを実行するとルートディレクトリ内の`docker`ディレクトリに生成され、Sail の  `Dockerfile` やそこで使用している設定ファイルが出力される。

```
$ sail artisan sail:publish
Copied Directory [/vendor/laravel/sail/runtimes] To [/docker]
Publishing complete.
```

```bash
$ tree ./docker
./docker
├── 7.4
│   ├── Dockerfile
│   ├── php.ini
│   ├── start-container
│   └── supervisord.conf
├── 8.0
│   ├── Dockerfile
│   ├── php.ini
│   ├── start-container
│   └── supervisord.conf
└── 8.1
    ├── Dockerfile
    ├── php.ini
    ├── start-container
    └── supervisord.conf

3 directories, 12 files
```

### 設定を変更してみる

現在 `docker-compose.yml` で読み込まれている `8.1` は以下の `Dockerfile` を開くと、デフォルトではタイムゾーンが `UTC` になっているので、これを変更してみる。

.**/docker/8.0/Dockerfile**

```bash
# 変更前
ENV TZ=UTC

# 変更後
ENV TZ='Asia/Tokyo'
```

Dockerfile を変更したので、以下のコマンドでイメージをビルドし、コンテナを起動する。

```bash
$ sail build --no-cache && sail up
```

 `date` コマンドで現在時刻を確認すると、日本時間で表示され、コンテナに変更が反映していることが確認できる。

```bash
$ sail shell
sail@c46566744d71:/var/www/html$ date
Sun Apr  3 07:50:42 JST 2022
```

アプリケーション用のコンテナに変更が反映された事が確認できた。

## さいごに

 `Docker` や `docker-compose` について少しの知見があれば、 `Laravel Sail` を使うと簡単に環境構築が出来て、これをベースにカスタマイズも出来るので非常に便利だと思った。

この記事ではデフォルトの `docker-compose.yml` に書かれているうち、 `laravel.test` と `mysql` には触れたが、この他の `redis` , `meilisearch` , `mailhog` , `selenium` というコンテナには触れていない。

またの機会にこれらについても一つずつ見ていきたいと思う。

## 参考記事

https://readouble.com/laravel/9.x/ja/sail.html

https://readouble.com/laravel/9.x/ja/installation.html

https://www.ritolab.com/entry/217

https://chigusa-web.com/blog/laravel-sail/