こんにちは! ニアです。
最近、Laravelを使ったWebアプリ開発の勉強しているので、健忘録として、Docker上での開発環境の構築方法を記事に書きました。
さらに、Docker上のLaravelの開発環境と一緒に使うと便利なDockerイメージをいくつか紹介します。
1. docker-compose用ファイル作成
ここでは、php-fpm 7.3 + nginx + MySQL 5.7 + Redisの構成で構築します。
※執筆者のホスト環境
- macOS 10.14 Mojave
- Docker Desktop for Mac (2.1.0.1)
- docker (19.03.1)
- docker-compose (1.24.1)
1.1. ディレクトリ構成
docker
フォルダにdocker-composeのファイル、laravel
フォルダにLaravelのソースファイルが入る構成とします。
.
├── docker
│ ├── docker-compose.yml
│ ├── .env
│ ├── mysql
│ │ └── init
│ │ └── grant.sh
│ ├── nginx
│ │ ├── conf
│ │ │ └── laravel.conf
│ │ └── logs
│ └── php-fpm
│ └── Dockerfile
└── laravel
1.2. php-fpmコンテナ
Dockerfile
Debian 10 (buster)をベースに、php-fpmとcomposerをインストールしたコンテナイメージを作成します。
FROM debian:buster-slim
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
ca-certificates \
apt-transport-https \
curl \
unzip \
zlib1g-dev \
php7.3 \
php7.3-fpm \
php7.3-mysql \
php7.3-xml \
php7.3-mbstring \
php-oauth \
php7.3-zip \
&& rm -rf /var/lib/apt/lists/* \
# php-fpmのListenをソケットから9000/tcpに変更します。
&& sed -iE '/^listen/s/\/run\/php\/php7\.3-fpm\.sock/9000/g' /etc/php/7.3/fpm/pool.d/www.conf \
&& mkdir -p /var/www/html \
&& mkdir -p /run/php \
&& curl -sS https://getcomposer.org/installer | php \
&& mv composer.phar /usr/local/bin/composer
# composerをrootで実行できるようにします。
ENV COMPOSER_ALLOW_SUPERUSER 1
# Laravelのソースコードのフォルダを作業ディレクトリにします。
WORKDIR /var/www/html
EXPOSE 9000
# `docker-compose up`実行時、`/usr/sbin/php-fpm7.3 -F`で起動するようにします。
CMD [ "/usr/sbin/php-fpm7.3", "-F" ]
※DebianでPHP 7.3をインストールする場合
- Debian 10(buster)のデフォルトリポジトリでは、PHP 7.3なので、追加のリポジトリは不要です。
- Debian 9(stretch)以前のデフォルトリポジトリでは、PHP 7.0以前なので、DEB.SURY.ORGのリポジトリを追加する必要があります。
1.3. MySQLコンテナ
grant.sh
後述のdocker-compose.yml
内のMySQLコンテナの環境変数で指定する、MySQLユーザー「MYSQL_USER
」に権限を付与するスクリプトを作成します。
(
mysql -uroot -p$MYSQL_ROOT_PASSWORD <<EOF
GRANT ALL ON *.* TO '$MYSQL_USER'@'%';
EOF
)
1.4. nginxコンテナ
laravel.conf
Laravel用のnginxの設定ファイルを作成します。
log_format vhost '$host $remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent"';
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
server {
server_name localhost;
listen 80;
# アクセスログとエラーログ
access_log /var/log/nginx/laravel_access.log vhost;
error_log /var/log/nginx/laravel_error.log;
# ルートディレクトリは、{Laravelのルート}/publicです。
root /var/www/html/public;
index index.php index.html;
# URL内の'index.php'を取り除きます。
location / {
try_files $uri $uri/ /index.php?$query_string;
}
# PHPファイルへのリクエスト時、php-fpmコンテナに送信します。
location ~ .php$ {
fastcgi_pass php-fpm:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root/$fastcgi_script_name;
include fastcgi_params;
}
}
1.5. docker-compose.yml
version: "3"
services:
# コンテナ
# php-fpm
php-fpm:
image: laravel-php-fpm:7
build: ./php-fpm/
depends_on:
- mysql
- redis
volumes:
# Laravelのソースファイル
- ../laravel:/var/www/html
# nginx
nginx:
image: nginx:mainline-alpine
depends_on:
- php-fpm
ports:
- 80:80
volumes:
# nginxの設定ファイル
- ./nginx/conf/laravel.conf:/etc/nginx/conf.d/default.conf:ro
# Laravelのソースファイル
- ../laravel:/var/www/html
# nginxのログ
- ./nginx/logs:/var/log/nginx
# MySQL
mysql:
image: mysql:5.7
environment:
MYSQL_ROOT_PASSWORD: password # MySQLのrootパスワード
# 以下に指定したDB名やユーザーは、MySQLコンテナの新規作成時に作成されます。
MYSQL_DATABASE: laravel # DB名
MYSQL_USER: laravel_user # ユーザー名
MYSQL_PASSWORD: laravel_pass # パスワード
ports:
- 3306:3306
volumes:
# このマウント先に入れたシェルスクリプトは、コンテナ作成後自動的に実行されます。
- ./mysql/init:/docker-entrypoint-initdb.d:ro
# MySQLのデータファイル
- mysql-data:/var/lib/mysql
# Redis
redis:
image: redis:alpine
volumes:
# Redisのデータファイル
- redis-data:/data
# ネットワークはデフォルトのものを使用します。
#networks:
# default:
# driver: bridge
# データ用のボリューム
volumes:
mysql-data:
driver: local
redis-data:
driver: local
.env
ファイル
docker-compose
コマンドで使用する環境変数COMPOSE_PROJECT_NAME
にプロジェクト名を設定します。
これを設定すると、docker-compose
コマンドを実行した時にCOMPOSE_PROJECT_NAME
を使って、コンテナやネットワーク、ボリュームに名前を付けられます。
- コンテナ名:
${COMPOSE_PROJECT_NAME}_${サービス名}_${連番}
- ネットワーク名:
${COMPOSE_PROJECT_NAME}_${networks直下に定義した識別子}
- ボリューム名:
${COMPOSE_PROJECT_NAME}_${volumes直下に定義した識別子}
COMPOSE_PROJECT_NAME=laravel
2. Laravelのプロジェクトを新規作成
php-fpmコンテナからcomposer create-project
コマンドを実行して、Laravelのプロジェクトを新規作成します。
# 先ほど作成したdocker-compose.ymlがあるディレクトリで実行します。
# (php-fpmにリンクしたMySQLとRedisコンテナはここでは不要なので、--no-depsオプションを付けます。)
$ docker-compose run --rm --no-deps php-fpm composer create-project laravel/laravel --prefer-dist .
# ※ネットワーク及び、MySQLとRedisコンテナのボリュームは、ここで作成されます。
Creating network "laravel_default" with driver "bridge"
Creating volume "laravel_mysql-data" with local driver
Creating volume "laravel_redis-data" with local driver
# 以降、composerコマンドの実行結果が出力されます。
さらにphp-fpmコンテナから、composer require predis/predis
コマンドを実行して、predis/predisをインストールします。
docker-compose run --rm --no-deps php-fpm composer require predis/predis
2.1. laravel/.env
を編集
Laravelのプロジェクトを作成したら、laravel/.env
を編集し、データベースとRedisの接続情報を設定します。
# MySQL
DB_CONNECTION=mysql
DB_HOST=mysql # MySQLコンテナのサービス名を指定します。
DB_PORT=3306
DB_DATABASE=laravel # MySQLコンテナの環境変数「MYSQL_DATABASE」の値を指定します。
DB_USERNAME=laravel_user # MySQLコンテナの環境変数「MYSQL_USER」の値を指定します。
DB_PASSWORD=laravel_pass # MySQLコンテナの環境変数「MYSQL_PASSWORD」の値を指定します。
# Redis
REDIS_HOST=redis # Redisコンテナのサービス名を指定します。
REDIS_PASSWORD=null
REDIS_PORT=6379
3. コンテナの起動
docker-compose up -d
コマンドでコンテナを作成・起動します。
$ docker-compose up -d
Creating laravel_mysql_1 ... done
Creating laravel_redis_1 ... done
Creating laravel_php-fpm_1 ... done
Creating laravel_nginx_1 ... done
# コンテナが起動しているか確認します。
$ docker-compose ps
Name Command State Ports
----------------------------------------------------------------------------------------------
laravel_mysql_1 docker-entrypoint.sh mysqld Up 0.0.0.0:3306->3306/tcp, 33060/tcp
laravel_nginx_1 nginx -g daemon off; Up 0.0.0.0:80->80/tcp
laravel_php-fpm_1 /usr/sbin/php-fpm7.3 -F Up 9000/tcp
laravel_redis_1 docker-entrypoint.sh redis ... Up 6379/tcp
これで、ブラウザから http://localhost にアクセスすると、Laravelのページが表示されます。
3.3. データベースのMigration
php-fpmコンテナからphp artisan migrate
コマンドを実行します。
$ docker-compose exec php-fpm php artisan migrate
Migration table created successfully.
Migrating: 2014_10_12_000000_create_users_table
Migrated: 2014_10_12_000000_create_users_table (0.04 seconds)
Migrating: 2014_10_12_100000_create_password_resets_table
Migrated: 2014_10_12_100000_create_password_resets_table (0.02 seconds)
# テーブルが作成されたことを確認
$ docker-compose exec mysql mysql -ularavel_user -ularavel_pass -Dlaravel -e 'show tables;'
+-------------------+
| Tables_in_laravel |
+-------------------+
| migrations |
| password_resets |
| users |
+-------------------+
以上で、Laravelの開発環境を構築は完了です。お疲れさまでした!
上記で作成した、docker-composeのファイルは、GitHubに置いてあります。
https://github.com/Nia-TN1012/docker-laravel注: 使用する時は、laravelフォルダ内の
.gitkeep
を削除してください。laravelフォルダが空でないと、前述のcomposer create-project
コマンドが失敗する原因になります。またMacの場合、Finderから削除すると、その際に
_.DS_Store
が作成されてしまう場合があるので、ターミナルからrm
コマンドで削除するとよいでしょう。$ rm laravel/.gitkeep
4. Laravelの開発環境と一緒に使うと便利なDockerイメージ
ここでは、以下の4つのDockerイメージを紹介していきます。
- MailCatcher: (公式サイト / Docker Hub)(Ruby製の簡易SMTPサーバー)
- Node.js: (公式サイト / Docker Hub)(Laravel Mixでのビルド用)
- MinIO: (公式サイト / Docker Hub / GitHub)(S3互換のオブジェクトストレージサーバー)
- ElasticMQ: (公式サイト / Docker Hub / GitHub)(SQS互換のメッセージキュー)(※別の記事で紹介します。)
4.1. MailCatcher
MailCatcherイメージを使うと、Laravelから送信したメールをブラウザで確認することができます。
docker-compose.yml
に追加
# services:
# MailCatcher
mailcatcher:
image: schickling/mailcatcher
ports:
- 1080:1080
$ docker-compose up -d
Creating laravel_mailcatcher_1 ... done
http://localhost:1080 にアクセスすると、MailCatcherのメール送信トレイが表示されます。
Laravelからメールを送信する
Laravelで標準実装されているユーザー認証機能では、パスワードリセットのリクエスト時にメールが送信される仕様なので、MailCatcherを使ってシミュレーションしてみましょう。
まず、laravel/.env
を編集します。
# Mail
MAIL_DRIVER=smtp
MAIL_HOST=mailcatcher # MailCatcherコンテナのサービス名を指定します。
MAIL_PORT=1025 # MailCatcherへの送信ポートは1025番です。
MAIL_USERNAME=null # ユーザー名や暗号化設定はnullのままでOKです。
MAIL_PASSWORD=null
MAIL_ENCRYPTION=null
次に、php-fpmコンテナからphp artisan make:auth
コマンドを実行し、Laravelのユーザー認証機能を追加します。
$ docker-compose exec php-fpm php artisan make:auth
Authentication scaffolding generated successfully.
Laravel 6では、
artisan make:auth
コマンドがないので、代わりにcomposerコマンドでlaravel/ui
をインストールし、artisan ui:auth
コマンドを実行します。$ docker-compose exec php-fpm composer require laravel/ui $ docker-compose exec php-fpm php artisan ui:auth Authentication scaffolding generated successfully.
http://localhost/register にアクセスして、ユーザー登録をします。
ユーザー登録をしたら、一旦ログアウトし、http://localhost/login にアクセスします。
「Forgot Your Password」のリンクを押します。
ユーザー登録時のメールアドレスを入力して、パスワードリセットのリクエストを送信します。
パスワード再設定メールがLaravelからMailCatcherに送信されるので、http://localhost:1080 を開き、メール内のパスワード再設定ボタンを押します。
新しいパスワードを入力します。
これで、パスワードリセットの完了です。めでたし、めでたし。。。
4.2. Node.js
Laravelには、フロントエンドのアセットファイル(CSSやJavaScriptなど)をビルドするLaravel Mix(webpackベース)が同梱されています。
ホスト環境にNode.jsがインストールされていれば、それを使ってビルドすることができますが、nodeイメージのコンテナを使うことで、ホスト環境側のバージョンに依存せずにビルドできたり、Laravelソースファイルが入った名前付きボリュームをマウントしてビルドしたりすることができます。
ここでは、Laravelのアセットファイルの変更を監視し、自動的に再ビルドできるnodeコンテナを立てます。
docker-compose.yml
に追加
# services:
# Node.js
node:
image: node:lts-alpine
working_dir: /opt/laravel # ワーキングディレクトリをLaravelのルートディレクトリにします。
command: ["npm", "run", "watch-poll"] # `docker-compose up -d`実行時のコマンドを`npm run watch-poll`にします。
volumes:
# npmのキャッシュファイル
- npm-cache:/root/.mpm
# Laravelのソースファイル
- ../laravel:/opt/laravel
# volumes:
npm-cache:
driver: local
webpack.mix.js
の編集
laravel/webpack.mix.js
に以下のオプションを追加します。
mix.webpackConfig({
watchOptions: {
poll: 1000,
// node_modulesフォルダ内を監視の対象外にします。
// (このオプションを忘れると、nodeコンテナがCPUを爆食いします。。。)
ignored: /node_modules/
}
});
nodeパッケージのインストール
nodeコンテナからnpm install
コマンドを実行します。
$ docker-compose run --rm node npm install --no-optional
アセットファイルの変更を監視
nodeコンテナを起動すると、アセットファイルがビルドされ、その後はアセットファイル変更を監視するようになります。
$ docker-compose up -d
Creating laravel_node_1 ... done
ビルドの結果を見るには、nodeコンテナのログを見ます。
$ docker-compose logs node
Attaching to laravel_node_1
# 中略
node_1 | Asset Size Chunks Chunk Names
node_1 | /css/app.css 173 KiB /js/app [emitted] /js/app
node_1 | /js/app.js 1.38 MiB /js/app [emitted] /js/app
アセットファイルのビルド(+圧縮)
nodeコンテナからnpm run dev
or npm run prod
コマンドを実行すると、アセットファイルがビルド(prod
の場合、さらに圧縮)されます。
# アセットのビルド
$ docker-compose run --rm node npm run dev
# アセットのビルド
$ docker-compose run --rm node npm run prod
4.3. MinIO
MinIOイメージを使って、Laravelからオブジェクトストレージにファイルをアップロード・ダウンロードすることができます。
docker-compose.yml
に追加
# services:
# MinIO
minio:
image: minio/minio
environment:
MINIO_ACCESS_KEY: LARAVEL_MINIO_ACCESS_KEY # アクセスキー
MINIO_SECRET_KEY: laravel_mino_hogefuga # シークレットキー
ports:
- 9000:9000
command: server /data
volumes:
- minio-data:/data
# volumes:
minio-data:
driver: local
docker-compose up -d
Creating volume "laravel_minio-data" with local driver
Creating laravel_minio_1 ... done
http://localhost:9000 にアクセスすると、MinIOのログインページが開くので、環境変数に指定したアクセスキーとシークレットキーを入力してログインします。
バケットの作成
右下の「+」ボタンから「Create bucket」ボタンを押します。
バケット名を入力し、Enterキーを押して、バケットを作成します。
LaravelからMinIOにアクセスする
php-fpmコンテナからcomposer require
コマンドを実行し、league/flysystem-aws-s3-v3をインストールします。
$ docker-compose exec php-fpm composer require league/flysystem-aws-s3-v3
laravel/config/filesystems.php
の'disk'内に以下を追加します。
'disk' = [
// 中略
'minio' => [
'driver' => 's3',
'key' => env('AWS_ACCESS_KEY_ID'),
'secret' => env('AWS_SECRET_ACCESS_KEY'),
'region' => env('AWS_DEFAULT_REGION'),
'bucket' => env('AWS_BUCKET'),
'url' => env('AWS_URL'),
'endpoint' => env('MINIO_ENDPOINT'),
'use_path_style_endpoint' => true,
],
]
laravel/.env
を編集します。
AWS_ACCESS_KEY_ID=LARAVEL_MINIO_ACCESS_KEY # minioコンテナの環境変数「MINIO_ACCESS_KEY」の値を指定します。
AWS_SECRET_ACCESS_KEY=laravel_mino_hogefuga # minioコンテナの環境変数「MINIO_SECRET_KEY」の値を指定します。
AWS_DEFAULT_REGION=us-east-1 # 使用しません。
AWS_BUCKET=laravel # 先ほど作成した、minioコンテナ内のバケット名を指定します。
MINIO_ENDPOINT=http://minio:9000 # minioコンテナのエンドポイントをセットします。(http://{MinIOコンテナのサービス名}:9000)
FILESYSTEM_CLOUD=minio # ファイルシステムは'minio'を指定します。(ちなみに省略すると、's3'が指定されます。)
Laravelからの疎通確認には、php-fpmコンテナからphp artisan tinker
(Laravel版のインタラクティブシェル(REPL))を使って確認します。
$ docker-compose exec php-fpm php artisan tinker
// '{ "hello": "world" }'を'hello.json'として、MinIOコンテナにアップロード
>>> Storage::cloud()->put( 'hello.json', '{ "hello": "world" }' );
=> true
// MinIOコンテナから'hello.json'をダウンロード
>>> Storage::cloud()->get( 'hello.json' );
=> "{ "hello": "world" }"
// exitで終了します。
>> exit
Exit: Goodbye
MinIOコンテナのlaravelバケットを確認すると、先ほどアップロードしたファイルがリストに表示されています。
4.4. ElasticMQ
LaravelをElasticMQ(Amazon SQS互換)と連携してみる