💢

PHP(Debian + Apache) + MongoDBの構成でDocker環境を構築 + エラー解決の記録

2021/07/06に公開

概要

慣れているPHPで環境構築を行った結果、泥沼にハマったのでメモ。

環境

HomeBrewDocker Desktop for MacVSCodeが入ったMacOS

前提知識

  • Docker
  • Docker-compose
  • PHP
Docker構成だけ知りたい人用

ディレクトリ構成

.
├── docker
│   ├── app
│   │     └── dockerfile
│   └── config
│         ├── 000-default.conf
│         └── php.ini
...省略...
├── vendor
├── composer.json
├── composer.lock
└── docker-compose.yml
./docker/app/dockerfile
# composer
FROM composer:latest AS composer
# PHP
FROM php:8.0.7-apache-buster

# 設定ファイルをコピー
COPY ./docker/config/php.ini /usr/local/etc/php/
COPY ./docker/config/000-default.conf /etc/apache2/sites-available/000-default.conf

# composer実行ファイルをコピー
COPY --from=composer /usr/bin/composer /usr/bin/composer

# パッケージインストール
RUN \
    # Apache rewriteモジュール有効化
    a2enmod rewrite \
    && apt-get update \
    && apt-get install -y unzip libzip-dev zlib1g-dev \
    && apt-get install -y libcurl4-openssl-dev pkg-config libssl-dev \
    # Extension
    && docker-php-ext-install zip \
    # MongoDB driver
    && pecl install mongodb \
    # Extension有効化
    && docker-php-ext-enable zip mongodb
/docker/config/000-default.conf
<VirtualHost *:80>
    ServerAdmin webmaster@localhost
    DocumentRoot /var/www/html
    ErrorLog ${APACHE_LOG_DIR}/error.log
    CustomLog ${APACHE_LOG_DIR}/access.log combined

    <Directory /var/www/html>
        Options Indexes FollowSymLinks
        AllowOverride All
        Require all granted
    </Directory>
</VirtualHost>
./docker/config/php.ini
[Date]
date.timezone = "Asia/Tokyo"

[Mbstring]
default_charset = "UTF-8"
mbstring.language = "Japanese"
./docker-compose.yml
version: "3.8"

services:
  # App Server
  app:
    build:
      context: .
      dockerfile: ./docker/app/Dockerfile
    container_name: mongo_app
    volumes:
      - .:/var/www/html
    ports:
      - 8180:80
    networks:
      - mongo

  # MongoDB
  mongo:
    image: mongo:latest
    container_name: mongo_db
    restart: always
    environment:
      # 接続情報
      MONGO_INITDB_ROOT_USERNAME: fendi
      MONGO_INITDB_ROOT_PASSWORD: fendi
    volumes:
      - mongo:/data/db
    ports:
      - "27017:27017"
    networks:
      - mongo

  # MongoDB GUI
  mongo_express:
    image: mongo-express:latest
    container_name: mongo_express
    restart: always
    ports:
      - "8181:8081"
    environment:
      # MongoDBの接続情報
      ME_CONFIG_MONGODB_ADMINUSERNAME: fendi
      ME_CONFIG_MONGODB_ADMINPASSWORD: fendi
      # MongoDBのポート
      ME_CONFIG_MONGODB_PORT: 27017
      # MongoDBのコンテナ名
      ME_CONFIG_MONGODB_SERVER: mongo_db
    networks:
      - mongo

networks:
  # 起動時のdefaultネットワーク再生成防止のためにdefaultを明示
  default:
    external:
      name: bridge
  mongo:
    name: mongo_db

volumes:
  mongo:
    driver: local

MongoDB PHP Library
MongoDBをPHPで操作するためのライブラリが公式で配布されている。

docker-compose exec app composer require "mongodb/mongodb"

ディレクトリ構成

.
├── docker
│   ├── app
│   │     └── dockerfile
│   └── config
│         ├── 000-default.conf
│         └── php.ini
...省略...
├── vendor
├── composer.json
├── composer.lock
└── docker-compose.yml
./docker/app/dockerfile
# composer
FROM composer:latest AS composer
# PHP
FROM php:8.0.7-apache-buster

# 設定ファイルをコピー
COPY ./docker/config/php.ini /usr/local/etc/php/
COPY ./docker/config/000-default.conf /etc/apache2/sites-available/000-default.conf

# composer実行ファイルをコピー
COPY --from=composer /usr/bin/composer /usr/bin/composer

# パッケージインストール
RUN \
    # Apache rewriteモジュール有効化
    a2enmod rewrite \
    # debianパッケージ
    && apt-get update \
    && apt-get install -y unzip libzip-dev zlib1g-dev \
    # Extension
    && docker-php-ext-install zip \
    # MongoDB driver
    && pecl install mongodb \
    # Extension有効化
    && docker-php-ext-enable zip mongodb
/docker/config/000-default.conf
<VirtualHost *:80>
    ServerAdmin webmaster@localhost
    DocumentRoot /var/www/html
    ErrorLog ${APACHE_LOG_DIR}/error.log
    CustomLog ${APACHE_LOG_DIR}/access.log combined

    <Directory /var/www/html>
        Options Indexes FollowSymLinks
        AllowOverride All
        Require all granted
    </Directory>
</VirtualHost>
./docker/config/php.ini
[Date]
date.timezone = "Asia/Tokyo"

[Mbstring]
default_charset = "UTF-8"
mbstring.language = "Japanese"
./docker-compose.yml
version: "3.8"

services:
  # App Server
  app:
    build:
      context: .
      dockerfile: ./docker/app/Dockerfile
    container_name: mongo_app
    volumes:
      - .:/var/www/html
    ports:
      - 8180:80
    networks:
      - mongo

  # MongoDB
  mongo:
    image: mongo:latest
    container_name: mongo_db
    restart: always
    environment:
      # 接続情報
      MONGO_INITDB_ROOT_USERNAME: fendi
      MONGO_INITDB_ROOT_PASSWORD: fendi
    volumes:
      - mongo:/data/db
    ports:
      - "27017:27017"
    networks:
      - mongo

  # MongoDB GUI
  mongo_express:
    image: mongo-express:latest
    container_name: mongo_express
    restart: always
    ports:
      - "8181:8081"
    environment:
      # MongoDBの接続情報
      ME_CONFIG_MONGODB_ADMINUSERNAME: fendi
      ME_CONFIG_MONGODB_ADMINPASSWORD: fendi
      # MongoDBのポート
      ME_CONFIG_MONGODB_PORT: 27017
      # MongoDBのコンテナ名
      ME_CONFIG_MONGODB_SERVER: mongo_db
    networks:
      - mongo

networks:
  # 起動時のdefaultネットワーク再生成防止のためにdefaultを明示
  default:
    external:
      name: bridge
  mongo:
    name: mongo_db

volumes:
  mongo:
    driver: local

手順

1. Docker起動

docker-compose up -d --build

MongoDBドライバのインストールを確認。

MongoDBにも問題なく接続できる。

2. 「MongoDB PHP Library」をインストール

MongoDBをPHPで操作するためのライブラリが公式で配布されているのでcomposerでインストールを行う。

docker-compose exec app composer require "mongodb/mongodb"

3. テスト

とても簡単なスクリプトを実行して動作チェック。

test.php
<?php

// composerライブラリをインクルード
require './vendor/autoload.php';

use MongoDB\Client;

// MongoDBに接続
$mongo = new Client('mongodb://fendi:fendi@localhost:27017');
// $mongo = new Client(
//     'mongodb://localhost:27017',
//     [
//         'username' => 'fendi',
//         'password' => 'fendi',
//     ]
// );

// client->db->collectionの順にインスタンスを生成
$collection = $mongo
    ->test_db
    ->test_collection;

// jsonデータを登録
$collection->insertOne([
    'test_1' => 'データを登録',
    'test_2' => 'データを挿入',
]);

上記スクリプトを実行したところ謎のエラーが発生。

MongoDB\Driver\Exception\ConnectionTimeoutException: No suitable servers found (`serverSelectionTryOnce` set): [connection refused calling ismaster on 'localhost:27017'] in /var/www/html/vendor/mongodb/mongodb/src/functions.php on line 431

4. エラー解決

サーバーの接続先が見つからないのがエラーの原因らしい。
PHPのバージョンを落とす、Dockerコンテナ構成を変える、Laravel上でLaravel-mongodbを導入するなど色々試して解決を図るもうまくいかず。

ググっても解決手段が分からず、GitHubのissueでも同様の報告が多数上がっているよう。
解決できずissueの片隅で悲鳴を上げている人もちらほら。

ふとDBへ接続する際のホスト名をlocalhostからコンテナ名に変更してみたところ…

./test.php
<?php

// composerライブラリをインクルード
require './vendor/autoload.php';

use MongoDB\Client;

// ホストをlocalhostからコンテナ名に変更
$mongo = new Client('mongodb://fendi:fendi@mongo_db:27017');

...省略...

また別のエラーが発生。

MongoDB\Driver\Exception\AuthenticationException: The SCRAM_SHA_256 authentication mechanism requires libmongoc built with ENABLE_SSL in /var/www/html/vendor/mongodb/mongodb/src/Operation/InsertOne.php on line 134

調べたところibmongoc SSLがdisabledになっているのがエラーの原因のようなのでsslパッケージを追加して再度コンテナをビルド。

./docker/app/dockerfile
...省略...

# composer実行ファイルをコピー
COPY --from=composer /usr/bin/composer /usr/bin/composer

# パッケージインストール
RUN \
    # Apache rewriteモジュール有効化
    a2enmod rewrite \
    # debianパッケージ
    && apt-get update \
    && apt-get install -y unzip libzip-dev zlib1g-dev \
    # Extension
    && docker-php-ext-install zip \
    # mongodb接続用sslパッケージ
    && apt-get install -y libcurl4-openssl-dev pkg-config libssl-dev \
    # MongoDB driver
    && pecl install mongodb \
    # Extension有効化
    && docker-php-ext-enable zip mongodb

インストール成功。

再度スクリプトを実行すると…


うおおおおおおおおお!!!

Discussion