はじめに
この記事は、Docker入門シリーズ記事3本の3本目です。
Dockerで環境構築するための最低限の概念理解
Dockerで環境構築するための最低限のコマンドを一通り実践する
【Docker Compose】設定内容を1行ずつ理解しながらLaravel環境構築(PHP-FPM、Nginx、MySQL、Redis)
また、WindowsでDockerを利用する際の重要な設定に関する記事もありますのでこちらもご確認ください。
WindowsでDockerを使う時、正しくファイル配置しないと激重になるので注意
概要
この記事の目的
この記事の目的は
Docker Composeを利用してLaravelの環境構築をすること
ではなく
Docker Composeの設定内容をちゃんと理解しながらLaravelの環境構築をすること
です。
Docker Composeを利用してLaravelの環境構築方法を解説する記事は多くありますが、
docker-compose.ymlやDockerfile、Nginx設定ファイルの完成形が貼ってあって、
コマンドを実行すれば環境構築完了、
というものが多いです。
Laravelが動く環境がとりあえず欲しいだけの場合はこれで良いですが、
Docker Composeの勉強を目的としている場合には
解説が不十分だと思ったので、
自分の復習を兼ねて記事にしました。
また、前回の記事でやった一通りのDocker環境構築フロー、
ポート公開、マウントなどの基本概念は理解している前提で進めます。
構築する環境
今回構築するコンテナは4つです。
- Nginxコンテナ
- PHP-FPMコンテナ
- MySQLコンテナ
- Redisコンテナ
そして、PHP-FPMコンテナには
Laravel公式のサンプルアプリプログラム完成形をそのまま置いて、
実際にアプリが動作するようにします。
https://github.com/laravel/quickstart-intermediate
このアプリは、
ユーザ登録機能、ログイン機能、タスク登録削除機能がある
シンプルなタスク管理アプリです。
DBへのデータ登録やログイン機能があるので、
MySQLコンテナへのデータ保存や
Redisコンテナへのセッション保存を
実際にアプリを通して利用することができます。
環境構築フロー
今回はこのような流れでLaravel環境構築を進めます。
- Nginxコンテナ作成
- PHP-FPMコンテナ作成
- NginxコンテナとPHP-FPMコンテナ間の通信設定
- Laravelサンプルアプリを導入
- MySQLコンテナ作成
- Redisコンテナ作成
ディレクトリ構成
今回は、ホストPCにlaravel-sample
というディレクトリを作成し、
その中にdocker-compose.ymlや各コンテナの設定ファイル、
Laravelサンプルアプリのソースを配置していきます。
最終的にはこのようなディレクトリ構成になります。
laravel-sample/
├─ docker-compose.yml
├─ nginx/
│ └─ default.conf
├─ php-fpm/
│ └─ Dockerfile
├─ mysql/
│ └─ data/
└─ source/
└─ app/
└─ bootstrap/
└─ ...etc
一番上のlaravel-sample
のディレクトリだけ作成し、
次の作業に進んでください。
Nginxコンテナ作成
それでは、さっそくNginxコンテナを作成します。
まずは、laravel-sample
ディレクトリの下に、
docker-compose.yml
ファイルを作成します。
laravel-sample/
└─ docker-compose.yml
docker-compose.yml
にこのように記述します。
version: '3'
services:
nginx:
image: nginx:1.15
ports:
- 80:80
version: '3'
これはDocker Composeのバージョンです。
現在の最新バージョンである3にします。
services:
servicesには、作成するコンテナを定義します。
現在はNginxコンテナのみ記述していますが、
このあとPHP-FPM、MySQL、Redisのコンテナ定義がここに追記されていきます。
nginx:
この下にNginxコンテナのオプションを色々記述していきます。
image: nginx:1.15
Nginxコンテナのもとになるイメージをここで指定しています。
今回はDocker Hubに公式で用意されているNginxイメージを利用します。
バージョンは、現在最新である1.15にします。
ports
これは前回の記事で実践した-p
コマンドと同じです。
ホスト側の80番ポートとコンテナ側の80番ポートをつなげる
という設定になります。
このオプションをつけていないと
ホストPCのブラウザからNginxコンテナにアクセスすることができません。
これでとりあえず
Nginxコンテナを作成するための最低限の設定が完了しているので、
実際にコンテナを作成します。
laravel-sampleディレクトリでターミナルを開き、
このコマンドを実行します。
docker-compose up -d
docker-compose up
はdocker-compose.yml
の設定に従い
コンテナを作成、起動するコマンドです。
-d
は、dockerをバックグラウンド実行するためのオプションです。
docker ps -a
でコンテナを確認します。
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
5288fd7aeea5 nginx:1.15 "nginx -g 'daemon of…" 12 seconds ago Up 11 seconds 0.0.0.0:80->80/tcp laravel-sample_nginx_1
このように表示されれば正しくコンテナ作成できています。
ホストPCのブラウザから
http://localhost
にアクセスします。
このようにNginxのウェルカムページが表示されれば
正しくコンテナのポート公開ができています。
ホスト側のディレクトリをコンテナ側にマウントする
次に、前回の記事で-v
オプションを使ってやったように、
ホストPCに置いた
・プログラムソースファイル
・サーバ設定ファイル
をコンテナ側にマウントします。
このようにnginxフォルダにdefault.confを、
sourceフォルダにindex.htmlを置きます。
laravel-sample/
├─ docker-compose.yml
├─ nginx/
│ └─ default.conf
└─ source/
└─ index.html
index.htmlの内容はなんでもいいので
適当に記述しておきます。
default.confの内容は、コンテナの中にある
/etc/nginx/conf.d/default.conf
をコピーしてください。
default.confの内容
server {
listen 80;
server_name localhost;
#charset koi8-r;
#access_log /var/log/nginx/host.access.log main;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
}
#error_page 404 /404.html;
# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
# proxy the PHP scripts to Apache listening on 127.0.0.1:80
#
#location ~ \.php$ {
# proxy_pass http://127.0.0.1;
#}
# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
#
#location ~ \.php$ {
# root html;
# fastcgi_pass 127.0.0.1:9000;
# fastcgi_index index.php;
# fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name;
# include fastcgi_params;
#}
# deny access to .htaccess files, if Apache's document root
# concurs with nginx's one
#
#location ~ /\.ht {
# deny all;
#}
}
そして、マウントするためのオプションを
docker-compose.yml
に追記します。
version: '3'
services:
nginx:
image: nginx:1.15
ports:
- 80:80
volumes:
- ./source:/usr/share/nginx/html
- ./nginx/default.conf:/etc/nginx/conf.d/default.conf
これでコンテナの削除、再作成を行ってください。
#コンテナ削除
docker-compose down
#コンテナ作成
docker-compose up -d
これで http://localhost にアクセスします。
先ほどホスト側で作成したindex.htmlの内容が表示されれば
正しくマウントできています。
さらにホスト側でindex.htmlを更新して
それがちゃんと反映されるか確認してください。
これでNginxコンテナの作成は完了です。
PHP-FPMコンテナ作成
次に、PHP-FPMのコンテナを作成します。
Nginxコンテナは既存イメージをそのまま利用しましたが、
PHP-FPMコンテナはDockerfileからイメージをbuildし、コンテナを作成します。
Dockerfile作成
ホストPCにphp-fpmというフォルダを作り、
そこにDockerfileを置きます。
laravel-sample/
├─ docker-compose.yml
├─ nginx/
│ └─ default.conf
├─ php-fpm/
│ └─ Dockerfile
└─ source/
└─ index.html
Dockerfileの内容はこのようにします。
FROM php:7.2-fpm
FROMにphp:7.2-fpmのイメージを指定しているだけです。
後々Laravelを動かすための設定を色々と追記しますが、
今はこれだけでいいです。
次にdocker-compose.ymlにPHP-FPMコンテナの情報を追記します。
version: '3'
services:
nginx:
image: nginx:1.15
ports:
- 80:80
volumes:
- ./source:/usr/share/nginx/html
- ./nginx/default.conf:/etc/nginx/conf.d/default.conf
php-fpm:
build: ./php-fpm
volumes:
- ./source:/var/www/html
php-fpm
サービス一覧にphp-fpmを追加します。
build: ./php-fpm
このコンテナの元になるDockerfileがおいてあるパスを指定しています。
volumes:
PHP-FPMコンテナでもホストPC側のプログラムソースディレクトリをマウントしておきます。
それではコンテナを作成します。
Dockerfileから作成する場合には、--build
オプションを追加してコマンド実行します。
docker-compose up -d --build
docker ps -a
で確認してみます。
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
5288fd7aeea5 nginx:1.15 "nginx -g 'daemon of…" 12 seconds ago Up 11 seconds 0.0.0.0:80->80/tcp laravel-sample_nginx_1
e823a579a920 laravel-sample_php-fpm "docker-php-entrypoi…" 12 seconds ago Up 11 seconds 9000/tcp laravel-sample_php-fpm_1
正しくコンテナ起動できています。
コンテナにログインして
/var/www/html
にホスト側のindex.htmlが
マウントできていることを確認してください。
#コンテナにログイン
docker exec -it laravel-sample_php-fpm_1 bash
また、このコンテナ内で正しくPHPが動作するかどうか確認してみます。
まず、ホスト側にあるindex.htmlを削除し、
index.phpを置きます。
laravel-sample/
├─ docker-compose.yml
├─ nginx/
│ └─ default.conf
├─ php-fpm/
│ └─ Dockerfile
└─ source/
└─ index.php ←これを追加
ファイル内容は、このように簡単なPHPプログラムを書きます。
<?php
echo(123456789);
echo(PHP_EOL);
これでまたコンテナにログインし、
php index.php
コマンドを実行してPHPが動作するか確認します。
#コンテナにログインしている状態
php index.php
123456789
「1234567879」が出力されたので、正しくPHPが動作していることが確認できました。
NginxコンテナとPHP-FPMコンテナ間の通信設定
次に、
ブラウザからhttp://localhost にアクセスしたら
先ほどのindex.phpが実行され
「123456789」が画面に表示されるように設定をします。
いまhttp://localhost にアクセスするとNginxの403ページが表示されます。
原因はこの2つです。
- 「/」で終わるURLでアクセスされた時の表示ページとしてindex.phpが設定されていない
- 「*.php」のファイルにアクセスされた時にPHP-FPMと通信してPHPを実行させるための設定がされていない
Nginx設定ファイルdefault.confを修正することでこれを解決します。
まずはこれの修正。
「/」で終わるURLでアクセスされた時の表示ページとしてindex.phpが設定されていない
default.confのこの部分にindex.phpを追加します。
location / {
root /usr/share/nginx/html;
index index.html index.htm index.php;
#↑これを追加
}
次にこれの修正
「*.php」のファイルにアクセスされた時にPHP-FPMと通信してPHPを実行させるための設定がされていない
default.confの「pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000」
と書いてある部分をこのように修正します。
#変更前
#location ~ \.php$ {
# root html;
# fastcgi_pass 127.0.0.1:9000;
# fastcgi_index index.php;
# fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name;
# include fastcgi_params;
#}
#↓↓↓↓↓↓↓↓変更↓↓↓↓↓↓↓↓
location ~ \.php$ {
root /var/www/html;
fastcgi_pass php-fpm:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
これでNginxコンテナに対してphpファイルにアクセスが来た際に、
PHP-FPMコンテナと通信してPHPを実行する設定ができました。
設定を反映するためにコンテナを再起動します。
docker-compose stop
docker-compose start
http://localhost にアクセスして
画面に「123456789」が表示されれば
Nginx経由でPHPが正しく実行できている状態です。
Laravelを導入
PHPが動くようになりましたが、
まだLaravelを動かすために必要な設定が色々あります。
Laravelサンプルアプリの画面を実際に表示できるところまで
設定します。
Laravelサンプルアプリソースをcloneする
まずはLaravelサンプルアプリのソースをcloneしてきます。
先ほどのindex.phpは削除して、
/sourceのディレクトリでこのコマンドを実行します。
git clone https://github.com/laravel/quickstart-intermediate.git .
これで現在このようなディレクトリ構成になっています。
laravel-sample/
├─ docker-compose.yml
├─ nginx/
│ └─ default.conf
├─ php-fpm/
│ └─ Dockerfile
└─ source/
└─ app/
└─ bootstrap/
└─ ...etc
※...etcの部分は省略していますがLaravelのソースが色々置いてあります。
PHP-FPMコンテナに、Laravelに必要なライブラリ、ツールをインストール
Laravelが動作するためにサーバにインストールする必要のあるものがいくつかあるので、
PHP-FPMのDockerfileをこのように修正します。
FROM php:7.2-fpm
#pdoインストール
RUN docker-php-ext-install pdo_mysql
#composerインストール
RUN curl -sS https://getcomposer.org/installer | php
RUN mv composer.phar /usr/local/bin/composer
#gitインストール
RUN apt-get update
RUN apt-get install -y git
これでもう一度buildし直してコンテナを作ります。
docker-compose up -d --build
composer installを実行する
次に、PHP-FPMコンテナでcomposer installコマンドを実行します。
#PHP-FPMコンテナにログイン
docker exec -it laravel-sample_php-fpm_1 bash
#コンテナにログインしている状態
#composer install実行
composer install
この結果、souce/
フォルダの下に
vendor/
というフォルダが追加されているはずです。
NginxコンテナとPHP-FPMコンテナのドキュメントルートを修正
Laravelの仕様で、
ドキュメントルートはプロジェクトのルートディレクトリではなく、
1つ下の階層にあるpublicというディレクトリにする必要があります。
Nginxのdefault.confファイルの
下記2か所(「publicを追加」と書いてある箇所)を修正します。
location / {
root /usr/share/nginx/html/public;
#↑publicを追加
index index.html index.htm index.php;
}
...
location ~ \.php$ {
root /var/www/html/public;
#↑publicを追加
fastcgi_pass php-fpm:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
この設定を反映させるためにコンテナを再起動します。
docker-compose stop
docker-compose start
これでhttp://localhost にアクセスしてみます。
このLaravelサンプルアプリの画面が表示されれば
Laravel動作のための設定が正しくできています。
ルートディレクトリ以外が404になる現象を解消
今の状態でLaravelサンプルアプリの画面右上の「Register」ボタンなどをクリックし
画面遷移すると
404ページが表示されます。
原因は、Nginxのルーティングのために必要な設定が抜けているからです。
dafault.confにこの設定を追加します。
location / {
root /usr/share/nginx/html/public;
index index.html index.htm index.php;
try_files $uri $uri/ /index.php$is_args$args;
###↑この行を追加
}
これでもう一度コンテナ再起動。
docker-compose stop
docker-compose start
これで画面遷移しても404ページにならないようにできました。
この状態で右上の「Register」ボタンからユーザ登録操作をしてみると、
このようにDB接続エラーになると思います。
当然ですが、今は接続先のDBが存在していないためこのようなエラーが発生します。
次にMySQLコンテナを作成してこれを解決します。
MySQLコンテナ作成
docker-compose.ymlにMySQLの設定を追記します。
version: '3'
services:
nginx:
image: nginx:1.15
ports:
- 80:80
volumes:
- ./source:/usr/share/nginx/html
- ./nginx/default.conf:/etc/nginx/conf.d/default.conf
php-fpm:
build: ./php-fpm
volumes:
- ./source:/var/www/html
mysql:
image: mysql:5.7
environment:
MYSQL_DATABASE: laravel_sample_db
MYSQL_ROOT_PASSWORD: password
MYSQL_USER: laravel_sample_user
MYSQL_PASSWORD: password
TZ: Asia/Tokyo
ports:
- 3306:3306
image: mysql:5.7
MySQLコンテナは公式のこのイメージをそのまま使います。
environment:
これはMySQLコンテナ作成時に初期作成される
データベースの名前やユーザ、パスワードの設定を書いています。
ports:
ホスト側3306番ポートとコンテナ側3306番ポートをつなげる設定です。
コンテナのポートをホスト側に公開しなくても
Laravelサンプルアプリは動作しますが、
ホストPCからMySQL WorkbenchやA5:SQL Mk-2などのツールを使って
接続したい場合にはコンテナのポートをこのように公開する必要があります。
これでMySQLコンテナを作成します。
docker-compose up -d
MySQLコンテナに入って確認してみます。
#コンテナにログイン
docker exec -it laravel-sample_mysql_1 bash
#コンテナにログインしている状態
#コンテナのMySQLにログイン
mysql -u laravel_sample_user -p
password
#MySQLにログインしている状態
#データベース一覧表示
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| laravel_sample_db |
+--------------------+
2 rows in set (0.01 sec)
docker-compose.ymlに書いた通りの
ユーザ、パスワードでログインでき、
「laravel_sample_db」が存在することが確認できました。
Laravelのenvファイル修正
LaravelサンプルアプリからこのMySQLコンテナに接続するためには
DB情報をLaravelの.envに記述する必要があります。
sourceディレクトリ配下にある.envファイルの
DB情報をこのように変更します。
DB_HOST=mysql
DB_DATABASE=laravel_sample_db
DB_USERNAME=laravel_sample_user
DB_PASSWORD=password
これでLaravelからDBにアクセスできるはずなので、
migration実行してみます。
#PHP-FPMコンテナにログイン
docker exec -it laravel-sample_php-fpm_1 bash
#コンテナにログインしている状態
#migration実行
php artisan migrate
これでアプリに必要なテーブルも作成できたので、
実際にアプリを動かしてみます。
ユーザ登録やタスク登録など正しく動作しています。
コンテナのポートを公開しているので、
好きなDBクライアントツールを使って
localhostの3306番ポートに接続すれば
GUIでのデータ確認、更新もできます。
MySQLのデータ永続化
今の状態でこのMySQLコンテナを削除し、再作成すると
先ほど登録したデータは消えてしまいます。
(テーブル自体が消えます)
これを解消するために、
プログラムソースファイルと同じように
ホスト側のディレクトリをMySQLコンテナのデータ置き場にマウントし、
コンテナを削除してもデータが消えないようにします。
docker-compose.ymlにこのvolumesオプションを追記するだけです。
version: '3'
services:
nginx:
image: nginx:1.15
ports:
- 80:80
volumes:
- ./source:/usr/share/nginx/html
- ./nginx/default.conf:/etc/nginx/conf.d/default.conf
php-fpm:
build: ./php-fpm
volumes:
- ./source:/var/www/html
mysql:
image: mysql:5.7
environment:
MYSQL_DATABASE: laravel_sample_db
MYSQL_ROOT_PASSWORD: password
MYSQL_USER: laravel_sample_user
MYSQL_PASSWORD: password
TZ: Asia/Tokyo
ports:
- 3306:3306
volumes:
- ./mysql/data:/var/lib/mysql
これでコンテナを削除し再作成すれば、
MySQLのデータが永続化できる状態になります。
docker-compose down
docker-compose up -d
ホスト側には自動で/mysql/dataのディレクトリが作られます。
laravel-sample/
├─ docker-compose.yml
├─ nginx/
│ └─ default.conf
├─ php-fpm/
│ └─ Dockerfile
├─ mysql/
│ └─ data/
└─ source/
└─ app/
└─ bootstrap/
└─ ...etc
もう一度migrationを実行し、
データをいろいろ登録してから
コンテナ削除、再作成をしてデータが消えないことを確認してみてください。
Redisコンテナ作成
これまでの作業で、Laravelサンプルアプリが
動作するために必要な作業はすべて完了していますが、
最後におまけでRedisコンテナを作成し、
キャッシュ保存先をRedisにしてみます。
docker-compose.ymlにRedisの設定を追記します。
version: '3'
services:
nginx:
image: nginx:1.15
ports:
- 80:80
volumes:
- ./source:/usr/share/nginx/html
- ./nginx/default.conf:/etc/nginx/conf.d/default.conf
php-fpm:
build: ./php-fpm
volumes:
- ./source:/var/www/html
mysql:
image: mysql:5.7
environment:
MYSQL_DATABASE: laravel_sample_db
MYSQL_ROOT_PASSWORD: password
MYSQL_USER: laravel_sample_user
MYSQL_PASSWORD: password
TZ: Asia/Tokyo
ports:
- 3306:3306
volumes:
- ./mysql/data:/var/lib/mysql
redis:
image: redis:latest
ports:
- 6379:6379
image: redis:latest
公式のredisイメージ最新をそのまま利用します。
ports:
これもMySQLコンテナを作成したときと同じで、
アプリを動作させる分にはRedisコンテナのポートを公開する必要はありません。
Medisなど、Redisに接続してGUIでデータを確認、操作するツールを利用したい場合は
このようにポートを公開してください。
これでコンテナ作成します。
docker-compose up -d
LaravelのRedis利用設定
Redisコンテナ自体は作成できましたが、
Laravel側でRedisを利用するために必要な設定がいくつかあるのでしていきます。
まず、composerを使ってpredisというライブラリをインストールします。
#PHP-FPMコンテナにログイン
docker exec -it laravel-sample_php-fpm_1 bash
#コンテナにログインしている状態
#predisインストール
composer require predis/predis
次にLaravelの.envの設定を変更します。
このように下記箇所を変更してください。
1
CACHE_DRIVER=file
↓
CACHE_DRIVER=redis
2
SESSION_DRIVER=file
↓
SESSION_DRIVER=redis
3
REDIS_HOST=127.0.0.1
↓
REDIS_HOST=redis
これでRedisを使うための設定はすべて完了です。
Laravelサンプルアプリを操作し、
ログインやログアウト操作を正常に行えれば問題ないです。
本当にこれでセッション保存先としてRedisがちゃんと使われているのかわからないので、
先ほど言ったようにMedisなどのツールを使ってlocalhostの6379番ポートに接続してみるといいです。
実際にデータが入っていることが確認できます。
最後に
これで、
- Nginxコンテナ
- PHP-FPMコンテナ
- MySQLコンテナ
- Redisコンテナ
のコンテナをDocker Composeで作成し、
実際にLaravelアプリが動作する環境が構築できました。
実際にチームで開発を進める際には、
・docker-compose.yml
・各コンテナのDockerfile
・各コンテナのサーバ設定ファイル
・プログラムソースファイル
を共有しながら進めるといいです。
Dockerの概念理解
コンテナ単体での基本操作実践
コンテナ複数を管理するDocker Compose実践
と進めてきましたが、
機会があれば
複数サーバ上で複数コンテナを管理するためのツールである
KubernetesやDocker Swarmも実践記事を書ければと思います。
(最後まで見ていただいた方、最初の記事からLGTMしてもらえるとうれしいです)