docker-composeでLaravelの開発環境を整える方法とその解説

docker-composeでLaravelの開発環境を整える方法とその解説

Laravelの開発環境は、Laravel Homestead、Laravel Valetなどを使うとかんたんに構築することができますが、本記事ではdocker-composeを使ってLEMP環境(Linux, Nginx, MySQL, PHP)をイチから構築し、Laravelをインストールする方法を解説します。

docker-composeを使って開発環境を構築することには、複数のアプリケーションやミドルウェアの複雑な依存関係をコードで簡潔に管理できるようになるという利点があります。

docker-composeによるLEMP環境の構築・Laravelのインストールをわかりやすく解説するため、最初から完成形のコードを紹介することはせず、Nginxサーバーを立ち上げる→PHPを動かす→MySQLを用意する→Laravelをインストールするという順番でコードを組み立てて解説していきます。

Dockerによる開発環境構築について

本記事では、Dockerとは何なのかについて詳細に書くことはしません。Dockerとは何なのか?をざっくりと知りたい方は、QiitaにあるDockerについてなるべくわかりやすく説明するという記事が、Dockerの用語を冷凍チャーハンに例えて説明してあり、大変わかりやすかったので、ぜひご参照ください。

Dockerによる開発環境構築では、Dockerfiledocker-compose.ymlの2つのファイルに設定を記述して開発環境を構築します。

Dockerfile

Dockerfileには、Docker独自の言語を使って、ビルドするイメージの構成を定義します。
Docker Hubというレジストリに公開されたベースを指定し、必要なパッケージ等をインストールするスクリプトを書き込んでいきます。本記事では、PHPコンテナの定義に使います。(まずはdocker-composeでイメージの指定だけして進め、Laravelのインストールの章でDockerfileを作成し、詳細にイメージの構成を定義します。)

docker-compose.yml

docker-compose.ymlには、yaml形式で、複数のコンテナで構成されるサービスを構築・実行する手順等を記述します。
それぞれのコンテナについて、イメージ作成時にベースにするDockerイメージやDockerfileの指定・ホスト側からDockerコンテナ側へのポートフォワーディングの指定・Dockerコンテナに共有する(ホスト側の)ファイル群の指定などを定義することができます。

Nginxコンテナを用意し、Webサーバーを立ち上げる

本記事のゴールは、PHPの実行環境を構築し、Laravelをインストールすることなので、PHPを動作させるWebサーバーをNginxで用意する必要があります。
まずは、default.conf(Nginxの設定ファイル)・docker-compose.yml・index.htmlを作成し、Nginxによる静的コンテンツ(index.html)配信サーバーを立ち上げます。PHPの実行環境は、次章のNginx上でPHPを動作させるにて構築します。

Nginxのコンテナ定義、設定ファイルを用意する

まずは、開発環境の起点となるディレクトリをmkdirコマンドで作成し、cdコマンドで作成したディレクトリ内に移動します。

mkdir docker-compose-laravel
cd docker-compose-laravel

以下の構成で各ファイル・ディレクトリを作成します。

.
├── docker
│   └── web
│       └── default.conf
├── docker-compose.yml
└── index.html

docker-compose.yml

docker-compose.ymlには、Dockerによる開発環境構築についてで説明したとおり、複数のコンテナで構成されるサービスを構築・実行する手順等を記述しますが、ここでは、まず、NginxのDockerコンテナの情報のみ記述します。

version: '3'
services:
  web:
    image: nginx:1.15.6
    ports:
      - '8000:80'
    volumes:
      - ./docker/web/default.conf:/etc/nginx/conf.d/default.conf
      - .:/var/www/html

versionは、docker-compose.ymlのファイルフォーマットのバージョンを宣言しています。version: '3'はファイルの記述定義のうち、安定して利用できる(記事執筆時点での)最新版です。

services要素のwebは、コンテナの名前の定義であり、更にその1つ下の階層が、実行するコンテナの定義です。

imageは、イメージ作成時にベースにするDockerイメージ(ここではDocker Hubで配布されているnginxのDockerイメージ)を指定します。:1.15.6で、Dockerイメージのバージョンを指定しています。

portsは、ポートフォワーディングの指定で、ホスト側のポート:コンテナ側のポートの形式で記述します。ここでは、'8000:80'と記述しているので、ホスト側からlocalhost:8000にアクセスすると、コンテナ側のlocalhost(80はhttpのデフォルトポート)にアクセスできるようになります。

volumesは、ホスト・コンテナ間でのファイル共有の指定で、ホスト側のパス:コンテナ側のパスの形式で記述します。ここでは、ホスト側で編集した./docker/web/default.confがコンテナ側の/etc/nginx/conf.d/default.confに反映され、さらに、ホスト側のルートディレクトリ(ここでは、作成したディレクトリ./docker-compose-laravel)の内容がホスト側の/var/www/htmlに反映されます。
volumesを設定することファイルやディレクトリは、永続化(コンテナを削除してもホスト側にファイルやディレクトリが残る)させることができます。

default.conf

default.confには、Nginxの設定ファイルを記述します。

server {
    listen 80;

    root  /var/www/html;
    index index.html;

    access_log /var/log/nginx/access.log;
    error_log  /var/log/nginx/error.log;
}

listenでは、Webサーバがリクエストを受け付けるIPアドレスやポート番号を設定します。listen IPアドレス:ポート番号の形式で記述します。ここでは、IPアドレスを省略しており、デフォルトの設定として、すべてのIPアドレスの80番ポートでリクエストを受け付けます。

root, indexでは、それぞれ、ドキュメントルートのディレクトリ・インデックスとして使われるファイル名を設定します。
ここでは、リクエストのURIが/で終わっている(つまりディレクトリになっている)ときに、/var/www/html/(リクエストのURI)/index.htmlをレスポンスとして返すように設定しています。

access_log, error_logでは、それぞれ、アクセスログの出力先パス・エラーログの出力先パスを指定しています。

動作確認

NginxのDockerコンテナの定義、Nginxの設定が済み、静的コンテンツを配信するWebサーバーを立ち上げる準備が整いました。Nginxが動作していることを確認するための静的コンテンツとして、index.htmlを用意します。

<h1>docker-compose-laravel</h1>
<p>Served by Nginx</p>

ここまでの状態で、./docker-compose-laravelディレクトリで、以下のコマンドを実行してください。

docker-compose up -d

docker-compose upコマンドは、docker-compose.ymlの記述のとおりにコンテナを作成・開始するコマンドで、-dオプションをつけることでバックグラウンドでコンテナを実行させることができます。-dオプションをつけなかった場合、各コンテナの出力が表示され続けます。

コンテナの作成・開始が成功した場合、以下のように表示されます。

Creating network "docker-compose-laravel_default" with the default driver
Creating docker-compose-laravel_web_1 ... done

ブラウザでhttp://localhost:8000にアクセスすると、以下のように表示されます。

これで、静的コンテンツを配信するWebサーバーを立ち上げ、それを確認することができました。
ここまでのコードはこちらのリポジトリにまとめてありますので、ご参照ください。

Nginx上でPHPを動作させる

前章のNginxコンテナを用意し、Webサーバーを立ち上げるでは、静的なコンテンツを配信するWebサーバーを立ち上げました。本章では、PHP-FPMを使ってNginx上でPHPを動作させ、PHPの実行環境を構築します。

PHP-FPMとは何なのか、Nginxとの関係などについては、Qiitaにあるnginx と PHP-FPM の仕組みをちゃんと理解しながら PHP の実行環境を構築するという記事が詳細でわかりやすくまとめられていたため、ぜひご参照ください。

PHP-FPMのコンテナを定義、Nginxの設定

docker-compose.ymlにPHP-FPMのコンテナ定義を追加

前章のNginxコンテナを用意し、Webサーバーを立ち上げるにて作成したdocker-compose.ymlを下記の通り編集します。

version: '3'
services:
  web:
    image: nginx:1.15.6
    ports:
      - '8000:80'
    depends_on:
      - app
    volumes:
      - ./docker/web/default.conf:/etc/nginx/conf.d/default.conf
      - .:/var/www/html
  app:
    image: php:7.2-fpm
    volumes:
      - .:/var/www/html

まず、servicesappを追加します。ベースとなるイメージはphp:7.2-fpmを指定します。volumesも設定し、/var/www/html配下のファイルやディレクトリを永続化させます。
これで、このdocker-compose.ymlはWebサーバーを立ち上げるコンテナと、PHPを動作させるコンテナの2つのコンテナを定義していることになります。

次に、webdepends_onを追加し、appを設定します。depends_onは、サービスの依存関係を定義するオプションです。ここでは、NginxがPHPを実行するので、NginxがPHPに依存しているということを定義しています。
このように定義すると、コンテナ起動時に、サービスの依存関係を考慮してコンテナが起動するようになります。つまり、PHPコンテナが起動したあとに、Nginxコンテナが起動するようになります。

PHPを実行できるようにNginxを設定

Nginxの設定ファイルdefault.confを編集して、PHPを実行できるように設定します。

server {
    listen 80;

    root /var/www/html;
    index index.php index.html index.htm;

    access_log /var/log/nginx/access.log;
    error_log /var/log/nginx/error.log;

    location / {
        try_files $uri $uri/ /index.php$is_args$args;
    }

    location ~ \.php$ {
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_pass 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;
    }
}

まず、indexの設定を修正します。インデックスページの設定をindex.htmlからindex.php index.html index.htmに変更しています。
インデックスページを複数設定した場合、ドキュメントルートのディレクトリにファイルが複数存在したとき、先に記述したファイルから優先してインデックスページに設定されるようになります。index.phpとindex.htmlが両方存在した場合、index.phpが優先してインデックスページに設定されます。
PHPを実行できるようにするため、index.phpを先頭に設定しています。

次に、locationで、URIごとにどのファイルを配信するのかを設定しています。
10行目のlocationでは、URIのパスにファイルがあるか、なかった場合ディレクトリがあるか、ファイルもディレクトリもなかった場合、index.phpを返す、という設定をしています。また、$is_args$argsでは、getパラメータを取得しています。
14行目のlocationでは、NginxがPHP-FPMにリクエストを渡すための設定をしています。詳細については、上で紹介した記事にて解説されているので、ご参照ください。fastcgi_passapp:9000についてですが、本来は127.0.0.1:9000のように設定するのですが、docker-composeではすべてのサービス間に自動でリンクが張られており、appのようなサービス名で設定することができます。9000はPHP-FPMが起動するデフォルトのポート番号です。

動作確認

PHP-FPMコンテナの定義、PHPを実行するためのNginxの設定が済み、NginxでPHPを動作させる準備が整いました。index.htmlと同じ階層にindex.phpを用意します。

<h1>docker-compose-laravel</h1>
<p>Served by Nginx</p>
<?php phpinfo();?>

phpinfo()はPHPのバージョンなどの設定内容を出力する関数です。

この状態で、先程と同様にdocker-compose up -dコマンドを実行すれば、以下のような画面が表示されます。
まだdocker-compose up -dをしてそのままの状態であれば、一度docker-compose downし、コンテナを停止・削除してから行ってください。

これで、NginxでPHPを動作させ、それを確認することができました。
ここまでのコードはこちらのリポジトリにまとめてありますので、ご参照ください。

MySQLコンテナを用意する

Laravelで利用するDBとして、MySQLを用意します。DBが用意できれば、Laravelをインストールする準備が整います。

MySQLのコンテナを定義

docker-compose.ymlを下記の通り編集し、MySQLのコンテナを定義します。

version: '3'
services:
  web:
    image: nginx:1.15.6
    ports:
      - '8000:80'
    depends_on:
      - app
    volumes:
      - ./docker/web/default.conf:/etc/nginx/conf.d/default.conf
      - .:/var/www/html
  app:
    image: php:7.2-fpm
    volumes:
      - .:/var/www/html
    depends_on:
      - mysql
  mysql:
    image: mysql:5.7
    environment:
      MYSQL_DATABASE: sample
      MYSQL_USER: user
      MYSQL_PASSWORD: password
      MYSQL_ROOT_PASSWORD: password
    ports:
      - "3306:3306"
    volumes:
      - mysql-data:/var/lib/mysql
volumes:
  mysql-data:

environmentでは、MySQLコンテナでの環境変数を設定しています。MYSQL_DATABASEはイメージの起動時に作成するデータベースの名前、MYSQL_USER, MYSQL_PASSWORDは新規ユーザーの作成とそのユーザーのパスワードの設定、MYSQL_ROOT_PASSWORDはMySQLにおけるルートユーザーであるrootアカウントのパスワードの設定です。

トップレベルにあるvolumesは、mysql-dataをサービス内で共通化して、他のコンテナからも参照できるようにするための設定です。

動作確認

docker-compose up -dしたあと、下記のコマンドを実行することで、MySQLコンテナに入ることができます。

docker-compose exec mysql bash

コンテナに入ると、コマンドプロンプトがroot@xxxxxxxxxxxx:/#のような状態に変化します。この状態で下記のコマンドを実行することで、MySQLを起動させることができます。パスワードが求められますので、docker-compose.ymlのenvironmentで設定したパスワードを入力してください。

mysql -h localhost -u user -p

MySQLが起動すると、コマンドプロンプトがmysql>に変化します。この状態で、下記のコマンドを実行することで、データベースの一覧を確認することができます。

show databases;

イメージ起動時に作成されたデータベースsampleが表示されていることが確認できます。

mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| sample             |
+--------------------+
2 rows in set (0.00 sec)

これで、MySQLの起動・データベースの作成ができたことが確認できました。Laravelのインストール後、接続の設定をします。
ここまでのコードはこちらのリポジトリにまとめてありますので、ご参照ください。

Laravelのインストール

Nginx, PHP, MySQLのコンテナが揃いましたので、いよいよLaravelのインストールです。

Laravelは、Composerというパッケージ管理システムを使用してインストールします。Composerをインストールするには、appコンテナ内で様々な作業が必要なわけですが、Dockerfileを使えば、それらの作業をコード化して、コンテナ作成時に自動実行させることができます。

Dockerfileの作成

docker/phpディレクトリを作成し、そこにDockerfileを作成してください

FROM php:7.2-fpm

# install composer
RUN cd /usr/bin && curl -s http://getcomposer.org/installer | php && ln -s /usr/bin/composer.phar /usr/bin/composer
RUN apt-get update \
&& apt-get install -y \
git \
zip \
unzip \
vim

RUN apt-get update \
    && apt-get install -y libpq-dev \
    && docker-php-ext-install pdo_mysql pdo_pgsql

WORKDIR /var/www/html

FROMphp:7.2-fpmを指定しています。

RUNでDockerイメージビルド時にDockerコンテナ内で実行するコマンドを定義しています。ここでは、curlコマンドを使ってComposerをインストール、apt-getコマンドでgit, zip, unzip, vimをインストール、docker-php-ext-installコマンド(コンテナイメージに事前に圧縮されて入っている拡張機能を解凍・インストールするコマンド)を使ってPDOをインストールするスクリプトを記述しています。

WORKDIRは、RUNなどの命令を実行するときの作業ディレクトリを指定しています。

Dockerfileをもとにイメージを作成する

docker-compose.ymlのappについて、下記の通り編集します。

version: '3'
services:
  web:
    image: nginx:1.15.6
    ports:
      - '8000:80'
    depends_on:
      - app
    volumes:
      - ./docker/web/default.conf:/etc/nginx/conf.d/default.conf
      - .:/var/www/html
  app:
    build: ./docker/php
    volumes:
      - .:/var/www/html
    depends_on:
      - mysql
  mysql:
    image: mysql:5.7
    environment:
      MYSQL_DATABASE: sample
      MYSQL_USER: user
      MYSQL_PASSWORD: password
      MYSQL_ROOT_PASSWORD: password
    ports:
      - "3306:3306"
    volumes:
      - mysql-data:/var/lib/mysql
volumes:
  mysql-data:

appは、imageでベースのイメージを指定しているだけでしたが、ここで、buildでDockerfileがあるディレクトリを指定し、Dockerfileをもとにイメージを作成するように定義しなおします。

Laravelプロジェクトの作成

この状態で、docker-compose up -d(依存パッケージのインストールがあるため、時間がかかります)、docker-compose exec app bashしてPHPコンテナに入ります。イメージ作成時にComposerがインストールされているので、composerコマンドを使うことができます。

Laravelプロジェクトのディレクトリを作成したい場所で、以下のコマンドを実行し、Laravelのプロジェクトを作成します。my-laravel-appの部分はプロジェクト名の指定なので、なんでもよいです。

composer create-project --prefer-dist laravel/laravel my-laravel-app

こちらもかなり時間かかりますので、待機します。プロジェクトが作成されると、以下のようにmy-laravel-appディレクトリが作成されます。

.
├── docker
│   ├── php
│   │   └── Dockerfile
│   └── web
│       └── default.conf
├── docker-compose.yml
├── index.html
├── index.php
└── my-laravel-app
    ├── …
    …

LaravelとMySQLの接続

my-laravel-app/.envの一部を編集し、LaravelとMySQLとを接続します。docker-compose.ymlのmysqlサービスenvironmentオプションに合わせて設定します。

DB_CONNECTION=mysql
DB_HOST=mysql
DB_PORT=3306
DB_DATABASE=sample
DB_USERNAME=user
DB_PASSWORD=password

DB_HOSTにはmysql(docker-composeで定義したサービス名)を指定することで、名前解決されます。

一度、docker-compose downし、docker-compose up -dしたあと、docker-compose exec app bashでコンテナ内に入り以下のコマンドを実行し、マイグレーションを実行します。

cd my-laravel-app
php artisan migrate

以下のようなログが表示されれば、MySQLの接続は完了です。

Migration table created successfully.
Migrating: 2014_10_12_000000_create_users_table
Migrated:  2014_10_12_000000_create_users_table (0.09 seconds)
Migrating: 2014_10_12_100000_create_password_resets_table
Migrated:  2014_10_12_100000_create_password_resets_table (0.04 seconds)
Migrating: 2019_08_19_000000_create_failed_jobs_table
Migrated:  2019_08_19_000000_create_failed_jobs_table (0.03 seconds)

Nginxの設定をLaravelにあわせて修正

Laravelアプリケーションへのすべてのリクエストは、まず最初にmy-laravel-app/public/index.phpに渡さなければなりません。docker/web/default.confを編集して、Nginxの設定を修正します。

server {
    listen 80;

    root /var/www/html/my-laravel-app/public;
    index index.php index.html index.htm;

    access_log /var/log/nginx/access.log;
    error_log /var/log/nginx/error.log;

    location / {
        try_files $uri $uri/ /index.php$is_args$args;
    }

    location ~ \.php$ {
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_pass 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;
    }
}

rootでのルートディレクトリの指定を/var/www/html/my-laravel-app/publicに変更します。

動作確認

Laravelを動かすためのすべての準備が整いました。一度、docker-compose downし、docker-compose up -dしたあと、http://localhost:8000を確認します。Laravelの初期画面が表示されます。

これで、docker-composeによるLaravelの環境構築は完了です。お疲れさまでした。
ここまでのコードはこちらのリポジトリにまとめてありますので、ご参照ください。(.envなどのファイルは.gitignoreによってトラッキング対象外となっているため、リポジトリにあがっていませんのでご注意ください)

まとめ

開発環境の要件が用意されてあり、Dockerfileをゼロから作りたい、となった場合、Qiitaにある効率的に安全な Dockerfile を作るにはという記事にあるやり方が大変参考になりましたので、ぜひご参照ください。

本記事が、Dockerで開発環境構築したいどなたかの助けになれば幸いです。

アジャイル開発のノウハウをオンラインセミナーでも発信中

メンバーズグループでは、アジャイル開発に関する様々なオンラインセミナーも開催しています!無料で誰でもご参加いただけるので、ぜひどうぞ!

採用情報

メンバーズエッジで最高のチームで最高のプロダクトを作りませんか?

最高のプロダクトをつくる 最高のチームで働く

在宅でも、地方でも、首都圏でも。多様な働き方で最高のチームをつくり、お客様のプロダクトパートナーを目指します。アジャイル開発を通じ、開発現場の第一線で活躍し続けたいエンジニアを募集しています。