はじめに
この記事では、App / DB / Mail コンテナの開発環境の構築手順について記載します。
今回は Docker Compose を使わずに構築します。
※Docker Compose を使った構築手順については、こちらの記事に記載しています。
開発環境
開発環境は以下の通りです。
- Windows 11
- Docker Engine 26.1.1
- PHP 8.3
- MySQL 8.4.0
- Mailpit 1.18
要件を整理する
まず今回構築するコンテナの要件を整理します。
全体
- コンテナ停止時に自動削除
- コンテナはバックグラウンド実行
- 日本標準時で動作
- コンテナ間通信可能
App コンテナ app
- ブラウザでアクセス可能
- ソースコードはホストマシンで実装
DB コンテナ db
- デバッグ用にホストマシンから接続可能
- データはコンテナを削除しても残す
- 初期データを用意
Mail コンテナ mail
- ブラウザでアクセス可能
- メールデータはコンテナを削除しても残す
ディレクトリを準備する
今後 Dockerfile や各種設定ファイルなどを作成していくために以下の構成のディレクトリを準備します。
├─docker
│ ├─app
│ ├─db
│ └─mail
└─src
各コンテナのイメージを準備して、コンテナを起動する
次は各コンテナのイメージを準備して、コンテナを起動してみます。
App コンテナ app
ベースイメージはタグが 8.3 の PHP イメージを利用します。
また、MySQL サーバーとの接続やメール送信のための SMTP クライアント用に拡張モジュールが必要なため、/docker/app
ディレクトリに Dockerfile を作成し、ベースイメージを拡張・ビルドします。
FROM php:8.3
RUN docker-php-ext-install pdo_mysql
RUN apt-get update
RUN apt-get install -y msmtp-mta
COPY ./msmtprc /etc/msmtprc
CMD ["/usr/local/bin/php", "--server", "0.0.0.0:8000", "--docroot", "/my-work"]
-
RUN docker-php-ext-install pdo_mysql
: MySQL を利用するためのモジュールPDO_MYSQL
をインストール
-
RUN apt-get install -y msmtp-mta
: SMTP クライアントmsmtp
をインストール
-
COPY ./msmtprc /etc/msmtprc
: メールサーバーのホストなどを指定するための設定ファイルmsmtprc
をホストマシンの./msmtprc
からイメージの/etc/msmtprc
にコピー -
CMD ["/usr/local/bin/php", "--server", "0.0.0.0:8000", "--docroot", "/my-work"]
: コンテナ起動時、ビルドイン Web サーバーを起動-
--server
オプションで起動するアドレスとポートを指定 -
--docroot
オプションでドキュメントルートを指定
-
次は、上記の COPY
インストラクションでコピー元となる msmtprc
を/docker/app
ディレクトリに作成します。
host mail
port 1025
from "service@example.com"
timeout 5
-
host
: Mail コンテナ -
port
: Mail コンテナのポート -
from
: メールの送信元アドレス
port
の 1025
は、Mail イメージとして利用する Mailpit の SMTP サーバーのデフォルト値になります。1
必要なファイルが揃ったので、Dockerfile からイメージをビルドします。
docker image build --tag app:0.1.0 docker/app
イメージがビルドできたか確認します。
docker image ls app
ビルドしたイメージをもとに App コンテナを起動します。
docker container run `
--name app `
--rm `
--detach `
--publish 8000:8000 `
app:0.1.0 `
/usr/local/bin/php --server 0.0.0.0:8000 --docroot /
Dockerfile にドキュメントルートとして記載した /my-work
ディレクトリはまだ作成していないので、現時点では /
としています。
コンテナが起動できている場合、ブラウザで http://localhost:8000/ にアクセスすると、以下の画面が表示されます。
DB コンテナ db
ベースイメージはタグが 8.4.0
の MySQL イメージを利用します。
以下のコマンドでコンテナを起動します。
docker container run `
--name db `
--rm `
--detach `
--env MYSQL_ROOT_PASSWORD=password `
--env MYSQL_USER=app `
--env MYSQL_PASSWORD=password `
--env MYSQL_DATABASE=sample `
--env TZ=Asia/Tokyo `
--publish 3306:3306 `
mysql:8.4.0
コンテナが起動できている場合、ホストマシンから DB コンテナに接続できます。
mysql --host=127.0.0.1 --port=3306 --user=app --password=password sample
また、DB コンテナが日本標準時になっています。
select now();
Mail コンテナ mail
ベースイメージはタグが v1.18
の Mailpit イメージを利用します。
以下のコマンドでコンテナを起動します。
docker container run `
--name mail `
--rm `
--detach `
--env TZ=Asia/Tokyo `
--publish 8025:8025 `
axllent/mailpit:v1.18
Web UI にホストマシンのブラウザからアクセスできるようにコンテナの 8025
ポートをホストマシンの 8025
ポートにマッピングしています。8025
は Mailpit の Web UI のデフォルト値です。2
コンテナが起動できている場合、ブラウザで http://localhost:8025/ にアクセスすると、以下の画面が表示されます。
起動確認できたので、コンテナを全て停止します。
docker container stop app db mail
要件の達成状況を確認する
現時点での要件の達成状況を確認します。
全体
- ✅コンテナ停止時に自動削除
- ✅コンテナはバックグラウンド実行
- ✅日本標準時で動作
- コンテナ間通信可能
App コンテナ app
- ✅ブラウザでアクセス可能
- ソースコードはホストマシンで実装
DB コンテナ db
- ✅デバッグ用にホストマシンから接続可能
- データはコンテナを削除しても残す
- 初期データを用意
Mail コンテナ mail
- ✅ブラウザでアクセス可能
- メールデータはコンテナを削除しても残す
各コンテナで必要なリソースを準備する
残りの要件を満たすため、各コンテナで必要なリソースを準備します。
App コンテナ app
App コンテナで満たす必要がある残りの要件は、以下の2つです。
- コンテナ間通信可能
- ソースコードはホストマシンで実装
ネットワーク
コンテナ間通信のため、work-network
というネットワークを作成します。
docker network create work-network
作成できたか確認します。
docker network ls --filter name=work-network
コンテナ起動時のパラメーターに --network
オプションを追加し、作成した work-network
へ接続します。
- ✅コンテナ間通信可能
ソースコード
メールを送信するソースコードを実装します。
ホストマシンの src/index.php
にファイルを追加して実装します。
<?php
$users = [];
try {
// Connect to DB
$dsn = 'mysql:host=db;port=3306;dbname=sample';
$username = 'root';
$password = 'password';
$pdo = new PDO($dsn, $username, $password);
// Get users table
$statement = $pdo->query('select * from users');
$statement->execute();
while ($row = $statement->fetch()) {
$users[] = $row;
}
// Disconnect
$pdo = null;
} catch (PDOException $e) {
echo '<p>Database connection failed: ' . $e . '</p>';
}
// Display users information
foreach ($users as $user) {
echo '<p>id: ' . $user['id'] . ', name: ' . $user['name'] . '</p>';
}
// Send mail
$subject = 'Test Mail';
$message = 'Here is Docker Hub => https://hub.docker.com/';
foreach ($users as $user) {
$success = mb_send_mail($user['email'], $subject, $message);
if ($success) {
echo '<p>Mail sent to ' . $user['name'] . '</p>';
} else {
echo '<p>Mail sending failed</p>';
}
}
バインドマウント
ホストマシンに追加したソースコードをコンテナと共有するため、バインドマウントを利用します。
コンテナ起動時のパラメーターに --mount
オプションを追加します。オプションの <key>
/ <value>
は以下の通りです。
type=bind
source="$(pwd)" /src
target=/my-work
マウント先の /my-work
は、Dockerfile にドキュメントルートとして記載したディレクトリです。
- ✅ソースコードはホストマシンで実装
DB コンテナ
DB コンテナで満たす必要がある残りの要件は、以下の2つです。
- コンテナ間通信可能
- データはコンテナを削除しても残す
- 初期データを用意
ネットワーク
コンテナ間通信のため、App コンテナと同じネットワークに接続する必要があります。
コンテナ起動時のパラメーターに --network
オプションを追加し、work-network
へ接続します。
- ✅コンテナ間通信可能
ボリューム
データが消えないようにボリュームを利用します。
まず DB コンテナ用のボリュームを作成します。
docker volume create --name work-db-volume
次にコンテナ起動時のパラメーターへ --mount
オプションを追加し、work-db-volume
をマウントします。オプションの <key>
/ <value>
は以下の通りです。
type=volume
source=work-db-volume
target=/var/lib/mysql
マウント先の /var/lib/mysql
は、MySQL サーバーがデータを保存するデフォルトのディレクトリになります。MySQL 設定ファイル(/etc/my.cnf
)で定義されています。
docker container run --rm mysql:8.4.0 cat /etc/my.cnf
[mysqld]
...
datadir=/var/lib/mysql
...
- ✅データはコンテナを削除しても残す
初期データ
初期データとしてコンテナ起動時、users
テーブルを作成し、Wyatt と Billy を登録するようにします。
上記のクエリをコンテナ起動時に実行する SQL ファイルをホストマシンの /docker/init/init-users.sql
に作成します。
create table users(id int, name varchar(32), email varchar(32));
insert users (id, name, email) values (1, "Wyatt", "wyatt@example.com");
insert users (id, name, email) values (2, "Billy", "billy@example.com");
バインドマウント
ホストマシンに追加した SQL ファイルをコンテナと共有するため、バインドマウントを利用します。
コンテナ起動時のパラメーターに --mount
オプションを追加します。オプションの <key>
/ <value>
は以下の通りです。
type=bind
source="$(pwd)"/docker/db/init
target=/docker-entrypoint-initdb.d
マウント先の値 /docker-entrypoint-initdb.d
は、Docker Hub の MySQL リポジトリの Initializing a fresh instance に記載があります。3
- ✅初期データを用意
Mail コンテナ mail
Mail コンテナで満たす必要がある残りの要件は、以下の2つです。
- コンテナ間通信可能
- メールデータはコンテナを削除しても残す
ネットワーク
コンテナ間通信のため、App / DB コンテナと同じネットワークに接続する必要があります。
コンテナ起動時のパラメーターに --network
オプションを追加し、work-network
へ接続します。
- ✅コンテナ間通信可能
ボリューム
データが消えないようにボリュームを利用します。
まず Mail コンテナ用のボリュームを作成します。
docker volume create --name work-mail-volume
次にコンテナ起動時のパラメーターへ --mount
オプションを追加し、work-mail-volume
をマウントします。オプションの <key>
/ <value>
は以下の通りです。
type=volume
source=work-mail-volume
target=/data
マウント先の value
である /data
は、Docker Hub の Mailpit リポジトリの Setting Mailpit options のサンプルでデータの保存先を指定する環境変数 MP_DATABASE
と同じにしています。4
docker run -d \
--name=mailpit \
--restart unless-stopped \
-v /path/to/mailpit-data:/data \
-e MP_DATABASE=/data/mailpit.db \
-e MP_UI_AUTH_FILE=/data/authfile \
-e TZ=Europe/London \
-p 8025:8025 \
-p 1025:1025 \
axllent/mailpit
- ✅メールデータはコンテナを削除しても残す
コンテナを起動する
全ての要件を満たすことができたので、3つのコンテナを起動します。
App コンテナ app
docker container run `
--name app `
--rm `
--detach `
--publish 8000:8000 `
--mount type=bind,source="$(pwd)"/src,target=/my-work `
--network=work-network `
app:0.1.0 `
/usr/local/bin/php --server 0.0.0.0:8000 --docroot /my-work
コンテナの起動状況を確認します。
docker container ls
DB コンテナ db
docker container run `
--name db `
--rm `
--detach `
--env MYSQL_ROOT_PASSWORD=password `
--env MYSQL_USER=app `
--env MYSQL_PASSWORD=password `
--env MYSQL_DATABASE=sample `
--env TZ=Asia/Tokyo `
--publish 3306:3306 `
--mount type=volume,source=work-db-volume,target=/var/lib/mysql `
--mount type=bind,source="$(pwd)"/docker/db/init,target=/docker-entrypoint-initdb.d `
--network=work-network `
mysql:8.4.0
コンテナの起動状況を確認します。
docker container ls
Mail コンテナ mail
docker container run `
--name mail `
--rm `
--detach `
--env TZ=Asia/Tokyo `
--env MP_DATABASE=/data/mailpit.db `
--publish 8025:8025 `
--network=work-network `
--mount type=volume,source=work-mail-volume,target=/data `
axllent/mailpit:v1.18
コンテナの起動状況を確認します。
docker container ls
動作確認
ブラウザで動作確認をします。
App コンテナ( http://localhost:8000/ )にアクセスすると、以下の画面が表示されます。
ユーザー一覧と各ユーザーにメールを送信したことが確認できます。
Mail コンテナ( http://localhost:8025/ )にアクセスすると、以下の画面が表示されます。
2通のメールが確認できます。また、メールのタイトル、本文ともに実装通りの文言になっています。
所感
- Docker を利用した Web サービスの開発環境構築の全体像の理解度が向上した
- コンテナ一つひとつ管理するのは大変(Docker Compose 便利)5
参考
- Built-in web server
- msmtp
- MySQL Functions (PDO_MYSQL)
- mysql - Official Image
- php - Official Image
- Runtime options | Mailpit
- 鈴木亮「開発系エンジニアのためのDocker絵とき入門」秀和システム、2024
関連URL
-
https://mailpit.axllent.org/docs/configuration/runtime-options/ ↩
-
https://mailpit.axllent.org/docs/configuration/runtime-options/ ↩
-
環境をはじめて起動するために必要なコマンド実行は全部で7回になります(
docker image build
: 1、docker volume create
: 2、docker network create
: 1、docker container run
: 3)。また、docker container run
のパラメーターは、30 も指定しています。 ↩