💯

PHP8, apache, MariaDB, XdebugによるDocker環境の作成

2023/01/09に公開

目的

docker環境の生成は、過去に作った環境をコピーすれば躓くことはほぼないが、初めて作る場合は、予期しないエラーに躓き、非常に時間を食うことが多い。
よって、筆者がテンプレート化している環境のうち、今回は必要最小限の機能を盛り込んだ汎用性の高いテンプレートを紹介する。
また、laravelやwordpressのdocker環境生成もこの最小構成の基本テンプレートに手順を追加をするだけで簡単に環境生成できるので、別の記事で紹介するのでご期待を。

バージョン情報

  • 手元の作業PC: Apple M1 Pro
  • Docker: 20.10.21
  • イメージ: php:8.1-apache
  • イメージ: mariadb:10.3
  • イメージ: phpmyadmin:latest
  • イメージ: mailhog/mailhog:latest
  • PHP: 8.1
  • DB: mariadb 10.3
  • Docker-compose: 2.13.0

ゴール

http://localhost でindex.phpの内容が表示されること

ディレクトリ構成

プロジェクトルート
├── .vscode
│    └── launch.json (vscode エディタのデバッガー設定)
├── www
│    └── html
│        └── index.php
├── docker-compose.yml
└── docker
    ├── app
    │    ├── apache2
    │    │    ├── sites-available
    │    │    │   └── 000-default.conf
    │    │    └ apache2.conf
    │    ├── php.ini
    │    └── Dockerfile
    ├── msmtp
    │    └── msmtprc (メール送信のSMTP設定用)
    └── mysql
         ├── initdb (sqlの初期化用)
         ├── storage (データのマウント用)
         ├── Dockerfile
         └── server.cnf

手順1: 各ファイルの作成・編集

index.phpを作成・編集

www/html/index.php
<?php
phpinfo();

docker-compose.ymlを作成・編集

docker-compose.yml
version: "3"
services:
  app:
    build:
      context: "docker/app/"
    ports:
      - 80:80
    working_dir: /var/www
    volumes:
      - "./www/:/var/www"
      - "./docker/app/apache2/apache2.conf:/etc/apache2/apache2.conf"
      - "./docker/app/apache2/sites-available/000-default.conf:/etc/apache2/sites-available/000-default.conf"
      - "./docker/msmtp/msmtprc:/etc/msmtprc"
    depends_on:
      - mysql
      - mailhog
  mysql:
    build:
      context: "docker/mysql/"
    command: --default-authentication-plugin=mysql_native_password
    environment:
      - MYSQL_DATABASE=test_db_name
      - MYSQL_HOST=mysql
      - MYSQL_USER=test_user
      - MYSQL_PASSWORD=test_pass
      - MYSQL_ROOT_PASSWORD=test_root_pass
      - TZ=Asia/Tokyo
    volumes:
      - "./docker/mysql/storage/:/var/lib/mysql"
      - "./docker/mysql/initdb/:/docker-entrypoint-initdb.d"
    ports:
      - "3306:3306"
  phpmyadmin:
    image: phpmyadmin:latest
    ports:
      - 8080:80
    environment:
      - PMA_HOST=mysql
      - PMA_USER=test_user
      - PMA_PASSWORD=test_pass
    depends_on:
      - mysql
  mailhog:
    image: mailhog/mailhog:latest
    ports:
      - "8025:8025"
      - "1025:1025"
volumes:
  storage:

000-default.confを作成・編集

docker/app/apache2/sites-available/000-default.conf
# DocumentRoot設定
DocumentRoot /var/www/html

<Directory "/var/www/html/">
    #
    # Possible values for the Options directive are "None", "All",
    # or any combination of:
    #   Indexes Includes FollowSymLinks SymLinksifOwnerMatch ExecCGI MultiViews
    #
    # Note that "MultiViews" must be named *explicitly* --- "Options All"
    # doesn't give it to you.
    #
    # The Options directive is both complicated and important.  Please see
    # http://httpd.apache.org/docs/2.4/mod/core.html#options
    # for more information.
    #
    Options +ExecCGI +Indexes +FollowSymLinks
    AddHandler cgi-script .cgi
    # SSI
AddType text/html .shtml .php
AddOutputFilter INCLUDES .shtml .php

    #
    # AllowOverride controls what directives may be placed in .htaccess files.
    # It can be "All", "None", or any combination of the keywords:
    #   Options FileInfo AuthConfig Limit
    #
    AllowOverride All

    #
    # Controls who can get stuff from this server.
    #
    Require all granted
</Directory>

# vhosts設定の場合
# DocumentRoot "/var/www/vhosts/mycompany.co.jp/httpdocs"

apache2.confを作成・編集

docker/app/apache2/apache2.conf
# Global configuration
#

#
# ServerRoot: The top of the directory tree under which the server's
# configuration, error, and log files are kept.
#
# NOTE!  If you intend to place this on an NFS (or otherwise network)
# mounted filesystem then please read the Mutex documentation (available
# at <URL:http://httpd.apache.org/docs/2.4/mod/core.html#mutex>);
# you will save yourself a lot of trouble.
#
# Do NOT add a slash at the end of the directory path.
#
#ServerRoot "/etc/apache2"

#
# The accept serialization lock file MUST BE STORED ON A LOCAL DISK.
#
#Mutex file:${APACHE_LOCK_DIR} default

#
# The directory where shm and other runtime files will be stored.
#

DefaultRuntimeDir ${APACHE_RUN_DIR}

#
# PidFile: The file in which the server should record its process
# identification number when it starts.
# This needs to be set in /etc/apache2/envvars
#
PidFile ${APACHE_PID_FILE}

#
# Timeout: The number of seconds before receives and sends time out.
#
Timeout 300

#
# KeepAlive: Whether or not to allow persistent connections (more than
# one request per connection). Set to "Off" to deactivate.
#
KeepAlive On

#
# MaxKeepAliveRequests: The maximum number of requests to allow
# during a persistent connection. Set to 0 to allow an unlimited amount.
# We recommend you leave this number high, for maximum performance.
#
MaxKeepAliveRequests 100

#
# KeepAliveTimeout: Number of seconds to wait for the next request from the
# same client on the same connection.
#
KeepAliveTimeout 5


# These need to be set in /etc/apache2/envvars
User ${APACHE_RUN_USER}
Group ${APACHE_RUN_GROUP}

#
# HostnameLookups: Log the names of clients or just their IP addresses
# e.g., www.apache.org (on) or 204.62.129.132 (off).
# The default is off because it'd be overall better for the net if people
# had to knowingly turn this feature on, since enabling it means that
# each client request will result in AT LEAST one lookup request to the
# nameserver.
#
HostnameLookups Off

# ErrorLog: The location of the error log file.
# If you do not specify an ErrorLog directive within a <VirtualHost>
# container, error messages relating to that virtual host will be
# logged here.  If you *do* define an error logfile for a <VirtualHost>
# container, that host's errors will be logged there and not here.
#
ErrorLog ${APACHE_LOG_DIR}/error.log

#
# LogLevel: Control the severity of messages logged to the error_log.
# Available values: trace8, ..., trace1, debug, info, notice, warn,
# error, crit, alert, emerg.
# It is also possible to configure the log level for particular modules, e.g.
# "LogLevel info ssl:warn"
#
LogLevel warn

# Include module configuration:
IncludeOptional mods-enabled/*.load
IncludeOptional mods-enabled/*.conf

# Include list of ports to listen on
Include ports.conf


# Sets the default security model of the Apache2 HTTPD server. It does
# not allow access to the root filesystem outside of /usr/share and /var/www.
# The former is used by web applications packaged in Debian,
# the latter may be used for local directories served by the web server. If
# your system is serving content from a sub-directory in /srv you must allow
# access here, or in any related virtual host.
<Directory />
    Options FollowSymLinks
    AllowOverride None
    Require all denied
</Directory>

<Directory /usr/share>
    AllowOverride None
    Require all granted
</Directory>

<Directory "/var/www">
    Options Indexes FollowSymLinks
    AllowOverride None
    Require all granted
</Directory>

#<Directory /srv/>
#   Options Indexes FollowSymLinks
#   AllowOverride None
#   Require all granted
#</Directory>




# AccessFileName: The name of the file to look for in each directory
# for additional configuration directives.  See also the AllowOverride
# directive.
#
AccessFileName .htaccess

#
# The following lines prevent .htaccess and .htpasswd files from being
# viewed by Web clients.
#
<FilesMatch "^\.ht">
    Require all denied
</FilesMatch>


#
# The following directives define some format nicknames for use with
# a CustomLog directive.
#
# These deviate from the Common Log Format definitions in that they use %O
# (the actual bytes sent including headers) instead of %b (the size of the
# requested file), because the latter makes it impossible to detect partial
# requests.
#
# Note that the use of %{X-Forwarded-For}i instead of %h is not recommended.
# Use mod_remoteip instead.
#
LogFormat "%v:%p %h %l %u %t \"%r\" %>s %O \"%{Referer}i\"
\"%{User-Agent}i\"" vhost_combined
LogFormat "%h %l %u %t \"%r\" %>s %O \"%{Referer}i\"
\"%{User-Agent}i\"" combined
LogFormat "%h %l %u %t \"%r\" %>s %O" common
LogFormat "%{Referer}i -> %U" referer
LogFormat "%{User-agent}i" agent

# Include of directories ignores editors' and dpkg's backup files,
# see README.Debian for details.

# Include generic snippets of statements
IncludeOptional conf-enabled/*.conf

# Include the virtual host configurations:
IncludeOptional sites-enabled/*.conf

# vim: syntax=apache ts=4 sw=4 sts=4 sr noet

php.iniを作成・編集

docker/app/php.ini
default_charset = "UTF-8"

; [mbstring]
mbstring.language = "Japanese"
mbstring.internal_encoding = "UTF-8"
mbstring.encoding_translation = Off
mbstring.detect_order = "UTF-8,SJIS,EUC-JP,JIS,ASCII"
mbstring.substitute_character = none

; [Date]
date.timezone = "Asia/Tokyo"

; [Options]
expose_php = On

; [Resource Limits]
max_execution_time = 300
max_input_time = 60
memory_limit = 512M
post_max_size = 512M
upload_max_filesize = 512M

; [Error handling and logging]
error_reporting = E_ALL
display_errors = On

; [Extensions]
extension=zip.so

; [mail function]
SMTP = localhost
smtp_port = 1025
sendmail_path = "/usr/bin/msmtp -C /etc/msmtprc -t"

; xdebug
zend_extension=/usr/local/lib/php/extensions/no-debug-non-zts-20210902/xdebug.so
xdebug.mode=debug
xdebug.client_port=9004
xdebug.start_with_request=yes
xdebug.remote_cookie_expire_time=3600
xdebug.client_host=host.docker.internal
xdebug.log=/var/log/xdebug.log

Dockerfile を作成・編集

docker/app/Dockerfile
FROM php:8.1-apache

# PHP 設定ファイル
# COPY php.ini /etc/php.ini
COPY php.ini /usr/local/etc/php/php.ini

# Composerのインストール
RUN cd /usr/bin && curl -s http://getcomposer.org/installer | php &&
ln -s /usr/bin/composer.phar /usr/bin/composer

# ミドルウェアインストール
# https://hub.docker.com/_/php/
RUN apt-get update && apt-get install -y \
        libfreetype6-dev \
        libjpeg62-turbo-dev \
        libpng-dev \
        git \
        zip \
        unzip \
        vim \
        libpng-dev \
        libpq-dev \
        libonig-dev \
        libxml2-dev \
        libzip-dev \
        wget \
        msmtp \
        msmtp-mta \
    && docker-php-ext-configure gd --with-freetype --with-jpeg \
    && docker-php-ext-install -j$(nproc) gd \
    && docker-php-ext-install pdo pdo_mysql mysqli \
    # && docker-php-ext-configure imap --with-kerberos --with-imap-ssl \
    # && docker-php-ext-install imap \
    && docker-php-ext-install zip

# Install and enable imagick PECL extensions
# RUN pecl install imagick \
#   && docker-php-ext-enable imagick

# xdebug
RUN pecl install xdebug-3.1.5 && \
    docker-php-ext-enable xdebug

# modRewriteを有効化する
RUN mv /etc/apache2/mods-available/rewrite.load /etc/apache2/mods-enabled
RUN /bin/sh -c a2enmod rewrite

# タイムゾーン設定
RUN ln -sf  /usr/share/zoneinfo/Asia/Tokyo /etc/localtime

CMD ["apache2-foreground"]

msmtprc を作成・編集

docker/msmtp/msmtprc
# Set default values for all following accounts.
defaults
auth off
tls off
tls_trust_file /etc/ssl/certs/ca-certificates.crt
syslog on
aliases /etc/aliases

# mailhog
account mailhog
host mailhog
port 1025
from username@example.com
user username
password password

# Set a default account
account default : mailhog

initdbディレクトリを作成

初期化したいSQLがあったら、docker/mysql/initdb
の中にsqlファイルを入れておけば、dockerを立ち上げた時に自動で実行してくれるが、今回は空のまま

storageディレクトリ

docker/mysql/storage は自動で作られるので、特にディレクトリを手動で作る必要なし

Dockerfileを作成・編集

docker/mysql/Dockerfile
FROM mariadb:10.3
COPY server.cnf /etc/mysql/mariadb.conf.d/

server.cnfを作成・編集

server.cnf
[mysqld]
character-set-server=utf8mb4
collation-server=utf8mb4_general_ci
max_allowed_packet=16M
[client]
default-character-set=utf8mb4

手順2: ビルド

docker-compose build を実行

docker-compose build

手順3: 起動

docker-compose up -d を実行

docker-compose up -d

WEBページを確認

確認1: http://localhost でphpinfo()が表示されていることを確認

確認2: phpmyadminにアクセス、ログインできることを確認

確認3: メール送信テストと確認

メール送信テスト

appコンテナにアクセスした後、mail送信のスクリプトを実行する

$ docker-compose exec app bash

root@f761b2f53458:/var/www# php -r "mail('test@example.com',
'テストタイトル', 'テスト本文', 'From: from@example.com');";

メール確認

http://localhost:8025/ にアクセス

確認4: デバッガーの確認 (VSCodeをエディタで使っている人のみ)

VScodeのデバッガーの設定

.vscode/launch.json
{
    // Use IntelliSense to learn about possible attributes.
    // Hover to view descriptions of existing attributes.
    // For more information, visit:
https://go.microsoft.com/fwlink/?linkid=830387
    "version": "0.2.0",
    "configurations": [

        {
            "name": "Listen for Xdebug",
            "type": "php",
            "request": "launch",
            "port": 9004,
            "pathMappings": {
                "/var/www": "${workspaceRoot}/www"
            }
        },
        {
            "name": "Launch currently open script",
            "type": "php",
            "request": "launch",
            "program": "${file}",
            "cwd": "${fileDirname}",
            "port": 9004
        }
    ]
}

ブレークポイントの設定

www/html/index.phpを以下のように編集した後、3行目にブレークポイントを設定して、Listen for Xdebugをクリック

index.php
<?php
$now = date('Y-m-d H:i:s');
phpinfo();

http://localhost にアクセス

ブレークポイントで停止して、$nowの情報が確認できる。

確認1~4を全て確認したら終わりです。

終わりに

今回は、LAMP環境を作成する最小構成のテンプレートを紹介いたしました。(ただし、MySQLではなくMariaDB)
htmlディレクトリの中身を別のアプリケーションで置換すれば、別のアプリケーションやフレームワークも動作させることができるので、汎用的な構成となっております。
また、apacheの代わりにnginxで動かすテンプレートの紹介やwordpressやlaravelなど作成する雛形も紹介していくので、続編にご期待を。

成果物

成果物をgithubにアップしましたので、いいねしてくださった方のみ、cloneを許可しますので、評価をよろしくお願いいたします。

https://github.com/napojin/php8.1-lamp

Discussion