PHP
Laravel
Docker

Dockerでいい感じにPHP(Laravel)のローカル開発環境を作る


概要

Dockerで一からLaravelの環境を作ってみたかったんです。

流れを理解できるように記事として残します。

構成はNginx + php-fpm + (MySQL, PostgreSQL, Redis)

この記事では扱いませんが、XdebugとPhpStormの連携と言ったことも後々言及する予定です。

設定はGithubにあがっています。


この記事で学べること


  • DockerとDocker Composeを使用して一からDockerのPHP(Laravel)環境を作る方法


この記事で触れないこと


  • Nginxやphp-fpmの細かい設定ファイルについて

  • 各種パフォーマンスチューニング

さっそく始めていきます。


Nginx

まずはNginxのコンテナを準備。

Nginxの公式イメージをそのまま使います。

下記のようなdocker-compose.ymlを作成します。


docker-compose.yml

version: '2'

services:
nginx:
image: nginx
container_name: "laravel-nginx"
ports:
- "8080:80"


nginxのコンテナを起動します。

$ docker-compose build # image作成

$ docker-compose up -d # コンテナ起動

コンテナが立ち上がっているか確認します。

$ docker-compose ps

Name Command State Ports
----------------------------------------------------------------------------
laravel-nginx nginx -g daemon off; Up 0.0.0.0:8080->80/tcp

http://localhost:8080でアクセスしてNginxの初期画面が表示されれば完了です。

Screenshot from 2017-10-31 16-35-58.png


PHP

phpディレクトリを作成し、その中にDockerfileを作成します。


php/Dockerfile

FROM php:fpm


ここでdocker-compose.ymlから直接イメージを指定してもいいですが、後々DockerfileをカスタマイズするのでDockerfileを作成しましょう。

servicesの中にphp用の記述を追加していきます。


docker-compose.yml

services:

# ...
# ...
php:
build: ./php
container_name: "laravel-php"

さっき行ったようにビルド、立ち上げ、プロセスを確認するとPHPのコンテナが立ち上がっているのがわかります。

$ docker-compose build

$ docker-compose up -d
$ docker-compose ps
Name Command State Ports
----------------------------------------------------------------------------
laravel-nginx nginx -g daemon off; Up 0.0.0.0:8080->80/tcp
laravel-php docker-php-entrypoint php-fpm Up 9000/tcp

外部からコンテナのコマンドを実行してみましょう。

$ docker-compose exec php php -v

PHP 7.1.10 (cli) (built: Oct 10 2017 01:30:46) ( NTS )
Copyright (c) 1997-2017 The PHP Group
Zend Engine v3.1.0, Copyright (c) 1998-2017 Zend Technologies

phpというサービスに対してコマンドを流し込んでいます。

ここではphp -vです。

これでPHP自体は立ち上がったことが確認できます。


ファイル共有

NginxとPHPのコンテナにローカルのファイルを共有していきます。


まずはNginxから

srcディレクトリを作成し、配下にindex.htmlを置きましょう。


src/index.html

<p>Nginx HTML</p>


2つのディレクトリとファイルをマッピングします。


  • srcディレクトリをNginxのコンテナの/srcディレクトリに

  • Nginxの設定ファイルを上書き

まずはdocker-compose.ymlから書き換えます。


docker-compose.yml

  nginx:

image: nginx
container_name: "laravel-nginx"
ports:
- "8080:80"
volumes:
- ./src:/src
- ./nginx/default.conf:/etc/nginx/conf.d/default.conf

volumesを追加します。

次にNginxの設定ファイルです。


default.conf

server {

index index.php index.html;
root /src;
}

default.confは最低限だけ記述しておきます。

コンテナの再起動を行いましょう。

$ docker-compose down

$ docker-compose up -d

ここではdocker-compose.ymlの書き換えなのでbuildは必要ありません。

これでindex.htmlの内容が表示されるはずです。

Screenshot from 2017-10-31 18-46-22.png


次にPHP

まずsrcディレクトリにPHPの情報を吐き出すinfo.phpを作成しておきましょう。


info.php

<?php

phpinfo();

Nginxと同様にローカルのsrcディレクトリとコンテナ内の/srcをマッピングします。


docker-compose.yml

  php:

build: ./php
container_name: "laravel-php"
volumes:
- ./src:/src

Nginxの設定にphp-fpmの設定を追加します。

server {

index index.php index.html;
root /src;

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

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

fastcgi_passにあるphpはサービス名です。

composeはVersion 2からはコンテナ内ではデフォルトでサービス名=ホスト名として解決することができるようになりました。

docker-composeのVersion1ではlinksの設定が必要だったのですが、Version2からは不要です。

https://docs.docker.com/compose/compose-file/compose-versioning/#version-2

これでhttp://localhost:8080/info.phpにアクセスしていつもの画面が出てくれば成功です。

Screenshot from 2017-10-31 19-54-24.png

ここで一旦PHPのコンテナはおいておいて、他のデータベースなどのコンテナの作成をしていきます。


各種データベース

ざっとMySQLとPostgreSQL、Redisをコンテナとして起動するようにしておきます。


docker-compose.yml

  mysql:

image: mysql
container_name: "laravel-mysql"
restart: always
environment:
MYSQL_DATABASE: root
MYSQL_ROOT_PASSWORD: root
ports:
- 13306:3306

postgres:
restart: always
image: postgres:alpine
container_name: "laravel-postgres"
environment:
POSTGRES_USER: root
POSTGRES_PASSWORD: root
ports:
- 15432:5432

redis:
image: redis:alpine
container_name: "laravel-redis"
ports:
- 16379:6379


各コマンドで動作しているか確認しておきます。

$ mysql -h 0.0.0.0 --port 13306 -u root -p


$ psql -h 0.0.0.0 -U root --port 15432

$ redis-cli -h 0.0.0.0 -p 16379


composer

公式サイトのダウンロードの手順に従ってphpのイメージに組み込んであげましょう。

php/Dockerfileを書き換えます。


php/Dockerfile

FROM php:fpm

RUN php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" \
&& php -r "if (hash_file('SHA384', 'composer-setup.php') === '544e09ee996cdf60ece3804abc52599c22b1f40f4323403c44d44fdfdd586475ca9813a858088ffbc1f233e9b180f061') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;" \
&& php composer-setup.php \
&& php -r "unlink('composer-setup.php');" \
&& mv composer.phar /usr/local/bin/composer


$ docker-compose exec php composer --version

Do not run Composer as root/super user! See https://getcomposer.org/root for details
Composer version 1.5.2 2017-09-11 16:59:25

一旦composerのインストールが成功し、コンテナからcomposerコマンドを実行すると上記のような表示になるかと思います。

Dockerコンテナではrootでコマンドが実行されるので下記のようにDockerfileにCOMPOSER_ALLOW_SUPERUSERを追加することによって消すことができますので追記しておきましょう。

https://getcomposer.org/doc/03-cli.md#composer-allow-superuser


php/Dockerfile

ENV COMPOSER_ALLOW_SUPERUSER 1


$ docker-compose exec php composer --version

Composer version 1.5.2 2017-09-11 16:59:25

また、composerの各パッケージのダウンロードにはZIP拡張が使用されるので、Dockerfileのcomposerのインストールの前にZIP拡張のインストール処理も追記しておきましょう。


php/Dockerfile

FROM php:fpm

RUN apt-get update \
&& apt-get install -y zlib1g-dev \
&& docker-php-ext-install zip

# 以下composerのインストール


zip extensionを入れておかないと下記のようなエラーが発生するので注意です。

root@0c36413d6c4a:/var/www/html# composer global require 'phpunit/phpunit'

Changed current directory to /root/.composer
Using version ^6.4 for phpunit/phpunit
./composer.json has been created
Loading composer repositories with package information
Updating dependencies (including require-dev)
Package operations: 28 installs, 0 updates, 0 removals
Failed to download sebastian/version from dist: The zip extension and unzip command are both missing, skipping.
A php.ini file does not exist. You will have to create one.
Now trying to download from source
- Installing sebastian/version (2.0.1): Cloning 99732be0dd

Installation failed, deleting ./composer.json.

[RuntimeException]
Failed to clone https://github.com/sebastianbergmann/version.git, git was not found, check that it is installed and in your PATH env.
sh: 1: git: not found

composerのグローバルパッケージがインストールされるディレクトリも変更しておきます。


php/Dockerfile

ENV COMPOSER_HOME /composer


更にグローバルにインストールするパッケージに関してはPATHも通しておきます。


php/Dockerfile

ENV PATH $PATH:/composer/vendor/bin


composerのインストールはこれで完了です。


Laravel

ここでLaravelのインストールです。

インストーラーをインストールしておきます。

最後のWORKDIRの前に下記の記述を追加してあげましょう。


php/Dockerfile

RUN composer global require "laravel/installer" 


再度イメージのビルドを行います。

これで下記コマンドでLaravelをインストールすることができるようになります。

$ docker-compose exec php laravel new sample

先にイメージ作成時にプロジェクトを作成しても良いのですが、今回はそれぞれコンテナ上に自由に作成できるようにしてみました。

上記のコマンドでは/src/sampleというディレクトリにLaravelがインストールされます。

ですので、Nginxの設定ファイルのルートディレクトリもLaravelの公開ディレクトリのパスに書き換えてあげましょう。


default.conf

root /src/sample/public;


Nginxの再起動(設定の再読み込み)はサービスをrestartすることでできます。

$ docker-compose exec nginx restart

これでlocalhost:8080にアクセスするとLaravelのデフォルト画面が表示されます。

Screenshot from 2017-11-06 02-22-57.png


PHPの拡張

先にLaravelをインストールしましたが、実はこの状態だとまだPDOが使えません。

PDO拡張が使用しているPHPのイメージには入っていないためです。

$ docker-compose exec php php ./sample/artisan migrate

[Illuminate\Database\QueryException]
could not find driver (SQL: select * from information_schema.tables where table_s
chema = homestead and table_name = migrations)

[PDOException]
could not find driver

ですので、php/Dockerfileの最後のWORKDIRの前に以下の記述を追加してあげましょう。


php/Dockerfile

RUN apt-get update \

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

PostgreSQLの拡張ではlibpq-devをインストールしないとこのようなエラーが出るので注意です。

configure: error: Cannot find libpq-fe.h. Please specify correct PostgreSQL installation path

インストールが完了して先ほどのコマンドを実行すると、PDOExeptionの内容がドライバエラーから接続エラーに変わります。

$ docker-compose exec php php ./sample/artisan migrate          

[Illuminate\Database\QueryException]
SQLSTATE[HY000] [2002] Connection refused (SQL: select * from information_schema.
tables where table_schema = homestead and table_name = migrations)

[PDOException]
SQLSTATE[HY000] [2002] Connection refused

.envにDB情報を記述してあげます。

今回はMySQLを使用してみます。

docker-compose.ymlで指定したサービス名、データベース、パスワードと入力していきます。

HOSTはdocker-composeで立てたネットワーク内ではサービス名=ホスト名として各コンテナのIPを保管してくれるのでそのままサービス名を記述します。


.env

DB_CONNECTION=mysql

DB_HOST=mysql
DB_PORT=3306
DB_DATABASE=root
DB_USERNAME=root
DB_PASSWORD=root

これで再度コマンドを叩いてみましょう。

$ docker-compose exec php php ./sample/artisan migrate

Migration table created successfully.
Migrating: 2014_10_12_000000_create_users_table
Migrated: 2014_10_12_000000_create_users_table
Migrating: 2014_10_12_100000_create_password_resets_table
Migrated: 2014_10_12_100000_create_password_resets_table

正常にマイグレートが完了しました。

これで最低限のLaravelの環境が構築できました。

ちなみにここでは動作確認しませんが、Redisはこんな感じです。


php/Dockerfile

RUN pecl install -o -f redis \

&& rm -rf /tmp/pear \
&& docker-php-ext-enable redis


Node.js

Laravel MixやWebpackなどで使うことになるのでNode.jsをインストールします。

また最後のWORKDIRの前に以下を記述しましょう。


php/Dockerfile

RUN curl -sL https://deb.nodesource.com/setup_6.x | bash - \ 

&& apt-get update \
&& apt-get install -y nodejs

$ docker-compose exec php node -v              

v6.11.5


完成

一通りイメージ作成、コンテナの使用ができるようになりました。

実際使ってみると便利でいいです、Docker。

何か間違っている点や、こうしたほうが良いというところがあればコメントお願いします。