Dockerリソース制限の極意:1つのコンテナにサーバー全体をダウンさせないために

Docker tutorial - IT technology blog
Docker tutorial - IT technology blog

「サーバー停止」という恐怖

Linuxサーバーが快適に動いていたのに、突然フリーズしてSSHが反応しなくなり、Webサイトに504エラーが出る……。これはエンジニアにとって悪夢のような光景です。私自身、Node.jsコンテナのメモリリークにより、わずか10分で16GBのRAMを使い果たしてしまった苦い経験があります。その結果、カーネルがOOM (Out Of Memory) Killを発動し、システムを救済するために他の重要なプロセスまで強制終了させてしまいました。

問題の核心は、デフォルトの状態ではDockerコンテナがホストのリソースを制限なく使用できてしまう点にあります。**リソース制限(Resource Limits)**という「制約」を設けなければ、システムを常に危険にさらすことになります。リソース制限の導入には、主に3つの大きなメリットがあります。

  • 隣人トラブル(Noisy Neighbor)の回避: 1つの挙動が不安定なサービスが、同じホストに同居する他のサービスに悪影響を与えるのを防ぎます。
  • コストの最適化: 私はかつて50以上のノードを持つクラスターを管理していましたが、適切な制限を設けることでコンテナを効率的に集約でき、クラウドインフラのコストを35%削減できました。
  • システムに「余裕」を確保: 管理タスクやロギングのために、サーバーのリソースに常に一定の空きを確保できます。

RAM(メモリ制限)の設定:安定稼働の要

RAMは最も枯渇しやすいリソースです。Dockerでは、以下の2つの閾値を明確に区別して設定する必要があります。

ハードリミット (–memory)

これは絶対的な上限値です。コンテナがこの数値を超えようとすると、カーネルによって即座に強制終了(Kill)されます。通常、アプリケーションの平均使用量の1.5倍程度に設定するのが推奨されます。

docker run -d --name production-api --memory="1g" nginx

上記の例では、アプリケーションが使用できるRAMを最大1GBに強制的に制限しています。

ソフトリミット (–memory-reservation)

これは保証される最低ラインのような数値です。サーバーのリソースに余裕がある場合、コンテナはこの閾値を超えてメモリを使用できます。しかし、リソースが不足し始めると、Dockerはコンテナに対してメモリを予約(reservation)レベルまで解放するよう強制します。

docker run -d --name worker-app --memory="2g" --memory-reservation="512m" my-app

この設定は非常に柔軟です。通常時は512MBで動作しますが、重い処理を行う際には2GBまで拡張でき、即座にKillされる心配もありません。

CPUパワーの制御

CPUはRAMとは異なり、上限を超えてもコンテナが強制終了されることはありません。代わりに処理が遅くなる(スロットリング)が発生します。しかし、1つのコンテナがCPUを100%占有し続けると、サーバー全体が極端に重くなってしまいます。

–cpus パラメータの使用

最も直感的な方法です。例えば、サーバーに8コアあり、特定のアプリを最大2.5コアまでに制限したい場合は、次のように実行します。

docker run -d --name heavy-task --cpus="2.5" python-worker

CPUシェア (–cpu-shares)

このパラメータは優先順位の重み付けに使用されます。例えば、アプリAに1024、アプリBに512を設定した場合、両者がCPUリソースを奪い合う状況下では、アプリAにアプリB of 2倍の処理時間が割り当てられます。

ディスクI/O(IOPS)の制限も忘れずに

ディスクI/Oの制限は、ハードドライブがボトルネックになるまで忘れられがちです。大量のログを出力するコンテナや負荷の高いデータベースは、サーバー全体のI/Oを停止させる可能性があります。実際、私はバックアップタスクやクローラーに対して、常に書き込み速度を制限しています。

docker run -d --name backup-job \
  --device-write-bps /dev/sda:20mb \
  ubuntu-backup

このコマンドは、/dev/sda への書き込み速度を20MB/s以下に固定します。これにより、バックアップ実行中もシステムのレスポンスが維持されます。

Docker Composeによる標準的なデプロイ

実際の運用環境では、Docker Composeが主要なツールとなります。バージョン3以降、リソース設定は deploy セクション内に記述します。以下は、APIサービスでよく使用する設定例です:

version: '3.8'
services:
  backend-api:
    image: nodejs-api:v2.1
    deploy:
      resources:
        limits:
          cpus: '0.50' # 0.5コアに制限
          memory: 1G
        reservations:
          cpus: '0.10'
          memory: 256M
    restart: unless-stopped

モニタリングと負荷試験(ストレスリサーチ)

推測で判断してはいけません。`docker stats` コマンドを使用して、リアルタイムのリソース消費量を確認しましょう。「MEM USAGE / LIMIT」の列を見れば、アプリが許可された範囲の何%を消費しているか一目でわかります。

ストレス・テストの実施

検証には、progrium/stress イメージを使用してRAM消費をシミュレートするのが効果的です。例えば、200MBに制限したコンテナで300MBの負荷をかけると、コンテナが即座にKillされるのを確認できます。これを行うことで、本番環境(Production)へのデプロイに自信が持てるようになります。

最後のアドバイス:サーバーがダウンしてから設定を始めるのではなく、リソース制限をデプロイの標準規格として捉えてください。最初は余裕のある数値から始め、docker stats から得られる実際のデータに基づいて徐々に微調整していくのがベストです。

Share: