Contents
OOMKilled の概要と Kubernetes におけるステータス表示
Kubernetes 上でコンテナがメモリ上限を超えて強制終了されると、Pod の status.reason に OOMKilled が記録されます。この情報はカーネルの OOM Killer がプロセスを殺した事実を Kubelet が取得し、API サーバーに返す仕組みです。本節では、OOMKilled がどのように生成されるかと、ステータスを見る際のポイントを解説します。
OOM Killer と Kubelet の連携
Kubernetes は各コンテナに cgroup を割り当て、memory.limit_in_bytes で上限を設定します。メモリが不足するとカーネルは OOM Killer を起動し対象プロセスを終了させます。その直後、Kubelet が cgroup.events 等から OOM イベントを取得し、Pod のステータスに反映させます。
Pod が OOMKilled になる条件
- コンテナの cgroup メモリ上限 (
memory.limit_in_bytes) を実際使用量が超える。 - ノード全体でメモリ圧迫が発生し、Kubelet の eviction ポリシーに従って対象 Pod が選択される。
kubectl get pod … -o jsonpath='{.status.containerStatuses[0].state.terminated.reason}'が OOMKilled を返す場合は、上記いずれかが起因しています。
詳細なトラブルシューティング手順は GKE 公式ドキュメント(2026年3月版)をご参照ください → https://cloud.google.com/kubernetes-engine/docs/troubleshooting/oom-events?hl=ja
Pod のリソース設定と QoS クラスの関係
Pod が OOMKilled になるか否かは、requests / limits と QoS(Quality of Service)クラス の組み合わせで決まります。本節ではそれぞれの役割と相互作用を整理し、実務で陥りがちな落とし穴を具体例と共に示します。
resources.requests と resources.limits の役割
requests は Scheduler が Pod を配置する際の基準、limits は cgroup に設定されるメモリ上限です。両者が一致していないと QoS が低下し、OOM 発生時に優先度が下がります。
| 設定例 | requests (Mi) | limits (Mi) | QoS クラス |
|---|---|---|---|
| A | 200 | 400 | Burstable |
| B | 300 | 300 | Guaranteed |
| C | - | 500 | BestEffort* |
* requests が未設定の場合は BestEffort とみなされます。
- リクエスト未設定 → デフォルトは 0(BestEffort)。ノードが過負荷になると最初に対象になります。
- リミット未設定 → コンテナはノード全体の空きメモリを無制限に使用でき、突発的なスパイクで OOM Killer が介入しやすくなります。
QoS クラス(Guaranteed / Burstable / BestEffort)の判定基準
QoS は requests と limits の有無・一致度で自動的に決まります。クラスごとの特徴は次の通りです。
- Guaranteed: すべてのコンテナが
requests == limitsを満たす。メモリ不足時でも最も保護されます。 - Burstable: 少なくとも一つのコンテナで
requests < limits、または一部未設定。通常は保護されますが、スパイクでlimits超過すると対象になります。 - BestEffort:
requestsもlimitsも未設定。ノード圧迫時に最初に削除対象となります。
LimitRange と ResourceQuota による組織全体のリソース上限設定は、公式ドキュメントに従って導入してください。
主な原因別診断手順
OOMKilled が検知されたら、まずは以下のチェックリストを順番に実行します。すべて kubectl とクラスタ内部ツールだけで完結できるよう設計しています。
メモリ上限不足の検証方法
現在設定されている limits.memory が実際の使用量を下回っていないか確認します。
|
1 2 3 4 5 |
# Pod のリミット取得 kubectl get pod myapp -o jsonpath='{.spec.containers[*].resources.limits.memory}' # 現在のメモリ使用量(metrics-server が有効な場合) kubectl top pod myapp --containers |
| 実測使用量 (Mi) | 設定上限 (Mi) | 判定 |
|---|---|---|
| 450 | 400 | 超過 → OOM の可能性 |
| 300 | 500 | OK |
- 対策:
limitsが足りない場合は Pod 定義を更新し、上限値を引き上げます。
リクエスト未設定・過小設定のチェック
requests.memory が未設定または実際の負荷に比して極端に低いと、Scheduler が不適切なノードへ割り当て、結果的に OOM が起きやすくなります。
|
1 2 |
kubectl describe pod myapp | grep -A3 "Requests" |
| コンテナ | requests (Mi) | 実測使用量 (Mi) | 判定 |
|---|---|---|---|
| web | 100 | 350 | 過小 |
| worker | 200 | 210 | OK |
- 対策: ベンチマーク結果の 1.5 倍程度を目安に
requestsを設定し直します。
ノード全体のメモリ圧迫と cgroup 設定ミス
ノード自体がメモリ不足になると、Kubelet が QoS の低い Pod を優先的に削除します。まずはノードの使用率を把握しましょう。
|
1 2 3 4 5 |
# ノードのメモリ使用状況 kubectl top node # Kubelet の eviction 設定確認(/var/lib/kubelet/config.yaml) grep eviction /etc/kubernetes/kubelet.conf |
| ノード | 容量 (Gi) | 使用率 (%) | 判定 |
|---|---|---|---|
| gke-node-1 | 64 | 92 | 圧迫 |
| gke-node-2 | 64 | 45 | OK |
- 対策: Node Auto‑Provisioning を有効にし、スケールアウトで余裕を確保します。
カーネル OOM Killer のログ確認
Kubernetes が取得できない情報(例:他プロセスとの競合)はカーネルログに出力されます。dmesg で直近の OOM エントリを抽出しましょう。
|
1 2 3 |
# OOM Killer の直近ログを抽出 sudo dmesg | grep -i "oom-killer" | tail -n 20 |
典型的な出力例
|
1 2 3 |
[12345.678901] Out of memory: Kill process 9876 (myapp) score 945 or sacrifice child [12345.679012] Killed process 9876 (myapp) total-vm:204800kB, anon-rss:150000kB, file-rss:5000kB |
- 対策:
memory.limit_in_bytesが期待値より低い場合は、Pod のlimitsを再評価してください。
診断フローチャート(表形式)
| ステップ | 実行コマンド | 判定基準 | 次のアクション |
|---|---|---|---|
| 1 | kubectl describe pod … |
status.reason=OOMKilled 有無 |
無 → 別原因調査、終了 |
| 2 | kubectl top pod … / limits 確認 |
使用量 > limits | limits 増加 |
| 3 | requests 確認 |
未設定 or 過小 | requests 再設定 |
| 4 | kubectl top node |
ノード使用率 > 85% | Node Auto‑Provisioning 設定 |
| 5 | dmesg | grep oom |
カーネルログに OOM エントリ | cgroup 設定見直し |
予防策と自動化機能
OOMKilled を根本的に抑えるには、リソース設定の標準化と自動スケーリングの活用が鍵です。本節では組織レベルで有効な制御手段と、ノード・Pod の自動調整機構について具体例を交えて紹介します。
LimitRange と ResourceQuota による組織的制御
Namespace 単位でデフォルトの requests/limits を強制し、BestEffort Pod の作成を防ぎます。以下は典型的な LimitRange 定義です。
|
1 2 3 4 5 6 7 8 9 10 11 12 |
apiVersion: v1 kind: LimitRange metadata: name: default-mem-lr spec: limits: - type: Container default: memory: "512Mi" defaultRequest: memory: "256Mi" |
この設定により、memory.request が未指定の場合は自動的に 256 Mi が適用されます。さらに ResourceQuota を併用すれば、名前空間全体のメモリ使用上限を管理できます。
Vertical Pod Autoscaler と Node Auto‑Provisioning の活用
VPA は実行中の Pod の CPU・メモリ使用状況を解析し、推奨 requests/limits を自動算出します。Node Auto‑Provisioning(GKE)と組み合わせれば、クラスター全体のリソース需要に応じてノード数が増減します。
|
1 2 3 4 5 6 7 8 9 10 11 12 |
apiVersion: autoscaling.k8s.io/v1 kind: VerticalPodAutoscaler metadata: name: myapp-vpa spec: targetRef: apiVersion: "apps/v1" kind: Deployment name: myapp updatePolicy: updateMode: "Auto" |
|
1 2 3 4 |
gcloud container clusters update my-cluster \ --enable-autoprovisioning \ --max-cpu=32 --max-memory=128Gi |
ノードレベルの sysctl 設定と OOM Score Adj
Kubernetes のノードでは swap を無効化、vm.overcommit_memory=1 を設定することで OOM Killer の挙動が予測しやすくなります。また重要サービスには oom_score_adj を負の値にしてカーネルからの殺害対象外にできます。
|
1 2 3 4 5 6 7 8 |
# swap 無効化と永続化 sudo swapoff -a echo "vm.swappiness=0" | sudo tee -a /etc/sysctl.conf # overcommit 設定 echo "vm.overcommit_memory=1" | sudo tee -a /etc/sysctl.conf sudo sysctl -p |
|
1 2 3 |
# コンテナ起動時に OOM Score Adj を設定(例) ENV OOM_SCORE_ADJ=-1000 |
- ポイント: これらのシステムレベル調整はノード全体の安定性を高め、Kubernetes が正確にメモリ圧迫を検知できるようにします。
実践的チェックリストと事例
典型パターン別 OOMKilled 発生シナリオ
以下は現場でよく見られる OOMKilled の原因と、発見手段・推奨対策をまとめた表です。
| パターン | 主な原因 | 発見手段 | 推奨対策 |
|---|---|---|---|
| メモリリーク | アプリが GC できず徐々に使用量増加 | kubectl top pod の時系列推移 + ログ解析 |
limits 増加、VPA 導入、コード修正 |
| 突発的スパイク負荷 | バッチ処理や突発トラフィック | イベント直後の kubectl describe pod Events |
Burstable → Guaranteed 変更、HPA と併用 |
| ノード競合 | 複数高メモリ Pod が同一ノードに集積 | kubectl get nodes -o wide + kubectl top node |
Node Auto‑Provisioning、有効化、PodAntiAffinity 設定 |
| cgroup 設定ミス | limits 未設定・単位ミス (Mi vs Gi) | YAML 検証ツール(kubeval)で CI に組込む | 正しい単位で limits を明示、Lint 導入 |
対策フローと即適用できる設定サンプル
実際に OOMKilled が発生したら次の手順で迅速に対処します。
- リソース定義確認
bash
kubectl get pod <pod> -o yaml | grep -A4 resources: - QoS 判定 →
kubectl describe podのQoS Classを確認。 - ノード状態把握
bash
kubectl top node - カーネルログ確認
bash
sudo dmesg | grep -i oom | tail -n 10 - 修正例(Limits と Requests の統一)
yaml
resources:
requests:
memory: "512Mi"
limits:
memory: "1024Mi"
- LimitRange 設定で組織全体に適用(前述の YAML を利用)。
- VPA と Node Auto‑Provisioning の有効化
bash
gcloud container clusters update my-cluster \
--enable-autoprovisioning --max-cpu=32 --max-memory=128Gi
- 再デプロイ後の検証
bash
kubectl top pod <pod> --containers
まとめ
- OOMKilled の根本原因は cgroup のメモリ上限超過。
status.reason=OOMKilledが示す情報をまず確認することが第一歩です。 - requests / limits と QoS クラスの整合性 を保つことで、Pod が OOM 発生時に最優先で残るかどうかが決まります。Guaranteed もしくは Burstable に統一し、BestEffort は極力回避しましょう。
- 診断手順は
kubectl describe,kubectl top,dmesgの組み合わせ で進め、原因別にチェックリストを適用すれば迅速に根本要因を特定できます。 - 予防策としては LimitRange / ResourceQuota による標準化と、VPA・Node Auto‑Provisioning といった自動化機能の導入 が有効です。さらにノードレベルで swap 無効化や
vm.overcommit_memory設定を行うことで、カーネル側の挙動も安定させられます。 - 実践チェックリストとフローを日常的に活用 すれば、典型的な OOM パターンへの対処が体系化され、障害復旧時間を大幅に短縮できます。
これらのベストプラクティスを導入することで、Kubernetes 環境における OOMKilled の発生頻度とインシデント対応コストを効果的に低減できるでしょう。