はじめに
Docker環境が重すぎて頭を抱えている人は多いのではないでしょうか。
私自身も激重なDocker環境を触っていて、原因を一つずつ潰していった経験が何度かあります。
今回は、ローカルのDocker環境(Windows)を軽量化するための方法について、実際に行った手順と一緒に紹介します。
ただし、リソース削除やnamed volumeの移行など、確認やバックアップを省略するとローカル環境へ影響が出る操作も含まれます。各手順の注意事項を確認し、まずは作業用コピーや不要なデータで試してください。
上から順に、自分の環境に効きそうなところから試してみてください。
注意点:.dockerignore、マルチステージビルド、ベースイメージの軽量化、ビルドキャッシュの活用は、主にビルド・pull・コンテナ再作成・起動時間の改善に効きます。稼働中のページレスポンスを直接速くする対策ではありません。
1. 不要なリソースを掃除する
Dockerはビルドキャッシュや古いイメージ、停止済みのコンテナを自動では削除しません。これが積み重なってディスクを圧迫し、ホスト全体の動作が重くなっていることがあります。
手順
- 現在のディスク使用状況を確認する
docker system df
-
RECLAIMABLE列の数値をチェックする。数十GB単位で溜まっていたら要注意です - 削除対象を確認する
docker ps -a
docker images
docker volume ls
- 未使用のイメージ・コンテナ・ネットワークをまとめて削除する
docker system prune
停止中のコンテナを復旧用に残している場合は、実行前に対象を確認してください。削除後にComposeで管理しているサービスを再作成する場合は、docker compose up -dを実行します。
2. docker statsでボトルネックを特定する
「なんとなく重い」で対処法を選ぶと、的外れな作業になりがちです。まずは何が原因かを見ます。
手順
- 稼働中の全コンテナのリソース状況を確認する
docker stats --no-stream
-
CPU %とMEM USAGEの列を見て、特定のコンテナだけ突出していないかチェックする - 見つかったらそのコンテナのアプリケーションログを確認する
一つのコンテナだけ異常に高負荷なら、Docker自体ではなくそのアプリケーションのコード側に原因がある可能性が高いです。
ページの応答時間も変更前後で測定します。PowerShellではcurlがInvoke-WebRequestのエイリアスになっている場合があるため、curl.exeを明示します。
1..30 | ForEach-Object {
curl.exe -s -o NUL -w "TTFB=%{time_starttransfer}s TOTAL=%{time_total}s`n" http://localhost:8080/
}
TTFBは最初のデータが返るまでの時間、TOTALはレスポンス完了までの時間です。
curlで測定できるのはHTTPリクエストの応答時間です。ブラウザ上の表示完了時間には、画像・JavaScript・CSSの読み込み、ブラウザキャッシュ、外部サービスの処理時間なども含まれます。最終確認では、ブラウザの開発者ツールも併用してください。
3. ログの肥大化を防ぐ
Dockerのデフォルトログドライバーjson-fileは、放っておくとログファイルが際限なく膨らみます。アクセス数の多いサイトだと、これが原因でディスクを圧迫していることも珍しくありません。
手順
-
docker-compose.ymlを開き、重くなっているサービスに以下を追記する
services:
web:
logging:
driver: json-file
options:
max-size: "10m"
max-file: "10"
- 設定を反映させる
docker compose up -d --force-recreate web
既存コンテナを再作成するため、一時的にサービスが停止します。webは実際のサービス名へ置き換えてください。
max-fileでは保持するファイル数を指定します。過去のログにさかのぼって調査することができなくなるため、この設定を利用する場合はそのデメリットも留意してください。
4. Docker Desktopのリソース割り当てを見直す
意外と見落としがちなのが、Docker Desktop自体に割り当てているCPU・メモリの設定です。
手順
- Docker Desktopの「Settings」→「Resources」を開く
- CPUsとMemoryの割り当てを確認する(WSL2バックエンドの場合は
C:\Users\ユーザー名\.wslconfigで設定) - ホスト側に余裕があれば、少しずつ増やして再起動する
-
docker statsで改善したか確認する
設定を増やす方向の変更でも、ホスト側のメモリやCPUを圧迫すると、IDEやブラウザを含めた環境全体が遅くなる場合があります。少しずつ変更して同じ測定手順で効果を比較してください。
当環境では.wslconfigに以下のように記述しています。
5. WSL2のファイル共有方式を見直す
Windowsホスト上のプロジェクトをbind mountしている場合、PHP、Node.js、Pythonなどが大量の小さなファイルを読み書きすると、ファイルアクセスがボトルネックになることがあります。
手順
- プロジェクトがWindows側(
C:\...)とWSL2のLinux側(\\wsl$\...)のどちらに置かれているか確認する - Docker DesktopでWSL2バックエンドが有効になっているか「Settings」→「General」で確認する
- 既存の作業フォルダをバックアップ、またはローカルの作業用コピーをWSL2のLinux側へ移動する
- 同じページの応答時間とファイル変更検知を、移動前後で比較する
Windows側のファイルシステムよりLinux側のファイルシステムからbind mountした方が、ファイルアクセス性能を得やすい傾向があります。
バインドマウントを多用しているNode.jsやPHPのプロジェクトほど恩恵を感じやすいです。
6. .dockerignoreでビルドコンテキストを絞る
ビルドのたびに重くなっている場合、node_modulesや.gitなどの不要なファイルまでDockerに送ってしまっているケースがよくあります。
手順
- プロジェクト直下に
.dockerignoreファイルを作成する
node_modules
.git
*.log
- ビルドを実行し、送信されるコンテキストのサイズが減っているか確認する
docker build .
ビルド時に表示されるSending build context to Docker daemonの値が小さくなっていれば効果が出ています。
7. マルチステージビルドでイメージを軽量化する
ビルドに必要なツールがそのまま実行用イメージに残っていると、イメージが無駄に大きくなり、コンテナの起動や再作成が遅くなります。
使用しているフレームワーク・言語によってDockerfileの書き方は大きく異なります。実行時に必要な共有ライブラリ、設定ファイル、証明書、ファイル権限を確認してから適用してください。
手順
- Dockerfileをビルド用ステージと実行用ステージに分ける
FROM node:20 AS builder
WORKDIR /app
COPY . .
RUN npm ci && npm run build
FROM node:20-slim
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
CMD ["node", "dist/index.js"]
成果物の種類は言語やフレームワークによって異なります。PHPではPHP拡張、Composerのvendor、ApacheまたはPHP-FPM設定など、実行時に必要なファイルを最終ステージへ含める必要があります。
- ビルドし直してイメージサイズを比較する
docker images
ただし、成果物や共有ライブラリをコピーし忘れると起動後にエラーになるため、起動、DB接続、ファイルアップロード、画像処理、主要ページを確認してください。
8. ベースイメージの軽量版を選ぶ
FROMで指定しているベースイメージが必要以上に大きいと、イメージのpullやコンテナの起動に時間がかかります。
・Node.js: node:20 → node:20-slim
・PHP: php:8.2-apache → php:8.2-apache-slim、Apacheが不要ならCLI版のphp:8.2-cli
・Python: python:3.12 → python:3.12-slim
手順
- Dockerfileの1行目でベースイメージを確認する
- 軽量版タグ(
-slimや-alpine)が存在するか確認する - タグを軽量版に変更してビルドする
docker compose build
alpine系はglibcではなくmuslベースなので、依存パッケージが動かないことがあります。特にPHPのapt前提で拡張をビルドしている場合は、alpine(apk)向けにパッケージ名が変わる点に注意してください。変更後は、起動、DB接続、ログイン、ファイルアップロード、画像処理、主要ページ、バッチ処理まで確認してください。
9. ビルドキャッシュを効かせる
古いDockerのビルダーだと、ソースコードを少し変えただけでキャッシュが効かず、毎回フルビルドになっていることがあります。
ポイント
・依存関係の定義ファイル(package.json、composer.jsonなど)だけを先にCOPYし、インストールを実行してから、残りのソースコードをCOPYする順番にすると、ソース変更時でも依存関係のレイヤーがキャッシュされたまま再利用されます
COPY composer.json composer.lock ./
RUN composer install
COPY . .
BuildKitでは、パッケージマネージャーのダウンロードキャッシュをビルド間で再利用するcache mountも利用できます。
# syntax=docker/dockerfile:1
RUN --mount=type=cache,target=/root/.npm npm ci
手順
- BuildKitと使用するビルダーを確認する
docker buildx ls
- PowerShellでBuildKitを有効にしてビルドする
$env:DOCKER_BUILDKIT = "1"
docker compose build
Docker DesktopではBuildKitが標準で使用される場合もあるため、環境変数を設定しても追加効果がないことがあります。
10. 頻繁に読み書きするディレクトリをnamed volumeに切り替える
ホストのディレクトリをそのままマウントするbind mountは、特にWindowsではファイルアクセスのたびにOSの壁を越える処理が入り、パフォーマンスが落ちやすいです。DB用ディレクトリなど頻繁に読み書きする場所は、named volumeに切り替えると体感が変わります。
手順
- 切り替え前に必ず既存データをバックアップする
docker compose exec -T db mysqldump -u root -p mydb > backup.sql
-
docker-compose.ymlのvolume定義を書き換える
services:
db:
volumes:
- db-data:/var/lib/mysql
volumes:
db-data:
- コンテナを再作成し、バックアップからデータを復元する
docker compose down
docker compose up -d --force-recreate db
docker compose exec -T db mysql -u root -p mydb < backup.sql
- アプリケーションが正常にデータを読み書きできるか動作確認する
db、mydb、/var/lib/mysqlは例なので、実際の設定へ置き換えてください。docker compose down -vを実行するとnamed volumeも削除されるため、DBデータを残したい場合は-vを付けないでください。
バックアップの取得成功だけでなく、別のローカルvolumeや作業用コピーへのリストア成功まで確認してください。
まとめ
・リソースの掃除、ボトルネック特定、ログ上限設定はまず最初にやっておいて損はありません。ただし、docker system pruneは削除対象を確認し、DB volumeを削除しないよう注意してください
・Docker DesktopのリソースやWSL2のファイル共有方式は、リソース不足やbind mountがボトルネックの場合に効果があります
・.dockerignore、マルチステージビルド、ベースイメージの軽量化、ビルドキャッシュの活用は、主にビルド・pull・コンテナ再作成・起動時間の改善に効きます
・named volumeへの切り替えは、DBやキャッシュなどのI/Oがボトルネックの場合に効果があります。既存データのバックアップとリストア確認は必ず実施してください
・変更前後でdocker system df、docker stats、ページ応答時間を測定し、中央値と95パーセンタイルを比較してください
今回はDocker環境の軽量化・高速化について、私が実際にやってみた手法をご紹介しました。
当環境はこれでだいぶマシな挙動になり、ビルド時の速度やローカル環境のページ表示はずいぶん早くなりました。
効果を実感できるかどうかは環境によって変わるため、ご紹介した方法を試してもうまくいかなかった場合、Docker系ファイルをAIに読ませ、何がボトルネックになってるのか聞いてみるのがおすすめです。
そうするとずらーっと改善策を教えてくれますが、うかつに全部適用すると環境が破壊されることもあるため、何を実行して大丈夫かはご自身で判断する必要があります。
まずは安全な方法から1つずつ試していきましょう!
参考
・Docker ログ設定:https://docs.docker.com/engine/logging/configure/
・Docker リソース制限:https://docs.docker.com/engine/containers/resource_constraints/
・Docker WSL2ベストプラクティス:https://docs.docker.com/desktop/features/wsl/best-practices/
・Docker ボリューム:https://docs.docker.com/engine/storage/volumes/
