Dockerを本番で使い始めると、こうした事故に一度は直面します。
Dockerはデフォルトでは、コンテナがホストのリソース(メモリ・CPU)を無制限に使用できてしまいます。アプリのバグやメモリリーク、突発的なスパイクで1つのコンテナが暴走すると、同じホスト上の他のコンテナやシステムプロセスが道連れになります。
この記事では、
--memory・--cpusオプションによるリソース制限の設計と、docker statsを使ったリアルタイム監視の実践方法を解説します。Docker Composeのdeploy.resources設定、cgroupsを使った実体確認まで、本番ホストを守る設計を一通りカバーします。この記事のポイント
・--memory でコンテナのメモリ上限を設定し、OOM Killerでホストを守る
・--cpus=1.5 でCPU使用量を、--cpu-shares で優先度を制御する
・docker stats --no-stream でリソース使用量をスナップショット確認できる
・Composeではdeploy.resources.limitsでまとめて制限を宣言する
でも安心してください。プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
なぜコンテナにリソース制限が必要か
物理サーバーやVMでも「1つのプロセスがリソースを使い切る」リスクはありますが、Dockerコンテナは起動コストが低い分、複数コンテナを同一ホストに詰め込む運用が一般的です。この「密度の高さ」が、リソース競合リスクを一層高めます。たとえばRAM 8GBのホストに制限なしでコンテナを10個起動し、そのうち1つがメモリリークを起こした場合、Linuxカーネルの OOM Killer(Out-Of-Memory Killer)がプロセスを強制終了しにかかります。どのプロセスが対象になるかはカーネルのスコアリング次第で、重要なコンテナが殺されることもあります。
制限を設けることで、1コンテナの暴走を「そのコンテナ内」に封じ込められます。これはマイクロサービス設計における「障害の隔離(Bulkheadパターン)」に相当します。
制限なし運用の典型的な事故シナリオ
・夜間バッチコンテナがメモリを食い尽くす → WebアプリコンテナがOOM Killerで停止・CPUバウンドな処理がスパイク → レイテンシに敏感な他コンテナのAPIが遅延
・開発者がテスト用コンテナを放置 → 知らぬ間にホストのCPUを90%占有
メモリ制限の設計 --memory・--memory-swap・OOM Killer
1. --memory でメモリ上限を設定する
--memory(短縮形:-m)は、コンテナが使えるメモリの上限(ハードリミット)を設定します。単位はバイト、または k・m・g のサフィックスで指定します。# メモリ上限を512MBに制限してコンテナを起動 docker run -d --name web-app --memory=512m nginx # 上限をdocker inspectで確認(Bytes単位で出力される) docker inspect web-app | grep '"Memory"' # "Memory": 536870912
Exited)し、ホストは守られます。2. --memory-swap でスワップを制御する
--memory-swapは、メモリとスワップの合計上限を指定します。設定の意味は以下のとおりです。・
--memory=512m --memory-swap=512m:スワップなし(合計512MB = スワップ0)・
--memory=512m --memory-swap=1g:スワップ最大512MB(合計1GB のうち512MBがスワップ)・
--memory=512m --memory-swap=-1:スワップ無制限(本番では非推奨)スワップを無効化することでレイテンシのスパイクを防げますが、その分 OOM Killer が早く発動します。ユースケースに応じて選択してください。デフォルトでは
--memory-swap は --memory の2倍(スワップが同量有効)になるため、本番では明示的に設定することを推奨します。3. --memory-reservation でソフトリミットを設ける
--memory-reservationは「推奨上限(ソフトリミット)」です。通常はこの値を超えて使えますが、ホストのメモリが逼迫してきたとき、カーネルがこの値まで使用量を引き下げようとします。# ハードリミット512MB、ソフトリミット256MB、スワップ無効 docker run -d --name api-server \ --memory=512m \ --memory-reservation=256m \ --memory-swap=512m \ myapp:latest
CPU制限の設計 --cpus・--cpu-shares・--cpuset-cpus
1. --cpus でCPUコア数を制限する
--cpusは、コンテナが使えるCPUの最大数(小数点指定可)を設定します。ホストが4コアなら --cpus=1.5 で「最大1.5コア分」に制限できます。# CPUを1.5コア分に制限 docker run -d --name worker --cpus=1.5 python-worker:latest # 確認(NanoCpus = 1500000000 = 1.5コア) docker inspect worker | grep '"NanoCpus"' # "NanoCpus": 1500000000
2. --cpu-shares で相対的優先度を設定する
--cpu-sharesは、CPUが競合しているときの「優先度ウェイト」を指定します。デフォルト値は1024です。2048を指定すると、デフォルトのコンテナと比べて約2倍のCPU時間が割り当てられます。ただし、CPUに余裕があるとき(競合がないとき)は制限されません。本番のバッチコンテナに低い値(512など)を設定しておくと、スパイク時に重要なWebコンテナを優先できます。
# Webアプリは高優先度(デフォルト1024の2倍) docker run -d --name web --cpu-shares=2048 myapp:latest # バッチは低優先度 docker run -d --name batch --cpu-shares=512 batch-job:latest
3. --cpuset-cpus でCPUコアを固定する
--cpuset-cpusは、コンテナを特定のCPUコアにピン止めします。NUMAアーキテクチャのサーバーや、レイテンシが厳しいリアルタイム処理で有効です。# コア0番と1番のみ使用 docker run -d --name realtime-app --cpuset-cpus="0,1" myapp:latest # コア0番から3番を使用(範囲指定) docker run -d --name multi-worker --cpuset-cpus="0-3" worker:latest
docker stats で制限効果をリアルタイム監視する
制限を設定したら、実際に効いているか確認しましょう。docker statsコマンドはコンテナのリソース使用状況をリアルタイムで表示します。# 全コンテナを継続監視(Ctrl+Cで終了) docker stats # スナップショットとして1回だけ出力(スクリプト連携に便利) docker stats --no-stream # 特定コンテナのみ監視 docker stats web-app redis-cache # 出力フォーマットをカスタマイズ docker stats --format "table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}\t{{.MemPerc}}"
CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS 3f2a8b91c4d2 web-app 0.12% 145.3MiB / 512MiB 28.37% 1.2kB / 856B 0B / 8.19kB 8 e7d4f23a1b0c redis-cache 0.08% 52.7MiB / 256MiB 20.59% 234B / 0B 0B / 0B 5 a1b9c8d7e2f0 batch-job 87.43% 493.1MiB / 512MiB 96.31% 0B / 0B 0B / 102MB 12
MEM USAGE / LIMIT列を見ると、web-app は 512MiB の制限に対して 145.3MiB(28%)、batch-job は 493MiB(96%)まで使っていることがわかります。batch-job は制限に近づいているため、このまま上昇すると OOM Killer が発動します。docker statsの主要カラム説明
| カラム名 | 内容 |
|---|---|
| CPU % | ホストのCPU総使用率に対する比率(全コア合算) |
| MEM USAGE / LIMIT | 実際のメモリ使用量 / 設定した制限値(制限なしはホスト総量) |
| MEM % | 制限値に対するメモリ使用率 |
| NET I/O | ネットワーク送受信量(コンテナ起動からの累積) |
| BLOCK I/O | ブロックデバイス読み書き量(ディスクI/O) |
| PIDS | コンテナ内で動作中のプロセス数 |
Composeでリソース制限を設定するdeploy.resources
複数コンテナをDocker Composeで管理する場合、各サービスにリソース制限を宣言しておくことが推奨されます。Composeファイルに書くことで、チーム全員が同じ制限値で運用できます。# docker-compose.yml(Compose V2以降) services: web: image: myapp:latest deploy: resources: limits: cpus: '1.0' memory: 512M reservations: cpus: '0.25' memory: 128M batch: image: batch-job:latest deploy: resources: limits: cpus: '0.5' memory: 256M reservations: cpus: '0.1' memory: 64M
limitsはハードリミット、reservationsはソフトリミット(最低保証)に相当します。注意点として、Compose V2(
docker composeコマンド)ではdeploy.resourcesがそのまま有効ですが、旧来のdocker-compose(ハイフンあり)コマンドではSwarmモード専用の設定として無視される場合がありました。現在のDockerをお使いであればdocker compose(スペースあり)を使用してください。# Compose V2で起動 docker compose up -d # 起動後のリソース制限を確認 docker compose stats --no-stream
cgroupsの実体確認(/sys/fs/cgroup)
Dockerのリソース制限はLinuxカーネルのcgroups(Control Groups)機能を使って実現されています。設定が本当に反映されているかを、cgroupsの実体ファイルで直接確認できます。現代のDockerはcgroups v2を使用しており、制御ファイルは
/sys/fs/cgroup/配下に配置されます。# コンテナIDを取得 CONTAINER_ID=$(docker inspect --format '{{.Id}}' web-app) # メモリ制限の実体(バイト単位) cat /sys/fs/cgroup/system.slice/docker-${CONTAINER_ID}.scope/memory.max # 536870912 ← 512MiB = 512 * 1024 * 1024 # CPU制限の実体(quota period形式) cat /sys/fs/cgroup/system.slice/docker-${CONTAINER_ID}.scope/cpu.max # 100000 100000 ← 100ms中100ms使用可 = 1.0コア相当
memory.maxの値(536870912)は512MiBのバイト数であり、--memory=512mが正しく反映されていることが確認できます。cpu.maxの100000 100000は「100msの期間のうち100ms使用可」を意味し、--cpus=1.0の設定値と一致します。cgroupsはLinuxのファイルシステムとして
/sys/fs/cgroupにマウントされています。Linuxのファイルシステムマウントの基本については、mountコマンドの使い方でも解説しています。本番環境のベストプラクティスと設計指針
1. まずは計測してから制限値を決める
制限値を感覚で決めるのは危険です。本番投入前にステージング環境でdocker statsを使い、実際のリソース使用量のピーク値を計測してください。その値に20~30%のバッファを加えた値を制限値として設定するのが現実的です。2. --memory-swap を明示的に設定する
デフォルトでは--memory-swapは--memoryの2倍になります(スワップが--memoryと同量有効)。スワップを有効にするとメモリ枯渇時に応答が劇的に遅くなります。本番では--memory-swapを--memoryと同じ値(スワップ実質ゼロ)に設定することを強く推奨します。3. コンテナの用途でCPU制限戦略を変える
・APIサーバー(レイテンシ重視):--cpusで上限を設けつつ--cpu-sharesを高めに設定・バッチ処理(スループット重視):
--cpusで上限を低く抑え、他のコンテナへの影響を遮断・複数コンテナ混在:
--cpu-sharesで相対優先度を設定し、スパイク時の競合を制御4. --pids-limit でフォーク爆弾を防ぐ
メモリとCPUだけでなく、プロセス数も制限しましょう。--pids-limitを設定することで、コンテナ内でのフォーク爆弾(無限にプロセスを生成するバグ・攻撃)を防げます。本番では200~500程度が目安です。# メモリ・CPU・プロセス数をまとめて制限 docker run -d --name web-app \ --memory=512m \ --memory-swap=512m \ --cpus=1.0 \ --pids-limit=200 \ myapp:latest
5. docker stats を定期実行してベースラインを把握する
docker statsはリアルタイム監視に便利ですが、長時間の傾向分析にはPrometheusやcAdvisorとの連携が実務では一般的です。まずはdocker stats --no-streamを定期的にcronで実行してログに残すだけでも、ベースラインの把握に役立ちます。Linuxのプロセス監視ツール(
ss・lsof等)との組み合わせについては、Linux ポート確認の全コマンドも参考にしてください。よくあるトラブルと対処法
OOM Killedでコンテナが突然停止する場合
コンテナがExited (137)で停止しているとき、OOM Killerによる強制終了が疑われます。docker inspectでフラグを確認できます。# OOMKilledフラグを確認 docker inspect web-app | grep '"OOMKilled"' # "OOMKilled": true ← OOM Killerで停止 # ホストのカーネルログでも確認可能 dmesg | grep -i "oom\|killed process" # [12345.678] Out of memory: Kill process 3142 (java) ...
--memoryの値を引き上げるか、アプリのメモリリークを調査してください。制限値の設定が厳しすぎると正常なリクエストでも落ちるため、docker statsでベースラインを計測してから再設定することを推奨します。--cpus設定後もCPU %が高く見える場合
--cpus=1.0を設定しているのにdocker statsでCPU %が100%を超えるように見える場合は、docker statsのCPU %がホスト全CPUコア数に対する割合で表示されることに注意してください。4コアのホストで--cpus=1.0であれば、最大25%(1コア/4コア)が上限です。制限は正しく効いています。本記事のまとめ
Dockerコンテナのリソース制限と監視に関する主要なオプション・コマンドをまとめます。| やりたいこと | コマンド / 設定 |
|---|---|
| メモリ上限を設定する | docker run --memory=512m コンテナ名 |
| スワップを無効にする | docker run --memory=512m --memory-swap=512m コンテナ名 |
| CPU使用量を制限する | docker run --cpus=1.5 コンテナ名 |
| CPU相対優先度を設定する | docker run --cpu-shares=2048 コンテナ名 |
| 特定のCPUコアに固定する | docker run --cpuset-cpus="0,1" コンテナ名 |
| プロセス数を制限する | docker run --pids-limit=200 コンテナ名 |
| リソース使用量を一覧確認する | docker stats --no-stream |
| Composeでメモリ制限を宣言する | deploy.resources.limits.memory: 512M |
| cgroupsで制限値を確認する | cat /sys/fs/cgroup/.../memory.max |
deploy.resourcesを定義することで、チーム全員が同じ制限値で運用できます。本記事で紹介した
--memory・--cpus・docker statsの実践的な使い方をさらに深く学びたい方は、Dockerコンテナ実践ガイド(docker.linuxmaster.jp)をご覧ください。Docker入門から本番運用まで体系的に解説しています。
Dockerのリソース制限を「なぜ設定するか」から理解するには、Linuxサーバー構築の「型」が土台になります
--memoryや--cpusはコマンドとして覚えられても、「どの値を設定すべきか」「なぜcgroupsがホストを守れるのか」を説明できるかどうかで現場での信頼が変わります。
現場で通用する安全なLinuxサーバー構築の「型」を体系的に身につけたい方へ、『Linuxサーバー構築入門マニュアル(図解60P)』を完全無料でプレゼントしています。
「独学の時間がもったいない」「プロから直接、現場の技術を最短で学びたい」という本気の方には、2日で実務レベルのスキルが身につく【初心者向けハンズオンセミナー】も開催しています。
3,100名以上が実践した「型」を無料で公開中
プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
その「型」を図解60Pにまとめた入門マニュアルを、完全無料でプレゼントしています。
登録10秒/合わなければ解除3秒 / 詳細はこちら
- 次のページへ:Dockerのログ運用設計|loggingドライバとjson-fileローテーションで肥大化を防ぐ方法
- 前のページへ:docker buildのキャッシュと.dockerignore設計|Dockerfileの命令順序でCIビルドを高速化する実践手順
- この記事の属するカテゴリ:Dockerへ戻る

無料メルマガで学習を続ける
Linuxの実践スキルをメールで毎週お届け。
登録は1分、解除もいつでも可。
登録無料・いつでも解除できます