Laravel 9でLaravel Mixの代わりに新しく導入されたViteを使えるLaravel開発環境を、docker上に実現する方法を紹介します。
ゴール
- WindowsやMac上に、dockerを使ったLaravel 9開発環境を作る。
- Laravel MixではなくViteを使う
- サーバーはnginx, php, mysqlを別にする。
- Viteの利点であるページリロード無しでのCSS/JS更新を実現する。
ところでViteの発音は?
Viteは「ヴィート」と発音します。フランス語だそうです。アメリカ人なら「ヴァイト」と読みそうですが、アメリカ人も「ヴィート」と読んでます。
ところでLaravel Mixではだめなのか?
Laravel 9の途中から、フロントエンド・ビルド・ツールとして長年標準だったLaravel Mixが非標準となりViteが標準となりました。
現時点ではLaravel 9公式でMixもサポートしています。したがって、現時点でMixを使うことに問題はない一方で、今後はViteに完全に置き換わる可能性も考えておいたほうが良いでしょう。
ところでなぜViteなのか?
Laravel Mixはwebpackを使っていますが、Viteは全く違う構造になっています。
詳しく見ていくと本記事の本題から外れるのでやめておきますが、webpackと比較したViteについてはざっと以下のようなイメージだけ持っていればよいかもしれません。
- 開発中の再コンパイルが圧倒的に高速になる
- ソースへの変更が瞬時にフロントページに反映される
- フロントページでAjax通信したあとのページのままデザインや動作を調整できる
- フロントページで動作しているReactのステートを変更せずソース変更が反映される
- ViteはIE11をサポートしてるけど開発中はIE11で見られないなど、これまでになかった制限がある
- でも本番リリース用のビルドはwebpackとあまり変わらない
前提
- PHP 8の知識がある
- Laravel 9の知識がある
- Laravel 9に必要な環境構築ができる
- Dockerで簡単な環境構築ができる
本投稿ではWindowsを使っているので、MacやLinuxで合わないところは読み替えてください。
Dockerに各サーバーを準備する
目指すフォルダ構成はこんな感じにします。(Docker関連のみ)
l9vitedev (プロジェクトフォルダ)
├ docker
│ ├ mysql
│ │ └ (中身たくさん)
│ ├ nginx
│ │ ├ logs
│ │ ├ default.conf
│ │ └ Dockerfile
│ └ php
│ ├ Dockerfile
│ └ php.ini
└ docker-compose.yml
Dockerをインストールする
ホストOSにDockerをインストールした状態から開始。
プロジェクトフォルダにDocker用ファイルを作る
今回はドキュメントフォルダの下にQiita
フォルダを作り、l9vitedev
フォルダを作ります。
作り終わったら、そのフォルダの中にDocker用ファイルを作っていきましょう。
docker-compose.yml
まずはdocker-compose.yml
から。
version: '3'
services:
l9vitedev-nginx:
container_name: "l9vitedev-nginx"
build:
context: ./docker/nginx
depends_on:
- l9vitedev-app
ports:
- 80:80
volumes:
- ./:/src
l9vitedev-app:
container_name: "l9vitedev-app"
build:
context: ./docker/php
depends_on:
- l9vitedev-mysql
volumes:
- ./:/src
- /src/node_modules
- /src/vendor
- ./docker/php/php.ini:/usr/local/etc/php/php.ini
l9vitedev-mysql:
image: mysql:8.0.28
command: --max_allowed_packet=32505856
container_name: "l9vitedev-mysql"
volumes:
- ./docker/mysql:/var/lib/mysql
environment:
- MYSQL_ROOT_PASSWORD=root
- MYSQL_DATABASE=l9vitedev
ports:
- 3306:3306
l9vitedev-redis:
image: redis:alpine
container_name: "l9vitedev-redis"
ports:
- 16379:6379
いくつかポイントを抑えます。
Webサーバーはnginxなので、appサーバー(PHPサーバー)を別に立ち上げてます。
→今回、後で出てくる説明で対象サーバーをわかりやすくするため意図的にWebサーバーとappサーバーを分けてます。Apache+PHPで同一サーバーにしても全く問題ありません。
Webサーバーはnginxで、ポート80をDocker外に見せて起動します。
AppサーバーはPHPが動作するようにし、ポートはDocker外に見せず、nginxからdocker内のネットワークで呼び出されて動作します。PHPの設定を指定しやすいように、php.iniだけdocker/phpフォルダで見えるようにしています。
また、/src/node_modules
フォルダと/src/vendor
フォルダはボリュームマウントから外してます。これは依存ライブラリ群をマウント対象から外すことで動作の高速化を狙ってします。Viteを使った開発環境ではその特性上★超重要★で、これをしないとページのロードに平気で1分とかかかります。
DBサーバーはMySQLで、デフォルトポートの3306をDocker外に見せて起動します。
ポート3306をDocker外に見せなくても動作はしますが、開発中にHeidiSQLやSequel Aceを使ってDBの中身を見るためには必要です。MySQLユーザー情報は、ユーザーIDがroot、パスワードがenvironmentで指定したMYSQL_ROOT_PASSWORDになります。
MySQL(とredis)はここでイメージを指定し、個別のDockerfile
を持たない設定とします。
max_allowed_packetオプションは指定しなくてもいいです。(が、指定したくなることが多いのでここに書いてます)
nginxのDockerfile等
まずは手動でプロジェクトフォルダ/docker/nginx
フォルダを作ります。
nginx
のDockerfile
を作ります。
同時に、最低限必要な設定であるdefault.conf
を記述します。
FROM nginx:1.21
COPY ./default.conf /etc/nginx/conf.d/default.conf
さらに、nginx
の動作を設定するdefault.conf
を作ります。
server {
listen 80;
server_name _;
client_max_body_size 1G;
root /src/public;
index index.php;
access_log /src/docker/nginx/logs/access.log;
error_log /src/docker/nginx/logs/error.log;
location / {
try_files $uri $uri/ /index.php$is_args$args;
}
location ~ \.php$ {
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass l9vitedev-app:9000;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
}
}
開発を進める上でnginx
の設定を変更したい場合はここに記載することになります。
phpのDockerfile等
まずは手動でプロジェクトフォルダ/docker/php
フォルダを作ります。
php
のDockerfile
を作ります。
同時に、最低限必要な設定であるphp.ini
を記述します。
FROM php:8.1-fpm
RUN apt-get update \
&& apt-get install -y \
git \
zip \
unzip \
vim \
libfreetype6-dev \
libjpeg62-turbo-dev \
libmcrypt-dev \
libpng-dev \
libfontconfig1 \
libxrender1
RUN docker-php-ext-configure gd --with-freetype --with-jpeg
RUN docker-php-ext-install gd
RUN docker-php-ext-install bcmath
RUN docker-php-ext-install pdo_mysql mysqli exif
RUN cd /usr/bin && curl -s http://getcomposer.org/installer | php && ln -s /usr/bin/composer.phar /usr/bin/composer
ENV NODE_VERSION=16.14.0
RUN curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.34.0/install.sh | bash
ENV NVM_DIR=/root/.nvm
RUN . "$NVM_DIR/nvm.sh" && nvm install ${NODE_VERSION}
RUN . "$NVM_DIR/nvm.sh" && nvm use v${NODE_VERSION}
RUN . "$NVM_DIR/nvm.sh" && nvm alias default v${NODE_VERSION}
ENV PATH="/root/.nvm/versions/node/v${NODE_VERSION}/bin/:${PATH}"
RUN node --version
RUN npm --version
WORKDIR /src
ADD . /src/storage
RUN chown -R www-data:www-data /src/storage
大事なポイントは、Composer
とNode
を入れている部分です。Laravel 9では、Composerは2以上、Nodeは16以上が必要です。
開発中にNode
のバージョンコントロールもしたい場合があるので、NVM
も入れています。
gd
などいくつかライブラリを入れていますが、このあたりは趣味(私の場合、自分がよく使うレンタルサーバーに寄せてる)なので好きなライブラリを入れましょう。
upload_max_filesize=256M
post_max_size=256M
上記では例としてupload_max_filesize
とpost_max_size
を指定していますが、特に設定がなければ空のファイルでもOKです。
実行テストする
ターミナル(WindowsならPowerShell)を起動し、プロジェクトフォルダに移動します。
たとえばWindowsのPowerShellを起動して、ドキュメントフォルダのQiitaフォルダに作ったl9vitedev
プロジェクトフォルダに移動した場合は:
ターミナルで、nginxとMySQLの起動に必要なフォルダを準備します。
※gitを使う場合、これらはignoreすべきで、gitからcloneした直後に実行すべきターミナルコマンドです。
# ログファイルを格納するディレクトリを作る(Gitを使う場合はこの``logs``フォルダをignoreすべき)
> mkdir ./docker/nginx/logs
# MySQLで使用するディレクトリを作る(Gitを使う場合はこの``mysql``フォルダをignoreすべき)
> mkdir ./docker/mysql
これでDocker起動に必要なすべてのファイルが揃いました。起動しましょう。
> docker compose up -d
うまく行けば以下のように4つのコンテナが正常起動します。
Webサーバーとして機能しているか確認するため、Chromeを開いてhttp://localhost
にアクセスしてみましょう。
まだドキュメントルートに何もアップロードしてないので「File not found.」で正解です。サーバーは正しく起動できました。
Laravelをインストールする
Laravelのインストール先は、appサーバー=phpサーバーです。
appサーバーに入って、composerを使ってLaravelをインストールします。
appサーバーのコンソールに入る
> docker compose exec l9vitedev-app bash
以下のように入れたらOKです。
root@681925db0ad3:/src#
ここからは「>
」がホストOS(ここではWindows)、「$
」がDockerコンテナ内のOSとして表記します。
上記/src#の#部分が$だと思って読み替えてください。
サブフォルダにLaravelをインストールする
Laravelをプロジェクトフォルダ(l9vitedevフォルダ)にインストールします。
ただ、プロジェクトフォルダにはすでにdocker関連ファイルがあるので、以下のようにサブフォルダ「l9vitedev_tmp」に一旦インストールし、その後中身をプロジェクトフォルダに移動することにします。
まずサブフォルダにLaravelをインストール。ここではバージョン9を指定しています。
$ composer create-project "laravel/laravel=9.*" l9vitedev_tmp --prefer-dist
次に中身をプロジェクトフォルダに移動し、一時サブフォルダを削除。
$ mv l9vitedev_tmp/* ./
$ mv l9vitedev_tmp/.* ./
$ rm l9vitedev_tmp -rf
このとき以下のように、/.や/..が移動できないというメッセージ以外に問題がなければ成功です。
最終的に、フォルダ構成が以下のようになるはずです。
ここまでroot
ユーザーでLaravelをインストールしましたが、storage
フォルダ以下のフォルダはWeb用のユーザーwww-data
が書き込みできなくてはいけないので、書き込み権限を付与しておきます。
これをやっておかないと、実行テストをした時点で「The stream or file "/src/storage/logs/laravel.log" could not be opened in append mode」というエラーになります。
$ chmod -R guo+w storage
Laravel公式のstorage利用手順にも指定のあるように、storageのシンボリックリンクを設置します。
$ php artisan storage:link
実行テストする
WebサーバーにアクセスするとLaravelが動作するはずなので、Chromeを開いてhttp://localhost
にアクセスしてみましょう。
Viteを使う
この記事ではViteを使ってCSSを変更し、その変更がフロントに反映されるまでを実装してみます。
依存のインストール
$ npm install
ViewにViteを埋め込む
\resources\views\welcome.blade.php
をエディタで開き、以下のようにViteを埋め込みます。
<!DOCTYPE html>
<html ...>
<head>
{{-- ... --}}
@vite(['resources/css/app.css', 'resources/js/app.js'])
</head>
埋め込もうとしているapp.css
やapp.js
は、Laravelインストール時に自動生成されているファイルです。
まずはこの状態でhttp://localhost
を見てみましょう。
Viteを起動していないので、当然エラーになるわけです。
Viteを起動する
次に、Viteを起動します。
$ npm run dev
起動成功したので、http://localhost
を見てみましょう。
ブラウザのコンソールログにいくつかエラーが出ます。これは、Viteから提供されるはずのファイルが読めない状態です。
通信先は127.0.0.1:5173
です。これが失敗しているのは、以下2つが原因です。
- そもそもポート5173がDocker外に開かれていない
- Viteは
Local:
は見ているがNetwork:
は見ていない
先程Viteを起動したときにLocal: http://localhost:5173/
と表示されているのがローカル(appサーバー)に反応するという意味です。
Network: use --host to expose
となっているのは、ネットワーク経由でもアクセスするなら--host
オプションをつけろという意味です。
appサーバーのポート5173を見せる
Docker外にいるブラウザから、appサーバー内にいるViteが見えるよう、ポート5173を見せるため、docker-compose.yml
にポートを追記します。
...
l9vitedev-app:
container_name: "l9vitedev-app"
build:
context: ./docker/php
depends_on:
- l9vitedev-mysql
ports:
- 5173:5173
volumes:
- ./:/src
- /src/node_modules
- /src/vendor
- ./docker/php/php.ini:/usr/local/etc/php/php.ini
...
ports:
が追記されました。これでDocker外からlocalhost:5173
にアクセスすると、appサーバーのポート5173につながります。
また、/docker/php/Dockerfile
の先頭にもEXPOSEの設定が必要です。
FROM php:8.1-fpm
EXPOSE 5173
RUN apt-get update \
...
Dockerfileへの変更を反映するには、Dockerコンテナを再起動する必要があります。
この時、composer
とnpm
のinstallコマンドを再実行することに注意してください。理由はこの記事の下部に書きます。
> docker compose down
> docker compose up -d --build
> docker compose exec l9vitedev-app bash
$ composer install
$ npm install
--host オプションを追加する
--host オプションは、Vite起動オプションなのでvite.config.js
またはnpm run dev
の中身を変更することになります。どちらの方法でも大丈夫ですが、あとでvite.config.js
は追記するのでvite.config.js
側に設定を集約するほうが後々楽でしょう。
vite.config.js
で設定する場合は、以下のようにserver
host
を設定します。
import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin';
export default defineConfig({
plugins: [
...
],
server: {
host: true,
},
});
package.json
で設定する場合は、以下のように--hostオプションを追記します。
{
"private": true,
"scripts": {
"dev": "vite --host",
"build": "vite build"
},
...
}
Viteを起動(すでに起動中の場合はCtrl+Cで停止してから再度起動)します。
$ npm run dev
今度はNetwork:
も見ていることがわかります。
もう一度、http://localhost
を見てみましょう。
またエラーが出ています。
今度はhttp://[::]:5173/
以下のファイルが見えないというエラーです。
これはViteの真骨頂であるホットリロード機構HMR
と通信できていないことを意味します。解決には、HMR
の設定でホストをlocalhost
にすればOKです。
...
server: {
host: true,
hmr: {
host: 'localhost',
},
},
});
ファイルを保存したら、Viteを再起動して、もう一度、http://localhost
を見てみましょう。
コンソールのエラーが消えました。
CSSを変更し、フロントに反映する
ではCSSを変更してみましょう。
/resources/css/app.css
をエディタで開くと空ですので、以下のように入力し保存します。
.underline{
color: red;
}
ブラウザでhttp://localhost
を見てみましょう。(Windowsでは)反映されていません。
※MacやLinuxの場合は問題が出ないと思われますので、読み飛ばしてください。
Viteを再起動して、もう一度、http://localhost
を見てみましょう。
反映されました!
しかし、これではViteの最重要機能「CSS保存で瞬時に反映」が実現できていません。
問題は、appサーバー内でViteがファイル編集イベントに反応できていないことで起こっています。この問題はどうもWSL2に関連する問題のようで、Vite公式でも言及されています。
こういった場合、Viteをポーリングモードで動作させることができます。Viteの設定ファイルにポーリングモードで動作するようusePolling
を記述します。
...
server: {
host: true,
hmr: {
host: 'localhost',
},
watch: {
usePolling: true,
},
},
});
Viteを再起動し、ブラウザでhttp://localhost
を開き、リンク文字が赤いことを確認したら、以下のようにリンク文字のCSS設定を黄色にして保存してみましょう。
.underline{
color: yellow;
}
保存後すぐにViteが以下のように反応するはずです。
ブラウザ側でもすぐリンク文字が黄色くなります。
Dockerコンテナの操作におけるノウハウ
Dockerコンテナの起動と終了
起動
> docker compose up -d
リビルドと起動
> docker compose up -d --build
停止
> docker compose down
Dockerコンテナ起動時に毎回実行すべきコマンド
今回は、動作を高速にするために/src/node_modules
フォルダと/src/vendor
フォルダはボリュームマウントから外してます。
これにより、composer
やnpm
でインストールされた依存ライブラリはDockerコンテナを終了するごとに消えてしまいます。したがって、docker compose up
するときは毎回直後に依存ライブラリをインストールする必要があります。
> docker compose up -d
> docker compose exec l9vitedev-app bash
$ composer install
$ npm install
面倒だという方は起動時自動実行シェルスクリプトを組み込むことをおすすめします。それも面倒なら、appサーバーのDockerfileに以下の行を入れれば幸せになれるかも。
ENTRYPOINT [ "bash", "-c", "composer install; npm install; exec php-fpm" ]
以上です!お疲れ様でした!