0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

PHP 8.4 + MySQL 8.4 + Apache の  Docker LAMP環境をWSL2で構築する

0
Last updated at Posted at 2026-03-04

環境・前提条件

  • 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 になれば起動成功です。

Dockerコンテナ起動.png


⑧ ブラウザで確認

URL 確認内容
http://localhost/ Apache起動確認
http://localhost:8082/ phpMyAdminログイン確認

Apachesrc/ が空の場合は Index of / が表示されます)
Apache.png

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


⑨ コマンドで詳細確認

# コンテナの状態確認
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.

ApacheMySQLプロセス起動確認.png

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/

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?