Docker Composeを使ってLAMP環境を作る(Nginx編)

Docker ComposeでLAMP環境を構築します。WebサーバにNginxを使い、SQLはMySQL、PHPはバージョン8.0を使います。

はじめに

他の記事にDocker ComposeでLAMP環境の構築方法を記載(https://codeaid.jp/blog/docker-lamp/)していますが、ここではWebサーバにNginxを使い、PHPのバージョンを8.0にしています。

他の記事では手軽にLAMP環境を構築するためコンテナにphp-apacheを使っています。

Webサーバを別のコンテナにしてサービスをそれぞれで起動して環境を作ることで拡張しやすくなるでしょう。

LAMP環境の構成

ここでは以下のサービスを使ってLAMP環境を構築します。

  • Webサーバ:Nginx最新版
  • データベース:MySQL5.7
  • DB管理:phpMyAdmin最新版
  • サーバ側スクリプト:PHP8.0

まずこの環境を動作させ使用するためのフォルダを作成します。

そのフォルダ配下に以下のフォルダとファイルを作成していきます。

.
 ├── docker-compose.yml
 ├── htdocs
 ├── nginx
 │   └── nginx.conf
 └── php
     ├── Dockerfile
     └── php.ini

docker-compose.yml ⇒ 複数のコンテナ・アプリケーションを実行するための定義ファイル

htdocs ⇒ Webサーバのドキュメントルート

nginx.conf ⇒ Nginxの設定ファイル

Dockerfile ⇒ PHPのDockerイメージを必要な機能を含めて作成するためのファイル

php.ini ⇒ PHPの設定ファイル

docker-compose.ymlの作成

このファイルには複数のコンテナを定義します。

docker-composeコマンドによりまとめて定義しているコンテナを動作させることができます。

ファイルの内容は以下です。

version: '3.8'

services:
  nginx:
    image: nginx:latest
    ports:
      - 80:80
    volumes:
      - ./nginx/nginx.conf:/etc/nginx/conf.d/default.conf
      - ./htdocs:/var/www/html
    depends_on:
      - php
    restart: always

  php:
    build: ./php
    volumes:
      - ./htdocs:/var/www/html
    depends_on:
      - mysql
    restart: always

  mysql:
    image: mysql:5.7
    volumes:
      - db-data:/var/lib/mysql
    environment:
      MYSQL_ROOT_PASSWORD: 'password'
    restart: always

  phpmyadmin:
    image: phpmyadmin/phpmyadmin:latest
    environment:
      PMA_HOST: mysql
    ports:
      - 8080:80
    depends_on:
      - mysql
    restart: always

volumes:
  db-data:

Nginx

WebサーバのDockerイメージはNginxの最新版です。

image: nginx:latest

ポート番号は80を使います。

ports:
  - 80:80

もし他のポート番号を使う場合は例えば「8000:80」などを設定してください。

Nginxは必要な設定をnginx.confに記述します。

ホスト環境で作成したnginx.confを使うためvolumesを定義します。

また、Webページやアプリのスクリプトなどもホスト環境で作成するので、Webサーバのドキュメントルートをvolumesに定義します。

volumes:
  - ./nginx/nginx.conf:/etc/nginx/conf.d/default.conf
  - ./htdocs:/var/www/html

volumesはコンテナのものをホスト環境にマウントします。

「./htdocs」はホスト環境に用意するWebサーバのドキュメントルートです。

「./nginx/nginx.conf」はホスト環境に用意するNginxの設定ファイルです。

相対パスで指定しているので、docker-compose.ymlファイルと同じ場所にhtdocsフォルダを作成し、nginxフォルダとその配下にnginx.confファイルを作成します。

nginx.confの内容は以下です。

server {
  listen 80;
  server_name _;

  root /var/www/html;
  index index.php index.html;

  location / {
    try_files $uri $uri/ /index.php$is_args$args;
  }

  location ~ [^/]\.php(/|$) {
    fastcgi_split_path_info ^(.+?\.php)(/.*)$;
    fastcgi_pass php:9000;
    fastcgi_index index.php;
    include fastcgi_params;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    fastcgi_param PATH_INFO $fastcgi_path_info;
  }
}

「listen」はWebサーバのポート番号をで指定し、ここでは80を設定しています。

「server_name」はサーバ名を指定し、「_」ですべてのホストを意味します。

「root」はWebサーバのドキュメントルートを設定します。

コンテナ内部ではドキュメントルートは「/var/www/html」となります。

「index」はリクエストURLが「/」で終わっているときに使われるファイル名を設定します。

「location」はリクエストURLのパスがlocationのパス条件に一致した場合にlocationブロックに記述した設定が適用されます。

location / {
 …
 }

これはルートからのパスとなるので、すべてのパスを扱います。

「try_files」はディレクトリやファイルが存在しているかをチェックします。

存在しなかった場合は指定場所へリダイレクトします。

location / {
   try_files $uri $uri/ /index.php$is_args$args;
 }

これはURIにファイルが存在すればそのファイルを返し、なければindex.phpにリダイレクトします。

その際、もしクエリがあった場合(?s=abc&id=123 など)は、クエリを付けてリダイレクトします。

「$uri」はそのときのURI(https://example.com/foo.php など)が入ります。

ここでは「$uri $uri/」としているので、URIにあるファイルがあるか、URIパスの配下にファイルがあるかを見ます。

「$is_args」はクエリストリング(s=abc など)がある場合は「?」に変換し、なければ空白となります。

「$args」はクエリストリング(s=abc など)です。

「$is_args$args」で$argsに値があれば?で文字列をつなぎ、なければ空白となります。

location ~ [^/]\.php(/|$) {
 …
 }

これは拡張子がphpの場合にlocationブロックに記述した設定が適用されます。

「fastcgi_split_path_info」はfastcgi_path_info変数の値を取得する正規表現を定義します。

正規表現は2つ定義しそれにより分割されて、1つ目は「$fastcgi_script_name」に格納され、2つ目は「fastcgi_path_info」に格納されます。(SCRIPT_NAMEとPATH_INFOにそれぞれ格納される)

参考:https://www.nginx.com/resources/wiki/start/topics/examples/phpfcgi/

参考:http://nginx.org/en/docs/http/ngx_http_fastcgi_module.html#fastcgi_split_path_info

location ~ [^/]\.php(/|$) {
  fastcgi_split_path_info ^(.+?\.php)(/.*)$;
  fastcgi_pass php:9000;
  fastcgi_index index.php;
  include fastcgi_params;
  fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
  fastcgi_param PATH_INFO $fastcgi_path_info;
}

例えばhttps://example.com/index.php/some/stuff?foo=barというURLでアクセスした場合は、SCRIPT_NAMEに/index.php、PATH_INFOに/some/stuffが格納されます。

fastcgi_pass php:9000;

「fastcgi_pass」はPHP FPM(FastCGI Process Manager)との接続先を定義します。

通常はIPやホスト名やUNIXソケットを指定しますが、Dockerの場合はPHPのサービス名を指定します。

ポート番号はデフォルトの9000で問題ないです。

ApacheはMODモジュールを使ってPHP処理を行いますが、NginxはPHP処理を行うプロセスと接続し処理します。

fastcgi_index index.php;

「fastcgi_index」はスラッシュ「/」で終わる$fastcgi_script_nameだった場合に追加するインデックスを定義します。

この場合はindex.phpとなるので$fastcgi_script_nameが「foo.php/」の場合は「/foo.php/index.php」となります。

include fastcgi_params;

「include」は指定したファイルの内容を取り込みます。

この場合はfastsci_paramsファイルの内容を取り込むので、デフォルトで定義されているfastcgi_paramの内容がここで適用されます。

参考:http://mogile.web.fc2.com/nginx_wiki/nginx_wiki201510/start/topics/examples/phpfcgi.html

これでnginx.confの設定は完了です。

NginxでPHPが動作できるようにするため依存関係を定義します。

depends_on:
 php 

ここで指定するのはdocker-compose.ymlファイルで定義するPHPのサービス名です。

最後にホストマシンが再起動ときも自動でコンテナが再起動できるように定義します。

restart: always

PHP

PHPはバージョン8.0を使用します。

またデフォルトで実装されている以外の機能を取り込むため、Dockerfileを使ってPHP環境を構築します。

build: ./php

これはphpフォルダ以下の情報を使ってビルドする定義となります。

Dockerfileはこのphpフォルダ配下に作成します。

また、PHPが動作するために必要な設定ファイルであるphp.iniもここに作成します。

Dockerfileの内容は以下です。

FROM php:8.0-fpm
 COPY ./php.ini /usr/local/etc/php/
 RUN apt-get update \
   && apt-get install -y libfreetype6-dev libjpeg62-turbo-dev libpng-dev libonig-dev \
   && docker-php-ext-install pdo_mysql mysqli gd iconv exif

FROMで指定しているのはDockerイメージです。

ここでは「8.0-fpm」を指定しています。

COPYではコンテナ内部で定義されるphp.iniの場所にホスト環境で作成したphp.iniをコピーしています。

RUNでコンテナ内部で実行するコマンドを指定しています。

パッケージをアップデートして最新にしてから、「libfreetype6-dev libjpeg62-turbo-dev libpng-dev libonig-dev」のライブラリをインストールします。

libfreetype6-dev libjpeg62-turbo-dev libpng-dev ⇒ PHP拡張機能としてインストールする「gd」で必要なライブラリ

libonig-dev ⇒ mbstring機能を提供するライブラリ(PHP7.4以降ではmbstringをPHP拡張としてインストールするとエラーとなるのでこのライブラリをインストールする)

DockerでPHPビルド時に以下のようなエラーとなる場合はmbstringに関するエラーです。

configure: error: Package requirements (oniguruma) were not met:
No package 'oniguruma' found

以下のPHP拡張機能もインストールしています。

pdo_mysql ⇒ MySQLにPDO接続

mysqli ⇒ MySQL機能拡張ライブラリ

gd ⇒ 画像処理

iconv ⇒ 文字セット変換

exif ⇒ EXIF画像情報

Dockerfileの記述は以上です。

php.iniの内容は以下です。

[Date]
 date.timezone = "Asia/Tokyo"
 [mbstring]
 mbstring.internal_encoding = "UTF-8"
 mbstring.language = "Japanese"

date.timezone ⇒ タイムゾーン「Asia/Tokyo」で日本時間となります

mbstring.internal_encoding ⇒ マルチバイト文字の文字コード

mbstring.language ⇒ マルチバイト文字の言語

php.iniの記述は以上です。

PHPでMySQLを扱うため依存関係を定義します。

depends_on:
 mysql 

MySQLのサービス名で定義します。

最後にホストマシンが再起動ときも自動でコンテナが再起動できるように定義します。

restart: always

MySQL

MySQLのDockerイメージはバージョン5.7を使います。

image: mysql:5.7

作成したデータベースをDockerが終了しても消えないよう永続化するための定義をします。

volumes:
 db-data:/var/lib/mysql 

「volumes」はコンテナ内部の指定場所をdb-dataにマウントします。

db-dataはトップレベルのvolumesに定義して、その名前でDocker内部に領域を確保します。

volumes:
   db-data:

こうすることでdb-dataという名前でマウントした領域を共有できます。

MySQLのシステム環境変数でルートパスワードを定義します。

environment:
   MYSQL_ROOT_PASSWORD: 'password'

ここではpasswordというパスワードを設定することになります。

最後にホストマシンが再起動ときも自動でコンテナが再起動できるように定義します。

restart: always

phpMyAdmin

ブラウザ画面でデータベースを操作できるWebアプリのphpMyAdminを使えるように定義します。

phpMyAdminのDockerイメージは最新版を使います。

image: phpmyadmin/phpmyadmin:latest

phpMyAdminのシステム環境変数で接続するSQLを定義します。

environment:
   PMA_HOST: mysql

MySQLのサービス名を指定します。

phpMyAdminをブラウザで利用するときのポート番号を定義します。

ports:
 8080:80 

Nginxのポート番号と重複しないようにします。

ここでは8080ポートにしています。

phpMyAdminでMySQLを扱うため依存関係を定義します。

depends_on:
 mysql 

最後にホストマシンが再起動ときも自動でコンテナが再起動できるように定義します。

restart: always

Docker Composeの起動・停止・終了

以上でDocker Composeを使うための定義は完了です。

docker-compose.ymlファイルが完成したら、同じディレクトリでdocker-composeコマンドを使ってコンテナを起動します。

起動

$ docker-compose up -d

このコマンドでコンテナが起動されますが、Dockerイメージが存在しない場合はイメージ作成からはじめるので少し時間がかかります。

コンテナを起動した状態でホストマシンを停止しても自動起動を設定しているので、ホストマシンを起動したときにコンテナも起動されます。

起動しているコンテナを停止するには以下のコマンドを実行します。

停止

$ docker-compose stop

この場合はサービスは停止しますがコンテナは残っています。(”docker-compose ps”コマンドで確認できます)

コンテナも終了する場合は以下のコマンドを実行します。

$ docker-compose down

これでコンテナも終了しますが、作成・編集したデータベースは「volumes」で永続化しているので削除されません。

コンテナ終了時にデータベースも削除するには以下のコマンドを実行します。

$ docker-compose down --volumes

作成したDockerイメージを削除する場合は以下のコマンドを実行します。

$ docker rmi <[イメージ名] or [イメージID]>

まとめ

WebサーバにNginxを使ったLAMP環境の構築を行いました。

php-apacheを使ったLAMP環境の構築について他の記事(https://codeaid.jp/blog/docker-lamp/)も参照してください。