久しぶりにLaravelで開発することになり、最近のDocker開発環境はどうなってるんだろうと調べてみたら、Laravel 8.xから利用できるようになったLaravel Sailが素敵そうだったので調べてみました。

Laravel Sailとは

Laravel Sail – Laravel – The PHP Framework For Web Artisans
https://laravel.com/docs/8.x/sail#introduction

Laravel Sailは、LaravelのデフォルトのDocker開発環境と対話するための軽量のコマンドラインインターフェースです。Sailは、Dockerの経験がなくても、PHP、MySQL、Redisを使用してLaravelアプリケーションを構築するための優れた出発点を提供します。

基本的に、Sailは、プロジェクトのルートに保存されるdocker-compose.ymlファイルとsailスクリプトです。このsailスクリプトは、docker-compose.ymlファイルで定義されたDockerコンテナーと対話するための便利なメソッドをCLIに提供します。

Laravel Sailは、macOS、Linux、およびWindows(WSL2経由)でサポートされています。

まさか公式が対応してくれているとは!

インストール

Mac、Windows、Linuxそれぞれのインストール手順がありましたが、基本的には同じ手順でしたので、Macで試してみます。

前提

前提としてDockerアプリが必須となります。Dockerアプリのインストールについては割愛します。

Mac

Installation – Laravel – The PHP Framework For Web Artisans
https://laravel.com/docs/8.x/installation#getting-started-on-macos

8.x公式のSailを利用したインストール手順インストール手順にあるコマンドcurl -s https://laravel.build/example-app | bash について先に調べてみます。

コマンドに含まれるhttps://laravel.build/example-appへアクセスするとセットアップスクリプトがダウンロードできます。

https://laravel.build/example-app へアクセスしてみると以下のスクリプトが取得できます。
composerのコンテナイメージを取得・起動してそのコンテナ内でlaravel new example-appを実行してプロジェクト作成しています。なるほどー

https

docker info > /dev/null 2>&1

# Ensure that Docker is running...
if [ $? -ne 0 ]; then
    echo "Docker is not running."

    exit 1
fi

docker run --rm \
    -v $(pwd):/opt \
    -w /opt \
    laravelsail/php80-composer:latest \
    bash -c "laravel new example-app && cd example-app && php ./artisan sail:install"

cd example-app

CYAN='\033[0;36m'
LIGHT_CYAN='\033[1;36m'
WHITE='\033[1;37m'
NC='\033[0m'

echo ""

if sudo -n true 2>/dev/null; then
    sudo chown -R $USER: .
    echo -e "${WHITE}Get started with:${NC} cd example-app && ./vendor/bin/sail up"
else
    echo -e "${WHITE}Please provide your password so we can make some final adjustments to your application's permissions.${NC}"
    echo ""
    sudo chown -R $USER: .
    echo ""
    echo -e "${WHITE}Thank you! We hope you build something incredible. Dive in with:${NC} cd example-app && ./vendor/bin/sail up"
fi

実際に実行してみます。途中、端末のパスワードを求められるので入力します。

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

Unable to find image 'laravelsail/php80-composer:latest' locally
latest: Pulling from laravelsail/php80-composer
852e50cd189d: Pull complete
0266fc315b01: Pull complete
(略)
Digest: sha256:b387b05f2d55d32d9ab1b861b4bc8347f75b36ca2b259231a3359118682dabad
Status: Downloaded newer image for laravelsail/php80-composer:latest


 _                               _
| |                             | |
| |     __ _ _ __ __ ___   _____| |
| |    / _` | '__/ _` \ \ / / _ \ |
| |___| (_| | | | (_| |\ V /  __/ |
|______\__,_|_|  \__,_| \_/ \___|_|

Warning: TTY mode requires /dev/tty to be read/writable.
    Creating a "laravel/laravel" project at "./example-app"
(略)
Discovered Package: laravel/tinker
Discovered Package: nesbot/carbon
Discovered Package: nunomaduro/collision
Package manifest generated successfully.
    74 packages you are using are looking for funding.
    Use the `composer fund` command to find out more!
    > @php artisan key:generate --ansi
    Application key set successfully.

Application ready! Build something amazing.

Please provide your password so we can make some final adjustments to your application's permissions.

Password:

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

つおい。もう開発環境の準備が整いました。
ディレクトリを覗いてみるといたせりつくせり感満載です。

$ cd example-app
$ ls -al
total 640
drwxr-xr-x  27 kai  339809989     864  2 19 19:53 ./
drwxr-xr-x   3 kai  339809989      96  2 19 19:51 ../
-rw-r--r--   1 kai  339809989     220  2 17 01:58 .editorconfig
-rw-r--r--   1 kai  339809989     865  2 19 19:53 .env
-rw-r--r--   1 kai  339809989     815  2 19 19:53 .env.example
-rw-r--r--   1 kai  339809989     111  2 17 01:58 .gitattributes
-rw-r--r--   1 kai  339809989     191  2 17 01:58 .gitignore
-rw-r--r--   1 kai  339809989     181  2 17 01:58 .styleci.yml
-rw-r--r--   1 kai  339809989    3780  2 17 01:58 README.md
drwxr-xr-x   7 kai  339809989     224  2 17 01:58 app/
-rwxr-xr-x   1 kai  339809989    1686  2 17 01:58 artisan*
drwxr-xr-x   4 kai  339809989     128  2 17 01:58 bootstrap/
-rw-r--r--   1 kai  339809989    1646  2 17 01:58 composer.json
-rw-r--r--   1 kai  339809989  268563  2 19 19:51 composer.lock
drwxr-xr-x  16 kai  339809989     512  2 17 01:58 config/
drwxr-xr-x   6 kai  339809989     192  2 17 01:58 database/
-rw-r--r--   1 kai  339809989    2614  2 19 19:53 docker-compose.yml
-rw-r--r--   1 kai  339809989     473  2 17 01:58 package.json
-rw-r--r--   1 kai  339809989    1202  2 17 01:58 phpunit.xml
drwxr-xr-x   7 kai  339809989     224  2 17 01:58 public/
drwxr-xr-x   6 kai  339809989     192  2 17 01:58 resources/
drwxr-xr-x   6 kai  339809989     192  2 17 01:58 routes/
-rw-r--r--   1 kai  339809989     563  2 17 01:58 server.php
drwxr-xr-x   5 kai  339809989     160  2 17 01:58 storage/
drwxr-xr-x   6 kai  339809989     192  2 17 01:58 tests/
drwxr-xr-x  45 kai  339809989    1440  2 19 19:53 vendor/
-rw-r--r--   1 kai  339809989     559  2 17 01:58 webpack.mix.js

.gitignore用意されているので、Gitリポジトリへのコミットもはまらずにできそうです。

.gitignore

/node_modules
/public/hot
/public/storage
/storage/*.key
/vendor
.env
.env.backup
.phpunit.result.cache
docker-compose.override.yml
Homestead.json
Homestead.yaml
npm-debug.log
yarn-error.log

docker-compose.ymlを眺めてみるとlaravelのDockerfileは./vendor/laravel/sail以下のファイルを参照していることがわかります。コメントアウトされていますが、seleniumやPostgreSQL、memcachedにも対応しているみたいです。

ポート変更したい場合、環境変数が利用できるようになっているので、例えばlaravel.testのポートを変更したい場合、.envファイルにAPP_PORT=8000などのように追記すると変更できます。

Laravel SailでDocker環境構築 | RE:ENGINES
https://re-engines.com/2021/01/25/laravel-sail/

docker-compose.yml

# For more information: https://laravel.com/docs/sail
version: '3'
services:
    laravel.test:
        build:
            context: ./vendor/laravel/sail/runtimes/8.0
            dockerfile: Dockerfile
            args:
                WWWGROUP: '${WWWGROUP}'
        image: sail-8.0/app
        ports:
            - '${APP_PORT:-80}:80'
        environment:
            WWWUSER: '${WWWUSER}'
            LARAVEL_SAIL: 1
        volumes:
            - '.:/var/www/html'
        networks:
            - sail
        depends_on:
            - mysql
            # - pgsql
            - redis
            # - selenium
    # selenium:
    #     image: 'selenium/standalone-chrome'
    #     volumes:
    #         - '/dev/shm:/dev/shm'
    #     networks:
    #         - sail
    mysql:
        image: 'mysql:8.0'
        ports:
            - '${FORWARD_DB_PORT:-3306}:3306'
        environment:
            MYSQL_ROOT_PASSWORD: '${DB_PASSWORD}'
            MYSQL_DATABASE: '${DB_DATABASE}'
            MYSQL_USER: '${DB_USERNAME}'
            MYSQL_PASSWORD: '${DB_PASSWORD}'
            MYSQL_ALLOW_EMPTY_PASSWORD: 'yes'
        volumes:
            - 'sailmysql:/var/lib/mysql'
        networks:
            - sail
        healthcheck:
          test: ["CMD", "mysqladmin", "ping"]
#    pgsql:
#        image: postgres:13
#        ports:
#            - '${FORWARD_DB_PORT:-5432}:5432'
#        environment:
#            PGPASSWORD: '${DB_PASSWORD:-secret}'
#            POSTGRES_DB: '${DB_DATABASE}'
#            POSTGRES_USER: '${DB_USERNAME}'
#            POSTGRES_PASSWORD: '${DB_PASSWORD:-secret}'
#        volumes:
#            - 'sailpostgresql:/var/lib/postgresql/data'
#        networks:
#            - sail
#        healthcheck:
#          test: ["CMD", "pg_isready", "-q", "-d", "${DB_DATABASE}", "-U", "${DB_USERNAME}"]
    redis:
        image: 'redis:alpine'
        ports:
            - '${FORWARD_REDIS_PORT:-6379}:6379'
        volumes:
            - 'sailredis:/data'
        networks:
            - sail
        healthcheck:
          test: ["CMD", "redis-cli", "ping"]
    # memcached:
    #     image: 'memcached:alpine'
    #     ports:
    #         - '11211:11211'
    #     networks:
    #         - sail
    mailhog:
        image: 'mailhog/mailhog:latest'
        ports:
            - '${FORWARD_MAILHOG_PORT:-1025}:1025'
            - '${FORWARD_MAILHOG_DASHBOARD_PORT:-8025}:8025'
        networks:
            - sail
networks:
    sail:
        driver: bridge
volumes:
    sailmysql:
        driver: local
#    sailpostgresql:
#        driver: local
    sailredis:
        driver: local

laravelコンテナのDockerfileを眺めてみるとタイムゾーンを変更したい場合にカスタマイズしたいかなぁと思う程度です。

Dockerfile

FROM ubuntu:20.04

LABEL maintainer="Taylor Otwell"

ARG WWWGROUP

WORKDIR /var/www/html

ENV DEBIAN_FRONTEND noninteractive
ENV TZ=UTC

RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone

RUN apt-get update \
    && apt-get install -y gnupg gosu curl ca-certificates zip unzip git supervisor sqlite3 libcap2-bin libpng-dev python2 \
    && mkdir -p ~/.gnupg \
    && chmod 600 ~/.gnupg \
    && echo "disable-ipv6" >> ~/.gnupg/dirmngr.conf \
    && apt-key adv --homedir ~/.gnupg --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys E5267A6C \
    && apt-key adv --homedir ~/.gnupg --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys C300EE8C \
    && echo "deb http://ppa.launchpad.net/ondrej/php/ubuntu focal main" > /etc/apt/sources.list.d/ppa_ondrej_php.list \
    && apt-get update \
    && apt-get install -y php8.0-cli php8.0-dev \
       php8.0-pgsql php8.0-sqlite3 php8.0-gd \
       php8.0-curl php8.0-memcached \
       php8.0-imap php8.0-mysql php8.0-mbstring \
       php8.0-xml php8.0-zip php8.0-bcmath php8.0-soap \
       php8.0-intl php8.0-readline \
       php8.0-msgpack php8.0-igbinary php8.0-ldap \
       php8.0-redis \
    && php -r "readfile('http://getcomposer.org/installer');" | php -- --install-dir=/usr/bin/ --filename=composer \
    && curl -sL https://deb.nodesource.com/setup_15.x | bash - \
    && apt-get install -y nodejs \
    && curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - \
    && echo "deb https://dl.yarnpkg.com/debian/ stable main" > /etc/apt/sources.list.d/yarn.list \
    && apt-get update \
    && apt-get install -y yarn \
    && apt-get install -y mysql-client \
    && apt-get -y autoremove \
    && apt-get clean \
    && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*

RUN setcap "cap_net_bind_service=+ep" /usr/bin/php8.0

RUN groupadd --force -g $WWWGROUP sail
RUN useradd -ms /bin/bash --no-user-group -g $WWWGROUP -u 1337 sail

COPY start-container /usr/local/bin/start-container
COPY supervisord.conf /etc/supervisor/conf.d/supervisord.conf
COPY php.ini /etc/php/8.0/cli/conf.d/99-sail.ini
RUN chmod +x /usr/local/bin/start-container

EXPOSE 8000

ENTRYPOINT ["start-container"]

./vendor/bin/sail up コマンドを実行してDockerコンテナを立ち上げます。
docker-compose up コマンドでも立ち上げることができました。

$ ./vendor/bin/sail up

Creating network "example-app_sail" with driver "bridge"
Creating volume "example-app_sailmysql" with local driver
Creating volume "example-app_sailredis" with local driver
Pulling redis (redis:alpine)...
alpine: Pulling from library/redis
ba3557a56b15: Pull complete
(略)
Digest: sha256:6ea115e574af216b2175f3783bb1119140b24619632e522841c1aac6990f5e79
Status: Downloaded newer image for redis:alpine
Building laravel.test
(略)
Creating example-app_redis_1   ... done
(略)
Creating example-app_laravel.test_1 ... done
Attaching to example-app_mailhog_1, example-app_redis_1, example-app_mysql_1, example-app_laravel.test_1
mailhog_1       | [HTTP] Binding to address: 0.0.0.0:8025
mailhog_1       | 2021/02/19 11:08:44 Using in-memory storage
mailhog_1       | 2021/02/19 11:08:44 [SMTP] Binding to address: 0.0.0.0:1025
mailhog_1       | 2021/02/19 11:08:44 Serving under http://0.0.0.0:8025/
mysql_1         | 2021-02-19 11:08:44+00:00 [Note] [Entrypoint]: Entrypoint script for MySQL Server 8.0.21-1debian10 started.
redis_1         | 1:C 19 Feb 2021 11:08:44.329 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
redis_1         | 1:C 19 Feb 2021 11:08:44.329 # Redis version=6.0.10, bits=64, commit=00000000, modified=0, pid=1, just started
redis_1         | 1:C 19 Feb 2021 11:08:44.329 # Warning: no config file specified, using the default config. In order to specify a config file use redis-server /path/to/redis.conf
mailhog_1       | Creating API v1 with WebPath:
mysql_1         | 2021-02-19 11:08:44+00:00 [Note] [Entrypoint]: Switching to dedicated user 'mysql'
redis_1         | 1:M 19 Feb 2021 11:08:44.330 * Running mode=standalone, port=6379.
redis_1         | 1:M 19 Feb 2021 11:08:44.330 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128.
redis_1         | 1:M 19 Feb 2021 11:08:44.330 # Server initialized
mailhog_1       | Creating API v2 with WebPath:
mysql_1         | 2021-02-19 11:08:44+00:00 [Note] [Entrypoint]: Entrypoint script for MySQL Server 8.0.21-1debian10 started.
redis_1         | 1:M 19 Feb 2021 11:08:44.331 * Ready to accept connections
mysql_1         | 2021-02-19 11:08:44+00:00 [Note] [Entrypoint]: Initializing database files
mysql_1         | 2021-02-19T11:08:44.731409Z 0 [System] [MY-013169] [Server] /usr/sbin/mysqld (mysqld 8.0.21) initializing of server in progress as process 45
mysql_1         | 2021-02-19T11:08:44.739160Z 1 [System] [MY-013576] [InnoDB] InnoDB initialization has started.
mysql_1         | 2021-02-19T11:08:45.144394Z 1 [System] [MY-013577] [InnoDB] InnoDB initialization has ended.
laravel.test_1  | 2021-02-19 11:08:45,721 INFO Set uid to user 0 succeeded
laravel.test_1  | 2021-02-19 11:08:45,726 INFO supervisord started with pid 17
mysql_1         | 2021-02-19T11:08:46.142439Z 6 [Warning] [MY-010453] [Server] root@localhost is created with an empty password ! Please consider switching off the --initialize-insecure option.
laravel.test_1  | 2021-02-19 11:08:46,730 INFO spawned: 'php' with pid 19
laravel.test_1  | 2021-02-19 11:08:47,733 INFO success: php entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)
mysql_1         | 2021-02-19 11:08:49+00:00 [Note] [Entrypoint]: Database files initialized
mysql_1         | 2021-02-19 11:08:49+00:00 [Note] [Entrypoint]: Starting temporary server
mysql_1         | 2021-02-19T11:08:49.524217Z 0 [System] [MY-010116] [Server] /usr/sbin/mysqld (mysqld 8.0.21) starting as process 92
mysql_1         | 2021-02-19T11:08:49.538476Z 1 [System] [MY-013576] [InnoDB] InnoDB initialization has started.
laravel.test_1  | Starting Laravel development server: http://0.0.0.0:80
mysql_1         | 2021-02-19T11:08:49.706431Z 1 [System] [MY-013577] [InnoDB] InnoDB initialization has ended.
mysql_1         | 2021-02-19T11:08:49.809603Z 0 [System] [MY-011323] [Server] X Plugin ready for connections. Socket: /var/run/mysqld/mysqlx.sock
mysql_1         | 2021-02-19T11:08:49.959222Z 0 [Warning] [MY-010068] [Server] CA certificate ca.pem is self signed.
mysql_1         | 2021-02-19T11:08:49.959381Z 0 [System] [MY-013602] [Server] Channel mysql_main configured to support TLS. Encrypted connections are now supported for this channel.
mysql_1         | 2021-02-19T11:08:49.961133Z 0 [Warning] [MY-011810] [Server] Insecure configuration for --pid-file: Location '/var/run/mysqld' in the path is accessible to all OS users. Consider choosing a different directory.
mysql_1         | 2021-02-19T11:08:49.991284Z 0 [System] [MY-010931] [Server] /usr/sbin/mysqld: ready for connections. Version: '8.0.21'  socket: '/var/run/mysqld/mysqld.sock'  port: 0  MySQL Community Server - GPL.
mysql_1         | 2021-02-19 11:08:50+00:00 [Note] [Entrypoint]: Temporary server started.
laravel.test_1  | [Fri Feb 19 11:08:49 2021] PHP 8.0.2 Development Server (http://0.0.0.0:80) started
mysql_1         | Warning: Unable to load '/usr/share/zoneinfo/iso3166.tab' as time zone. Skipping it.
mysql_1         | Warning: Unable to load '/usr/share/zoneinfo/leap-seconds.list' as time zone. Skipping it.
mysql_1         | Warning: Unable to load '/usr/share/zoneinfo/zone.tab' as time zone. Skipping it.
mysql_1         | Warning: Unable to load '/usr/share/zoneinfo/zone1970.tab' as time zone. Skipping it.
mysql_1         | 2021-02-19 11:08:52+00:00 [Note] [Entrypoint]: Creating database example_app
mysql_1         |
mysql_1         | 2021-02-19 11:08:52+00:00 [Note] [Entrypoint]: Stopping temporary server
mysql_1         | 2021-02-19T11:08:52.783401Z 11 [System] [MY-013172] [Server] Received SHUTDOWN from user root. Shutting down mysqld (Version: 8.0.21).
mysql_1         | 2021-02-19T11:08:54.552265Z 0 [System] [MY-010910] [Server] /usr/sbin/mysqld: Shutdown complete (mysqld 8.0.21)  MySQL Community Server - GPL.
mysql_1         | 2021-02-19 11:08:54+00:00 [Note] [Entrypoint]: Temporary server stopped
mysql_1         |
mysql_1         | 2021-02-19 11:08:54+00:00 [Note] [Entrypoint]: MySQL init process done. Ready for start up.
mysql_1         |
mysql_1         | 2021-02-19T11:08:55.019311Z 0 [System] [MY-010116] [Server] /usr/sbin/mysqld (mysqld 8.0.21) starting as process 1
mysql_1         | 2021-02-19T11:08:55.027315Z 1 [System] [MY-013576] [InnoDB] InnoDB initialization has started.
mysql_1         | 2021-02-19T11:08:55.193856Z 1 [System] [MY-013577] [InnoDB] InnoDB initialization has ended.
mysql_1         | 2021-02-19T11:08:55.297841Z 0 [System] [MY-011323] [Server] X Plugin ready for connections. Bind-address: '::' port: 33060, socket: /var/run/mysqld/mysqlx.sock
mysql_1         | 2021-02-19T11:08:55.410836Z 0 [Warning] [MY-010068] [Server] CA certificate ca.pem is self signed.
mysql_1         | 2021-02-19T11:08:55.411005Z 0 [System] [MY-013602] [Server] Channel mysql_main configured to support TLS. Encrypted connections are now supported for this channel.
mysql_1         | 2021-02-19T11:08:55.413393Z 0 [Warning] [MY-011810] [Server] Insecure configuration for --pid-file: Location '/var/run/mysqld' in the path is accessible to all OS users. Consider choosing a different directory.
mysql_1         | 2021-02-19T11:08:55.435750Z 0 [System] [MY-010931] [Server] /usr/sbin/mysqld: ready for connections. Version: '8.0.21'  socket: '/var/run/mysqld/mysqld.sock'  port: 3306  MySQL Community Server - GPL.
mailhog_1       | [APIv1] KEEPALIVE /api/v1/events

とくにエラーもなくコンテナが起動しました。素晴らしい。-d オプションをつけることでバックグラウンドで起動することもできました。-d オプションで起動した場合、./vendor/bin/sail stopコマンドでコンテナを終了することができます。

http://localhost/ へアクセスするとLaravelアプリが表示されることが確認できます。

MailHogのコンテナも起動しているので、http://localhost:8025/でダッシュボードが表示できます。

phpMyAdminが使いたい場合

MySQLに関してはphpMyAdminなどの管理ツールは用意されていないので、必要であれば個別に追加するのがよさそうです。

docker-compose.yml に以下の定義を追加するとphpMyAdminが利用できるようになります。

How to add phpmyadmin to laravel 8 sail docker-compose.yml – Stack Overflow
https://stackoverflow.com/questions/66195113/how-to-add-phpmyadmin-to-laravel-8-sail-docker-compose-yml

docker-compose.yml

  phpmyadmin:
        image: phpmyadmin/phpmyadmin
        links:
            - mysql:mysql
        ports:
            - 8080:80
        environment:
            MYSQL_USERNAME: '${DB_USERNAME}'
            MYSQL_ROOT_PASSWORD: '${DB_PASSWORD}'
            PMA_HOST: mysql
        networks:
            - sail

初期状態だとユーザー名rootパスワード空でログインできます。

Windows

Installation – Laravel – The PHP Framework For Web Artisans
https://laravel.com/docs/8.x/installation#getting-started-on-windows

Windows Subsystem for Linux 2(WSL2)がインストールされている前提で手順はMacと同じです。

Linux

Installation – Laravel – The PHP Framework For Web Artisans
https://laravel.com/docs/8.x/installation#getting-started-on-linux

Macと同じです。

まとめ

非常に手軽にLaravelの開発環境を構築することができました。Dockerの知識がなくとも利用することができ、開発に集中できるとてもよいツールだったので積極的に利用したいと思います。

参考

Laravel Sail – Laravel – The PHP Framework For Web Artisans
https://laravel.com/docs/8.x/sail#introduction

Laravel SailでDocker環境構築 | RE:ENGINES
https://re-engines.com/2021/01/25/laravel-sail/

How to add phpmyadmin to laravel 8 sail docker-compose.yml – Stack Overflow
https://stackoverflow.com/questions/66195113/how-to-add-phpmyadmin-to-laravel-8-sail-docker-compose-yml

Laravel Sail なら Docker 開発環境がコマンド 2 撃で構築できる。PHP/MySQLからキューやメール環境までオールインワン
https://www.ritolab.com/entry/217

【Docker】Laravel Sailのインストールと使い方を確認 | アールエフェクト
https://reffect.co.jp/laravel/laravel-sail

元記事はこちら

Laravelの開発環境をDockerで構築しようとしたら公式さんがLaravel Sailって素敵ツールを提供してくれていました