※この記事は初めてdocker-composeを使って
nginx+php+mariadb(mysql)+adminer
の開発環境を作成したときのメモ。
はまったことや、ポイントだったり、気づいたことなど。
#ディレクトリ構成
mydocker
├─ docker-compose.yml
├─ .env
│
├─hostos
│ └─nginx
│ └─conf.d
│ └─ vhosts.conf
├─ nginx
│ ├─ conf ※3
│ │ ├─ conf.d
│ │ │ └─ vhosts.conf
│ │ └─ nginx.conf
│ └─ Dockerfile
│
├─ php
│ ├─ php-fpm.d
│ │ └─www.conf
│ ├─ unix-socket
│ ├─ php.ini
│ └─ Dockerfile
│
├─ mariadb
│ ├─ data
│ └─ Dockerfile
│
└─ logs
├─ nginx
└─ php-fpm
ちなみにmydockerと同階層にsourceディレクトリがありphp等のソースコードを設置。
#docker-compose.yml
まずはファイルの中身から。
version: '3.4'
services:
nginx:
# image: nginx
container_name: nginx
restart: always
build:
context: ./nginx/
args:
project: ${PROJECT}
host_name: ${HOST_NAME}
user: ${PRJ_USER}
user_id: ${PRJ_USER_ID}
volumes:
# log
- ./logs/nginx/${HOST_NAME}:/var/log/nginx/${HOST_NAME}
# source(DocumentRoot)
- ./../source/${HOST_NAME}/public:/var/www/html/${HOST_NAME}
# unix-socket
- ./php/unix-socket:/var/run/php-fpm
ports:
- 8082:80
depends_on:
- phpfpm
phpfpm:
# image: 7.4.3-fpm
container_name: php
restart: always
build:
context: ./php/
args:
host_name: ${HOST_NAME}
user: ${PRJ_USER}
user_id: ${PRJ_USER_ID}
volumes:
# log
- ./logs/php-fpm/:/var/log/php-fpm
# source
- ./../source:/home/${PRJ_USER}/source
# unix-socket
- ./php/unix-socket:/var/run/php-fpm
depends_on:
- mariadb
mariadb:
# image: mariadb
container_name: mariadb
restart: always
build:
context: ./mariadb/
args:
user_id: ${PRJ_USER_ID}
group_id: ${PRJ_GROUP_ID}
volumes:
# データ永続化
- "./mariadb/data:/var/lib/mysql"
environment:
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
ports:
- 33060:3306
adminer:
image: adminer
container_name: adminer
restart: always
ports:
# コンテナ側は8080必須
- 8080:8080
depends_on:
- mariadb
※build - Dockerfileから起動するのでimageの記述は不要。
※└─ context - Dockerfileの場所
※└─ args - Dockerfileに変数を渡せる
※restart: always - ホストの起動時やDockerデーモンの起動時の自動起動設定。ただし、コンテナの起動設 定に不備があると、再起動を繰り返すので注意。
※depends_on - buildする順番。volumeやらコンテナ間通信やら考慮する
公式はhttp://docs.docker.jp/compose/toc.html
#共通で使う変数
Dockerファイル、およびdocker-compose.yml内で使える共通の変数がほしい
=>
.envファイルを使う。
PROJECT=hogeporject
PRJ_USER=developper
PRJ_USER_ID=1000
PRJ_GROUP_ID=1000
ENV=development
HOST_NAME=xxx.com
MYSQL_ROOT_PASSWORD=xxxxxxx
.envで設定した変数は、docker-compose.yml内で使える。
Dockerファイルでも利用したい場合は、argsに渡す。
build:
args: ← Dockerファイルに渡す。
project: ${PROJECT}
host_name: ${HOST_NAME}
user: ${PRJ_USER}
user_id: ${PRJ_USER_ID}
volumes:
- ./logs/nginx/${HOST_NAME}:/var/log/nginx/${HOST_NAME} ← docker-compose.yml内で使う。
受け取るDockerファイルでは初期値を書いておける
FROM ngin
ARG user="developper" ← 初期値
RUN useradd -m -s /bin/bash -u $user_id $user
#コンテナ内の設定ファイルに変数を渡す
例えば、Nginxコンテナ内のnginx.conf等にホスト名などを変数で渡したい。
私が使った方法は、Dockerfile内で
ARGで設定した変数でそのままsedして書き換える。
コンテナにもっていく設定ファイル(テンプレート)
server {
listen 80;
server_name host_name;
Dockerfileで「host_name」の部分を置換
ARG host_name="xxx.com"
ADD ./conf/conf.d /etc/nginx/conf.d/ ←ホスト側からテンプレートをもってくる
RUN sed -i -e "s/host_name/$host_name/g" /etc/nginx/conf.d/vhosts.conf
その他の人では、envsubstコマンドを使っている人もいるようです。
#timezoneをホストと合わせたい
ログとか、DBのtimestanmpとかとか、コンテナ内の時間をホストと合わせたい。
Dockerファイルで以下を設定
# timezone
RUN rm -fr /etc/localtime && ln -s /usr/share/zoneinfo/Asia/Tokyo /etc/localtime
#コンテナのファイルがホスト側で権限がなくて読めない
volumeで設定して、ホスト側と共有しても、コンテナ側の所有者設定になってしまい、ホスト側でいじれないという問題。
例えば、mariadbの場合の解決策。
ARG user_id=1000
ARG group_id=1000
# データの所有者をホストOSとあわせる
RUN usermod -u $user_id -o mysql
RUN groupmod -g $group_id mysql
※user_idとgroup_idに、ホスト側で作った、Docker操作ユーザのidを渡す。
※mariadbの場合は、mysqlユーザでデータを作るので、mysqlユーザのユーザ番号とグループ番号をあわせる
#コンテナ間で共有したいファイルは、共通ユーザを所有者にしたい
nginx+php(php-fpm)でunixソケット通信をしたかったので、それに沿って。
nginxコンテナの設定でこうしたい。
location ~ \.php$ {
fastcgi_split_path_info ^(.+\.php)(/.+)$;
if (!-f $document_root$fastcgi_script_name) {
return 404;
}
include /etc/nginx/fastcgi_params;
#fastcgi_pass 127.0.0.1:9000; ←普通?ならコレ
#fastcgi_pass php:9000; ←コンテナ間通信としては、とりあえずこれでもできる。
fastcgi_pass unix:/var/run/php-fpm/php-fpm.sock; ←こうしたい。
つまり
unix:/var/run/php-fpm/php-fpm.sock
をphpとnginxで共有する必要がある。
=>
php⇔ホスト⇔nginx
でvolume設定をし、所有者権限も同じにする。
- volume設定
docker-compose.ymlをこうする
services:
nginx:
container_name: nginx
build:
context: ./nginx/
args:
user: ${PRJ_USER}
user_id: ${PRJ_USER_ID}
volumes:
- ./php/unix-socket:/var/run/php-fpm
phpfpm:
container_name: php
build:
context: ./php/
args:
user: ${PRJ_USER}
user_id: ${PRJ_USER_ID}
volumes:
# unix-socket
- ./php/unix-socket:/var/run/php-fpm
php⇔ホスト
nginx⇔ホスト
ホストで同じ場所を指定し、そこにphp-fpm.sockを格納できるようにしておく
- 共通ユーザ
php、nginx、ホストで共通のユーザを作成し
そのユーザをコンテナ内の実行ユーザにする。
まず、ホスト側で共通ユーザを作成し、ユーザIDを確認する。
$ useradd developper
$ id developper
このIDをDockerfileに渡して使っていく。
phpの設定
ARG user="developper"
ARG user_id=1000
# 共通ユーザ(ホスト+nginx+php)
RUN useradd -m -s /bin/bash -u $user_id $user
#copy php.ini
COPY ./php.ini /usr/local/etc/php/
#php-fpm unix-socket
Run mkdir -p /var/run/php-fpm && chown $user:$user /var/run/php-fpm ※1
COPY ./php-fpm.d/www.conf /usr/local/etc/php-fpm.d/www.conf ※2
Run sed -i -e "s/user_name/$user/g" /usr/local/etc/php-fpm.d/www.conf ※3
Run sed -i -e "s/listen = 9000/listen = \/var\/run\/php-fpm\/php-fpm\.sock/g" /usr/local/etc/php-fpm.d/zz-docker.conf ※4
※1 /var/run/php-fpmディレクトリを作成し、所有者変更をしておく
※2 ホストから設定ファイル(php-fpm.d/www.conf)をもってくる
user = user_name
group = user_name
;listen = 127.0.0.1:9000
listen = /var/run/php-fpm/php-fpm.sock
;listen.owner = nobody
listen.owner = user_name
;listen.group = nobody
listen.group = user_name
※3 設定ファイルを共通ユーザで書き換え。
※4 php-fpmの公式イメージ特有の問題。
www.confのlisten設定を上書きされているので対応。
/usr/local/etc/php-fpm.d/zz-docker.confのlistenを書き換える。
こちらなど参照。
Nginxの設定
# 共通ユーザ(ホスト+nginx+php)
RUN useradd -m -s /bin/bash -u $user_id $user
#copy conf
Run sed -i -e "s/user nginx;/user $user;/g" /etc/nginx/nginx.conf ※1
#php-fpm unix-socket
Run mkdir -p /var/run/php-fpm && chown $user:$user /var/run/php-fpm ※2
※1 実行ユーザを共通ユーザにする。
user nginx; ←これを書き換える
※2 phpと同様に、/var/run/php-fpmディレクトリを作成し、所有者変更をしておく
#Nginxでのホスト側からのリバースプロキシ
ホスト側でSSL接続でうけて、HTTPかつコンテナ側のポートにリバースプロキシして流す方法。
ホスト側のNginx設定
server {
listen 443 ssl;
server_name xxx.com;
location / {
# docker's port
proxy_pass http://localhost:8082; ※1
# https -> http
proxy_redirect http:// https://; ※2
# inherit remote info
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr; ※3
proxy_set_header X-Forwarded-Proto https; ※4
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
※1 Nginxコンテナに流す
※2 HTTPSからHTTPに
※3 クライアントの接続元IPを伝える
※4 クライアントのプロトコル(SSLであること)を伝える
ちなみに、このときはfuelphpをのせた。
https://qiita.com/uecoeco/items/b9a8460fac33d84be2f9
#PHP⇔Nginx間でのvolume設定
当たり前といえば、当たり前のことだけど、ぼーっとしてるとミスする。
nginxコンテナの設定を見直す
root /var/www/html/xxx.com; ※1
location ~ \.php$ {
fastcgi_split_path_info ^(.+\.php)(/.+)$;
if (!-f $document_root$fastcgi_script_name) {
return 404;
}
include /etc/nginx/fastcgi_params;
#fastcgi_pass 127.0.0.1:9000;
#fastcgi_pass php:9000;
fastcgi_pass unix:/var/run/php-fpm/php-fpm.sock;
fastcgi_index index.php;
fastcgi_read_timeout 30;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; ※2
}
はまったのは、このまま
xxx.com/index.php
を表示したところ
File not found.
なぜか。理由とポイントは
※1ドキュメントルートは/var/www/html/ではない。
※2 設定では、ドキュメントルート上のPHPスクリプトを探す。
PHPスクリプトはPHPコンテナにあるので
$document_rootがPHPコンテナにないと、探すことが出来ない。
解決策は以下2つ
-
fastcgi_param SCRIPT_FILENAMEの$document_rootをPHPコンテナに合わせたパスに書き換えておく
-
$document_rootをPHPコンテナに作って、PHPソースの場所からシンボリックリンクを作っておく
私は後者を採用。
RUN mkdir -p /var/www/html
Run ln -s /home/$user/source/public /var/www/html/xxx.com
#mariadb(mysql)のデータ永続化
/var/lib/mysqlをvolume設定すればよい。
所有者については先述の通り。
mariadb:
# image: mariadb
container_name: mariadb
restart: always
build:
context: ./mariadb/
args:
user_id: ${PRJ_USER_ID}
group_id: ${PRJ_GROUP_ID}
volumes:
# データ永続化
- "./mariadb/data:/var/lib/mysql"
#ポート固定のコンテナがある
コンテナ側のポートが固定されており、docker側からのオプションでポート指定してもうまく立ち上がらない
コンテナがある。
nginxコンテナのように指定したポートで受けてプロキシとかもする必要はない。(するとnginxでポート使用済みのエラーが発生する。)
例えば、adminerとか。
adminer:
image: adminer
container_name: adminer
restart: always
# build:
# context: ./adminer/
ports:
# コンテナ側は8080必須
- 8080:8080
depends_on:
- mariadb
例えば、
- 8080:80
としても立ち上がらない。
特に支障はないので、従えばよいのだが。
(どうしても変えたい場合の手段はあるだろうけど、そんなことはあまりないので。)
#コンテナがうまく立ち上がらないとき。
$ docker-compose up -d
$ docker-compose ps
をしてもExitedやRestartingになって立ち上がっていないとき。
=>
とにかくログをみる。
$ docker-compose logs -f
コンテナを削除してしまうとみれないので注意。
#Nginxのログ
これも、当たり前の話。
Nginxのログはrootユーザで作成されるので、コンテナ側とvolume設定しても、やはりroot所有者である。
いじることはないので、特に変える必要はないけど。