Docker Exit Code 137(OOMKilled)エラーの解決方法【2025年最新版】

スポンサーリンク

Docker Exit Code 137(OOMKilled)エラーの解決方法【2025年最新版】

Dockerコンテナが突然停止し、「Exit Code 137」や「OOMKilled」というエラーが表示されて困っていませんか?このエラーはメモリ不足が原因で発生する深刻な問題ですが、適切な対処法を知っていれば確実に解決できます。本記事では、Docker Exit Code 137の原因から具体的な解決方法、予防策まで詳しく解説します。


このエラーとは?発生する症状

Exit Code 137の正体

Docker Exit Code 137は、コンテナ内のプロセスがLinuxカーネルの「OOM Killer(Out of Memory Killer)」によって強制終了されたことを示すエラーコードです。Unix/Linuxシステムでは、プロセスがシグナルによって終了した場合、終了コードは「128 + シグナル番号」として計算されます。SIGKILL(強制終了シグナル)のシグナル番号は9なので、128 + 9 = 137となります。

このエラーが発生すると、以下のような症状が見られます:

  • コンテナが予期せず停止する
  • docker ps -aコマンドで確認すると「Exited (137)」と表示される
  • docker inspectコマンドで「OOMKilled: true」と表示される
  • アプリケーションのログが突然途切れる
  • 処理中のデータが失われる可能性がある

影響範囲

このエラーは単独のコンテナだけでなく、マイクロサービスアーキテクチャ全体に影響を与える可能性があります。例えば、データベースコンテナがOOMKilledで停止すると、それに依存するすべてのアプリケーションコンテナも正常に動作しなくなります。Kubernetes環境では、Podが繰り返し再起動する「CrashLoopBackOff」状態に陥ることもあります。

特に本番環境での発生は、サービス停止やデータ損失につながるため、迅速な対応と根本的な解決が求められます。


このエラーが発生する原因

原因1: コンテナのメモリ制限が不適切

最も一般的な原因は、コンテナに設定されたメモリ制限がアプリケーションの実際の要求量を下回っていることです。Dockerでは--memoryオプションでメモリ上限を設定できますが、この値が小さすぎると、アプリケーションが必要なメモリを確保できずにOOM Killerが発動します。

例えば、1GBのメモリを必要とするJavaアプリケーションに対して512MBの制限を設定すると、確実にこのエラーが発生します。docker-composeファイルでmem_limit: 512mのように設定している場合も同様です。

原因2: アプリケーションのメモリリーク

メモリリークとは、プログラムがメモリを使用した後、システムに返却しない状態が継続することです。時間の経過とともにメモリ消費量が増加し続け、最終的にコンテナのメモリ制限に到達してOOM Killerが発動します。

メモリリークの典型的なパターンには以下があります:
– オブジェクトの参照が解放されない
– イベントリスナーやコールバックが登録されたまま削除されない
– キャッシュが無制限に成長する
– 大量のログやデータがメモリに蓄積される

原因3: Javaアプリケーション特有の問題

Javaアプリケーションをコンテナで実行する場合、ヒープサイズ(-Xmx、-Xms)の設定が不適切だとOOMKilledが発生しやすくなります。JVMはヒープ領域以外にも、スレッドスタック、ガベージコレクション、メタスペース、ネイティブメモリ、ソケットバッファなど、追加のメモリを必要とします。

例えば、コンテナのメモリ制限が2GBで、-Xmxを2GBに設定すると、ヒープ以外のメモリ領域が確保できずにOOMKilledが発生します。

原因4: ホストマシンのメモリ不足

コンテナ自体にメモリ制限を設定していない場合でも、ホストマシン(Dockerが動作しているサーバー)のメモリが不足すると、Linuxカーネルのグローバルなmm Killerが発動してコンテナプロセスを終了させることがあります。

特に、複数のコンテナが同時に動作している環境では、リソースの競合が発生しやすくなります。

原因5: Docker Desktop(Windows/Mac)のメモリ割り当て

Windows版やMac版のDocker Desktopでは、仮想マシン(WSL2またはHyperKit)に割り当てられたメモリ量がボトルネックになることがあります。デフォルト設定では、ホストマシンのメモリの50%程度しか割り当てられていない場合があります。


解決方法1: メモリ制限の適切な設定(推奨)

手順1: 現在のメモリ使用量を確認する

まず、コンテナの実際のメモリ使用量を把握することが重要です。以下のコマンドで確認できます:

# リアルタイムでメモリ使用量を監視
docker stats

# 特定のコンテナのメモリ使用量を確認
docker stats <container_name>

出力例:

CONTAINER ID   NAME          CPU %   MEM USAGE / LIMIT     MEM %
a1b2c3d4e5f6   my-app        2.50%   456.2MiB / 512MiB     89.10%

「MEM USAGE / LIMIT」が90%を超えている場合は、メモリ制限の引き上げを検討すべきです。

手順2: OOMKilledの状態を確認する

コンテナが停止した原因がOOMKilledかどうかを確認します:

# OOMKilledフラグと終了コードを確認
docker inspect -f '{{.State.OOMKilled}} {{.State.ExitCode}}' <container_name>

「true 137」と表示されれば、確実にOOMKilledが原因です。

cgroupのメモリ制限も確認できます:

docker exec <container> sh -c 'cat /sys/fs/cgroup/memory.max 2>/dev/null || cat /sys/fs/cgroup/memory/memory.limit_in_bytes'

手順3: メモリ制限を適切に設定する

Docker CLIの場合

# メモリ制限を2GBに設定してコンテナを起動
docker run --memory=2g --memory-swap=2g my-image

# 既存のコンテナを更新(停止してから再作成が必要)
docker stop my-container
docker rm my-container
docker run --memory=2g --name my-container my-image

--memory-swap--memoryと同じ値に設定すると、スワップ領域を使用しないため、より予測可能な動作になります。

docker-compose.ymlの場合

version: '3.8'
services:
  my-app:
    image: my-image
    deploy:
      resources:
        limits:
          memory: 2G
        reservations:
          memory: 1G

reservationsはソフトリミットで、limitsはハードリミットです。

注意点

  • メモリ制限は実際の使用量の1.5〜2倍程度に設定することを推奨
  • 急激なトラフィック増加に備えて余裕を持たせる
  • 制限値はOSのページサイズ(通常4KB)の倍数に丸められる
  • スワップを無効にすると、メモリ不足時に即座にOOMKilledが発生する

解決方法2: Javaアプリケーションのヒープ設定

適切なJVMオプションの設定

Javaアプリケーションの場合、以下のベストプラクティスに従ってヒープサイズを設定します:

# コンテナのメモリ制限が2GBの場合、ヒープは1.5GB程度に
docker run --memory=2g \
  -e JAVA_OPTS="-Xms1536m -Xmx1536m" \
  my-java-app

重要なポイント:
– コンテナのメモリ制限が2GBなら、-Xmxは1.5GB(75%)程度に設定
– -Xmsと-Xmxを同じ値にすると、GCの動作が予測可能になる
– 残りの25%はメタスペース、スタック、ネイティブメモリ用に確保

コンテナ検出機能の活用

Java 10以降(およびJava 8u191以降)では、JVMがコンテナのメモリ制限を自動検出する機能が有効になっています:

# Java 8u191以降で明示的に有効化
-XX:+UseContainerSupport

# 古いJavaバージョン(8u131〜8u190)の場合
-XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap

Dockerfile での設定例

FROM openjdk:17-jdk-slim

ENV JAVA_OPTS="-Xms512m -Xmx1536m -XX:+UseContainerSupport"

COPY target/app.jar /app.jar

ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar /app.jar"]

解決方法3: 高度なトラブルシューティング

メモリプロファイリングの実施

アプリケーションのメモリ使用パターンを詳細に分析するには、プロファイリングツールを使用します:

Javaの場合

# JVM内蔵のメモリ監視
docker exec <container> jstat -gc <pid> 1000

# ヒープダンプの取得
docker exec <container> jmap -dump:format=b,file=/tmp/heap.hprof <pid>
docker cp <container>:/tmp/heap.hprof ./heap.hprof

Node.jsの場合

# メモリ使用量の制限を設定
docker run --memory=1g \
  -e NODE_OPTIONS="--max-old-space-size=768" \
  my-node-app

cgroupメモリ統計の確認

コンテナ内部から詳細なメモリ統計を確認できます:

# cgroup v2の場合
docker exec <container> cat /sys/fs/cgroup/memory.current
docker exec <container> cat /sys/fs/cgroup/memory.max

# cgroup v1の場合
docker exec <container> cat /sys/fs/cgroup/memory/memory.usage_in_bytes
docker exec <container> cat /sys/fs/cgroup/memory/memory.limit_in_bytes

Docker Desktopのリソース設定変更

Windows/Macの場合、Docker Desktopの設定でメモリ割り当てを増やします:

  1. Docker Desktopを開く
  2. Settings(設定)→ Resources(リソース)に移動
  3. Memoryスライダーで割り当て量を増加
  4. Apply & Restartをクリック

Windows(WSL2)の場合、.wslconfigファイルでも設定可能:

[wsl2]
memory=8GB
swap=2GB

エラーを予防するには

モニタリングの導入

メモリ使用量を継続的に監視し、閾値を超えた場合にアラートを発報する仕組みを導入しましょう:

# Prometheusメトリクスを収集するcAdvisorの起動
docker run -d \
  --name cadvisor \
  -p 8080:8080 \
  -v /:/rootfs:ro \
  -v /var/run:/var/run:ro \
  -v /sys:/sys:ro \
  -v /var/lib/docker/:/var/lib/docker:ro \
  gcr.io/cadvisor/cadvisor:latest

Kubernetesでのリソース管理

Kubernetes環境では、リソースリクエストとリミットを適切に設定します:

apiVersion: v1
kind: Pod
metadata:
  name: my-app
spec:
  containers:
  - name: app
    image: my-image
    resources:
      requests:
        memory: "512Mi"   # 最低保証量
      limits:
        memory: "1Gi"     # 最大使用量

定期的なメンテナンス

  • アプリケーションのメモリリークを定期的にチェック
  • 使用していないイメージやコンテナを削除:docker system prune
  • ログファイルのローテーション設定を確認
  • 依存ライブラリを最新版に更新してメモリ効率を改善

まとめ

Docker Exit Code 137(OOMKilled)エラーは、コンテナがメモリ不足で強制終了されたことを示す重要なシグナルです。この問題を解決するための主要なポイントをまとめます:

確認すべきこと:
docker statsでメモリ使用量を確認
docker inspectでOOMKilledフラグを確認
– Javaアプリケーションの場合はヒープ設定を見直す

解決のアプローチ:
1. コンテナのメモリ制限を適切な値(実使用量の1.5〜2倍)に設定
2. Javaアプリケーションは-Xmxをコンテナ制限の75%程度に設定
3. Docker Desktop(Windows/Mac)のメモリ割り当てを増加
4. メモリリークがないかプロファイリングで確認

これらの対策を講じても問題が解決しない場合は、アプリケーションのアーキテクチャ自体を見直すか、より大きなメモリを持つサーバーへの移行を検討してください。また、Kubernetes環境では水平ポッドオートスケーリング(HPA)を活用して、負荷に応じてPod数を自動調整することも有効です。


参考資料

コメント

タイトルとURLをコピーしました