[win11]Dockerによる仮想環境構築方法(LAMP構成)WEBサーバ-APサーバ分離パターン

コンピュータ関連
この記事は約24分で読めます。

2025.7.4 

以下の理由で、「仮想環境で開発環境を構築よう」、と思い立ってやってみた作業メモになります。

<仮想環境を使うメリット>

・本番環境、本稼働サーバからの独立性

 →高負荷作業や、環境、モジュールの変更といった作業や実験を本番環境から分離できるため、本番環境に影響を与えない

・環境の再現性

 →仮想環境の定義ファイルを共有することで、環境を統一化できる

仮想環境の中でもDockerを選んだのは、単純に利用者が多そうだからという理由で、深い意味はないです。

強いて言えば、クロスプラットフォームの開発環境を目指したので、Hyper-V等のWindows系は避けました。

この記事について・・・

Dockerについては右も左もわからない状態だったので、AIに聞きながら構築しましたが、色々抜けている部分が多かったので、それを補完しながら起動確認ができた状態の内容を載せいています。

<注意点>

この記事は、WEBサーバとAPサーバ(ここで言うApache2とPHP)を分離した構成例で記載しています

最近ではこの構成がベストプラクティスのようですが、従来のApache2に全部任せるパターンで本番環境を構成している場合は、うまくプログラムが動かない場合が多いです。

分離する利点は、「Nginx」「Apache」等のWEBサービスを切り替えても「PHP」モジュールに影響を与えない(WEBサービスの変更が容易にできる)、という点のようです。

WEBサーバ-APサーバ混合パターンはこちらの別記事にしているので、そちらを参照ください。

 

所要時間:初めてだったので、まる5日(約40時間)かかりました

 

0.実行環境

■開発環境

LinuxOS:Ubuntu24.04LTS

Apache:2.4

MySQL:8.0

PHP:7.4

 

■開発環境構築に利用した環境

・Windows11(24H2)

・VSCode(1.101.2)

 

1.Dockerインストールの前準備

Docker DesktopWSL 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の設定ファイル)
| ∟--- php-fpm.conf (apache2からphp実行ファイルを渡すための定義ファイル)

├---- db/ (DBの定義ディレクトリ)
| ∟--- test.sql (DBのダンプファイル)

├---- php/ (phpの定義ディレクトリ)
| ∟--- Dockerfile (追加インストーラ等のビルドファイル ※拡張子無し)

├---- php-app/ (WEBページのルートディレクトリのバインド先)
| ├---- html/ (ソースディレクトリ、この直下にWEBページのソースを格納)
| ├---- sessions/ (バインド後のセッションデータ格納ディレクトリ)
| ∟--- index.php (WEBページを開いた際に最初に表示されるページ)

∟--- sessions/ (セッションデータのバインドディレクトリ)

このようなディレクトリ構造を作成しましたら、各々必要な定義ファイルを作成していきます。

 

3-1.docker-compose.yml

Dockerのメイン定義ファイルになります。

ここに書いた内容によって、他のディレクトリに格納してある定義ファイルを間接的に読みに行ったり設定したりします。

私は次ように記載しました。

version: '3.8'

services:
# Apache2サービス
web:
# 安定版のApache2イメージを使用
image: httpd:2.4
ports:
# ホストの8081番ポートをコンテナの80番ポートにマッピング
- "8081:80"
volumes:
# Apache2設定ファイルのバインド
- ./apache/httpd.conf:/usr/local/apache2/conf/httpd.conf:ro
# PHPアプリケーションコードのバインド
- ./php-app:/usr/local/apache2/htdocs:ro
depends_on:
# PHPサービスに依存
- php
networks:
- app-network

# PHP-FPMサービス
php:
# PHPのFPMイメージ (バージョンは適宜変更)
#image: php:7.4-fpm (詳細は./php/Dockerfileに記載)
build: ./php
volumes:
# PHPアプリケーションコードのバインド
- ./php-app:/var/www/html:delegated
# php.iniファイルのバインド
- ./apache/php.ini:/usr/local/etc/php/php.ini
# sessionファイルのバインド
- ./sessions:/var/www/html/sessions
# PHP-FPM設定ファイルのバインド
#- ./apache/php-fpm.conf:/usr/local/etc/php-fpm.d/www.conf:ro
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」「php」「db」です。

私がホスト側ポートを「8081」にしているのは、所謂「80」ポートを別サービスで利用しているからなので、色々サービスを起動していなければ「80:80」と素直にポートマッピングしてもOKです。

 

3-2.httpd.conf

Apache2の定義ファイルになります。

私は次のように記載しました。

#apacheの定義ディレクトリ(Ubuntuだとここ)
ServerRoot "/usr/local/apache2"

#コンテナ側のHTTPポート設定(ここを変更する際は、Dockerの定義ファイルも変更すること)
Listen 80
ServerName localhost:80

#ユーザとグループ設定
User www-data
Group www-data

# 必要なモジュールをロード
LoadModule mpm_event_module modules/mod_mpm_event.so
LoadModule dir_module modules/mod_dir.so
LoadModule mime_module modules/mod_mime.so
LoadModule log_config_module modules/mod_log_config.so
LoadModule setenvif_module modules/mod_setenvif.so
LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_fcgi_module modules/mod_proxy_fcgi.so
LoadModule unixd_module modules/mod_unixd.so
LoadModule authz_core_module modules/mod_authz_core.so

<Directory />
AllowOverride None
Require all denied
</Directory>

DocumentRoot "/usr/local/apache2/htdocs"
<Directory "/usr/local/apache2/htdocs">
Options Indexes FollowSymLinks
AllowOverride None
Require all granted
</Directory>

#phpファイルはプロキシ転送(phpサービスに処理を任せる)
ProxyPassMatch "^/(.*\.php)$" "fcgi://php:9000/var/www/html/$1"

<IfModule dir_module>
DirectoryIndex index.php index.html
</IfModule>

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は利用しないので省いても大丈夫ですが、それ以外は合わせておくと良いでしょう。

(static) は初期で起動するモジュールなので、追加分は (shared) の分でいいと思います。

 

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の内容をそのままコピーするで問題ありません。(仮想環境中だと明示しないと動かなかったものだけ、ここに記載しました。)

php.ini」の場所は、Ubuntuだと「/etc/php/(バージョン)/apache2/」ディレクトリの中に入っています。

これはテストに出るので覚えておきましょう。(サーバ管理するならよく参照する)

 

3-4.php-fpm.conf

本来は「php.ini」がphpデータの処理を行うための設定ファイルになっていますが、本構成だとApache2(web)PHP(php)でサービスが分かれています。

ですので、こちらはphpデータを読み取った際に、webサービス→phpサービスへデータを転送する際に使用する定義を書くものとなっています。

俗にいう、WEBサーバとAPサーバが分かれている状態ですね。

次のように記載しました。

[www]
user = www-data
group = www-data
listen = 9000
listen.owner = www-data
listen.group = www-data
pm = dynamic
pm.max_children = 5
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3
chdir = /var/www/html

; セッションファイルの保存パスを設定
php_value[session.save_path] = /var/www/html/sessions

; 画面へのエラー表示をオフにする (最も重要!)
php_flag[display_errors] = off

; 起動時のエラー表示もオフにする (念のため)
php_flag[display_startup_errors] = off

; 警告(Warning)を含むすべてのエラーをログに記録するが、画面には表示しない設定
; E_ALL & ~E_NOTICE & ~E_DEPRECATED & ~E_STRICT は一般的な推奨設定
; もしWarningだけを特に消したい場合は E_ALL & ~E_WARNING も検討できる
php_value[error_reporting] = E_ALL & ~E_NOTICE & ~E_DEPRECATED & ~E_STRICT

; エラーログをファイルに出力する (非常に重要!オフにしてもログは残す)
php_flag[log_errors] = on
php_value[error_log] = /var/log/php_errors.log ; PHPエラーログの出力先を指定

webサービスは80番ポートで処理するのに対して、phpデータが来たらphpサービスの9000番ポートに転送してアプリケーション処理をしてもらう、みたいなことを定義したものです。

 

3-4.Dockerfile

Docker起動時にビルドされる内容が書かれたファイルになります。

例えばDocker起動時にディレクトリを作成したり、アプリケーションをインストールしたりなど、起動時に必要な環境を構築するのに利用されます。

次のように記載しました。

# php/Dockerfile
FROM php:7.4-fpm

WORKDIR /var/www/html

ENV TZ Asia/Tokyo

# セッション保存ディレクトリを作成し、適切な権限を設定
#RUN mkdir -p /var/www/html/sessions && chmod 777 /var/www/html/sessions

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

CMD ["php-fpm"]

このファイルを使う利点としては、モジュールの変更を簡単に行えるという事です。

今回は主に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起動時に入れ替える的なことで回避するといいかもしれません。

 

▼オススメ教本▼

  1. 右下から出てくるポップアップみたいなもの。 ↩︎

コメント

タイトルとURLをコピーしました