🦓

[Laravel]MySQL+Nginx+PHPの開発環境をDockerで構築する

に公開

はじめに

MySQL、Nginx、PHPを使用したLaravelの開発環境をDockerで構築していきます。

環境

Docker Desktop
PHP 8.x
MySQL 8.x
Nginx

tl;dr

  1. Dockerfileを作成する
  2. Nginxの設定ファイルを作成する
  3. docker-compose.ymlを作成する
  4. PHPの設定ファイルを作成する
  5. Composerの設定ファイルを作成する
  6. コンテナをビルドする
  7. 新規Laravelプロジェクトを作成する
  8. npmをインストールする
  9. artisanコマンドを実行する

ディレクトリーの構造:

project-root/
|-- docker-compose.yml
|-- nginx/
|   `-- default.conf
|-- src/
|-- public/
|     |-- app/
|     `-- ...
|-- .gitignore
|-- composer.json
|-- phpunit.xml
|-- server.php
|-- php.Dockerfile
|-- nginx.Dockerfile
|-- mysql/

nginx.Dockerfileを作成する

nginx.Dockerfile
FROM nginx:stable-alpine

ENV NGINXUSER=laravel
ENV NGINXGROUP=laravel

RUN mkdir -p /var/www/html/public

ADD nginx/default.conf /etc/nginx/conf.d/default.conf

RUN sed -i "s/user www-data/user laravel/g" /etc/nginx/nginx.conf

RUN adduser -g ${NGINXGROUP} -s /bin/sh -D ${NGINXUSER}

Nginxの公式イメージ(nginx:stable-alpine)をベースにし、laravelユーザーとlaravelグループを作成し、Nginxの設定を調整しています。

FROM nginx:stable-alpine: Nginxの安定したAlpine Linux版をベースとして使用します。

ENV NGINXUSER=laravel: Nginxが使用するユーザー名をlaravelに設定します。

ENV NGINXGROUP=laravel: Nginxが使用するグループ名をlaravelに設定します。

RUN mkdir -p /var/www/html/public: Laravelアプリケーションの公開ディレクトリを作成します。

ADD nginx/default.conf /etc/nginx/conf.d/default.conf: Nginx設定ファイル(default.conf)をNginxの設定ディレクトリに追加します。

RUN sed -i "s/user www-data/user laravel/g" /etc/nginx/nginx.conf: Nginxのメインの設定ファイルであるnginx.conf内のwww-dataユーザーをlaravelに変更します。

RUN adduser -g ${NGINXGROUP} -s /bin/sh -D ${NGINXUSER}: laravelユーザーとそのグループを作成します。

これにより、Nginxのユーザーとグループがlaravelに変更され、NginxがLaravelアプリケーションの公開ディレクトリを正しくサーブできるようになります。

nginx/nginx.confを作成する

nginx/default.conf
server {
    listen 80;
    index index.php index.html;
    server_name localhost;
    error_log  /var/log/nginx/error.log;
    access_log /var/log/nginx/access.log;
    root /var/www/html/public;

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    location ~ \.php$ {
        try_files $uri =404;
        fastcgi_pass php:9000;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
        fastcgi_param PATH_INFO $fastcgi_path_info;
    }
}

listen 80;: サーバーブロックはポート80でリクエストを待ち受けます。

index index.php index.html;: indexファイルのデフォルト設定を指定しています。index.phpが存在する場合はそれが優先され、存在しない場合はindex.htmlが試されます。

server_name localhost;: サーバーの名前をlocalhostに設定しています。

error_log /var/log/nginx/error.log;access_log /var/log/nginx/access.log;: エラーログとアクセスログの場所を指定しています。

root /var/www/html/public;: ドキュメントルートをLaravelアプリケーションのpublicディレクトリに設定します。

docker-compose.ymlを作成する

docker-compose.yml
version: '3.9'

services:
  nginx:
    build:
      context: .
      dockerfile: nginx.Dockerfile
    ports:
      - 80:80
    volumes:
      - ./src:/var/www/html
    depends_on:
      - mysql
      - php
  mysql:
    image: mysql:8.0
    ports:
      - 3306:3306
    environment:
      MYSQL_DATABASE: laravel
      MYSQL_USER: laravel
      MYSQL_PASSWORD: password
      MYSQL_ROOT_PASSWORD: password
    volumes:
      - ./mysql:/var/lib/mysql
  php:
    build:
      context: .
      dockerfile: php.Dockerfile
    volumes:
      - ./src:/var/www/html

使用するサービスを定義しています。

  1. nginx:

    • Nginxのビルドは、カレントディレクトリのnginx.Dockerfileを使用します。
    • ホストのポート80をコンテナのポート80にマッピングします。
    • ./srcディレクトリをコンテナ内の/var/www/htmlにマウントします。
    • 依存しているサービスとしてmysqlphpを指定しています。
  2. mysql:

    • MySQLの公式イメージ(mysql:8.0)を使用します。
    • ホストのポート3306をコンテナのポート3306にマッピングします。
    • MYSQL_DATABASEMYSQL_USERMYSQL_PASSWORDMYSQL_ROOT_PASSWORDを環境変数として指定しています。
    • ./mysqlディレクトリをコンテナ内の/var/lib/mysqlにマウントします。(プロジェクトのルートにmysqlディレクトリーを作成します。)
  3. php:

    • PHPのビルドは、カレントディレクトリのphp.Dockerfileを使用します。
    • ./srcディレクトリをコンテナ内の/var/www/htmlにマウントします。これはLaravelアプリのルートディレクトリです。
  4. composer:

    • ./src:/var/www/html は、ホストマシンの ./src ディレクトリをコンテナ内の /var/www/html にマウントします。Composerはこのディレクトリ内で依存関係を解決します。
    • working_dir: /var/www/html: コンテナ内の作業ディレクトリを指定します。

php.Dockerfileを作成する

php.Dockerfile
FROM php:8-fpm-alpine

ENV PHPGROUP=laravel
ENV PHPUSER=laravel

RUN adduser -g ${PHPGROUP} -s /bin/sh -D ${PHPUSER}

RUN sed -i "s/user = www-data = ${PHPUSER}/g" /usr/local/etc/php-fpm.d/www.conf
RUN sed -i "s/group = www-group = ${PHPGROUP}/g" /usr/local/etc/php-fpm.d/www.conf

RUN mkdir -p /var/www/html/public

RUN docker-php-ext-install pdo pdo_mysql

CMD ["php-fpm", "-F", "-y", "/usr/local/etc/php-fpm.conf", "-R"]

ENV PHPGROUP=laravelENV PHPUSER=laravel: ユーザーとグループの名前を設定しています。

RUN adduser -g ${PHPGROUP} -s /bin/sh -D ${PHPUSER}:

  • adduserコマンドを使用して、指定したグループとユーザーを追加します。
  • -gオプションはグループを指定し、-sオプションはデフォルトのシェルを指定します。
  • -Dオプションはユーザーにパスワードを設定しないようにします。

RUN sed -i "s/user = www-data = ${PHPUSER}/g" /usr/local/etc/php-fpm.d/www.confRUN sed -i "s/group = www-data = ${PHPGROUP}/g" /usr/local/etc/php-fpm.d/www.conf:

  • sedコマンドを使用して、www.confファイル内のユーザーとグループをlaravelに変更します。
  • ${PHPUSER}${PHPGROUP}ENVディレクティブで設定された変数です。

RUN mkdir -p /var/www/html/public: 必要なディレクトリ構造を作成します。これはLaravelプロジェクトのpublicディレクトリに対応しています。

RUN docker-php-ext-install pdo pdo_mysql: pdopdo_mysql拡張機能をPHPに追加します。これらはLaravelがデータベースとの通信に使用する拡張機能です。

CMD ["php-fpm", "-F", "-y", "/usr/local/etc/php-fpm.conf", "-R"]:

  • コンテナが起動したときに実行されるコマンドを設定します。
  • php-fpm-F(フロントモード)で実行し、-yで別のphp-fpm設定ファイルを指定し、-Rでリクエストを拒否せずに終了します。

composer.Dockerfileを作成する

composer.Dockerfile
FROM composer:2

ENV COMPOSERUSER=laravel
ENV COMPOSERGROUP=laravel

RUN adduser -g ${COMPOSERGROUP} -s /bin/sh -D ${COMPOSERUSER}

ENV COMPOSERUSER=laravelENV COMPOSERGROUP=laravel: laravel というユーザーとグループの環境変数を設定しています。

RUN adduser -g ${COMPOSERGROUP} -s /bin/sh -D ${COMPOSERUSER}:

  • この行は、laravel という名前のグループ (-g ${COMPOSERGROUP}) とユーザー (-D ${COMPOSERUSER}) を作成します。
  • -s /bin/sh は、ユーザーに /bin/sh シェルを割り当てるオプションです。

これにより、Composer コンテナ内に laravel という名前のユーザーが作成され、Composer プロジェクトを実行するときにこのユーザーが使用されます。

Docker コンテナのビルドと起動

docker-compose up -d --build
[+] Running 12/12
 mysql 11 layers [⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿]      0B/0B      Pulled                                              21.2s 
 8e0176adc18c Pull complete                                                                       4.6s 
 a6b6bf6e5d0f Pull complete                                                                       4.6s 
 c17b83f8620f Pull complete                                                                       4.6s 
 b2e259cd9b6c Pull complete                                                                       5.0s 
 366131ab00d1 Pull complete                                                                       5.0s 
 2f99ba83a3cb Pull complete                                                                       5.0s 
 f7c88955f01f Pull complete                                                                       7.1s 
 577fb415d7f8 Pull complete                                                                       7.1s 
 29160ed46eb1 Pull complete                                                                      17.5s 
 69ce9884ce5d Pull complete                                                                      17.5s 
 848f0dceb14c Pull complete    
[+] Building 28.8s (29/29) FINISHED                                                                        
 => [php internal] load .dockerignore                                                                 0.0s
 => => transferring context: 2B                                                                       0.0s
 => [php internal] load build definition from php.Dockerfile                                          0.0s
 => => transferring dockerfile: 477B                                                                  0.0s
 => [php internal] load metadata for docker.io/library/php:8-fpm-alpine                               2.6s
 => [composer internal] load build definition from composer.Dockerfile                                0.0s
 => => transferring dockerfile: 178B                                                                  0.0s
 => [composer internal] load .dockerignore                                                            0.0s
 => => transferring context: 2B                                                                       0.0s
 => [composer internal] load metadata for docker.io/library/composer:2                                2.6s
 => [composer auth] library/composer:pull token for registry-1.docker.io                              0.0s
 => [php auth] library/php:pull token for registry-1.docker.io                                        0.0s
 => [php 1/6] FROM docker.io/library/php:8-fpm-alpine@sha256:9a1f2a7dba5605e05926ad8d3e837609c17acc7  0.0s
 => CACHED [php 2/6] RUN adduser -g laravel -s /bin/sh -D laravel                                     0.0s
 => [php 3/6] RUN sed -i "s/user = www-data/user = laravel/g" /usr/local/etc/php-fpm.d/www.conf       0.4s
 => [composer 1/2] FROM docker.io/library/composer:2@sha256:fb3c5a283f2dc08e08841048498e8a82c3864648  0.0s
 => CACHED [composer 2/2] RUN adduser -g laravel -s /bin/sh -D laravel                                0.0s
 => [composer] exporting to image                                                                     0.0s
 => => exporting layers                                                                               0.0s
 => => writing image sha256:40093d96c788f7ce5eec06fd6f0be79cc07b8beddf6f1ed301f926e1f0bda6bc          0.0s
 => => naming to docker.io/library/lara-docker-composer                                               0.0s
 => [php 4/6] RUN sed -i "s/group = www-data/group = laravel/g" /usr/local/etc/php-fpm.d/www.conf     0.3s
 => [php 5/6] RUN mkdir -p /var/www/html/public                                                       0.3s
 => [php 6/6] RUN docker-php-ext-install pdo pdo_mysql                                               18.8s
 => [php] exporting to image                                                                          0.1s 
 => => exporting layers                                                                               0.1s 
 => => writing image sha256:24786a20832c3323f75e5ca0ea39cfcb8875df3d87c6a6b2a0941b776967d951          0.0s 
 => => naming to docker.io/library/lara-docker-php                                                    0.0s 
 => [nginx internal] load build definition from nginx.Dockerfile                                      0.0s 
 => => transferring dockerfile: 330B                                                                  0.0s 
 => [nginx internal] load .dockerignore                                                               0.0s
 => => transferring context: 2B                                                                       0.0s
 => [nginx internal] load metadata for docker.io/library/nginx:stable-alpine                          2.9s
 => [nginx auth] library/nginx:pull token for registry-1.docker.io                                    0.0s
 => [nginx 1/5] FROM docker.io/library/nginx:stable-alpine@sha256:62cabd934cbeae6195e986831e4f745ee1  2.4s
 => => resolve docker.io/library/nginx:stable-alpine@sha256:62cabd934cbeae6195e986831e4f745ee1646c17  0.0s
 => => sha256:e6295d4bbc4559ee7ed2e93830f4228a08af4114d7914db140a026f84e69adbb 16.32kB / 16.32kB      0.0s
 => => sha256:a352ab20253014461883d177aacee4ee6a2f82b3976d65015665bf4e0c05478a 625B / 625B            0.2s
 => => sha256:a4896b78e8db1b60c583e7bf115527dea0f1cc0207047cc318e6bab726a44765 3.85MB / 3.85MB        0.4s
 => => sha256:62cabd934cbeae6195e986831e4f745ee1646c1738dbd609b1368d38c10c5519 1.65kB / 1.65kB        0.0s
 => => sha256:e06ffa63074d69b12c7f7712d7aebc4cc72d25217e55788d54160a2bd77182f4 1.78kB / 1.78kB        0.0s
 => => sha256:9398808236ffac29e60c04ec906d8d409af7fa19dc57d8c65ad167e9c4967006 3.38MB / 3.38MB        0.4s
 => => sha256:b9258afd06398fe15821fa84d20d293de602601e8f3c3f607eb0fb82077933a1 956B / 956B            0.4s
 => => extracting sha256:9398808236ffac29e60c04ec906d8d409af7fa19dc57d8c65ad167e9c4967006             0.2s
 => => sha256:8799ab366479fb72b00d08daefe94af71814b80c946ba8a3a3262129f4ab6688 773B / 773B            0.6s
 => => sha256:07bc104f8702005e2c6acc3f3932ce08d11442367a62fd133a67157e7e5a8e6e 1.40kB / 1.40kB        0.6s
 => => sha256:8afc9a751a90c1bc306e89a586c8e7b759a620814cb85a3584bd8c7833b4e9e4 11.67MB / 11.67MB      0.8s
 => => extracting sha256:a4896b78e8db1b60c583e7bf115527dea0f1cc0207047cc318e6bab726a44765             0.5s
 => => extracting sha256:a352ab20253014461883d177aacee4ee6a2f82b3976d65015665bf4e0c05478a             0.0s
 => => extracting sha256:b9258afd06398fe15821fa84d20d293de602601e8f3c3f607eb0fb82077933a1             0.0s
 => => extracting sha256:8799ab366479fb72b00d08daefe94af71814b80c946ba8a3a3262129f4ab6688             0.0s
 => => extracting sha256:07bc104f8702005e2c6acc3f3932ce08d11442367a62fd133a67157e7e5a8e6e             0.0s
 => => extracting sha256:8afc9a751a90c1bc306e89a586c8e7b759a620814cb85a3584bd8c7833b4e9e4             0.6s
 => [nginx internal] load build context                                                               0.0s
 => => transferring context: 635B                                                                     0.0s
 => [nginx 2/5] RUN mkdir -p /var/www/html/public                                                     0.3s
 => [nginx 3/5] ADD nginx/default.conf /etc/nginx/conf.d/default.conf                                 0.0s
 => [nginx 4/5] RUN sed -i "s/user www-data/user laravel/g" /etc/nginx/nginx.conf                     0.2s
 => [nginx 5/5] RUN adduser -g laravel -s /bin/sh -D laravel                                          0.2s
 => [nginx] exporting to image                                                                        0.0s
 => => exporting layers                                                                               0.0s
 => => writing image sha256:f63eda8d884745b3a30572e1890028aeff9174706f1463f2319938958ec8be38          0.0s
 => => naming to docker.io/library/lara-docker-nginx                                                  0.0s
[+] Running 5/5
 Network lara-docker_default       Created                                                          0.1s 
 Container lara-docker-php-1       Started                                                          0.8s 
 Container lara-docker-composer-1  Started                                                          0.8s 
 Container lara-docker-mysql-1     Started                                                          0.8s 
 Container lara-docker-nginx-1     Started                                                          0.9s 

php, nginxとmysqlが起動されていることを確認します。

docker-compose ps
NAME                  IMAGE               COMMAND                  SERVICE             CREATED              STATUS              PORTS
lara-docker-mysql-1   mysql:8.0           "docker-entrypoint.s…"   mysql               About a minute ago   Up 59 seconds       0.0.0.0:3306->3306/tcp, 33060/tcp
lara-docker-nginx-1   lara-docker-nginx   "/docker-entrypoint.…"   nginx               About a minute ago   Up 59 seconds       0.0.0.0:80->80/tcp
lara-docker-php-1     lara-docker-php     "docker-php-entrypoi…"   php                 About a minute ago   Up 59 seconds       9000/tcp

新規Laravelプロジェクトを作成する

Composerコマンドを使って新規Laravelプロジェクトを作成します。

   docker-compose run --rm composer create-project laravel/laravel .
[+] Building 0.0s (0/0)                                                                                    
[+] Building 0.0s (0/0)                                                                                    
Creating a "laravel/laravel" project at "./"
Installing laravel/laravel (v10.2.9)
  - Downloading laravel/laravel (v10.2.9)
  - Installing laravel/laravel (v10.2.9): Extracting archive
Created project in /var/www/html/.
> @php -r "file_exists('.env') || copy('.env.example', '.env');"
Loading composer repositories with package information
Updating dependencies
....
> @php artisan vendor:publish --tag=laravel-assets --ansi --force

   INFO  No publishable resources for tag [laravel-assets].  

No security vulnerability advisories found.
> @php artisan key:generate --ansi

   INFO  Application key set successfully.  

新しい Laravel プロジェクトが ./src ディレクトリ内に作成されたことを確認します。
localhost にアクセスすると、Laravelアプリが実行されていることを確認します。

ComposerパッケージのインストールもComposerコンテナの実行と同じようにdocker-compose run --rmをつけます。
そうするとコンテナの実行が完了したらコンテナが削除されます。

docker-compose run --rm composer require ***/***

DBの環境変数を設定する

docker-compose.ymlに設定したDBのユーザー名とパスワードを追加します。
また、ローカルのmysqlではなく、mysqlコンテナを使用するため、HOST名をサービス名に変えます。

.env
- DB_HOST=127.0.0.1
- DB_USERNAME=root
- DB_PASSWORD=

+ DB_HOST=mysql
+ DB_USERNAME=laravel
+ DB_PASSWORD=password

npmをインストールする

docker-compose.ymlnpmのイメージを使います。

docker-compose.yml
services:
  npm:
    image: node:current-alpine
    volumes:
      - ./src:/var/www/html
    entrypoint: ["npm"]
    working_dir: /var/www/html

npmをインストールします。

docker-compose run --rm npm install
[+] Building 0.0s (0/0)                                                                                    
[+] Building 0.0s (0/0)                                                                                    

added 20 packages, and audited 21 packages in 7s

5 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities
npm notice 
npm notice New patch version of npm available! 10.2.3 -> 10.2.4
npm notice Changelog: https://github.com/npm/cli/releases/tag/v10.2.4
npm notice Run npm install -g npm@10.2.4 to update!
npm notice 

アセットをコンパイルします。

docker-compose run --rm npm run dev
[+] Building 0.0s (0/0)                                                                                    
[+] Building 0.0s (0/0)                                                                                    

> dev
> vite


  VITE v4.5.0  ready in 778 ms

  Local:   http://localhost:5173/
  Network: use --host to expose
  press h to show help

  LARAVEL v10.32.1  plugin v0.8.1

  APP_URL: http://localhost

artisanコマンドを実行する

Docker Composeを使用すると、各サービス(PHPやMySQL)ごとに独自のコンテナが作成され、それぞれのコンテナにはLaravelプロジェクトと依存関係があります。
そのため、artisanコマンドを実行するには、docker-compose run artisan ...という形式で、artisanサービスを指定してDockerコンテナ内で実行する必要があります。
docker-compose.ymlファイルでartisanサービスを設定することで、このコマンドを簡単に実行でき、Docker環境内でLaravelアプを管理できます。

docker-compose.yml
services:
  artisan:
    build: 
      context: .
      dockerfile: php.Dockerfile
    volumes:
      - ./src:/var/www/html
    working_dir: /var/www/html
    depends_on:
      - mysql
    entrypoint: ["php", "/var/www/html/artisan"]

artisan: サービスの名前。このサービスは Laravel アプリケーション内で artisan コマンドを実行します。
build: コンテナをビルドする設定。contextDockerfile が存在するディレクトリを指定します。
volumes: ホストマシンの ./src ディレクトリをコンテナ内の /var/www/html にマウントします。アプリのコードがホストマシンとコンテナ間で共有されます。
working_dir: コンテナ内での作業ディレクトリを指定します。/var/www/html は Laravel アプリのルートディレクトリです。
depends_on: mysql サービスが起動するのを待ってからコンテナを起動します。これにより、Laravel アプリケーションが MySQL データベースに依存する場合に、データベースの起動を待つことができます。
entrypoint: コンテナが起動されたときに実行されるコマンドを指定します。["php", "/var/www/html/artisan"]php コマンドで /var/www/html/artisan を実行します。

マイグレーションを実行してみます。

docker-compose run --rm artisan migrate
[+] Building 0.0s (0/0)                                                                                    
[+] Creating 1/0
 Container lara-docker-mysql-1  Running                                                             0.0s 
[+] Building 0.0s (0/0)                                                                                    

   INFO  Preparing database.  

  Creating migration table .................................................................... 74ms DONE

   INFO  Running migrations.  

  2014_10_12_000000_create_users_table ........................................................ 74ms DONE
  2014_10_12_100000_create_password_reset_tokens_table ........................................ 86ms DONE
  2019_08_19_000000_create_failed_jobs_table .................................................. 53ms DONE
  2019_12_14_000001_create_personal_access_tokens_table ....................................... 65ms DONE

artisanコマンドを使えるようになりました。

docker-compose run --rm artisan tinker
[+] Building 0.0s (0/0)                                                                                    
[+] Creating 1/0
 Container lara-docker-mysql-1  Running                                                             0.0s 
[+] Building 0.0s (0/0)                                                                                    
Psy Shell v0.11.22 (PHP 8.2.12 cli) by Justin Hileman
> User::all();
= Illuminate\Database\Eloquent\Collection {#7011
    all: [],
  }

終わりに

DockerでのLaravel開発環境を構築してみした。

Nginx Dockerイメージ
PHP Dockerイメージ
MySQL Dockerイメージ
Composer Dockerイメージ
Composer Dockerイメージ
参考した記事

Discussion