3
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Docker環境をガチで軽量化するための鉄板設定まとめ

3
Posted at

はじめに

Docker環境が重すぎて頭を抱えている人は多いのではないでしょうか。
私自身も激重なDocker環境を触っていて、原因を一つずつ潰していった経験が何度かあります。

今回は、ローカルのDocker環境(Windows)を軽量化するための方法について、実際に行った手順と一緒に紹介します。

ただし、リソース削除やnamed volumeの移行など、確認やバックアップを省略するとローカル環境へ影響が出る操作も含まれます。各手順の注意事項を確認し、まずは作業用コピーや不要なデータで試してください。

上から順に、自分の環境に効きそうなところから試してみてください。

注意点:.dockerignore、マルチステージビルド、ベースイメージの軽量化、ビルドキャッシュの活用は、主にビルド・pull・コンテナ再作成・起動時間の改善に効きます。稼働中のページレスポンスを直接速くする対策ではありません。

1. 不要なリソースを掃除する

Dockerはビルドキャッシュや古いイメージ、停止済みのコンテナを自動では削除しません。これが積み重なってディスクを圧迫し、ホスト全体の動作が重くなっていることがあります。

手順

  1. 現在のディスク使用状況を確認する
docker system df
  1. RECLAIMABLE列の数値をチェックする。数十GB単位で溜まっていたら要注意です
  2. 削除対象を確認する
docker ps -a
docker images
docker volume ls
  1. 未使用のイメージ・コンテナ・ネットワークをまとめて削除する
docker system prune

停止中のコンテナを復旧用に残している場合は、実行前に対象を確認してください。削除後にComposeで管理しているサービスを再作成する場合は、docker compose up -dを実行します。

2. docker statsでボトルネックを特定する

「なんとなく重い」で対処法を選ぶと、的外れな作業になりがちです。まずは何が原因かを見ます。

手順

  1. 稼働中の全コンテナのリソース状況を確認する
docker stats --no-stream
  1. CPU %MEM USAGEの列を見て、特定のコンテナだけ突出していないかチェックする
  2. 見つかったらそのコンテナのアプリケーションログを確認する

一つのコンテナだけ異常に高負荷なら、Docker自体ではなくそのアプリケーションのコード側に原因がある可能性が高いです。

ページの応答時間も変更前後で測定します。PowerShellではcurlInvoke-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は、放っておくとログファイルが際限なく膨らみます。アクセス数の多いサイトだと、これが原因でディスクを圧迫していることも珍しくありません。

手順

  1. docker-compose.ymlを開き、重くなっているサービスに以下を追記する
services:
  web:
    logging:
      driver: json-file
      options:
        max-size: "10m"
        max-file: "10"
  1. 設定を反映させる
docker compose up -d --force-recreate web

既存コンテナを再作成するため、一時的にサービスが停止します。webは実際のサービス名へ置き換えてください。

max-fileでは保持するファイル数を指定します。過去のログにさかのぼって調査することができなくなるため、この設定を利用する場合はそのデメリットも留意してください。

4. Docker Desktopのリソース割り当てを見直す

意外と見落としがちなのが、Docker Desktop自体に割り当てているCPU・メモリの設定です。

手順

  1. Docker Desktopの「Settings」→「Resources」を開く
  2. CPUsとMemoryの割り当てを確認する(WSL2バックエンドの場合はC:\Users\ユーザー名\.wslconfigで設定)
  3. ホスト側に余裕があれば、少しずつ増やして再起動する
  4. docker statsで改善したか確認する

設定を増やす方向の変更でも、ホスト側のメモリやCPUを圧迫すると、IDEやブラウザを含めた環境全体が遅くなる場合があります。少しずつ変更して同じ測定手順で効果を比較してください。

当環境では.wslconfigに以下のように記述しています。

image.png

5. WSL2のファイル共有方式を見直す

Windowsホスト上のプロジェクトをbind mountしている場合、PHP、Node.js、Pythonなどが大量の小さなファイルを読み書きすると、ファイルアクセスがボトルネックになることがあります。

手順

  1. プロジェクトがWindows側(C:\...)とWSL2のLinux側(\\wsl$\...)のどちらに置かれているか確認する
  2. Docker DesktopでWSL2バックエンドが有効になっているか「Settings」→「General」で確認する
  3. 既存の作業フォルダをバックアップ、またはローカルの作業用コピーをWSL2のLinux側へ移動する
  4. 同じページの応答時間とファイル変更検知を、移動前後で比較する

Windows側のファイルシステムよりLinux側のファイルシステムからbind mountした方が、ファイルアクセス性能を得やすい傾向があります。

バインドマウントを多用しているNode.jsやPHPのプロジェクトほど恩恵を感じやすいです。

6. .dockerignoreでビルドコンテキストを絞る

ビルドのたびに重くなっている場合、node_modules.gitなどの不要なファイルまでDockerに送ってしまっているケースがよくあります。

手順

  1. プロジェクト直下に.dockerignoreファイルを作成する
node_modules
.git
*.log
  1. ビルドを実行し、送信されるコンテキストのサイズが減っているか確認する
docker build .

ビルド時に表示されるSending build context to Docker daemonの値が小さくなっていれば効果が出ています。

7. マルチステージビルドでイメージを軽量化する

ビルドに必要なツールがそのまま実行用イメージに残っていると、イメージが無駄に大きくなり、コンテナの起動や再作成が遅くなります。

使用しているフレームワーク・言語によってDockerfileの書き方は大きく異なります。実行時に必要な共有ライブラリ、設定ファイル、証明書、ファイル権限を確認してから適用してください。

手順

  1. 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設定など、実行時に必要なファイルを最終ステージへ含める必要があります。

  1. ビルドし直してイメージサイズを比較する
docker images

ただし、成果物や共有ライブラリをコピーし忘れると起動後にエラーになるため、起動、DB接続、ファイルアップロード、画像処理、主要ページを確認してください。

8. ベースイメージの軽量版を選ぶ

FROMで指定しているベースイメージが必要以上に大きいと、イメージのpullやコンテナの起動に時間がかかります。

・Node.js: node:20node:20-slim
・PHP: php:8.2-apachephp:8.2-apache-slim、Apacheが不要ならCLI版のphp:8.2-cli
・Python: python:3.12python:3.12-slim

手順

  1. Dockerfileの1行目でベースイメージを確認する
  2. 軽量版タグ(-slim-alpine)が存在するか確認する
  3. タグを軽量版に変更してビルドする
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

手順

  1. BuildKitと使用するビルダーを確認する
docker buildx ls
  1. PowerShellでBuildKitを有効にしてビルドする
$env:DOCKER_BUILDKIT = "1"
docker compose build

Docker DesktopではBuildKitが標準で使用される場合もあるため、環境変数を設定しても追加効果がないことがあります。

10. 頻繁に読み書きするディレクトリをnamed volumeに切り替える

ホストのディレクトリをそのままマウントするbind mountは、特にWindowsではファイルアクセスのたびにOSの壁を越える処理が入り、パフォーマンスが落ちやすいです。DB用ディレクトリなど頻繁に読み書きする場所は、named volumeに切り替えると体感が変わります。

手順

  1. 切り替え前に必ず既存データをバックアップする
docker compose exec -T db mysqldump -u root -p mydb > backup.sql
  1. docker-compose.ymlのvolume定義を書き換える
services:
  db:
    volumes:
      - db-data:/var/lib/mysql

volumes:
  db-data:
  1. コンテナを再作成し、バックアップからデータを復元する
docker compose down
docker compose up -d --force-recreate db
docker compose exec -T db mysql -u root -p mydb < backup.sql
  1. アプリケーションが正常にデータを読み書きできるか動作確認する

dbmydb/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 dfdocker 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/

3
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?