🦓

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

2023/11/21に公開

はじめに

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.1APP_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