新しいことにはウェルカム

技術 | 電子工作 | ガジェット | ゲーム のメモ書き

Docker ComposeでNginx+PHP+MySQL環境構築メモ

phpLDAPadminを使いたいことがあって、Docker ComposeでNginxとPHPを立ち上げて、そこで動かしました。

今度また、WebでPHPを使いたくなった時用に、Docker ComposeでPHPが動くNginxを用意するための、自分用テンプレートメモです。

せっかくなので後々使い勝手がいいよう、MySQLも加えた、LEMP環境にしました。

…と言っても、大したことはしてなくて、Webサーバーとして、NginxとPHPの両方をインストールしたコンテナを作成しているだけなんですけどね。

構成

  • Web
    • NginxとPHPが稼働
    • 動作テスト用に「phpinfo()」と「phpMyAdmin」をインストール
  • MySQL
    • volumeでデータは外部に保存

テンプレート

docker-compose.yaml

  • WebサーバーはDockerfileを使って、自分でイメージを作ります
  • MySQLは公式からイメージを持ってきてます。バージョンは8です(後々このバージョンが原因でハマることになりましたが…)
version: "3"
services:
  web:
    build: "./web"
    ports:
      - 80:80
  mysql:
    image: mysql
    command: --default-authentication-plugin=mysql_native_password
    volumes:
      - mysql-data:/var/lib/mysql
    environment:
      MYSQL_ROOT_PASSOWRD: rootpassword
# phpmyadmin:
#   image: phpmyadmin/phpmyadmin
#   environment:
#     PMA_HOST: mysql
#   ports:
#     - 8080:80
volumes:
  mysql-data:

Dockerfile

WebサーバーのDockerfile。「./web/」に置きます。

FROM php:fpm

RUN apt-get update

# install php extension
RUN docker-php-ext-install mysqli && \
    docker-php-ext-enable mysqli

# install Nginx
RUN apt-get -y install nginx
COPY default /etc/nginx/sites-enabled/default

# test contents
COPY phpinfo.php /var/www/html/phpinfo.php
COPY phpMyAdmin/ /var/www/html/phpMyAdmin/

# run Nginx & PHP
COPY start.sh /start.sh
RUN chmod 744 /start.sh

EXPOSE 80
CMD ["/start.sh"]

default

Nginxの設定ファイルを修正して、PHPが実行されるようにします。

...
  location ~ \.php$ {
    include snippets/fastcgi-php.conf;
    fastcgi_pass localhost:9000;
  }
...

start.sh

Webサーバーで実行されるスクリプト。PHPとNginxを起動しています。

#!/bin/bash
nginx
php-fpm

動作テスト

http://localhost

docker-composeでNginx+php+MySQLメモ

http://localhost/phpinfo.php

docker-composeでNginx+php+MySQLメモ

http://localhost/phpMyAdmin/index.php

docker-composeでNginx+php+MySQLメモ

補足説明

Web:Nginx+PHP

NginxとPHPを同じコンテナで動かしたかったので、Dockerfileを使ってイメージを作成しています。

どちらをベースにしてもいいのですが、ここでは、PHPのイメージをベースに、Nginxをインストールしています。

NginxとPHPを同時に動かすため、それらを呼び出すスクリプト「start.sh」を作成し、CMDで呼び出しています。

コマンドnginxはバックグラウンドで実行され、コマンドphp-fpmはフォアグラウンドで実行されるので「nginx->php-fpm」の順で実行すると、Nginx・PHPが両方動いた状態で「start.sh」プロセスが永続します。

実行順を逆にしたい場合は、php-fpmをバックグラウンドで実行し、NginxのDockerfileのようにdeamon offオプションを使って、フォアグラウンドでnginxを実行させます。

#!/bin/bash
php-fpm &
nginx -g deamon off

PHP拡張モジュール

PHP拡張モジュールを、DockerのPHPイメージでインストールするには

RUN apt-get install php-xxx

とはせず

RUN docker-php-ext-install xxx

と、専用コマンドを使います。

RUN docker-php-ext-install <extension_name>でインストールし、RUN docker-php-ext-enable <extension_name>で利用可能にします。

インストールにあたり、依存関係のライブラリがあっても、ライブラリのインストールは自動ではしてくれないので、自分で関係ライブラリをインストールしてから、PHP拡張モジュールをインストールする必要があります。

例えば「phpLDAPadmin」は「php-ldap」「php-xml」「php-gettext」のPHP拡張モジュールを使うのですが、それらのPHP拡張モジュールは「libldap2-dev」「libxml2-dev」を必要とするので、下記のような記述をDockerfileに記載してインストールする必要がありました。

# install php-ldap
RUN apt-get install libldap2-dev -y && \
    docker-php-ext-install ldap && \
    docker-php-ext-enable ldap

# install php-xml
RUN apt-get install libxml2-dev -y && \
    docker-php-ext-install xml && \
    docker-php-ext-enable xml

# install php-gettext
RUN docker-php-ext-install gettext && \
    docker-php-ext-enable gettext

MySQL

MySQLのバージョン8から、デフォルトの認証プラグインが、バージョン5以前の「mysql_native_password」から「caching_sha2_password」に変更になっています。

PHPのMySQLライブラリはまだ「caching_sha2_password」に対応しておらず、phpMyAdminでログインしようとすると、下記のようなエラーが出てしまいます。

The server requested authentication method unknown to the client

デフォルトの認証プラグインはmysqldのオプションで変更できるので、「docker-compose.yaml」内の「command」で変更しています。

command: --default-authentication-plugin=mysql_native_password

ただし、オプションによるデフォルトの認証プラグインの設定は、MySQLの初回構築時のみ有効で、一度構築されると後からオプションを変更しても反映されません。

特にVolumeでデータを外部に保存するようにしていて、初回構築後に設定を変更すると、コンテナがエラーで終了するので注意が必要です。

認証プラグインの設定はユーザー毎に設定可能なので、デフォルトの認証プラグインの設定で対応せず、MySQLの初回構築が終わった後で、PHPでアクセスするユーザーの認証プラグインを「mysql_native_password」に変更して対応することもできます。(ここではその方法は割愛しました)

感想など

Nginx+PHP

本来、NginxとPHPのコンテナを分けるのが、Docker的な構成なんでしょうね。

ただ、コンテナを分けた場合、PHPファイルはNginx側ではなく、PHPコンテナ側に置く必要があります。 一方、PHPファイル以外のhtmlや画像ファイルはNginx側に置くので、phpMyAdminのように、PHP・HTML・画像などが1つのフォルダに入って配布されるアプリの場合、Nginx・PHP両方のコンテナにコピーを置く必要があります。

しかし、フォルダを一箇所にまるっと置いて終わりとしたいのが心情というもので、その場合、Volumeを使って、NginxコンテナとPHPコンテナでコンテンツフォルダを共有させたりします。

docker-compose.yaml だとこんな感じ。

version: "3"
services:
  nginx:
    build: "./nginx"
    ports:
      - 80:80
    volumes:
      - ./share/:/usr/share/nginx/html/
  php:
    build: "./php"
    volumes:
      - ./share/:/var/www/html/

最初そのやり方で作ってみたのですが、今回はコンテンツをコンテナと切り離して永続化したかったわけではなく、むしろ、コンテンツをコンテナの中に固めてパッケージ化したかったので、結局1つのコンテナにNginx・PHP・コンテンツを詰め込むことにしました。

Volumeを使うと、何か問題が発生した時、同じ条件でやってるつもりでも、実はVolumeの中身は同じではなく、それに気づかずハマるということがあるので、コンテンツの置き場所を、Volumeにしたくなかったというのもあります。

phpMyAdmin

ここでは、PHPのサンプルとして「phpMyAdmin」をWebサーバー上で動かしてみましたが、phpMyAdminのイメージが配布されているので、それを直接使った方が楽です。 (参考までにDockerfileのコメントアウト部分に記載しましたので、コメントを外すとポート8080でphpMyAdminに直接アクセスできるようになります)

その他

おまけで追加したつもりの、MySQL関連で一番ハマりました…。

MySQLバージョン8で、WITH句、ウィンドウ関数が使えるようになったんですね!いいこと知りました!

参考記事