1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

docker-compose挑戦

Last updated at Posted at 2020-03-18

※この記事は初めて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
まずはファイルの中身から。

mydocker/#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ファイルを使う。

mydocker/.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に渡す。

mydocker/docker-compose.yml
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ファイルでは初期値を書いておける

mydocker/nginx/Dockerfile
FROM ngin
ARG user="developper"  ← 初期値
RUN useradd -m -s /bin/bash -u $user_id $user

#コンテナ内の設定ファイルに変数を渡す
例えば、Nginxコンテナ内のnginx.conf等にホスト名などを変数で渡したい。

私が使った方法は、Dockerfile内で
ARGで設定した変数でそのままsedして書き換える。

コンテナにもっていく設定ファイル(テンプレート)

/etc/nginx/conf.d/vhosts.conf
server {
    listen       80;
    server_name  host_name;

Dockerfileで「host_name」の部分を置換

mydocker/nginx/Dockerfile
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ファイルで以下を設定

Dockerfile
# timezone
RUN rm -fr /etc/localtime && ln -s /usr/share/zoneinfo/Asia/Tokyo /etc/localtime

#コンテナのファイルがホスト側で権限がなくて読めない
volumeで設定して、ホスト側と共有しても、コンテナ側の所有者設定になってしまい、ホスト側でいじれないという問題。

例えば、mariadbの場合の解決策。

mydocker/mysql/Dockerfile
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コンテナの設定でこうしたい。

mydocker\nginx\conf\conf.d\vhosts.conf
    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をこうする

mydocker\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の設定

mydocker\php\Dockerfile(一部)
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)をもってくる

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の設定

mydocker\nginx\Dockerfile(一部)
# 共通ユーザ(ホスト+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 実行ユーザを共通ユーザにする。

/etc/nginx/nginx.conf
user nginx; ←これを書き換える

※2 phpと同様に、/var/run/php-fpmディレクトリを作成し、所有者変更をしておく

#Nginxでのホスト側からのリバースプロキシ
ホスト側でSSL接続でうけて、HTTPかつコンテナ側のポートにリバースプロキシして流す方法。

ホスト側のNginx設定

mydocker\hostos\nginx\conf.d\vhosts.conf
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コンテナの設定を見直す

mydocker\nginx\conf\conf.d\vhosts.conf
    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つ

  1. fastcgi_param SCRIPT_FILENAMEの$document_rootをPHPコンテナに合わせたパスに書き換えておく

  2. $document_rootをPHPコンテナに作って、PHPソースの場所からシンボリックリンクを作っておく

私は後者を採用。

mydocker\php\Dockerfile(一部)
RUN mkdir -p /var/www/html
Run ln -s /home/$user/source/public /var/www/html/xxx.com

#mariadb(mysql)のデータ永続化
/var/lib/mysqlをvolume設定すればよい。
所有者については先述の通り。

mydocker\docker-compose.yml
  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とか。

mydocker\docker-compose.yml
  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所有者である。
いじることはないので、特に変える必要はないけど。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?