(2025.7.17 記)
以下の理由で、「仮想環境で開発環境を構築よう」、と思い立ってやってみた作業メモになります。
<仮想環境を使うメリット>
・本番環境、本稼働サーバからの独立性
→高負荷作業や、環境、モジュールの変更といった作業や実験を本番環境から分離できるため、本番環境に影響を与えない
・環境の再現性
→仮想環境の定義ファイルを共有することで、環境を統一化できる
仮想環境の中でもDockerを選んだのは、単純に利用者が多そうだからという理由で、深い意味はないです。
強いて言えば、クロスプラットフォームの開発環境を目指したので、Hyper-V等のWindows系は避けました。
この記事について・・・
Dockerについては右も左もわからない状態だったので、AIに聞きながら構築しましたが、色々説明が抜けている部分が多かったので、それを補完しながら起動確認ができた状態の内容を載せいています。
<注意点>
この記事は、WEBサーバとAPサーバ(ここで言うApache2とPHP)を混合した構成例で記載しています。
最近ではWEBサービスとAPサービスを分ける構成がベストプラクティスのようですが、従来のApache2に全部任せるパターンで本番環境を構成している場合は、うまくプログラムが動かない場合が多いです。
分離する利点は、「Nginx」「Apache」等のWEBサービスを切り替えても「PHP」モジュールに影響を与えない(WEBサービスの変更が容易にできる)、という点のようです。
WEBサーバ-APサーバ分離パターンはこちらの別記事にしているので、そちらを参照ください。
所要時間:初めて触って約40時間(約5日)
0.実行環境
■環境環境
・LinuxOS:Ubuntu24.04LTS
・Apache:2.4
・MySQL:8.0
・PHP:7.4
■開発環境構築に利用した環境
・Windows11(24H2)
1.Dockerインストールの前準備
Docker DesktopはWSL 2 (Windows Subsystem for Linux 2) を利用するため、Windows 10 64-bit: Pro 21H1 (Build 19043) 以上、または Enterprise/Education 21H1 (Build 19043) 以上が必要です。
まず、上記が環境構築に必須な条件ですが、Windows11であれば全て補っています。
次に、WSL 2が有効になっていることを確認してください。(WSL 2が有効になっていない場合は、コマンドプロンプトを管理者として実行し、wsl –installを実行してインストールできます。↓)
wsl --install

インストールが終わると、下図の画面が開きます。閉じて大丈夫です。(DockerDesktopを利用するので、この画面を直接使う機会はほぼないです。)↓

次にWSLの規定バージョンを設定します。↓
wsl --set-default-version 2

次にWindows StorよりLinuxカーネルをインストールしていきます。(Linuxの種類とバージョンは本番環境に合わせました。)↓

「インストールされました」というトースト1が出るので、それをクリックすると初期設定コンソールが開きます。↓

この画面でユーザ名やパスワードの初期設定を行います。(このLinux環境をベースに環境が構築されていくので、本番環境に合わせた方がいいと思います。)
設定ができ、ログイン状態になれば、画面は閉じてしまってOKです。
以上。
2.Dockerインストール
まずはこちらのサイトから、Docker Desctop のアプリケーションをダウンロードし、インストールします。

ちなみにこのサイトにインストール手順も記載されているので、そのままインストールすると良いです。
最初のConfigurationは全てにチェックを入れておきました。↓

インストールが終わるとサブスクリプションの注意書きが出るので「Accept」しておきます(大企業でもなければ条件を満たさないので。)↓

Docker Desktopも直接利用はあまりしません。
環境構築時に、UIで仮想環境上のサーバに潜って調査する、みたいな使い方が便利なので、そのくらいですかね。
以上。
3.Docker定義ファイルの作成
以下のようなディレクトリ構造のプロジェクトフォルダを作成します。
下記は、ルートディレクトリ名を仮に「test」として記載しています。
test/ (ルートディレクトリ)
├---- docker-compose.yml (Dockerの定義ファイル)
|
├---- apache/ (Apache2の定義ディレクトリ)
| ├---- httpd.conf (Apache2の定義ファイル)
| ├---- php.ini (phpの設定ファイル)
| ∟--- Dockerfile (追加インストーラ等のビルドファイル ※拡張子無し)
|
├---- db/ (DBの定義ディレクトリ)
| ∟--- test.sql (DBのダンプファイル)
|
├---- php-app/ (WEBページのルートディレクトリのバインド先)
| ├---- html/ (ソースディレクトリ、この直下にWEBページのソースを格納)
| ├---- sessions/ (バインド後のセッションデータ格納ディレクトリ)
| ∟--- index.php (WEBページを開いた際に最初に表示されるページ)
|
∟--- sessions/ (セッションデータのバインドディレクトリ)
このようなディレクトリ構造を作成しましたら、各々必要な定義ファイルを作成していきます。
3-1.docker-compose.yml
Dockerのメイン定義ファイルになります。
ここに書いた内容によって、他のディレクトリに格納してある定義ファイルを間接的に読みに行ったり設定したりします。
私は次ように記載しました。
version: '3.8'
services:
# Apache2サービス
web:
build: ./apache
volumes:
# アプリケーションコードをマウント
- ./php-app:/var/www/html:delegated
# ★Apache設定ファイルのパスを確認
- ./apache/httpd.conf:/etc/apache2/apache2.conf:ro
# ★php.iniもApacheコンテナ内にマウント
- ./apache/php.ini:/usr/local/etc/php/php.ini
# セッションディレクトリのマウント
- ./sessions:/var/www/html/sessions
ports:
# ホストの80番ポートをコンテナの80番ポートにマッピング
- "8081:80"
networks:
- app-network
# MySQLサービス
db:
# MySQL 8.0イメージを使用
image: mysql:8.0
environment:
# ルートパスワード (必ず変更、[]ごと書き換える)
MYSQL_ROOT_PASSWORD: [ここにDBのルートパスワード]
# データベース名 (必ず変更、[]ごと書き換える)
MYSQL_DATABASE: [ここにDB名]
# ユーザー名 (必ず変更、[]ごと書き換える)
MYSQL_USER: [ここにユーザ名]
# パスワード (必ず変更、[]ごと書き換える)
MYSQL_PASSWORD: [ここにユーザのパスワード]
volumes:
- db_data:/var/lib/mysql
# 初期SQLファイルのバインド
- ./db/test.sql:/docker-entrypoint-initdb.d/test.sql:ro
networks:
- app-network
networks:
# サービス間通信用のカスタムネットワーク
app-network:
volumes:
# データベースのデータを永続化するためのボリューム
db_data:
肝になるのが、〇〇サービスと書かれている部分です。
これがDockerで起動した際に表示されるサービス名になり、他の定義ファイルや設定ファイルで参照する名前になるので注意です。
ここで言うと、「web」「db」です。
私がホスト側ポートを「8081」にしているのは、所謂「80」ポートを別サービスで利用しているからなので、色々サービスを起動していなければ「80:80」と素直にポートマッピングしてもOKです。
3-2.httpd.conf
Apache2の定義ファイルになります。
私は次のように記載しました。
#apacheの定義ディレクトリ(Ubuntuだとここ)
ServerRoot "/etc/apache2"
Listen 80
ServerName localhost:80
User www-data
Group www-data
# 必要なモジュールをロード
#LoadModule mpm_event_module /usr/lib/apache2/modules/mod_mpm_event.so
LoadModule dir_module /usr/lib/apache2/modules/mod_dir.so
LoadModule mime_module /usr/lib/apache2/modules/mod_mime.so
#LoadModule log_config_module /usr/lib/apache2/modules/mod_log_config.so
LoadModule setenvif_module /usr/lib/apache2/modules/mod_setenvif.so
#LoadModule proxy_module /usr/lib/apache2/modules/mod_proxy.so
#LoadModule proxy_fcgi_module /usr/lib/apache2/modules/mod_proxy_fcgi.so
#LoadModule unixd_module /usr/lib/apache2/modules/mod_unixd.so
LoadModule authz_core_module /usr/lib/apache2/modules/mod_authz_core.so
#LoadModule watchdog_module /usr/lib/apache2/modules/mod_watchdog.so
LoadModule http2_module /usr/lib/apache2/modules/mod_http2.so
#LoadModule logio_module /usr/lib/apache2/modules/mod_logio.so
#LoadModule version_module /usr/lib/apache2/modules/mod_version.so
LoadModule access_compat_module /usr/lib/apache2/modules/mod_access_compat.so
LoadModule alias_module /usr/lib/apache2/modules/mod_alias.so
LoadModule auth_basic_module /usr/lib/apache2/modules/mod_auth_basic.so
LoadModule authn_core_module /usr/lib/apache2/modules/mod_authn_core.so
LoadModule authn_file_module /usr/lib/apache2/modules/mod_authn_file.so
LoadModule authz_host_module /usr/lib/apache2/modules/mod_authz_host.so
LoadModule authz_user_module /usr/lib/apache2/modules/mod_authz_user.so
LoadModule autoindex_module /usr/lib/apache2/modules/mod_autoindex.so
LoadModule deflate_module /usr/lib/apache2/modules/mod_deflate.so
LoadModule env_module /usr/lib/apache2/modules/mod_env.so
LoadModule filter_module /usr/lib/apache2/modules/mod_filter.so
LoadModule mpm_prefork_module /usr/lib/apache2/modules/mod_mpm_prefork.so
LoadModule negotiation_module /usr/lib/apache2/modules/mod_negotiation.so
LoadModule reqtimeout_module /usr/lib/apache2/modules/mod_reqtimeout.so
LoadModule socache_shmcb_module /usr/lib/apache2/modules/mod_socache_shmcb.so
LoadModule status_module /usr/lib/apache2/modules/mod_status.so
#LoadModule core_module /usr/lib/apache2/modules/mod_core.so
#LoadModule so_module /usr/lib/apache2/modules/mod_so.so
LoadModule php7_module /usr/lib/apache2/modules/libphp7.so
Timeout 21600
KeepAlive On
MaxKeepAliveRequests 100
KeepAliveTimeout 5
TypesConfig /etc/mime.types
# PHPファイルのハンドラ設定
<FilesMatch \.php$>
SetHandler application/x-httpd-php
</FilesMatch>
# デフォルトのディレクトリインデックスにindex.phpを追加
<IfModule dir_module>
DirectoryIndex index.php index.html
</IfModule>
# ドキュメントルート設定
# ★Dockerコンテナ内のパス
DocumentRoot "/var/www/html"
<Directory "/var/www/html">
#Options Indexes FollowSymLinks
Options FollowSymLinks
AllowOverride All
Require all granted
</Directory>
ErrorLog "/proc/self/fd/2"
#LogLevel warn
LogLevel debug
<IfModule log_config_module>
LogFormat "%h %l %u %t \"%r\" %>s %b" common
CustomLog "/proc/self/fd/1" common
</IfModule>
リッスンポートの変更に関して、Dockerの定義ファイルの修正も必要になってくるので注意が必要です。
ロードモジュールについて、本番環境と合わせる場合は、本番環境で次のコマンドを入れると、実際に動いているモジュールの確認ができます。
apache2ctl -M

sslは利用しないので省いても大丈夫ですが、それ以外は合わせておくと良いでしょう。
3-3.php.ini
所謂phpの設定ファイルですね。
私は次のように記載しました。
; apache/php.ini
; セッションファイルの保存パスを設定
session.save_path = "/var/www/html/sessions"
; エラー表示設定 (Warning表示の件も兼ねてここにまとめる)
display_errors = Off
display_startup_errors = Off
error_reporting = E_ALL & ~E_NOTICE & ~E_DEPRECATED & ~E_STRICT
log_errors = On
error_log = /var/log/php_errors.log
;ここから下に実際のphp.iniの中身を記載
ここでは、セッションデータの格納場所と、エラーログの表示設定、ログの格納場所について設定しています。
もっと個別に細かく制御が必要な場合はここに記載していくことになります。
「php.ini」はUbuntuだと「/etc/php/(バージョン)/apache2/」の中にあります。
3-3.Dockerfile
Docker起動時にビルドされる内容が書かれたファイルになります。
例えばDocker起動時にディレクトリを作成したり、アプリケーションをインストールしたりなど、起動時に必要な環境を構築するのに利用されます。
次のように記載しました。
# apache/Dockerfile
# PHPが組み込まれたApacheイメージをベースにするのが最も手軽
FROM php:7.4-apache
WORKDIR /var/www/html
ENV TZ Asia/Tokyo
# 必要なApacheモジュールを有効化(phpモジュールは自動で有効化されることが多い)
# PHPのバージョンに合わせる (Debian/Ubuntuベースの場合)
#RUN a2enmod php7.4
# URL書き換えなどが必要な場合
#a2enmod rewrite
# 必要なPHP拡張機能をインストール(例: pdo_mysql, mbstring, zipなど)
# php:*-apache イメージはphp:*-fpmと同様にdocker-php-ext-installが使える
RUN apt update
RUN apt upgrade -y
RUN apt install -y apt-utils
#RUN a2enmod rewrite
RUN apt install -y libmcrypt-dev
RUN apt install -y libzip-dev
RUN apt install -y libonig-dev
RUN apt install -y libssl-dev
#RUN docker-php-ext-install mcrypt
RUN apt install -y libicu-dev
RUN docker-php-ext-install -j$(nproc) intl
RUN apt install -y libxml2-dev
RUN apt install -y curl
#RUN apt install -y libcurl4
RUN apt install -y libcurl4-openssl-dev
RUN apt install -y default-mysql-client
RUN docker-php-ext-install mbstring
RUN docker-php-ext-install zip
RUN docker-php-ext-install xml
RUN docker-php-ext-install xmlrpc
RUN docker-php-ext-install curl
RUN docker-php-ext-install soap
RUN docker-php-ext-install ftp
RUN docker-php-ext-install session
RUN docker-php-ext-install intl
RUN docker-php-ext-install pdo_mysql
RUN docker-php-ext-install opcache
# Apacheの設定ファイルをコピー
# Debian/Ubuntu系のパス
#COPY ./apache/httpd.conf /etc/apache2/apache2.conf
# アプリケーションコードをマウント
# ボリュームとしてマウントするため、DockerfileでのCOPYは不要(開発時)
# COPY ./php-app /var/www/html
# セッション保存ディレクトリを作成(ホストからマウントしない場合)
# RUN mkdir -p /var/www/html/sessions && chmod 777 /var/www/html/sessions
# Apacheがフォアグラウンドで実行されるようにする
# FROM php:*-apacheイメージは通常これをデフォルトで持っている
CMD ["apache2-foreground"]
このファイルを使う利点としては、モジュールの変更を簡単に行えるという事です。
今回は主にPHPで利用する、初期状態では足りないモジュールを追加でダウンロード&インストールするといった内容を書いています。
上記は自分が実環境に入れたモジュールになっています。
特にDBを利用する際はPHPとDBを繋ぐモジュールを入れないとDB接続できないので、最低でもpdo等のモジュールは必須となります。
3-5.index.php
これはあってもなくても良いですが、Dockerを起動してWEBサーバのルートに行った際に、サービスが正常に動いているかどうかを確認するため、という意図で作ったものです。
すべてのサービスが上手く動いていれば、欲しい情報が全て表示され、エラー時にはエラーコードや内容が表示されるので、サービス稼働状況の切り分けに使っています。
<?php
session_start();
// defineの値は環境によって変えてください。
$get_host = "db"; //Docker定義ファイルのサービス名
$get_dbname = "DB名";
$get_charset = "utf8";
$get_dbuser = "ユーザ名";
$get_dbpass = "パスワード";
try {
/// DB接続を試みる
$connect_str = 'mysql:host='.$get_host.'; dbname='.$get_dbname.'; charset='.$get_charset;
$pdo = new PDO($connect_str , $get_dbuser, $get_dbpass);
$msg = "MySQL への接続確認が取れました。";
$sql = "SQL文";
$prepare = $pdo->prepare($sql);
$prepare->setFetchMode(PDO::FETCH_ASSOC);
$prepare->execute();
while($row = $prepare->fetch()){
$msg = $msg."(".$row['何かしらの項目'].")";
}
$msg = $msg."(sessionstat:".session_status().")";
} catch (PDOException $e) {
$isConnect = false;
$msg = "MySQL への接続に失敗しました。<br>(" . $e->getMessage() . ")";
}
?>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>MySQL接続確認</title>
</head>
<body>
<h1>MySQL接続確認</h1>
<p><?php echo $msg; ?></p>
</body>
</html>
そもそも表示すらされない場合は、webかphpのどちらかのサービスに異常があります。
Docker Desktop上からログを確認し、AIなどに聞いてみるといいでしょう。
4.起動方法
プロジェクトファイルのルートに行き、右クリック→「ターミナルで開く」としコマンド入力画面を開きます。
まずはビルド情報をビルドします。
docker compose build --no-cache php

これはphpサービスにしかビルド情報を作っていないので、phpとだけしています。
他のサービスでも作成しているときは、それも追記してビルドします。
キャッシュを読み込むと次回以降の起動が早くなりますが、モジュールのバージョン等の兼ね合いで初回だけはキャッシュを使わないビルドをお勧めします。

上記のようにビルドが完了したら、次のコマンドでDockerを起動します。
docker compose up -d

あとは設定したWEBサイトにアクセスすればWEBページが開くと思います。(上記例だと、「localhost:8081」)
サービスが正常動作しているときは全て✅になっているはずです。
それでもアクセスできない場合は、何かのモジュールが足りないか、定義や設定がズレているかです。
同時に起動するDockerDesktopからも確認可能です。↓

サービス名をクリックすると、ログやディレクトリ情報等が確認できます。↓
正常にバインドされているか、必要なモジュールがあるか等UIで確認できるので楽です。



ちなみに、Dockerを終了する際は次のコマンドで行います。
docker compose down

これらはバッチを組んであげると親切かもしれませんね。
①起動前ビルドバッチ、②起動バッチ、③終了バッチ、みたく。
以上です。
5.最後に
サービスの起動状況はDockerDesktopを使えばUIで確認できます。
コマンドからも確認できますが、UIを使えるなら使った方が早いでしょう。
このプロジェクトファイルごとGitHub上に上げることで、ソースファイルとDockerの定義ファイルの両方がバージョン管理できるのと、チーム内共有が可能になるという寸法です。
また、環境を弄って壊しても、またGitHubからクローンしてくれば元通りなので、気にすることが減ります。
新しい開発者が来たら、次の手順でチーム内と同じ環境で開発できることになります。
<仮想開発環境構築手順>
1.WSL2有効化 → Linuxカーネルのインストール → DockerDesktopインストール
2.GitHubからGitを使ってリポジトリをローカルにクローン
3.Dockerビルド → Docker起動
4.VSCodeでクローンしたリポジトリを作業用ディレクトリに指定 → Git拡張でバージョン管理
正確にはDockerDesktopをインストールしなくても起動ができますが、以下3点注意が必要です。
<DockerDesktopを利用しない場合>
1.上記手順のうち、WSL2有効化とLinuxカーネルのインストールまでは必要
2.インストールしたLinux内にログインし、次の2つのコンポネントをインストールする必要有
①Docker Engine
②Docker Compose
※今回はインストールの説明を省きますが、AIに聞くとすぐ教えてくれると思います
DockerDesktopを利用しないケースって「商用利用で条件満たすとサブスクに変わる」点だと思いますが、大企業でない限りこれを満たさないケースが多いと思います。(従業員251人以上、または売上11億以上)
ちなみにですが、DBのダンプデータの中身には気を付けてくださいね、個人情報的な意味合いで。
テスト用に作ったダミーデータを入れるか、ダンプだけ別場所に保管し、Docker起動時に入れ替える的なことで回避するといいかもしれません。
▼オススメ教本▼

- 右下から出てくるポップアップみたいなもの。 ↩︎
コメント