Docker Composeで本番環境を運用する際の落とし穴と最適化
1.2GBのDockerイメージを本番環境へデプロイするたび、僕は心の中で「やめろ、もう回線をこれ以上消費するな」と祈っている。
この巨大なイメージが抱えるのは、単なるストレージの無駄ではない。デプロイ時間の増大、ネットワーク転送コストの肥大化、そして何よりビルドツールや不要なライブラリを含めることによる脆弱性の拡大だ。コンテナは軽量であるべきだという鉄則を無視した結果、本番環境で「デプロイ待ち」という名の無駄な時間が積み上がっていく。
ここでは、泥臭い試行錯誤の末に辿り着いた、実戦で通用するDocker Composeの最適化手法を共有する。
1. マルチステージビルドの真価:依存関係の「断捨離」
なぜビルドツールを本番に持ち込んではいけないのか。それは、`gcc`や`python`、`npm`といったツールチェーン自体が攻撃対象になり得るからだ。マルチステージビルドは単なる軽量化ではない。「実行環境に必要なものだけを抽出する」というセキュリティ要件の具現化だ。
# Stage 1: Build
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build && npm prune --production # 実行時不要なdevDependenciesを削除
# Stage 2: Runtime
FROM node:18-alpine
WORKDIR /app
# ビルド済み成果物と最小限の依存関係のみをコピー
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
USER node
CMD ["node", "dist/main.js"]
さらに、ビルド時間を短縮するなら`Docker Buildx`のキャッシュ機能を活用せよ。`--cache-from`や`--cache-to`をGitHub Actionsに組み込むだけで、変更のあったレイヤーだけを再ビルドし、CIの時間は劇的に短縮される。
2. Docker Desktopの同期速度問題:`virtiofs`への転換
開発効率を語る上で避けて通れないのが、ファイル同期の遅延だ。`volumes`でローカルをマウントすると、特に大規模プロジェクトではホットリロードが数秒遅れる。
かつては`delegated`オプションでマウントの整合性を犠牲にして速度を稼いでいたが、現在はDocker Desktopの設定で「VirtioFS」を選択するのが正解だ。 従来のgRPC FUSEと比較して、ファイルI/Oのパフォーマンスが段違いに向上している。
# docker-compose.yml
services:
web:
build: .
volumes:
- .:/app
- /app/node_modules # コンテナ側の依存関係を保護
develop:
watch:
- action: sync
path: ./src
target: /app/src
※最近のDocker Compose V2なら、`watch`モードを使うことでより直感的なホットリロードが実現できる。
3. 環境切り替えの黄金律:Composeオーバーライド
「開発環境では動くのに、本番環境では設定が足りなくて死ぬ」という事故を未然に防ぐには、設定ファイルを分離するのが鉄則だ。
- `docker-compose.yml`: ベース構成
- `docker-compose.prod.yml`: 本番用の最適化(レプリカ数、リソース制限、バックオフ設定)
# docker-compose.prod.yml
services:
web:
deploy:
replicas: 3
resources:
limits:
cpus: '0.50'
memory: 512M
restart: always
実行時は `docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d` と叩く。これだけで、本番環境特有の「うっかり設定忘れ」を環境差分として管理できる。
4. ネットワークとトラブルシューティングの極意
Docker Composeのネットワークでハマった時、多くのエンジニアはコンテナを再起動して祈る。だが、真の解決策は「コンテナ内に入って疎通を確認する」ことだ。
# コンテナ内のネットワーク状況を確認する
docker compose exec web sh -c "apk add --no-cache curl && curl -v http://db:5432"
もし疎通が取れないなら、コンテナ間のDNS解決が失敗しているか、ポートが公開されていない。`docker network inspect`でIPアドレスを確認し、泥臭くパケットの通り道を探れ。魔法はない。あるのはコマンドとログだけだ。
最後に:最適化は「未来の自分」への手紙だ
Dockerの最適化は、地味で、誰にも褒められない作業だ。しかし、デプロイが30秒で終わる環境と、10分かかる環境。どちらが精神衛生上良いかは言うまでもない。
技術的な負債は、放置すれば利子がついて返ってくる。今日削った100MBのイメージと、数分短縮したビルド時間は、明日、急な緊急修正が必要になった時に、必ず君の背中を助けてくれるはずだ。
コーヒーはもう冷めた。さあ、次はどの無駄を削ろうか。