概要
こんにちは。Firebaseに訓練された高専卒Webエンジニアの @mejileben です。
Dockerをずっと毛嫌いしていたので、ガッツリ環境構築にチャレンジしてみました。
勉強も兼ねてなので、Laradockは使いません。
前提として、本番運用ではなく個人開発環境としての運用を想定します。
また、複数人でチーム開発することも考えて、設定ファイル等を共有/理解しやすい形にしておければ尚良と思っています。
設定内容
ディレクトリ構成
.
├── Dockerfile_nginx
├── Dockerfile_php
├── docker-compose.yml
├── etc
│ ├── logs
│ │ └── nginx
│ │ ├── access.log
│ │ └── error.log
│ ├── mysql
│ │ ├── env
│ │ │ └── setting.env
│ │ └── mysql_conf
│ ├── nginx
│ │ └── conf.d
│ │ └── default.conf
│ └── php
│ ├── env
│ │ └── setting.env
│ └── php.ini
├── laravel
└── mnt
└── mysql
以下、docker-compose.yml
から順に、NginxやMySQLも含めた各設定ファイルの内容を提示、解説していきます。
docker-compose.yml
version: "3"
services:
nginx:
build:
context: .
dockerfile: ./Dockerfile_nginx
volumes:
- ./etc/logs/nginx:/etc/nginx/logs
- ./etc/nginx/conf.d:/etc/nginx/conf.d
- ./laravel:/var/www/laravel
ports:
- 8081:80
links:
- phpfpm
depends_on:
- phpfpm
phpfpm:
build:
context: .
dockerfile: ./Dockerfile_php
volumes:
- ./laravel:/var/www/laravel
links:
- mysql
depends_on:
- mysql
env_file:
./etc/php/env/setting.env
mysql:
image: mysql:5.7
volumes:
- ./mnt/mysql:/var/lib/mysql
- ./etc/mysql/mysql_conf:/etc/mysql/conf.d
env_file:
./etc/mysql/env/setting.env
ports:
- 13306:3306
思想としては、NginxとかMySQLの設定ファイルはetc
の中に詰め込んであげて、DockerのゲストOSにマウントしてあげます。
これがベストプラクティスかどうかは、運用してみて決めるかなとは思うのですが、実際問題Nginxの設定ファイルをGit管理したことってあまりなくて、もしetc/nginx
配下をLaravelソースと一緒にGit管理できたら、運用面で知見が得られそう、とは思います。
あくまで個人開発環境としてのDockerを想定しているので、NginxのConfをGit管理すること自体に問題はないかなと感じています。
また、NginxのログファイルもホストOS./etc/logs/nginx
にマウントするようにしています。ログの調査もこれで捗りますね。
localhostはポート番号80を取りたくないので8081番を使うように設定しました。
PHP
Dockerfile
WORKDIRをソースコードのrootにしておくと、あとでcomposerを使ってLaravelのセットアップをするときにわざわざDockerゲスト内に入らなくていいのでお得です。
phpのalpineを利用します。
FROM php:7.3.0RC6-fpm-alpine3.8
# setting working directory to source code root
WORKDIR /var/www/laravel
# copy php.ini
COPY ./etc/php/php.ini /usr/local/etc/php/
# install pdo, etc...
RUN apk update && apk add --no-cache \
freetype-dev libjpeg-turbo-dev libpng-dev libmcrypt-dev \
git vim unzip tzdata \
libmcrypt-dev \
libltdl \
&& docker-php-ext-install pdo_mysql mysqli mbstring gd iconv \
&& cp /usr/share/zoneinfo/Asia/Tokyo /etc/localtime \
&& apk del tzdata \
&& rm -rf /var/cache/apk/*
# install composer
RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
このDockerfile
は、先のdocker-compose.yml
にてphpfpm
のdockerfile: ./Dockerfile
で指定しているファイルになります。
やっていることは、PHPのalpineのDockerイメージに対して、php.iniをホストOSからコピーして配置してあげて、あとはalpine linux
のパッケージマネージャであるapk
を使ってもろもろパッケージを入れています。
最後にLaravelのセットアップのためにPHPのパッケージマネージャであるcomposer
も入れておきます。
alpine linux
がapk
というパッケージマネージャを使っているのを知らず、しばらく悪戦苦闘しましたが、Alpine Linux入門 -内部構造とapkでパッケージインストール編-の記事を参考に、コマンドの叩き方を調べたのと、いくつか記事を見ている中で、これくらいのパッケージをapk add
すればいいやろ、というのを入れるようなDockerfileになっています。
補足(Alpine Linuxにおけるタイムゾーンの設定について)
Alpine Linux
を使った場合、タイムゾーンを日本(東京)に変更する設定がdocker-compose.yml
からできないらしいです。手元ではできませんでした。
そこで、Alpine Linux でタイムゾーンを変更するを見て、Dockerfile
でtzdata
をapk add
した上で、用済みになった部分を全てdeleteする形で、Asia/Tokyo
の時刻に合わせるようにしました。
あとで、Nginxのほうでもこの対策は実施していますが、どちらかというとアクセスログなどで時刻を参照するNginxのほうがタイムゾーンの設定は必要ですね。
これだけでもQiitaなりMediumに書けそうなくらい罠ですねw
補足(mcryptについて)
mcryptっていうパッケージも調べた範囲で出てきたので入れようとしたのですがエラーで入らず・・・不要だと思っておきます。
php.ini
php.iniには最低限必要なものを。
[Date]
date.timezone = "Asia/Tokyo"
[mbstring]
mbstring.internal_encoding = "UTF-8"
mbstring.language = "Japanese"
DB_HOST=mysql
DB_DATABASE=mejileben
DB_USERNAME=root
DB_PASSWORD=pass
Nginx
先程も書きましたが、タイムゾーンの設定のために、tzdata
を入れています。
Dockerfile
FROM nginx:1.15.7-alpine
RUN apk --update add tzdata && \
cp /usr/share/zoneinfo/Asia/Tokyo /etc/localtime && \
apk del tzdata && \
rm -rf /var/cache/apk/*
default.conf
laravelのrootディレクトリは、docker-compose.yml
の設定で、ゲストOSの./laravel
にマウントしています。
後ほど、コンテナに入り込んで/var/www/laravel
にLaravelをセットアップします。
開発環境のつもりなので、そこまで本気のセッティングはしないでおきます。
ここで設定するaccess_log/error_logはホストOSにもマウントしているので見ることができるようになるはずです。
server {
listen 0.0.0.0:80;
server_name localhost;
charset utf-8;
root /var/www/laravel/public;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
index index.php;
location / {
access_log /etc/nginx/logs/access.log main;
error_log /etc/nginx/logs/error.log;
try_files $uri $uri/ /index.php$is_args$args;
}
location ~ \.php$ {
fastcgi_pass phpfpm:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
}
補足(Nginx✕Alpine Linuxについて)
Alpine Linux で Docker イメージを劇的に小さくするあたりを参考にして、もうすこしゴリゴリにDockerfileを書いても良いかもしれません。
MySQL
MYSQL_ROOT_PASSWORD=pass
MYSQL_DATABASE=mejileben
config用のディレクトリもマウントしているけど、特に今の時点で置くものがないので置いてないです。
docker-compose up
ここまでできたら、docker-compose up
を叩きます。
[mejileben]$ docker-compose up
Starting laravel_docker_mysql_1_aad144ffc82c ... done
Creating laravel_docker_phpfpm_1_526cdb24bb99 ... done
Creating laravel_docker_nginx_1_2244d1f9f74f ... done
Attaching to laravel_docker_mysql_1_aad144ffc82c, laravel_docker_phpfpm_1_1353a5b38614, laravel_docker_nginx_1_96e7d440124a
mysql_1_aad144ffc82c | Initializing database
phpfpm_1_1353a5b38614 | [21-Dec-2018 03:04:11] NOTICE: fpm is running, pid 1
phpfpm_1_1353a5b38614 | [21-Dec-2018 03:04:11] NOTICE: ready to handle connections
mysql_1_aad144ffc82c | 2018-12-21T03:04:10.371603Z 0 [Warning] TIMESTAMP with implicit DEFAULT value is deprecated. Please use --explicit_defaults_for_timestamp server option (see documentation for more details).
...
エラーパターンとして、このようなログが出たことがありました。
[mejileben]$ docker-compose up
Starting laravel_docker_mysql_1_aad144ffc82c ... error
ERROR: for laravel_docker_mysql_1_aad144ffc82c Cannot start service mysql: b'OCI runtime create failed: container_linux.go:348: starting container process caused "process_linux.go:402: container init caused \\"rootfs_linux.go:58: mounting \\\\\\"/Users/saitoyus/Documents/dev/laravel_docker/etc/mysql/mysql_conf\\\\\\" to rootfs \\\\\\"/var/lib/docker/overlay2/0f80e4463441b42748eaae98b154f59e4fb7a27ddba9a56c79db5a0f33c2d2e8/merged\\\\\\" at \\\\\\"/var/lib/docker/overlay2/0f80e4463441b42748eaae98b154f59e4fb7a27ddba9a56c79db5a0f33c2d2e8/merged/etc/mysql/conf.d\\\\\\" caused \\\\\\"not a directory\\\\\\"\\"": unknown: Are you trying to mount a directory onto a file (or vice-versa)? Check if the specified host path exists and is the expected type'
ERROR: for mysql Cannot start service mysql: b'OCI runtime create failed: container_linux.go:348: starting container process caused "process_linux.go:402: container init caused \\"rootfs_linux.go:58: mounting \\\\\\"/Users/saitoyus/Documents/dev/laravel_docker/etc/mysql/mysql_conf\\\\\\" to rootfs \\\\\\"/var/lib/docker/overlay2/0f80e4463441b42748eaae98b154f59e4fb7a27ddba9a56c79db5a0f33c2d2e8/merged\\\\\\" at \\\\\\"/var/lib/docker/overlay2/0f80e4463441b42748eaae98b154f59e4fb7a27ddba9a56c79db5a0f33c2d2e8/merged/etc/mysql/conf.d\\\\\\" caused \\\\\\"not a directory\\\\\\"\\"": unknown: Are you trying to mount a directory onto a file (or vice-versa)? Check if the specified host path exists and is the expected type'
ERROR: Encountered errors while bringing up the project.
この場合、Are you trying to mount a directory onto a file (or vice-versa)? Check if the specified host path exists and is the expected type
というログの通り、docker-compose.yml
で指定しているマウントするディレクトリが、存在しないというのが原因です。
僕の場合、
- ./etc/mysql/mysql_conf:/etc/mysql/conf.d
のmysql_confが、フォルダではなくファイルになってしまっていたことが原因でした。
vimのNERDTreeから作ったのでコマンドミスしてファイルになってしまってましたw(言い訳)。
mysqlを起動したことで、ホストOSの./mnt/mysql
に中身ができています。
[mejileben]$ tree mnt -L 2 (git)-[master]
mnt
└── mysql
├── auto.cnf
├── ca-key.pem
├── ca.pem
├── client-cert.pem
├── client-key.pem
├── ib_buffer_pool
├── ib_logfile0
├── ib_logfile1
├── ibdata1
├── ibtmp1
├── mysql
├── performance_schema
├── private_key.pem
├── public_key.pem
├── server-cert.pem
├── server-key.pem
├── sys
└── mejileben
5 directories, 14 files
Laravel用のソースコードセットアップ
起動が完了したら、自身のlocalhost
上でLaravelが起動しているはずですが・・・
http://localhost:8081
を開くと、Not Found
になってしまいます。ページが開けません。
docker-compose up
しているシェルの画面を開くと、
nginx_1_96e7d440124a | 172.18.0.1 - - [21/Dec/2018:03:05:07 +0000] "GET / HTTP/1.1" 403 555 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36" "-"
nginx_1_96e7d440124a | 2018/12/21 03:05:07 [error] 6#6: *1 directory index of "/var/www/laravel/public/" is forbidden, client: 172.18.0.1, server: localhost, request: "GET / HTTP/1.1", host: "localhost"
phpfpm_1_1353a5b38614 | 172.18.0.4 - 21/Dec/2018:03:05:08 +0000 "GET /index.php" 404
などと出ていることが確認できます。
※ここでは-d
をつけずに起動していますが、普段はdocker-compose up -d
することのほうが多いです。
これはLaravelの肝心のソースコードが入っていないことが原因なので、Laravelのソースコードを入れましょう。
docker-compose up
していることで、もうコンテナ上でゲストOSが起動しているので、そこの中に入ってあげて、Laravelのソースコードをcomposer
使って入れてあげる流れになります。
当然ですが、ここでホストOSにcomposer
をinstallしてしまうとDockerの意味がなくなってしまいます。
ですので、立ち上げたphpfpm
コンテナに入り込んで、その内部でLaravelをセットアップしましょう。
Dockerfile
にて、WORKDIRを/var/www/laravel
にしているので、ホストOSから直接docker-compose exec
で実行できます。
このコマンドはWORKDIRで実行されるため、コマンド末尾の.
は/var/www/laravel
を指します。
[mejileben]$ docker-compose exec phpfpm composer create-project laravel/laravel .
Do not run Composer as root/super user! See https://getcomposer.org/root for details
Installing laravel/laravel (v5.7.19)
- Installing laravel/laravel (v5.7.19): Loading from cache
Created project in .
> @php -r "file_exists('.env') || copy('.env.example', '.env');"
Loading composer repositories with package information
Updating dependencies (including require-dev)
Package operations: 86 installs, 0 updates, 0 removals
- Installing vlucas/phpdotenv (v2.5.1): Loading from cache
- Installing symfony/css-selector (v4.2.1): Loading from cache
- Installing tijsverkoyen/css-to-inline-styles (2.2.1): Loading from cache
- Installing symfony/polyfill-php72 (v1.10.0): Loading from cache
- Installing symfony/polyfill-mbstring (v1.10.0): Loading from cache
...
ここでうっかりプロジェクト名を指定すると、その名前のディレクトリにソースコードがセットアップされて、NginxからするとNot Foundになってしまうので注意です。
あと、冒頭でrootユーザーでInstallするな!って怒られていますが、僕は気にしませんでしたw
さて、このインストールが終わった頃に、ホストOSのほうでtree
コマンドしてみると
[mejileben]$ tree laravel -L 1 (git)-[master]
laravel
├── app
├── artisan
├── bootstrap
├── composer.json
├── composer.lock
├── config
├── database
├── package.json
├── phpunit.xml
├── public
├── readme.md
├── resources
├── routes
├── server.php
├── storage
├── tests
├── vendor
└── webpack.mix.js
10 directories, 8 files
見事にLaravel関連ファイルが入っていることが確認できます。
こうやって環境構築自体はDocker内でやって、実態のあるファイルは自分のOSにマウントするというのをやると、Docker使っている感が出ますね〜
この状況で
http://localhost:8081
を開くと、

無事にLaravelのページが表示されました!
次は
npm
のコンテナも入れて、Nuxt.js
の環境構築もしていきたいと思います!
参考記事
Laravel+MySQL+NginxでさくっとDocker開発立ち上げる
Alpine Linux で Docker イメージを劇的に小さくする
Alpine Linux でタイムゾーンを変更する
Alpine Linux入門 -内部構造とapkでパッケージインストール編-