DockerでLaravel動かす時に処理が重い問題の対処法
どうもLhaplus(ラプラス)です。 今回はDockerで仮想環境構築していて、Laravel動かしている時に処理が重たくなる問題について解決したので記事として記録しておきます。 コンテナ構築時に、ディレクトリーのマウントをしない場合は動作が重くなることはないのですが、普通はVS Codeとかでコード編集すると思うので、マウント環境は設定すると思います。 その場合の対処法を記します。
私の環境
  • Windows 10 Pro 64bit
  • Docker Desktop 19.03.12
  • Docker-compose 1.27.2

処理が重たくなる原因

Laravel開発するときには、おそらくコンテナ起動するときには、
docker run -v '[マウント元ディレクトリーパス]:/var/www/html' -itd -p 80:80 -p 443:443 --privileged [コンテナ] /sbin/init
こんな感じで起動させることが多いかもしれません。この例では、マウント元とマウント先「/var/www/html」でLaravelのプロジェクト領域全体を含める形にしています。 Laravelのプロジェクトディレクトリープロジェクトディレクトリー全体をマウントしてしまうのが手っ取り早いですが、これだとブラウザーでサイトにアクセスするたびに読み込みで待たされストレスが貯まります。 自分の場合だと、ページ遷移するたびに15秒以上かかってました。 なんでこんなに時間がかかってしまうのか色々調べた結果、Laravelが動作する毎に大量のファイルやデータが読み込み・書き込み・削除の処理に追われ、マウント元の同期に時間がかかることでした。 じゃあどの場所でそんな大量の処理をしているかというと、Laravelプロジェクトディレクトリ直下にある、「storage」が問題の場所でした。「storage」ディレクトリでは、LaravelのコンパイルされたBladeテンプレート、セッション情報、ファイルキャッシュ、ログなどのデータがアクセスするごとに処理するようになっているようで、一番の重い原因となっていました。その他にも、「bootstrap」ディレクトリではサイトキャッシュの制御も行われているので、これも重い原因になっているように想います。 なので、対処法としては「storage」と「bootstrap」以外のディレクトリをマウントすれば解決できます。

私の対処方法

上記の問題点を含めて考えると、「ファイルの生成と削除を大量に伴うディレクトリはマウントしないようにする」ことで極端に重くなることはなくなります。 マウントの設定をするときは少し手間になりますが、個別にディレクトリを指定するようにします。 「docker run」コマンドでも「-v」オプションの複数記載で実行もできますが、かなり長くなって扱いが面倒になるので、ここからは「docker-compose」を使ってコンテナを構築します。 docker-composeをまだ導入していない場合は、 [Docker] Windows 10 Pro 64bit に Docker と Docker Compose をインストールする で導入方法を開設しているので参考にしてみてください。 そしたら、まず「docker-compose.yml」ファイルを作成して、以下のように内容を記載します。
version: '2'
services:

  bildrop: # 任意のサービス名
    image: laravel-image:1.0 # 使用するイメージ
    container_name: bildrop_latest # コンテナ名
    #restart: always # 常にコンテナを起動状態にするか
    ports: # HTTP通信用のポートを開放
      - 80:80
      - 443:443
    cap_add:
      - SYS_ADMIN
    security_opt:
      - seccomp:unconfined
    privileged: true
    command: /sbin/init
    volumes: # マウントの設定
      - ./source/app:/var/www/html/app
      - ./source/config:/var/www/html/config
      - ./source/database:/var/www/html/database
      - ./source/public:/var/www/html/public
      - ./source/resources:/var/www/html/resources
      - ./source/routes:/var/www/html/routes
マウント元のディレクトリ構成は以下のようになります。
......
|-- docker-compose.yml
|-- source
  |-- app
  |-- config
  |-- database
  |-- public
  |-- resources
  |-- routes
手順としては、まずマウント元の「source」ディレクトリに上記で示したリストの空ディレクトリを作っておきます。その後、起動したコンテナにアタッチして、「/var/www/html」内で、Laravelプロジェクト一式をGitHubの自分のリポジトリからクローンしてあげれば完了です。 実際にこの環境で、処理速度を測ってみると、1秒もかからなくなりました。とりあえず、これで快適に開発ができますね。

Dockerコンテナのリソースを増やす

Dockerコンテナのリソースを増やすことで、Laravelアプリケーションのパフォーマンスを改善することができます。たとえば、コンテナのCPU割り当てやメモリサイズを増やすことができます。これにより、Laravelアプリケーションがより高速に動作するようになります。

キャッシュの使用

Laravelは、アプリケーションのパフォーマンスを改善するために、キャッシュを使用することができます。キャッシュを使用することで、データベースアクセスの回数を減らすことができます。キャッシュを有効にするには、Laravelの設定ファイルであるconfig/cache.phpを編集します。

「config/cache.php」編集方法

「config/cache.php」ファイルを編集することで、Laravelアプリケーションでキャッシュを有効にすることができます。以下の手順を参考にして、キャッシュを有効にする方法を確認してください。

1.Laravelアプリケーションのルートディレクトリにある「config/cache.php」ファイルを開く

2.「'default'」キーに、使用するキャッシュドライバを指定します。

たとえば、Memcachedを使用する場合は、以下のように設定します
'default' => env('CACHE_DRIVER', 'memcached'),

3.「'stores'」キーに、使用するキャッシュストアの設定を追加します。

'stores' => [
    'memcached' => [
        'driver' => 'memcached',
        'servers' => [
            [
                'host' => '127.0.0.1',
                'port' => 11211,
                'weight' => 100,
            ],
        ],
    ],
],
上記の設定は、Memcachedを使用する場合の設定例です。他のキャッシュドライバを使用する場合は、設定内容が異なる場合があります。

4.キャッシュを使用するコードを追加します。

たとえば、以下のようにキャッシュを使用することができます。
$value = Cache::remember('key', $minutes, function () {
    return DB::table('users')->get();
});
上記のコードは、キャッシュから値を取得できない場合に、クロージャー内のコードを実行し、その結果をキャッシュに保存します。$minutesはキャッシュの有効期間を指定します。 以上の手順を実行することで、Laravelアプリケーションでキャッシュを有効にすることができます。

クエリの最適化

LaravelのEloquent ORMを使用する場合、クエリの最適化を行うことでパフォーマンスを改善することができます。たとえば、必要なカラムだけを取得するようにクエリを書き換えることで、データベースアクセスの回数を減らすことができます。
また、インデックスを追加することで、クエリの実行速度を向上させることもできます。

Laravelでのクエリの最適化方法

クエリビルダを使用する

Laravelでは、クエリビルダを使用してデータベースクエリを簡単に書くことができます。クエリビルダを使用することで、SQL文を直接書く必要がなくなり、コードが簡潔になります。

インデックスを使用する

インデックスを使用することで、データベースの検索速度を大幅に改善することができます。Laravelでは、マイグレーションファイルを使用してテーブルにインデックスを追加することができます。また、クエリビルダを使用して、条件にマッチするデータを検索する際に、インデックスを使用するように指定することもできます。

Eagerローディングを使用する

Eagerローディングを使用することで、リレーションシップを持つ複数のテーブルからデータを取得する際に、N+1問題を回避することができます。Laravelでは、Eagerローディングを使用するための機能が用意されており、リレーションシップを持つモデルを取得する際に、withメソッドを使用してEagerローディングを指定することができます。

パフォーマンステストを実行する

Laravelには、パフォーマンステストを実行するための機能が用意されており、データベースクエリの実行時間やメモリ使用量などを計測することができます。パフォーマンステストを実行することで、どのクエリがパフォーマンスに影響を与えているかを特定し、最適化することができます。

キャッシュを使用する

クエリの結果をキャッシュすることで、データベースクエリを実行する回数を減らすことができます。Laravelには、キャッシュを使用するための機能が用意されており、キャッシュドライバを選択し、キャッシュを使用するタイミングを指定することができます。

バッチ処理の実行

Laravelアプリケーションでバッチ処理を実行することで、パフォーマンスを改善することができます。
たとえば、一度に多くのレコードを更新する場合、Eloquent ORMを使用するよりも、SQLのUPDATE文を使用した方が効率的です。
また、バッチ処理を実行する場合は、データベースのトランザクションを使用することで、データの整合性を保つことができます。

Laravelでバッチ処理を実行する方法

Artisanコマンドを作成する

まず、バッチ処理を実行するためのArtisanコマンドを作成する必要があります。Artisanコマンドは、Laravelで定義されたコマンドラインツールであり、バッチ処理を実行するためのスクリプトを記述することができます。
Artisanコマンドを作成するには、以下のコマンドを実行します。
php artisan make:command {コマンド名}
このコマンドを実行すると、app/Console/Commandsディレクトリに新しいコマンドクラスが作成されます。

バッチ処理の実行を記述する

次に、作成したArtisanコマンドのhandleメソッドに、バッチ処理を実行するスクリプトを記述します。バッチ処理の内容は、具体的な要件によって異なりますが、例えば、データベースのレコードを一括で更新する処理や、ファイルのバッチ処理などがあります。

Artisanコマンドを実行する

バッチ処理を実行するには、作成したArtisanコマンドを実行します。以下のコマンドを実行することで、作成したArtisanコマンドが実行されます。
php artisan {コマンド名}
また、バッチ処理を定期的に実行する場合は、Laravelに組み込まれているスケジューラーを使用することができます。
スケジューラーを使用することで、定期的にバッチ処理を実行するためのコードを記述することができます。

スケジュール設定ファイルにバッチ処理を追加する

スケジューラーを使用する場合、app/Console/Kernel.phpファイルのscheduleメソッドにバッチ処理を追加する必要があります。
スケジューラーを使用してバッチ処理を定期実行する場合は、以下のようにスケジュール設定ファイルに追記します。
$schedule->command('my:batch')->daily();
上記の例では、MyBatchCommandを1日ごとに実行するようにスケジュールされます。

まとめ

今回の方法を試す前に、最近利用している方も多い「docker-sync」も試してみましたが、最後の最後でうまく動作させることができず、利用を諦めました。 途中まで使ってみた感想は、Laravelのプロジェクトディレクトリ全体をマウントさせた状態でも、動作に1秒もかかっていない感じの爆速ぶりでした。 試しにこっちを最初に使ってみるのがいいかもしれません!!
おすすめの記事