概要
docker-composeを使ってLaravelの実行環境を構築します。
作成するコンテナはPHP-FPM、Nginx、MySQLの3つです。
環境
Docker:19.03 ホストOS:Ubuntu 18.04(Vagrant)
Laravel:6.1.0 PHP-FPM:7.3 PHP:7.3.10 Nginx:1.17.4 MySQL:8.0
*Dockerおよびdocker-composeはホストOS(Vagrant上のUbuntu)上にインストールされているものとします。
ディレクトリ構成
docker
|- docker-compose.yml
|- laravel
|- mysql
| |- Dockerfile
| |- my.cnf
| ∟ sql
| ∟ init.sql
|- nginx
| |- Dockerfile
| ∟ default.conf
∟ php
|- Dockerfile
∟ local.ini
構築手順
1.Laravelとパッケージのインストール
初めに、ホストOS上にLaravelとパッケージをインストールします。コンテナにマウントする形でLaravelのファイルのコピーを行います。
・Laravelのインストール
最上位のdockerディレクトリ内で以下のコマンドを実行します。gitがない場合、先にインストールしてください。
git clone https://github.com/laravel/laravel.git laravel
dockerディレクトリにlaravelというディレクトリが作成され、Git HubからLaravelのプロジェクトがクローンされます。
・パッケージのインストール
作成されたlaravelディレクトリに移動して、コンテナからcomposer installを実行します。
cd laravel
docker run --rm --interactive --tty --volume $PWD:/app composer install
このコマンドは、Composerイメージからコンテナを一時的に作成し、Laravelに必要なパッケージをインストールします。
Laravelディレクトリをコンテナ内にマウントすることで、Composerコンテナがcomposer.jsonファイルを読み込んで利用できるようになっています。コンテナは実行後に消滅しますが、ディレクトリをマウントしているためホストOS上にインストールしたパッケージが残ります。公式のComposerイメージで紹介されている使用法です。
*ComposerイメージがホストOS上にない場合、イメージがdocker hubよりpullされます。
*注意:Composer実行中に以下のエラーが発生する場合があります。
proc_open(): fork failed errors
これはホストのメモリ不足によるものです。VMのメモリが1024MBだと発生するのでメモリを増やしましょう。
2.docker-compose.yml作成
続いて、以下のようなdocker-compose.ymlを作成します。
version: '3'
services:
php:
build: ./php
image: app_php:7.3-fpm
container_name: php
restart: unless-stopped
working_dir: /var/www
volumes:
- ./php/local.ini:/usr/local/etc/php/conf.d/local.ini
- ./laravel:/var/www
networks:
- app-network
nginx:
build: ./nginx
image: app_nginx:1.17.4
container_name: nginx
restart: unless-stopped
ports:
- 80:80
- 443:443
volumes:
- ./laravel:/var/www
networks:
- app-network
depends_on:
- php
mysql:
build: ./mysql
image: app_mysql:8.0
container_name: mysql
restart: always
ports:
- 3306:3306
environment:
MYSQL_DATABASE: laravel
MYSQL_ROOT_PASSWORD: pass
MYSQL_USER: user
MYSQL_PASSWORD: pass
TZ: 'Asia/Tokyo'
command: --default-authentication-plugin=mysql_native_password
volumes:
- ./mysql/sql:/docker-entrypoint-initdb.d
- mysql-volume:/var/lib/mysql/
networks:
- app-network
networks:
app-network:
driver: bridge
volumes:
mysql-volume:
driver: local
・PHPコンテナ
・image
通常、imageはコンテナで使用するイメージを指定しますが、「build」を使ってDockerfileからコンテナを起動する場合、docker-composeで作成するイメージにイメージ名とタグ名を付けることができます。ここでは「app_php」というイメージ名、「7.3-fpm」というタグ名を付与しています。
・volumes
./php/local.ini:/usr/local/etc/php/conf.d/local.ini
ホスト上のphpディレクトリ内のlocal.iniを、コンテナ内の「/usr/local/etc/php/conf.d/local.ini」にコピーします。こうすることで、コンテナ内のデフォルトのphp.iniを上書きすることができます。php.iniファイル自体は上書きされませんが、phpinfo()で情報を出力すると上書きされていることを確認できます。local.iniは後に作成します。
./laravel:/var/www
ホスト上でインストールしたlaravelディレクトリを、コンテナの「/var/www」とマウントします。こうすることで、ドキュメントルートを「/var/www/public」とすることができます。
開発環境であれば問題ありませんが、データをホストOSにマウントするのは推奨されていないため、本番で使う時はDockerボリュームを作成してマウントすることを検討しましょう。
*注意
セキュリティを向上させるため、ディレクトリをリードオンリーでマウントすることが推奨されていますが、Laravelではファイルシステムをリードオンリーにするとエラーが発生します。
./laravel:/var/www:ro(リードオンリー) → エラーが発生します。
・Nginxコンテナ
・volumes
./laravel:/var/www
Nginxコンテナでもlaravelディレクトリをマウントします。こうすることで、Nginxが受け付けたリクエストをLaravelで返すことができます。
・MySQLコンテナ
・command
--default-authentication-plugin=mysql_native_password
上記のコマンドでMySQLの認証方式を変更しています。MySQLはバージョン8.0から認証方式を変更しており、「caching_sha2_password」がデフォルトとなっています。PHPのMySQL接続ライブラリが未対応のため、認証方式を「mysql_native_password」に戻します。
・volumes
./mysql/sql:/docker-entrypoint-initdb.d
コンテナ内の「docker-entrypoint-initdb.d」以下にSQLを配置することで、初期設定などを行うことができます。
mysql-volume:/var/lib/mysql/
MySQLのデータをDockerのボリュームにマウントして永続化します。Dockerのボリュームはコンテナを削除しても残ります。そのため、コンテナに変更を加える場合、volumeを削除しなければenvironment等の設定を変更しても反映されないので気を付けましょう。
・environment
任意のユーザーとパスワードを設定してください。
*注意
Dockerにおける永続化データの保存には、データ専用コンテナを作成する方法がありますが、docker-composeのバージョン3からvolumes_fromオプションが削除されているため使用することができません。
3.Dockerfileの作成
次に、各コンテナを生成するDockerfileを作成します。
###・PHPコンテナのDockerfile
phpディレクトリ内に以下のようなDockerfileを作成します。
FROM php:7.3-fpm
# Set working directory
WORKDIR /var/www
# Install dependencies
RUN apt-get update && apt-get install -y \
build-essential \
default-mysql-client \
libpng-dev \
libjpeg62-turbo-dev \
libfreetype6-dev \
libmcrypt-dev \
libmemcached-dev \
libzip-dev \
locales \
zip \
jpegoptim optipng pngquant gifsicle \
vim \
unzip \
git \
curl
# Clear cache
RUN apt-get clean && rm -rf /var/lib/apt/lists/*
# Install extensions
RUN docker-php-ext-install pdo_mysql exif bcmath && \
docker-php-ext-configure zip --with-libzip && \
docker-php-ext-install zip && \
docker-php-ext-configure gd \
--with-gd --with-freetype-dir=/usr/include/ --with-jpeg-dir=/usr/include/ --with-png-dir=/usr/include/ && \
docker-php-ext-install gd
# Install composer
RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
# Add user for laravel application
RUN groupadd -g 1000 php && \
useradd -u 1000 -ms /bin/bash -g php laravel
# Change current user to laravel
USER laravel
# Expose port 9000 and start php-fpm server
EXPOSE 9000
CMD ["php-fpm"]
・ベースイメージ
公式イメージである「php:7.3-fpm」をベースイメージとしています。php:7.3-fpmのベースイメージは「debian:buster-slim」となっています。LaravelのプログラミングはPHPコンテナに入って行いますが、debianの上で行っていることを頭に入れておきましょう。
・ライブラリ、エクステンションのインストール
PHPおよびLaravelの実行に必要なライブラリ、エクステンションをインストールしています。
default-mysql-client
debian:buster-slimはmariadbをデフォルトとしており、mariadb-clientが使用されます。そのため、mysql-clientはインストールできません。その代わり、mysqlを利用するために「default-mysql-client」というものが用意されているので、そちらをインストールしています。
bcmath
Laravelを実行するためのサーバー要件としていくつかのエクステンションが必要ですが、多くはPHP本体に含まれています。ただし、bcmathが含まれていないため、別途「docker-php-ext-install」でインストールしています。
・ユーザーの作成
rootユーザーでDockerを操作するのはセキュリティ上好ましくないため、laravelというユーザーを作成しています。
・NginxコンテナのDockerfile
nginxディレクトリ内に以下のDokcerfileを作成します。
FROM nginx:1.17.4
COPY default.conf /etc/nginx/conf.d/default.conf
イメージはdocker-compose.ymlに指定することもできますが、docker-composeで作成するイメージにイメージ名を付与することができるため、docker-composeのbuildからイメージを作成するようにしています。また、後に作成するdefault.confをコンテナ内にコピーしています。
・MySQLコンテナのDockerfile
mysqlディレクトリ内に以下のDokcerfileを作成します。
FROM mysql:8.0
COPY my.cnf /etc/mysql/conf.d/my.cnf
RUN chmod 644 /etc/mysql/conf.d/my.cnf
CMD ["mysqld"]
EXPOSE 3306
コンテナ内の「/etc/mysql/conf.d/my.cnf」に設定ファイルをコピーすることで、デフォルトのmy.cnfを上書きすることができます。
Windowsを使用している場合、VirtualBox経由でマウントしているディレクトリ/ファイルはすべてパーミッションが777 になります。MySQLは777の.cnfファイルを読み込みません。そのため、ファイルのパーミッションを変更しています。
4.設定ファイルの作成
次に、各コンテナで使用する設定ファイルを作成します。
###・PHPの設定ファイル
phpディレクトリ内にlocal.iniという設定ファイルを作成します。
disable_functions = pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,
memory_limit = 256M
post_max_size = 100M
upload_max_filesize = 100M
date.timezone = "Asia/Tokyo"
mysqlnd.collect_memory_statistics = Off
mbstring.language = Japanese
mbstring.internal_encoding = UTF-8
mbstring.http_input = pass
mbstring.http_output = pass
mbstring.encoding_translation = Off
mbstring.detect_order = auto
デフォルトのphp.iniが上書きされるので、変更が必要な項目のみ記載します。設定はLaravelの公式の開発環境であるHomesteadやLaradockを参考にしています。Homesteadではセキュリティの関係上、pcntl系の関数を使用しないようにしているようです。「memory_limit」や「post_max_size」などは、必要に応じて適宜変更してください。
・Nginxの設定ファイル
nginxディレクトリ内にdefault.confという設定ファイルを作成します。
server {
listen 80;
root /var/www/public;
add_header X-Frame-Options "SAMEORIGIN";
add_header X-XSS-Protection "1; mode=block";
add_header X-Content-Type-Options "nosniff";
index index.html index.htm index.php;
charset utf-8;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location = /favicon.ico { access_log off; log_not_found off; }
location = /robots.txt { access_log off; log_not_found off; }
error_page 404 /index.php;
location ~ \.php$ {
fastcgi_pass php:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
include fastcgi_params;
}
location ~ /\.(?!well-known).* {
deny all;
}
}
この設定はLaravelの公式ドキュメントの設定です。公式の設定では「fastcgi_pass unix:/var/run/php/php7.2-fpm.sock;」となっているところを「fastcgi_pass php:9000;」と変更しています。コンテナ間で通信が行えるようにするため、php7.2-fpm.sock(Unixドメインソケット)を使うのではなく、PHPコンテナの9000番のポートを指定(php:9000)してTCP通信を行います。
・MySQLの設定ファイル
mysqlディレクトリ内にmy.confという設定ファイルを作成します。
[mysql]
default-character-set=utf8mb4
[mysqld]
character-set-server=utf8mb4
skip-character-set-client-handshake
[mysqldump]
default-character-set=utf8mb4
[client]
default-character-set=utf8mb4
こちらもデフォルトのmy.cnfを上書きできるため、変更する項目のみ記載します。日本語を使えるようにする設定です。
また、mysql/sqlディレクトリ内に以下のinit.sqlというファイルを作成します。
GRANT ALL PRIVILEGES ON *.* to user@"%";
FLUSH PRIVILEGES ;
こちらはコンテナが起動する際に実行されるコマンドです。作成したユーザーに権限を与えます。
5.コンテナの起動とMySQLの接続確認
・コンテナの起動
docker-compose.yml、Dockerfile、設定ファイルを作成した後、コンテナを起動します。
コンテナを起動する前に、Laravelの設定ファイルを「.env」として作成します。その後、docker-compose.ymlファイルのあるdockerディレクトリに移動します。
cp .env.example .env
cd ..
docker-composeでコンテナを起動します。以下のコマンドを実行すると、各コンテナのイメージも作成されます。途中で「debconf: delaying package configuration, since apt-utils is not installed」という警告が出ますが、単なる警告なので問題ありません。すべての処理が完了するまで数分かかります。
docker-compose up -d
処理が完了したら、以下のコマンドで確認します。
docker-compose ps
以下のようにコンテナが立ち上がっていたら成功です。
Name Command State Ports
-----------------------------------------------------------------------------------------
mysql docker-entrypoint.sh --def ... Up 0.0.0.0:3306->3306/tcp, 33060/tcp
nginx nginx -g daemon off; Up 0.0.0.0:443->443/tcp, 0.0.0.0:80->80/tcp
php docker-php-entrypoint php-fpm Up 9000/tcp
次に、Laravelのアプリケーションキーを設定します。PHPコンテナ内でphp artisanコマンドを実行します。
docker-compose exec php php artisan key:generate
Laravelの設定ファイル.envの「APP_KEY」にキーが設定されます。次のコマンドで設定をキャッシュに反映させます。
docker-compose exec php php artisan config:cache
ホストOSのIPアドレス(Vagrantfileに設定したIP)にブラウザからアクセスして以下の画面が表示されたら正常に動作しています。
・MySQLの接続確認
最初に、Laravelのデータベースの設定を行います。.envファイルを次のように設定します。
DB_CONNECTION=mysql
DB_HOST=mysql
DB_PORT=3306
DB_DATABASE=laravel
DB_USERNAME=user
DB_PASSWORD=pass
DB_HOSTにはコンテナ名であるmysqlと記述します。続いて、config/database.phpファイルを設定します。「'strict' => false」は、「mysql_native_password」の認証方式を利用するための設定です。
'host' => env('DB_HOST', 'mysql'),
'port' => env('DB_PORT', '3306'),
'database' => env('DB_DATABASE', 'laravel'),
'username' => env('DB_USERNAME', 'user'),
'password' => env('DB_PASSWORD', 'pass'),
'strict' => false,
以下のコマンドで設定をキャッシュに反映させます。
docker-compose exec php php artisan config:cache
PHPコンテナに入り、接続の確認をしていきます。
docker exec -it php /bin/bash
コンテナに入ったら、以下のコマンドでデータベースのマイグレーションを実行します。
php artisan migrate
続いてtinkerを起動します。
php artisan tinker
次のコマンドを実行して、マイグレーションを行ったデータが返ってきたらMySQLとの接続が成功しています。
\DB::table('migrations')->get();
「Ctrl + C」を押してtinkerを終了し、ロールバックを行います。
php artisan migrate:rollback
なお、PHPコンテナにログインすると、Dokcerfileで定義したユーザーとしてログインします。rootユーザーではないため、コマンドの実行が制限されています。rootとしてログインしたい場合は、以下のようにuオプションに0を指定します。
docker exec -it -u 0 php /bin/bash
以上でDockerによるLaravelの実行環境の構築は終了です。
参考にした記事
今回の環境構築は次の記事を参考にさせて頂きました。
How To Set Up Laravel, Nginx, and MySQL with Docker Compose
Dockerは非常に奥が深く、習得するのに時間がかかるツールと思いますが、Dockerfileやdocker-compose.ymlを自分で書くことで理解を深めていくことができます。