2007年に執筆した『Smarty動的Webサイト構築入門』のサンプルコードリポジトリ(ninton/smartybook)を、現代のモダンな開発環境に合わせてリファクタリングする試み。
今回は第19回目として、長らく放置されていたレガシーな CentOS 7 + XAMPP内蔵環境 の docker-compose.yml を刷新し、公式イメージベースのモダンな構成へと近代化したプルリクエストを取り上げます。
修正前:レガシーなXAMPP構成の課題
これまでの環境は、ローカルビルドを行う Dockerfile(CentOS 7ベース)と、XAMPP(Lampp)をコンテナ内で動作させる構成でした。
version: '2'
services:
centos7_php74:
tty: true
container_name: centos7.smartybook.docker
build: centos7_php74
ports:
- 5402:80
- 5403:3306
volumes:
- .:/opt/lampp/htdocs:cached
- ./xampp-linux/lampp/logs:/opt/lampp/logs:cached
この構成には、次のようなレガシー開発環境特有の課題がありました。
- サポート終了OSへの依存: すでにサポートが終了したCentOS 7や古いXAMPPパッケージをビルドし続けるセキュリティリスク。
- 重いイメージとビルド時間: 独自のLampp環境をコンテナ内に構築するため、容量が大きくビルドに時間がかかる。
- ポート競合: 固定のホストポート(5402, 5403)や固定のコンテナ名 (
centos7.smartybook.docker) がハードコードされており、複数プロジェクトの並行開発で確実に競合する。 - データ永続化の不確実性: ボリュームの扱いにより、ホスト側のパーミッション問題やデータ管理の煩雑さが発生する。
修正後:モダンな公式イメージ構成への移行
今回のリファクタリングで、構成ファイルを docker/ ディレクトリ配下に整理し、公式イメージ(MariaDB, phpMyAdmin)と最新のDocker Composeのベストプラクティスを導入しました。
docker/
├── mariadb-db
│ ├── init
│ │ └── db_init.sql
│ └── my.cnf
└── php-web
├── Dockerfile
├── entrypoint.sh
└── php_ini.sed
実際の docker-compose.yml の全文は次の通りです。
services:
php-web:
tty: true
build:
context: docker/php-web
# ホスト側ポートは環境変数APP_PORTで指定(未定義の場合は3000)
ports:
- target: 80
published: ${APP_PORT:-3000}
host_ip: 127.0.0.1
protocol: tcp
environment:
XDEBUG_MODE: ${XDEBUG_MODE:-off}
XDEBUG_CONFIG: "client_host=${XDEBUG_CLIENT_HOST:-host.docker.internal} client_port=${XDEBUG_PORT:-9003}"
volumes:
- .:/var/www/html:cached
extra_hosts:
# Xdebug の接続先(xdebug.client_host=host.docker.internal)として使用
- "host.docker.internal:host-gateway"
depends_on:
- mariadb-db
mariadb-db:
image: mariadb:13.0
# ホストへは公開しない(ports 未設定)。DB操作は Docker ネットワーク経由で実施する。
environment:
MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD:-password}
MYSQL_DATABASE: ${DB_DATABASE:-smartybook}
MYSQL_USER: ${DB_USER:-user}
MYSQL_PASSWORD: ${DB_PASSWORD:-password}
volumes:
# 永続化 ホストのパスではなく、ボリューム名(db-data)を指定
- db_data:/var/lib/mysql
# 初期化スクリプト(読み取り専用:安全)
- ./docker/mariadb-db/init:/docker-entrypoint-initdb.d
# 設定ファイル(読み取り専用:確実)
- ./docker/mariadb-db/my.cnf:/etc/mysql/conf.d/my.cnf:ro
pma:
image: phpmyadmin:5.2-apache
environment:
PMA_HOST: mariadb-db
PMA_ARBITRARY: 0
depends_on:
- mariadb-db
# ホスト側ポートは環境変数 PMA_PORT(未指定時は 3001)を使用。
ports:
- target: 80
published: ${PMA_PORT:-3001}
host_ip: 127.0.0.1
protocol: tcp
volumes:
db_data:
サービス名の選定に関する設計判断
今回の設計でこだわったポイントの一つが サービス名(php-web, mariadb-db, pma)の命名 です。設計時に検討した4つの案と、最終的に採用した理由を共有します。
- 案1:
app,db(ボツ) - 一般的でシンプルです。しかし
appはトップレベルドメイン(TLD)であり、HSTS(HTTP Strict Transport Security)の対象です。将来的にSeleniumなどのブラウザコンテナからhttp://app/でアクセスした際、ブラウザが勝手にhttps://app/へ書き換えてSSLエラーを引き起こすリスクがあるため避けるべきと判断しました(過去に他プロジェクトでハマった経験があります)。 - 案2:
php-web,mariadb-db(採用) - 使用している技術スタックとコンテナの役割が明確です。
mariadb-dbは製品名と役割名が重複しますが、左側が製品名(mariadb)、右側が役割(db)なので許容範囲内。CLIでdocker compose run --rm php-web composer xxxと実行する際にも非常に自然です。また、ハイフン(-)を含んでいるため、将来TLD化するリスクも防げます。 - 案3:
my-web,my-db(次点) - どのプロジェクトでもこの命名規則に揃えられるメリットはありますが、表現力の面で
webと大きな違いがないため採用を見送りました。 - 案4:
smartybook-web,smartybook-db(ボツ) - リポジトリ名やプロジェクト名をサービス名に含める案です。しかし、CLIで叩くときにプロジェクトごとにサービス名が変わると認知負荷が高くなります。また、「The Twelve Factor App」の原則に従うなら、たとえ個人プロジェクトであってもコードや設定にプロジェクト固有の固有名詞はなるべく入れないほうがクリーンです。
近代化による具体的なメリット
1. 開発環境の柔軟性と競合防止の向上
- ポートの環境変数化 (
${APP_PORT:-3000}/${PMA_PORT:-3001}) と「競合管理」のパラダイムシフト- これまでは他プロジェクトとの競合を防ぐために
5402や5403といった独自ポートをdocker-compose.ymlに直接ハードコードしていました。 - しかし、この方式だと「コンテナを動かしていない状態であっても、Git管理しているすべてのプロジェクト間でポートが重複しないように割り当てを管理し続ける必要がある」という認知負荷と管理コストが発生します。
- 一方で、ポートを環境変数(
.env)に逃がしたモダンな構成では、「いま実際にローカルで同時に起動しているコンテナの間だけで競合しないように、必要に応じて3002や3010などに一時変更すればよい」という状態になります。静的な大域的管理から、動的な局所的調整への移行により、ポート番号の割り当て地獄から解放されます。 - 補足として、ローカル設定の退避先には
docker-compose.override.ymlを使うアプローチもありますが、overrideファイルは個人の環境差分や一時的なパッチが積み重なると、あっという間に「秘伝のタレ化(本人も全貌を把握できず、怖くて触れないファイル)」しがちです。今回は設定の「ノブ(変更可能なパラメータ)」のありかを.envに一本化する方法を選択しました。
- これまでは他プロジェクトとの競合を防ぐために
- 固定コンテナ名の廃止
container_nameの指定を削除し、Docker Composeのデフォルト命名規則に委ねることで、リポジトリの複数クローンやブランチ切り替え時に発生する「コンテナ名がすでに使われている」というエラーを回避できます。
2. データ永続性とセキュリティのモダン化
- 名前付きボリューム (
db_data:/var/lib/mysql) の採用: ホスト側の相対パスではなくDockerのボリューム管理に任せることで、ファイルパーミッション起因のトラブル(ホスト側でroot権限のファイルが生成されて削除できなくなる現象など)が解消されます。 - MariaDBの非公開化(ネットワーク分離):
mariadb-dbのports設定を廃止し、ホスト側へ直接3306ポートを露出させない設計にしました。DB操作はすべてコンテナ間ネットワーク経由に限定され、セキュリティが向上しています。
3. 初期構築の完全自動化
/docker-entrypoint-initdb.d機構の活用: コンテナの初回起動時にinit/配下のdb_init.sqlが自動的に流し込まれます。リポジトリをクローンしてdocker compose up -dを叩くだけで、手動のダンプインポート作業なしに即座に動作確認が可能な状態が整います。
4. 開発体験(DX)とデバッグ性能の強化
- Xdebugの環境変数制御:
XDEBUG_MODE=offをデフォルトにし、デバッグが必要なときだけ有効化することで、不要なパフォーマンス低下を防ぎます。また、host.docker.internalとhost-gatewayの組み合わせにより、クロスプラットフォームで安定したPhpStorm等のリモートデバッグが可能になりました。 - 公式イメージへの移行: メンテナンスされた公式の
mariadb:13.0やphpmyadmin:5.2-apacheを利用することで、脆弱性対応やパッケージ追加がスムーズに行えるようになっています。Renovate の自動更新対象です。
おわりに:めったに触らないからこそ、.env に集約する安心感
今回は、長年放置されていたレガシーなCentOS 7 + XAMPP構成の docker-compose.yml を、公式イメージベースのモダンな構成へと刷新したリフィクタリングについてご紹介しました。
ところで、こうした設定を .env に逃がしているとき、ふと「これってAIの助けを借りながら環境変数やプレースホルダーをガチャガチャ設定していて、なんだか回りくどいことをしているな…?」と感じる瞬間があります。正直、ポートやDBの接続情報なんて一度動いてしまえばめったに触らないものです。「セキュリティ的に問題ない環境のローカル開発なのだから、いっそハードコードでもいいのでは?」という誘惑にも駆られます。
それでも、あえて設定を .env(および .env.example)に集約する意義はどこにあるのでしょうか。
実際に運用してみて感じるのは、「めったに触らないからこそ、 .env を見れば自分のローカル環境の全貌がひと目でわかるという安心感」の大きさです。
あちこちに散らばったり、YAMLの中に直接ハードコードされたりしていると、「あれ、今回のプロジェクトのポート番号は何番に退避したんだっけ?」と探すハメになります。しかし、設定の「ノブ」が .env という一箇所に綺麗に整理されていれば、久しぶりにリポジトリを開いたときでも迷うことなく全体像を把握できます。
「めったに触らないからこそ、迷わないための仕組みを整えておく」。レガシーな環境からの脱却は、こうした小さな「認知負荷の軽減」の積み重ねなのだと改めて実感しています。

