環境・前提条件
- Windows11 + WSL2 (Ubuntu)
- Docker Desktop インストール済み
Docker・WSL2の基本操作については下記の記事をご参照ください。
Webバックエンド初心者向け Dockerコマンド基礎反復練習(WSL2 + Docker Desktop)
ディレクトリ構成
~/projects/
├── docker-lab/
│ └── lamp/ # Docker環境(使いまわし可能)
│ ├── docker-compose.yml # コンテナ構成定義
│ ├── docker-compose.pma.yml # phpMyAdmin任意起動用
│ ├── Dockerfile # PHP+Apacheイメージ定義
│ ├── php.ini # PHP設定
│ ├── .env # 環境変数(Gitに含めない)
│ ├── .env.example # .envのテンプレート
│ ├── .gitignore
│ ├── apache/
│ │ └── vhost.conf # Apacheバーチャルホスト設定
│ └── mysql/
│ ├── conf.d/
│ │ └── my.cnf # MySQLチューニング設定
│ └── init/ # 初期化SQL(任意)
└── review-app/ # PHPアプリ本体(Gitリポジトリ)
各設定ファイルの作成
docker-compose.yml
services:
web:
build: .
container_name: lamp_web
ports:
- "127.0.0.1:80:80"
volumes:
- ../../review-app:/var/www/html
- ./apache/vhost.conf:/etc/apache2/sites-enabled/000-default.conf
depends_on:
db:
condition: service_healthy
networks:
- lamp_network
restart: unless-stopped
db:
image: mysql:8.4
container_name: lamp_db
expose:
- "3306"
volumes:
- db_data:/var/lib/mysql
- ./mysql/init:/docker-entrypoint-initdb.d
- ./mysql/conf.d/my.cnf:/etc/mysql/conf.d/my.cnf
- mysql_logs:/var/log/mysql
env_file:
- .env
environment:
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
MYSQL_DATABASE: ${MYSQL_DATABASE}
MYSQL_USER: ${MYSQL_USER}
MYSQL_PASSWORD: ${MYSQL_PASSWORD}
shm_size: "256mb"
ulimits:
nofile:
soft: 65536
hard: 65536
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-p${MYSQL_ROOT_PASSWORD}"]
interval: 10s
timeout: 5s
retries: 10
start_period: 30s
networks:
- lamp_network
restart: unless-stopped
phpmyadmin:
image: phpmyadmin:latest
container_name: lamp_phpmyadmin
ports:
- "127.0.0.1:8082:80" # localhostのみ公開(外部非公開)
env_file:
- .env
environment:
PMA_HOST: db
PMA_PORT: 3306
PMA_USER: ${PMA_USER}
PMA_PASSWORD: ${PMA_PASSWORD}
UPLOAD_LIMIT: 256M
MEMORY_LIMIT: 256M
MAX_EXECUTION_TIME: 300
depends_on:
db:
condition: service_healthy
networks:
- lamp_network
restart: unless-stopped
volumes:
db_data:
mysql_logs:
networks:
lamp_network:
driver: bridge
Docker Compose V2以降、先頭の version: は不要です。記載すると警告が出るため削除してください。
→ 詳細はハマりポイントを参照
Dockerfile
FROM php:8.4-apache
RUN apt-get update && apt-get install -y \
libpng-dev \
libjpeg-dev \
libfreetype6-dev \
libzip-dev \
libicu-dev \
libonig-dev \
&& docker-php-ext-configure gd --with-freetype --with-jpeg \
&& docker-php-ext-install \
pdo_mysql \
mysqli \
gd \
zip \
intl \
bcmath \
exif \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
RUN a2enmod rewrite headers
COPY php.ini /usr/local/etc/php/php.ini
WORKDIR /var/www/html
# 画像アップロードディレクトリの所有者設定
RUN chown -R www-data:www-data /var/www/html/images
/var/www/html/images は画像アップロードディレクトリです。www-data(Apacheの実行ユーザー)に書き込み権限を付与しています。
→ 詳細はハマりポイントを参照
php.ini
[PHP]
upload_max_filesize = 256M
post_max_size = 256M
memory_limit = 256M
max_execution_time = 300
max_input_time = 300
[Date]
date.timezone = Asia/Tokyo
[Output]
output_buffering = On
[Session]
session.cookie_httponly = 1
session.use_strict_mode = 1
session.cookie_samesite = Lax
[opcache]
opcache.enable = 1
opcache.memory_consumption = 128
opcache.max_accelerated_files = 10000
opcache.revalidate_freq = 2
my.cnf(MySQL 8.4の注意点)
[mysqld]
character-set-server = utf8mb4
collation-server = utf8mb4_unicode_ci
character-set-filesystem = utf8mb4
init_connect = 'SET NAMES utf8mb4'
max_allowed_packet = 256M
bulk_insert_buffer_size = 64M
net_read_timeout = 300
net_write_timeout = 300
wait_timeout = 600
interactive_timeout = 600
local_infile = ON
innodb_buffer_pool_size = 512M
innodb_buffer_pool_instances = 2
innodb_redo_log_capacity = 256M
innodb_flush_method = O_DIRECT
innodb_flush_log_at_trx_commit = 1
innodb_deadlock_detect = ON
innodb_read_io_threads = 4
innodb_write_io_threads = 4
innodb_autoinc_lock_mode = 2
innodb_stats_persistent = ON
slow_query_log = ON
slow_query_log_file = /var/log/mysql/slow.log
long_query_time = 1
log_queries_not_using_indexes = ON
table_open_cache = 4000
table_definition_cache = 2000
sort_buffer_size = 4M
join_buffer_size = 4M
max_connections = 150
thread_cache_size = 16
tmp_table_size = 64M
max_heap_table_size = 64M
sql_mode = STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION
log_error = /var/log/mysql/error.log
general_log = OFF
[client]
default-character-set = utf8mb4
[mysql]
default-character-set = utf8mb4
[mysqldump]
default-character-set = utf8mb4
max_allowed_packet = 256M
skip-character-set-client-handshake はMySQL 8.4で廃止済みです。記載するとMySQLが起動できません。
→ 詳細はハマりポイントを参照
apache/vhost.conf
<VirtualHost *:80>
DocumentRoot /var/www/html
<Directory /var/www/html>
AllowOverride All
Require all granted
Options -Indexes
</Directory>
# セキュリティヘッダー
Header always set X-Content-Type-Options "nosniff"
Header always set X-Frame-Options "SAMEORIGIN"
Header always set X-XSS-Protection "1; mode=block"
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>
.env.example
MYSQL_ROOT_PASSWORD=
MYSQL_DATABASE=
MYSQL_USER=
MYSQL_PASSWORD=
PMA_USER=root
PMA_PASSWORD=
パスワードに $ などの特殊文字が含まれる場合はシングルクォートで囲んでください。
→ 詳細はハマりポイントを参照
作業手順
① Docker起動 / ② Ubuntu(WSL2)起動
Docker・WSL2の起動手順については下記の記事をご参照ください。
Webバックエンド初心者向け Dockerコマンド基礎反復練習(WSL2 + Docker Desktop)
③ ディレクトリ作成
ホームディレクトリにプロジェクトフォルダを作成します。
# 現在地確認・ホームへ移動
pwd
cd
ls -la
# ディレクトリ一括作成
mkdir -p ~/projects/docker-lab/lamp/apache \
~/projects/docker-lab/lamp/mysql/conf.d \
~/projects/docker-lab/lamp/mysql/init
④ ファイル配置
Windowsエクスプローラーのアドレスバーに以下を入力してWSL2のフォルダを開き、ファイルをドラッグ&ドロップします。
\\wsl$\Ubuntu\home\(ユーザー名)\projects\docker-lab\lamp\
配置するファイルは以下の通りです。
docker-lab/lamp/
├── docker-compose.yml ← version行削除済みのもの
├── docker-compose.pma.yml
├── Dockerfile
├── php.ini
├── .env.example
├── .gitignore
├── apache/
│ └── vhost.conf
└── mysql/
└── conf.d/
└── my.cnf ← skip-character-set-client-handshake削除済みのもの
⑤ Zone.Identifierファイル削除
Windowsからコピーしたファイルに付与される不要なメタデータファイルを削除します。
find ~/projects/docker-lab/lamp -name "*:Zone.Identifier" -delete
⑥ .env作成
.env.example をコピーして .env を作成し、自分の環境に合わせて値を設定します。
cp ~/projects/docker-lab/lamp/.env.example ~/projects/docker-lab/lamp/.env
nano ~/projects/docker-lab/lamp/.env
⑦ コンテナ起動
cd ~/projects/docker-lab/lamp
docker compose up -d
docker compose ps
全コンテナが Up になれば起動成功です。
⑧ ブラウザで確認
| URL | 確認内容 |
|---|---|
| http://localhost/ | Apache起動確認 |
| http://localhost:8082/ | phpMyAdminログイン確認 |
Apache(src/ が空の場合は Index of / が表示されます)

phpMyAdmin(起動直後はnginx画面が一瞬表示されますが、DBの起動待ちなので数秒後にリロードしてください)

⑨ コマンドで詳細確認
# コンテナの状態確認
docker compose ps
# MySQLの起動ログ確認
docker logs lamp_db
以下のログが出ていれば正常です。
[Note] [Entrypoint]: Creating database (DB名)
[Note] [Entrypoint]: Creating user (ユーザー名)
[Note] [Entrypoint]: MySQL init process done. Ready for start up.
Warning: Unable to load '/usr/share/zoneinfo/...' はタイムゾーンファイルの警告で動作に影響はありません。無視してOKです。
⑩ コンテナの停止・再開・削除
cd ~/projects/docker-lab/lamp
# 停止(DBデータ保持・再開可能)
docker compose stop
# 再開
docker compose start
# 停止+コンテナ削除(DBデータは保持)
docker compose down
# 停止+コンテナ+データ全削除(クリーンな再構築時)
docker compose down -v
| コマンド | コンテナ | DBデータ | 用途 |
|---|---|---|---|
stop |
停止 | 保持 | 作業終了時 |
start |
再開 | 保持 | 作業再開時 |
down |
削除 | 保持 | 設定変更時 |
down -v |
削除 | 削除 | クリーンな再構築時 |
down -v はDBのデータも全て削除されます。通常の作業終了時は stop を使用してください。
ハマりポイント
version: は不要(obsolete警告)
Docker Compose V2以降、version: の記載は不要になりました。記載すると以下のWARNが毎回表示されます。
the attribute `version` is obsolete, it will be ignored, please remove it to avoid potential confusion
対処: docker-compose.yml の先頭の version: 行を削除する。
# NG
version: "3.9"
services:
...
# OK
services:
...
skip-character-set-client-handshake は MySQL 8.4 で廃止済み
my.cnf にこのオプションを記載すると、MySQLが起動直後にクラッシュして無限再起動ループに陥ります。
[ERROR] [MY-000068] unknown option '--skip-character-set-client-handshake'
対処: my.cnf から該当行を削除する。MySQL 8.4では character-set-server = utf8mb4 のみで文字コードはutf8mb4に統一されます。
# NG
character-set-server = utf8mb4
skip-character-set-client-handshake ← この行を削除
# OK
character-set-server = utf8mb4
.env のパスワード特殊文字はシングルクォート必須
パスワードに $ などの特殊文字が含まれる場合、ダブルクォートや素のままでは環境変数として誤解釈されます。
# シェルで特別な意味を持つ主な特殊文字
# $ ... 変数展開 例)$HOME → /home/user に展開される
# ` ... コマンド置換
# ! ... ヒストリ展開(bash)
# # ... コメント扱い
# NG(特殊文字がシェルに解釈されてしまう)
MYSQL_PASSWORD="Sg7$Xp4#Zv9@Rt6H"
MYSQL_PASSWORD=Sg7$Xp4#Zv9@Rt6H
# OK(シングルクォートで囲むと全て文字列として扱われる)
MYSQL_PASSWORD='Sg7$Xp4#Zv9@Rt6H'
output_buffering が無効でheader()エラーが発生する
XAMPPやXServerでは output_buffering がデフォルトで有効なため、header() の前に出力があっても動作します。しかしDockerのPHPはデフォルトで無効のため、以下のエラーが発生します。
Warning: Cannot modify header information - headers already sent by (output started at ...)
以下のコマンドで確認できます。
docker exec -it lamp_web php -i | grep output_buffering
# output_buffering => 0 => 0 ← 無効になっている
対処: ビルド前に php.ini に以下を記述しておく。
[Output]
output_buffering = On
php.ini を変更した場合は docker compose restart では反映されません。必ず docker compose up -d --build web で再ビルドしてください。
DB接続ホスト名は localhost ではなく db
XAMPPではApacheとMySQLが同じPC上で動作するため localhost で接続できます。しかしDockerでは別々のコンテナで動作するため、localhost はコンテナ自身を指してしまいMySQLに繋がりません。
| 環境 | ホスト名 | 理由 |
|---|---|---|
| XAMPP | localhost |
同じPC上で動作 |
| Docker | db |
別コンテナのサービス名で接続 |
対処: config/database.php のホスト名を docker-compose.yml のサービス名(db)に変更する。
// NG
'host' => 'localhost'
// OK
'host' => 'db'
.htaccess のリファラ制限でDocker環境から画像が403になる
本番URLのみリファラを許可している .htaccess がある場合、http://localhost からのアクセスが403になります。
# NG(本番URLのみ許可)
RewriteCond %{HTTP_REFERER} ^https://example.com [NC]
RewriteRule ^ - [L]
対処: OR 条件で localhost を追加する。
# OK(localhostも許可)
RewriteCond %{HTTP_REFERER} ^https://example.com [NC,OR]
RewriteCond %{HTTP_REFERER} ^http://localhost [NC]
RewriteRule ^ - [L]
2つの RewriteCond を並べる場合、デフォルトは AND 条件です。OR にするには1行目の末尾に ,OR を付ける必要があります。
画像アップロードディレクトリに www-data の書き込み権限が必要
Dockerでは Apache が www-data ユーザーで動作します。アップロード先ディレクトリの所有者がWSLユーザーのままだと、PHPから move_uploaded_file() が失敗して画像を保存できません。
# エラー例
保存先ディレクトリに書き込めません。
対処: Dockerfile に以下を追加してビルドする。
# 画像アップロードディレクトリの所有者設定
RUN chown -R www-data:www-data /var/www/html/images
docker exec lamp_web chown www-data:www-data /var/www/html/images でコンテナ内から直接変更することもできますが、コンテナを再ビルドすると元に戻ります。必ず Dockerfile に記載してください。
他のDocker環境とphpMyAdminのポートが競合する
複数のDocker開発環境を使っている場合、ホスト側ポートが重複するとコンテナ起動時に以下のエラーになります。
Bind for 0.0.0.0:8080 failed: port is already allocated
今回の環境では、Laravel Sail環境で localhost:8080 をLaravel本体に割り当てていたため、LAMP環境のphpMyAdminが同じポートを使おうとして競合しました。
対処: LAMP環境のphpMyAdminは 8082 で公開するように変更しています。
phpmyadmin:
ports:
- "127.0.0.1:8082:80"
127.0.0.1 にバインドしているため、localhostのみ公開され、外部には公開されません。セキュリティ上の方針はそのまま維持できます。
Laravel Sailなど別のDocker環境と併用する場合は、以下のようにポートを分けておくと安全です。
| 用途 | URL |
|---|---|
| Laravel本体 | http://localhost:8080 |
| Laravel phpMyAdmin | http://localhost:8081 |
| LAMP phpMyAdmin | http://localhost:8082 |
補足
この記事で使用しているDocker LAMP環境の構成ファイルはGitHubで公開しています。
この環境を使用して、映画レビュー投稿アプリの開発を行っています。
ポートフォリオ
https://portfolio.honda-dev.com/

