requests と limits の仕組み
v1.37 時点(2026年10月26日 確認)
目次
この記事では、コンテナが使う CPU とメモリの量を Pod のマニフェストに書く requests と limits の仕組みを図で説明します。requests をどのコンポーネントが何に使うのか、limits を超えようとしたコンテナに何が起きるのかがわかります。requests と limits の書き方で決まる QoS クラスと、ノードの資源が足りなくなったときに、どの Pod が退避させられやすいかもわかります。後半では kind のクラスタで、メモリの limits を超えたコンテナが OOMKilled になる様子を観察する手順を示します。
この回から第6部(スケジューリングとリソース)に入ります。Pod がノードに置かれ、そのノードに留まることを扱った第8回の内容を前提にしています。まだの方は先にこちらをどうぞ。
Pod を動かして理解するKubernetes で作って管理できる最小の単位である Pod の仕組みを図で説明します。Pod とコンテナの関係、Pod の中のコンテナが共有するもの、Pod が 1 つのノードに置かれて移らないことを押さえ、kind のクラスタで Pod を直接作って削除し、もう戻らないことを確かめる手順を紹介します。以下の図は Pod のマニフェストに書いた requests と limits をどのコンポーネントが使うかを表しています。
requests は、kube-scheduler が Pod を置くノードを選ぶのに使います。limits は、ノードの kubelet がコンテナランタイム(第4回)に渡します。コンテナランタイムが cgroup で limits を適用し、動いているコンテナが limits より多くの資源を使えないようにします。ただし、メモリでは少し違います(あとで説明します)。第19回で resourceFieldRef を使って、コンテナの中から読んだのがこの 2 つの値です。図のマニフェストの値はあとで引用する公式ドキュメントの例のコンテナ 1 つ分です。
参考:Resource Management for Pods and Containers | Kubernetes、Resource Management for Pods and Containers | Kubernetes
requests と limits とは
公式ドキュメントによると、Pod を書くとき、コンテナごとにそれぞれの資源をどれだけ必要とするかを任意で書けます。よく書くのは CPU とメモリ(RAM)で、ほかの資源もあります。requests を書くと、kube-scheduler はその情報を使って Pod を置くノードを決めます。limits を書くと、kubelet はその limits を守らせ、動いているコンテナが limits より多くの資源を使えないようにします。ただし、メモリでは少し違います。詳しくは「limits を超えようとしたときの CPU とメモリの違い」で説明します。kubelet は少なくとも requests の量の資源を、そのコンテナが使う分として取っておきます。
API リファレンスは 2 つのフィールドを次のように説明しています。requests と limits は、コンテナに必要な資源の最小の量(requests)と、使ってよい資源の最大の量(limits) です。requests は limits を超えられません。
ここでいう資源は CPU やメモリのことです。公式ドキュメントは CPU とメモリをまとめて compute resources(計算の資源)、または単に resources と呼びます。これは Pod や Service のような、kube-apiserver を通して読み書きする API のリソースとは別のものだ、と書かれています。どちらも英語では resource なので、この記事では CPU やメモリを「資源」、Pod などを「リソース」と書き分けます。
Kubernetes に組み込まれている資源の種類は、v1.37 時点の公式ドキュメントでは次の 4 つです。この回では CPU とメモリを扱います。
| 資源の種類 | 内容 | 基本の単位 |
|---|---|---|
cpu | 計算の処理 | CPU(コア) |
memory | メモリ(RAM) | バイト |
ephemeral-storage | ノードのローカルな一時的なストレージ | バイト |
hugepages-<size> | huge pages(Linux だけ) | バイト |
huge pages は第19回で触れたとおり、メモリを大きな単位で割り当てる仕組みです。公式ドキュメントによると、hugepages-* は CPU やメモリと違い、ノードにある以上の量を割り当てること(overcommit)ができません。
マニフェストに書く場所
requests と limits はコンテナごとに resources の下に書きます。CPU なら spec.containers[*].resources.requests.cpu と spec.containers[*].resources.limits.cpu、メモリなら spec.containers[*].resources.requests.memory と spec.containers[*].resources.limits.memory です([*] は第8回で見た「各項目の」という意味で、公式ドキュメントでは [] と書かれています)。以下は公式ドキュメントの例です。2 つのコンテナを入れた Pod で、どちらのコンテナにも requests と limits を書いています。
---
apiVersion: v1
kind: Pod
metadata:
name: frontend
spec:
containers:
- name: app
image: images.my-company.example/app:v4
resources:
requests:
memory: "64Mi"
cpu: "250m"
limits:
memory: "128Mi"
cpu: "500m"
- name: log-aggregator
image: images.my-company.example/log-aggregator:v6
resources:
requests:
memory: "64Mi"
cpu: "250m"
limits:
memory: "128Mi"
cpu: "500m"イメージの名前(images.my-company.example/...)は架空の会社のもので、この例は説明のためのものです。公式ドキュメントによると、どちらのコンテナも requests は 0.25 CPU とメモリ 64MiB、limits は 0.5 CPU とメモリ 128MiB です。Pod 全体で見ると、requests は 0.5 CPU とメモリ 128MiB、limits は 1 CPU とメモリ 256MiB だと言えます。requests と limits はコンテナごとに書くものですが、ある資源についての Pod の requests と limits は、Pod の中の各コンテナの値を足したものです。
参考:Resource Management for Pods and Containers | Kubernetes、Resource Management for Pods and Containers | Kubernetes
CPU とメモリの量の書き方
第19回でも短く見た量の書き方を、公式ドキュメントに沿って次の表にまとめます。
| 資源 | 単位と書き方 |
|---|---|
| CPU | 1 は、ノードが物理的なマシンなら物理的な CPU のコア 1 つ、仮想マシンなら仮想的なコア 1 つにあたる。小数も書ける。0.1 と 100m は同じ量で、100m は「100 ミリ CPU」と読む |
| メモリ | バイトで数える。整数のほか、E、P、T、G、M、k の単位や、2 のべき乗の Ei、Pi、Ti、Gi、Mi、Ki の単位を付けて書ける |
参考:Resource Management for Pods and Containers | Kubernetes、Resource Management for Pods and Containers | Kubernetes
CPU について、公式ドキュメントは次の点も説明しています。requests.cpu を 0.5 にすると、1.0 のときの半分の CPU の時間を求めることになります。CPU の量はいつも絶対的な量で表し、相対的な量では表しません。たとえば 500m の CPU は、コンテナが 1 コアのマシンで動いても、2 コアや 48 コアのマシンで動いても、おおよそ同じ計算の能力を表します。また、1m(0.001 CPU)より細かい量は書けません。そのため公式ドキュメントは、1 CPU より少ない量は、小数ではなくミリ CPU の形で書くと便利だとしています。0.0005 より 0.5m のほうが書けない値だと気づきやすいからです。
メモリの単位について、API リファレンスの量(Quantity)の説明には、1024 は 1Ki だが 1000 は 1k だ、という注記があります。Ki や Mi は 1024 倍ずつ、k や M は 1000 倍ずつ大きくなる単位です。第19回で引用した公式ドキュメントの例でも、32Mi と 64Mi は、バイトで表すと 33554432 と 67108864 でした。公式ドキュメントは、次の値がどれもおおよそ同じ量を表す例だとしています。
参考:Quantity | Kubernetes API Reference、Resource Management for Pods and Containers | Kubernetes
メモリでは単位の大文字と小文字を取り違えないよう注意します。公式ドキュメントによると、M はメガバイトですが、m はミリバイト(1 バイトの 1000 分の 1)です。メモリを 400m と書くと 0.4 バイトを求めることになります。そう書いた人はおそらく 400Mi か 400M のつもりだったのだろう、と公式ドキュメントは注意しています。CPU の m と同じ文字ですが、メモリでは意味が変わります。
なぜ requests と limits が必要なのか
第8回で見たとおり、Pod はノードに置かれ、そのノードで動きます。1 つのノードにはいくつもの Pod が置かれ、ノードの CPU とメモリを分け合います。kube-scheduler が Pod を置くノードを選ぶときに、その Pod がどれだけの資源を必要とするかがわからなければ、ノードに収まるかどうかを判断できないと考えられます。それを伝えるのが requests です。
公式ドキュメントによると、kube-scheduler は資源の種類ごとに、ノードに置いたコンテナの requests の合計が、ノードが Pod に提供できる量を超えないようにします。この量を公式ドキュメントは capacity と書いていますが、あとで見る Node の status.capacity(ノードの資源の全体の量)とは分けて考えます。API リファレンスはスケジューリングに使える量を status.allocatable としています。ノードの実際の CPU やメモリの使用量がとても低くても、この確認に通らなければ、kube-scheduler はそのノードに Pod を置きません。あとで使用量が増えたとき(たとえば、1 日のうちでリクエストがいちばん多い時間帯)に、ノードの資源が足りなくなるのを防ぐためです。
参考:Resource Management for Pods and Containers | Kubernetes、Node | Kubernetes API Reference
一方、limits を書かないと、コンテナが使える量に上限がありません。公式ドキュメントによると、メモリの limits を書かず、Namespace にも既定の limits(LimitRange で決める既定の値。第30回)が無いとき、コンテナはノードで使えるメモリをすべて使うことがあり、それが OOM Killer を呼び出すことがあります。OOM Killer は一般に、メモリが足りなくなったときに、Linux のカーネルがプロセスを選んで強制終了させる仕組みです。さらに、OOM で強制終了が起きるときは、limits の無いコンテナのほうが強制終了されやすいと書かれています。CPU の limits を書かなかったときも、コンテナはノードで使える CPU をすべて使うことがあります。
参考:Assign Memory Resources to Containers and Pods | Kubernetes、Assign CPU Resources to Containers and Pods | Kubernetes
公式ドキュメントは requests と limits を書き分ける理由を次のように説明しています。requests を低めにしておくと、Pod がノードに置かれやすくなります。limits を requests より大きくしておくと、次の 2 つが両立します。
- 処理が一時的に増えたとき(バースト)に、たまたま空いているメモリや CPU を使える
- バーストのときに使える量を、ほどよい量までに抑えられる
参考:Assign Memory Resources to Containers and Pods | Kubernetes、Assign CPU Resources to Containers and Pods | Kubernetes
requests と limits の仕組み
requests で Pod を置くノードが決まる
第4回で見たとおり、kube-scheduler はノードが決まっていない Pod を見つけて、その Pod を動かすノードを選びます。公式ドキュメントによると、ノードの選び方は、条件に合うノードを絞り込む filtering と、残ったノードに点数を付ける scoring の 2 段階です。filtering の例として、PodFitsResources というフィルタが挙がっています。候補のノードに、Pod の requests を満たすだけの空いた資源があるかを確かめるものです。条件に合うノード(feasible なノード)が 1 つも無ければ、Pod は置ける場所が見つかるまで、どのノードにも割り当てられないままになります。
参考:Kubernetes Scheduler | Kubernetes、Kubernetes Scheduler | Kubernetes
ノードの資源の量は Node の status の 2 つのフィールドに表れます。v1.37 時点の公式ドキュメントと API リファレンスの説明を次の表にまとめます。
| フィールド | 内容 |
|---|---|
status.capacity | ノードが持つ資源の全体の量 |
status.allocatable | ノードの資源のうち、ふつうの Pod が使える量。スケジューリングに使える資源。書かれていなければ capacity と同じ |
公式ドキュメントによると、Pod が使える資源の量は、ノードの容量より少なくなります。システムのデーモン(ノードで動き続けるプログラム)が、資源の一部を使うからです。公式ドキュメントは、Pod が使える資源の量を表すのが status.allocatable だとし、詳しくは node allocatable の資源についてのページを見るよう案内しています。第19回で触れた node allocatable は、この status.allocatable が表す量のことだと読めます。
参考:Resource Management for Pods and Containers | Kubernetes、Reserve Compute Resources for System Daemons | Kubernetes
どのノードにも収まらない Pod は、第9回で見た Pending のフェーズのまま残ります。公式ドキュメントによると、kube-scheduler は Pod の置き場所を見つけられないたびに、イベント(Event)を記録します。公式ドキュメントの例では、kubectl describe pod の Events: は次のとおりです。
例は Pod frontend が、どのノードでも CPU が足りないために割り当てられなかった、という意味です。第8回で見たとおり、default-scheduler は既定のスケジューラー(kube-scheduler)の名前です。公式ドキュメントは対処の例として、ノードを足す、要らない Pod を終了させて場所を空ける、Pod がどのノードよりも大きくないかを確かめる、などを挙げています。たとえば、すべてのノードの容量が cpu: 1 なら、cpu: 1.1 を求める Pod はいつまでも置かれません。
requests は「この量までしか使えない」という上限ではありません。公式ドキュメントによると、Pod が動いているノードに資源の空きが十分にあれば、コンテナが requests より多くの資源を使うことはできますし、許されています。たとえば、コンテナのメモリの requests を 256MiB にして、メモリが 8GiB でほかに Pod の無いノードに置いた場合、そのコンテナはもっと多くのメモリを使おうとすることができます。
第10回で扱った init コンテナとサイドカーがある Pod では、Pod の requests の数え方が少し変わります。公式ドキュメントによると、ある資源についての Pod の実際の requests(effective request)は、Pod overhead に、次の 2 つの大きいほうを足したものです。
- init コンテナではないコンテナ(アプリケーションコンテナとサイドカー)の requests の合計
- すべての init コンテナの requests のうち、いちばん大きい値
第10回で見たとおり、サイドカーはマニフェストでは initContainers に書きます(コンテナごとの restartPolicy を Always にします)。ただし、この数え方ではサイドカーは 1 つ目の合計の側に入ります。Pod overhead は一般に、Pod を動かすための仕組み自体が使う資源で、この回では扱いません。kube-scheduler はこの effective request をもとに Pod を置きます。
参考:Sidecar Containers | Kubernetes、Sidecar Containers | Kubernetes、Init Containers | Kubernetes
limits を超えようとしたときの CPU とメモリの違い
公式ドキュメントによると、kubelet は Pod のコンテナを起動するとき、そのコンテナの CPU とメモリの requests と limits をコンテナランタイムに渡します。Linux では、コンテナランタイムはふつう、カーネルの cgroup を設定し、その cgroup が limits を適用して守らせます。第8回で見たとおり、cgroup はプロセスに割り当てる資源を制限する Linux の仕組みです。最終的に limits を守らせるのはカーネルで、その守らせ方は CPU とメモリとで少し違います。
参考:Resource Management for Pods and Containers | Kubernetes、Resource Management for Pods and Containers | Kubernetes
CPU の limits は CPU のスロットリング(throttling。使える量を絞ること)で守らせます。公式ドキュメントによると、コンテナが CPU の limits に近づくと、カーネルはそのコンテナが CPU を使うのを limits の分までに制限します。CPU の limits はコンテナが使える CPU の時間のはっきりした上限です。カーネルは CPU の時間を割り振る区切り(公式ドキュメントでは scheduling interval、time slice)ごとに limits を超えていないかを確かめ、超えていれば、その cgroup の実行を再開させる前に待たせます。ここでいうスケジューリングは一般に、カーネルがどのプロセスに CPU を使わせるかを決めることで、kube-scheduler が Pod を置くノードを選ぶこととは別のものです。CPU の limits を超えて CPU を使いすぎても、コンテナランタイムが Pod やコンテナを終了させることはありません。
参考:Resource Management for Pods and Containers | Kubernetes、Resource Management for Pods and Containers | Kubernetes
公式ドキュメントの CPU のタスクのページにも例があります。2 つの CPU を使おうとするコンテナの CPU の limits を 1 にすると、コンテナはおよそ 1 つの CPU しか使えず、CPU の使用はスロットリングされている、と説明されています。
メモリの limits は OOM(out of memory。メモリ不足)による強制終了(OOM kill)で守らせます。公式ドキュメントによると、コンテナがメモリの limits より多くのメモリを使うと、カーネルはそのコンテナを終了させることがあります。ただし、終了させるのは、カーネルがメモリの逼迫(memory pressure)を検知したときだけです。そのため、メモリを使いすぎたコンテナが、すぐに強制終了されるとは限りません。公式ドキュメントはこれを、メモリの limits は事後に(reactively)守らせるもの、と表現しています。コンテナが limits より多くのメモリを使うことはあり得ますが、そうすると強制終了されることがあります。
ここでいうメモリの逼迫が、そのコンテナの limits の範囲の話なのか、ノード全体の話なのかは、このページでは区別されていません。ただし、公式ドキュメントのメモリのタスクのページによると、limits より多くのメモリを確保したコンテナは終了させられる候補になり、limits を超えてメモリを使い続けると終了させられます。後半の「手元で試す」のように、limits を大きく超える量を確保し続けるコンテナは、強制終了されるはずです。
参考:Resource Management for Pods and Containers | Kubernetes、Assign Memory Resources to Containers and Pods | Kubernetes
強制終了のされ方について、公式ドキュメントは次のように説明しています。コンテナが limits より多くのメモリを確保しようとすると、Linux のカーネルの、メモリ不足を扱う仕組みが働きます。この仕組みはふつう、メモリを確保しようとしたコンテナの中のプロセスの 1 つを止めることで介入します。止めたプロセスがコンテナの PID 1 で、コンテナが再起動できる設定なら、Kubernetes はコンテナを再起動します。PID 1 は一般に、コンテナの中で最初に起動されるプロセスのことです。止められたのが PID 1 ではないプロセスなら、コンテナそのものは動き続けることもあると考えられます。
以下の図は CPU とメモリの limits を超えようとしたコンテナに、カーネルがどう対応するかの違いを表しています。
CPU は使いすぎても待たされるだけで、コンテナは動き続けます。メモリは使いすぎると、カーネルにコンテナのプロセスを強制終了されることがあります。図の赤は異常な終わり方であることを表しています。
limits だけでなく requests も、動いているあいだの資源の分け方に使われます。CPU とメモリの requests と limits の働きを、公式ドキュメントに沿って次の表にまとめます。
| CPU | メモリ | |
|---|---|---|
| requests | kube-scheduler が Pod を置くノードを選ぶのに使う。動いているあいだは、ふつう重み付けとして働く。CPU を取り合うとき、requests の大きいものほど多くの CPU の時間を割り当てられる | 主に kube-scheduler が Pod を置くノードを選ぶのに使う。cgroup v2 のノードでは、コンテナランタイムが memory.min と memory.low を設定する目安に使うことがある |
| limits を超えようとしたとき | スロットリングされる。CPU の使いすぎで終了させられることはない | カーネルがメモリの逼迫を検知すると、強制終了されることがある。limits を超えて使い続けると、強制終了される |
参考:Resource Management for Pods and Containers | Kubernetes、Assign Memory Resources to Containers and Pods | Kubernetes
cgroup v2 は cgroup の新しいバージョンです。memory.min と memory.low はメモリを取り上げられにくくする cgroup v2 の設定です。公式ドキュメントによると、memory.min を設定したメモリは、カーネルがどんな場合も取り戻しません。memory.low を設定したメモリは、カーネルが優先して残しますが、逼迫がひどいときは取り戻すことがあります。この回では名前だけ知っておけば十分です。
公式ドキュメントによると、emptyDir のようなメモリを使うボリュームでは、そこに置いたデータが使うメモリも、メモリの limits の対象になることがあります。kubelet はメモリを使う emptyDir を、一時的なストレージではなくコンテナのメモリの使用として数えます。ボリュームは第26回で見ました。
参考:About cgroup v2 | Kubernetes、Pod Quality of Service Classes | Kubernetes、Resource Management for Pods and Containers | Kubernetes
OOMKilled になったコンテナのその後
メモリの limits を超えて強制終了されたコンテナは、コンテナの状態の理由に OOMKilled が記録されます。公式ドキュメントの例では、kubectl describe pod の表示は次のとおりです(一部を省きました)。
公式ドキュメントによると、Restart Count: の 5 は、Pod の中のコンテナ simmemleak がこれまでに 5 回終了させられて再起動されたことを表します。Last State: の Reason: の OOMKilled は、コンテナが limits より多くのメモリを使おうとしたことを表します。第9回で見たとおり、Last State: は 1 つ前のコンテナの終わり方です。終了コード(Exit Code:)の 137 は一般に、128 に 9 を足した値で、9 番のシグナル(SIGKILL。強制終了の合図)で終わったことを表すと読めます。
公式ドキュメントのメモリのタスクのページによると、limits より多くのメモリを確保したコンテナは、終了させられる候補になります。limits を超えてメモリを使い続けると、コンテナは終了させられます。終了させられたコンテナが再起動できるなら、kubelet はほかの実行時の失敗と同じように、そのコンテナを再起動します。第9回で見たとおり、再起動するかどうかは Pod の restartPolicy で決まります。
参考:Assign Memory Resources to Containers and Pods | Kubernetes、Pod Lifecycle | Kubernetes
以下の図はメモリの limits を超えたコンテナの状態の移り変わりを表しています。第9回のコンテナの状態の図に、OOMKilled を当てはめたものです。
再起動しても、コンテナのメモリの使い方が変わらなければ、また limits を超えて強制終了されると考えられます。第9回で見たとおり、失敗を繰り返すコンテナの再起動の前には、倍々に延びる待ち時間が入ります。公式ドキュメントは CrashLoopBackOff の原因の 1 つに、起動に必要なメモリや CPU が足りないことを挙げています。公式ドキュメントのメモリのタスクのページの例でも、kubectl describe pod の表示に Back-off restarting failed container という行が出ています(後半の「手元で試す」で引用します)。
参考:Pod Lifecycle | Kubernetes、Assign Memory Resources to Containers and Pods | Kubernetes
公式ドキュメントは次の手として、アプリケーションのコードにメモリリーク(使い終わったメモリを返さずに、使用量が増え続ける不具合)が無いかを確かめることを挙げています。アプリケーションが期待どおりに動いているなら、そのコンテナのメモリの limits を(場合によっては requests も)上げることを検討するよう書かれています。
片方だけ書いたときと何も書かないとき
requests と limits は両方を書かなくても構いません。書き方ごとに、v1.37 時点の公式ドキュメントと API リファレンスの説明を次の表にまとめます。
| 書き方 | requests | limits |
|---|---|---|
| 両方を書く | 書いた値。limits を超えられない | 書いた値 |
| limits だけを書く | limits の値がコピーされる。ただし、アドミッション(すぐ後で説明する)の仕組みで既定の requests が入る場合を除く | 書いた値 |
| requests だけを書く | 書いた値 | 上限なし。Namespace に既定の limits があれば、その値が入る |
| どちらも書かない | API リファレンスでは、実装によって決まる値(implementation-defined value)。この回の手順では使わない | 上限なし。Namespace に既定の limits があれば、その値が入る |
参考:Resource Management for Pods and Containers | Kubernetes、Pod | Kubernetes API Reference、Assign Memory Resources to Containers and Pods | Kubernetes、Assign CPU Resources to Containers and Pods | Kubernetes
アドミッションは一般に、kube-apiserver がリクエストを受け付けて保存する前に、内容を確かめたり書き換えたりする段階のことで、第40回で扱います。Namespace ごとに既定の requests と limits を決めるには LimitRange を使います。LimitRange は次の第30回で扱います。
limits だけを書いた場合について、CPU のタスクのページは、CPU の limits だけを書くと Kubernetes が limits と同じ値の requests を自動で割り当てる、と説明しています。このページはメモリでも同じだと説明しています。
QoS クラス
QoS クラス(Quality of Service class。サービスの質のクラス)は、Pod の中のコンテナの requests と limits から、Kubernetes が Pod ごとに割り当てる区分 です。公式ドキュメントによると、Kubernetes はこの区分を、ノードの資源が足りないときに、どの Pod を退避させるかを決める手がかりにします。QoS クラスは Guaranteed、Burstable、BestEffort の 3 つで、Pod の status.qosClass に記録されます。
参考:Pod Quality of Service Classes | Kubernetes、Pod | Kubernetes API Reference
それぞれの QoS クラスになる条件を、v1.37 時点の公式ドキュメントに沿って次の表にまとめます。
| QoS クラス | 条件 |
|---|---|
Guaranteed | Pod のすべてのコンテナに、メモリの limits と requests があり(どちらも 0 より大きい)、両者が等しい。CPU についても同じ |
Burstable | Guaranteed の条件を満たさず、少なくとも 1 つのコンテナに、メモリか CPU の requests か limits がある |
BestEffort | Guaranteed にも Burstable にも当てはまらない。どのコンテナにも、メモリと CPU の requests と limits が無い |
参考:Pod Quality of Service Classes | Kubernetes、Pod Quality of Service Classes | Kubernetes、Pod Quality of Service Classes | Kubernetes
QoS クラスを決めるのは CPU とメモリだけです。公式ドキュメントによると、コンテナが CPU とメモリ以外の資源を求めていても、BestEffort に分類されることがあります。上の表の Burstable と BestEffort の条件には、あとで説明する Pod 単位の requests と limits も含まれます。
公式ドキュメントはそれぞれの QoS クラスを次のように説明しています。
| QoS クラス | 公式ドキュメントの説明 |
|---|---|
Guaranteed | 資源の上限がいちばん厳しく、退避させられる可能性がいちばん低い。limits を超えて資源を得ることはできない |
Burstable | requests に基づく、下限の保証がある。limits を書かなければ、limits はノードの容量と同じになり、資源が空いていれば柔軟に増やして使える |
BestEffort | ほかの QoS クラスの Pod に割り当てられていない、ノードの資源を使える。ノードの資源が逼迫すると、kubelet は BestEffort の Pod を優先して退避させる |
参考:Pod Quality of Service Classes | Kubernetes、Pod Quality of Service Classes | Kubernetes、Pod Quality of Service Classes | Kubernetes
Burstable の行の「limits はノードの容量と同じになる」は、「片方だけ書いたときと何も書かないとき」の表で limits を「上限なし」とした場合を言い換えたものと読めます。ノードの容量より多くは使えないので、上限はノードの容量になる、という見方です。
たとえば、さきほど引用した frontend の Pod は、どちらのコンテナも requests と limits の値が違うので、Guaranteed の条件を満たしません。コンテナに requests があるので、Burstable になるはずです。
一方、第8回で引用した公式ドキュメントの kubectl describe pod の例(Deployment の nginx の Pod)には、第8回では省いた部分に次の行があります(あいだの行は省きました)。
CPU とメモリのどちらも requests と limits が等しいので、Guaranteed の条件に当てはまります。この例の Deployment のマニフェストには limits だけが書かれていて、requests には「片方だけ書いたとき」の表のとおり、limits と同じ値が入ったと読めます。
参考:Pod Quality of Service Classes | Kubernetes、Debug Running Pods | Kubernetes
QoS クラスは Pod を作ったときに決まり、Pod が存在するあいだ変わりません。公式ドキュメントによると、あとで説明する、動いているコンテナの資源を変える操作で、QoS クラスが変わるような変更をしようとすると、その変更はアドミッションで拒否されます。init コンテナとサイドカーがある Pod では、QoS クラスはすべてのコンテナを合わせて決まります。
参考:Pod Quality of Service Classes | Kubernetes、Sidecar Containers | Kubernetes
ノードの資源が足りなくなったときの退避
第8回で見たとおり、kubelet はノードの資源を取り戻すために、自分から Pod を終了させることがあります(node-pressure eviction)。公式ドキュメントによると、kubelet はノードのメモリ、ディスクの空き、ファイルシステムの inode(ファイルを管理するための情報)などを見張っています。これらの使用量が決まった水準に達すると、kubelet はノードの 1 つ以上の Pod を失敗させて、資源を取り戻すことがあります。退避させる Pod のフェーズを Failed にして、Pod を終了させます。
先に結論を書くと、退避させる Pod を選ぶときにまず見るのは、Pod が requests を超えて資源を使っているかどうかです。QoS クラスは退避させられやすい順番を見積もる目安として使えるものです(すぐ後で説明します)。
kubelet がどの Pod から退避させるかは、公式ドキュメントによると、次の 3 つで決まります。
- Pod の資源の使用量が requests を超えているか
- Pod の優先度(Pod Priority)
- requests に対する、Pod の資源の使用量
その結果、kubelet は使用量が requests を超えているかどうかで Pod を次の 2 つのグループに分け、1 つ目のグループから先に退避させます。
- 使用量が requests を超えている
BestEffortとBurstableの Pod。優先度をもとに、次に使用量が requests をどれだけ超えているかをもとに退避させる - 使用量が requests より少ない
GuaranteedとBurstableの Pod。最後に、優先度をもとに退避させる
優先度は Pod の重要さを表す値で、このシリーズでは扱いません。このリストには優先度の向きが書かれていません。同じページの、特定の場面についての段落(あとで引用します)には、優先度の低い Pod から退避させるとあります。これをほかの場面にも当てはめると、グループの中では優先度の低い Pod から、また requests を大きく超えている Pod から先に退避させられると考えられます。BestEffort の Pod には requests が無いので、メモリを少しでも使っていれば、1 つ目に入ると考えられます。
QoS クラスのページはこの順番を QoS クラスで表しています。ノードの資源が尽きると、Kubernetes はまず BestEffort、次に Burstable、最後に Guaranteed の Pod を退避させ、資源の逼迫による退避では、requests を超えて使っている Pod だけが候補になる、という説明です。ただし、node-pressure eviction のページは、kubelet は退避の順番を決めるのに QoS クラスを使っていない、と注記しています。メモリのような資源を取り戻すときに、退避させられそうな順番を QoS クラスから見積もることはできる、という位置づけです。また、一時的なストレージ(ディスク)の requests には QoS クラスの分類が当てはまらないので、ディスクが足りない場合(DiskPressure)には、この見積もりは当てはまりません。
参考:Pod Quality of Service Classes | Kubernetes、Node-pressure Eviction | Kubernetes
Guaranteed の Pod も、退避させられないとは言い切れません。公式ドキュメントによると、Guaranteed の Pod が、ほかの Pod の資源の使用が原因で退避させられることはありません。しかし、kubelet などのシステムのデーモンが、取っておいた分(system-reserved や kube-reserved)より多くの資源を使っていて、ノードに requests より少なく使っている Guaranteed か Burstable の Pod しか残っていない場合は、kubelet はそのどれかを退避させる必要があります。そのときは優先度の低い Pod から退避させます。
kubelet が資源を取り戻す前に、ノードのメモリが尽きてしまうこともあります。公式ドキュメントによると、そのときは Linux のカーネルの OOM killer(oom_killer)が対応します。kubelet は Pod の QoS クラスをもとに、コンテナごとに oom_score_adj という値を設定しています。表のすぐ後で説明するとおり、OOM killer はこの値を足した点数のいちばん高いコンテナを止めるので、oom_score_adj が大きいコンテナほど先に止められやすくなります。
| QoS クラス | oom_score_adj |
|---|---|
Guaranteed | -997 |
BestEffort | 1000 |
Burstable | min(max(2, 1000 - (1000 × memoryRequestBytes) / machineMemoryCapacityBytes), 999) |
公式ドキュメントによると、OOM killer はコンテナごとに、ノードのメモリのうちどれだけを使っているかの割合から oom_score を計算し、そこに oom_score_adj を足して、点数のいちばん高いコンテナを強制終了させます。表の memoryRequestBytes はメモリの requests のバイト数、machineMemoryCapacityBytes はマシンのメモリの容量のバイト数と読めます。公式ドキュメントはその結果として、QoS の低い Pod のコンテナのうち、requests に比べて多くのメモリを使っているものから先に強制終了される、とまとめています。
ここまでで、資源が原因でコンテナや Pod が止められる場面が 3 つ出てきました。1 つ目と 2 つ目はどちらもカーネルによる強制終了ですが、きっかけが違います。1 つ目は、あるコンテナが自分のメモリの limits を超えたときです。2 つ目は、kubelet が資源を取り戻す前に、ノード全体のメモリが尽きたときです。2 つ目では、OOM killer は上で見た点数でコンテナを選ぶので、limits を超えていないコンテナも選ばれ得ると読めます。違いを次の表にまとめます。
| 場面 | 止めるもの | 止められる単位 | そのあと |
|---|---|---|---|
| コンテナがメモリの limits を超える | カーネル(cgroup の limits による OOM kill) | コンテナ(の中のプロセス) | 同じ Pod の中で、kubelet が restartPolicy に従ってコンテナを再起動できる |
| ノードのメモリが尽きる | カーネルの OOM killer | コンテナ | kubelet が restartPolicy に従ってコンテナを再起動できる |
| ノードの資源が逼迫する | kubelet(node-pressure eviction) | Pod のすべてのコンテナ | Pod は Failed になる。ワークロードのリソースの Pod なら、代わりの Pod が作られる |
参考:Pod Quality of Service Classes | Kubernetes、Node-pressure Eviction | Kubernetes、Node-pressure Eviction | Kubernetes、Resource Management for Pods and Containers | Kubernetes
公式ドキュメントによると、Pod の退避とは違い、OOM で強制終了されたコンテナは、kubelet が restartPolicy に従って再起動できます。QoS クラスのページも、limits を超えたコンテナは、同じ Pod のほかのコンテナに影響を与えずに強制終了され、再起動される、と説明しています。上で見たとおり、CPU の limits を超えようとしても終了はさせられないので、ここで強制終了されるのはメモリの場合だと考えられます。一方、コンテナが requests を超えていて、ノードの資源が逼迫すると、その Pod が退避の候補になり、退避させられると Pod のすべてのコンテナが終了させられます。Kubernetes が代わりの Pod を、ふつうは別のノードに作ることがある、と書かれています。第8回で見たとおり、直接作った Pod は退避させられると戻りません。代わりの Pod を作るのは、Deployment や StatefulSet などのワークロードのリソースのコントローラー(kube-controller-manager)です。
参考:Node-pressure Eviction | Kubernetes、Pod Quality of Service Classes | Kubernetes、Node-pressure Eviction | Kubernetes
node-pressure eviction は、kubectl などから API で頼む退避(API-initiated eviction)とは別のものです。公式ドキュメントによると、node-pressure eviction では、kubelet は PodDisruptionBudget(第36回)も、Pod の terminationGracePeriodSeconds(第8回で見た終了までの猶予の時間)も守りません。代わりに、退避を始める水準(閾値)の種類に応じて、kubelet の設定にある猶予の時間を使うか、猶予を 0 秒にしてすぐに終了させます。
動かしたままの変更と Pod 単位の書き方
第8回の補足で見たとおり、作ったあとの Pod の spec はほとんど変えられませんが、resize のサブリソースを通すと、コンテナの resources を変えられます。公式ドキュメントによると、動いている Pod のコンテナの CPU とメモリの requests と limits を、Pod を作り直さずに変えることを、in-place の Pod の垂直スケーリング(in-place Pod resize)と呼びます。この機能は v1.35 から Stable で、無効にできません。手元の kind のクラスタ(v1.37)でも、設定を変えずに使えるはずです。
参考:Resource Management for Pods and Containers | Kubernetes、Feature Gates | Kubernetes
資源を変えたときにコンテナを再起動するかどうかは、コンテナの resizePolicy で決められます。API リファレンスによると、resizePolicy の各項目には、資源の名前(resourceName。cpu か memory)と、その資源を変えたときの再起動の方針(restartPolicy)を書きます。この restartPolicy は resizePolicy の中のフィールドで、第9回で見た Pod の spec.restartPolicy とは別のものです。値は再起動せずに変える NotRequired と、コンテナを再起動して変える RestartContainer で、書かなければ NotRequired になります。公式ドキュメントは資源を変えるもう 1 つの方法として、Deployment などのひな形を変えて、コントローラーに新しい資源の Pod へ入れ替えてもらう方法も挙げています(第12回)。
参考:Pod | Kubernetes API Reference、Resize CPU and Memory Resources assigned to Containers | Kubernetes、Resource Management for Pods and Containers | Kubernetes
requests と limits はコンテナごとだけでなく Pod 単位でも書けます。公式ドキュメントによると、機能ゲート PodLevelResources が有効なクラスタでは、Pod の spec.resources に requests と limits を書けます。v1.37 時点ではベータで、既定で有効なので、手元の kind のクラスタでも設定を変えずに使えるはずです。Pod 単位で書けるのは、v1.37 時点では cpu、memory、hugepages だけです。Pod 全体の資源の予算を決められるので、コンテナが多くて、コンテナごとの必要量を見積もるのが難しいときに役立ちます。Pod の中のコンテナどうしで、空いている資源を融通し合えるようにもなります。
参考:Resource Management for Pods and Containers | Kubernetes、Feature Gates | Kubernetes
Pod 単位の requests と limits を使う Pod は、Pod 単位のメモリと CPU に、それぞれ limits と requests があって等しければ Guaranteed になります。なお、API リファレンスは spec.resources の説明でアルファのフィールドとしていますが、この回は概念のページと機能ゲートの一覧(ベータで既定で有効)に沿います。Pod 単位の値も動かしたまま変えられます(v1.37 時点ではベータで、既定で有効)。この回の手順ではコンテナごとの requests と limits だけを使います。
参考:Pod Quality of Service Classes | Kubernetes、Pod | Kubernetes API Reference、Feature Gates | Kubernetes
手元で試す
ここまでの内容を手元の kind のクラスタで試す手順を示します。公式ドキュメントのメモリのタスクのページの例をもとに、メモリの limits の中でメモリを使う Pod と、limits を超えてメモリを使おうとする Pod を作ります。前者が動き続け、後者が OOMKilled になって再起動を繰り返す様子を観察します。あわせて、ノードの割り当て可能な量と、Pod の QoS クラスも確かめます。コマンドは macOS や Linux のシェル(bash や zsh)で実行する前提で書いています。Windows では WSL(Windows の中で Linux を動かす仕組み)などを使ってください。試す環境は次のとおりです。
| 項目 | 内容 |
|---|---|
| kind | v0.33.0 |
| Kubernetes | v1.37.0(kind v0.33.0 の既定のノードイメージ) |
| ノード構成 | シングルノード(クラスタ名 kind):コントロールプレーン 1 |
| 必要なもの | Docker、kubectl(v1.36〜v1.38)、テキストエディタ、端末(ターミナル)2 つ |
参考:Release v0.33.0 · kubernetes-sigs/kind、Version Skew Policy | Kubernetes
kind でクラスタを作る手順は、以下の記事で解説しています。この回では第9回や第19回と同じく、シングルノードのクラスタを使います。メモリの limits を守らせるのはノードの中の仕組みで、ノードが 1 つでも確かめられるからです。
kind で検証用のクラスタを作るDocker のコンテナをノードにして、手元のマシンに Kubernetes のクラスタを作るツール kind の仕組みを図で説明します。kind create cluster で何が起きるかを押さえ、シングルノードとマルチノードのクラスタを作って、ノードの正体が Docker のコンテナであることを確かめる手順を紹介します。シングルノードのクラスタを用意する
まず、前の回のクラスタが残っていないかを確かめます。
$ kind get clusters
kind が表示されたら、作り直さずにそのまま使えるので、クラスタを作る次のコマンドは飛ばします。表示されなければ、クラスタを作ってから、もう一度一覧します。前の回の multi-node が表示されても、この回では使いません。手元のマシンの資源(メモリなど)に余裕がなければ、kind delete cluster --name multi-node で消してから進めてください。
$ kind create cluster
$ kind get clusters
kind get clusters に kind が表示されれば、クラスタができています。第3回で決めたとおり、ここからのコマンドには --context kind-kind を付けて、操作するクラスタをはっきりさせます。
続けて、ノードを一覧します。
$ kubectl get nodes --context kind-kind
ノードが 1 つ表示され、STATUS 列が Ready になっていれば準備ができています。Ready になっていなければ、少し待ってからもう一度実行します。
ノードの CPU のアーキテクチャを確かめる
この回では公式ドキュメントの例と同じ polinux/stress のイメージを使います。メモリを決まった量だけ確保し続ける stress というプログラムが入ったイメージです。2026年10月26日現在、Docker Hub の polinux/stress のタグの一覧には、amd64(一般に、Intel や AMD の 64 ビットの CPU)向けのイメージしか載っていません。そこで、ノードの CPU のアーキテクチャ(CPU の種類)を先に確かめます。status.nodeInfo.architecture は、API リファレンスでは「ノードが報告するアーキテクチャ」と説明されているフィールドです。
$ kubectl get nodes -o jsonpath='{.items[0].status.nodeInfo.architecture}{"\n"}' --context kind-kind
参考:polinux/stress | Docker Hub、Node | Kubernetes API Reference、JSONPath Support | Kubernetes
amd64 と表示されれば、このあとの手順をそのまま進められるはずです。arm64 など別の値が表示された場合(Apple シリコンの Mac などでは、こうなることが多いと考えられます)、polinux/stress のコンテナは起動できない可能性が高いと考えられます。
そのときは 1 つ目の端末の STATUS 列に、イメージを取得できないことや、コンテナが起動できずに失敗したことを知らせる表示が出ると考えられます。これはメモリの limits とは関係のない失敗で、OOMKilled ではありません。見分けるには、後半の「終了の理由と終了コードを確かめる」の jsonpath のコマンドや kubectl describe pod で、理由(reason、Reason:)を見ます。OOMKilled と表示されなければ、メモリの limits による強制終了ではありません。
arm64 のノードでも、次の手順は意味があります。
- Pod の QoS クラスを確かめる(コンテナが起動しなくても、Pod の作成時に決まるため)
kubectl describe nodesで、ノードの割り当て可能な量と requests の合計を見る- マニフェストと、公式ドキュメントの出力例を読む
Pod を監視する
ここからは端末を 2 つ使います。第11回・第13回〜第20回と同じく、1 つ目の端末で変化を追い続け、2 つ目の端末でマニフェストを渡したり、状態を確かめたりします。この回で 1 つ目の端末が追うのは Pod です。2 つ目の端末を開いたら、cd でマニフェストを保存するディレクトリに移動しておきます。
1 つ目の端末で、default の Namespace の Pod を監視します。第3回で見たとおり、--watch を付けると、一覧を表示したあとも変化を追い続けます。この監視は片付けの前まで止めずに続けます。
$ kubectl get pods --watch --context kind-kind
Pod がまだ無いので、何も表示されないか、見つからないことを知らせる 1 行が出たあと、止まったように見えると考えられます。
limits の中でメモリを使う Pod を作る
以下はメモリの requests を 100Mi、limits を 200Mi にしたコンテナで、150 MiB のメモリを確保する Pod のマニフェストです。公式ドキュメントのメモリのタスクのページの例から、metadata.namespace: mem-example の行だけを省きました(公式ドキュメントは mem-example という Namespace を作って使いますが、この回では default の Namespace を使うためです)。memory-demo.yaml という名前で保存します。
apiVersion: v1
kind: Pod
metadata:
name: memory-demo
spec:
containers:
- name: memory-demo-ctr
image: polinux/stress
resources:
requests:
memory: "100Mi"
limits:
memory: "200Mi"
command: ["stress"]
args: ["--vm", "1", "--vm-bytes", "150M", "--vm-hang", "1"]参考:Assign Memory Resources to Containers and Pods | Kubernetes
args に並ぶのは、Kubernetes の量の書き方ではなくコンテナの中で動く stress というプログラムへの引数です。そのため "150M" の M の意味もさきほど見た Kubernetes の決まり(M は 1000 倍ずつ)とは別のものです。公式ドキュメントは "--vm-bytes", "150M" を、コンテナに 150 MiB のメモリを確保させる引数だと説明しています。150 MiB は requests の 100 MiB より多く、limits の 200 MiB には収まります。args のほかの引数(--vm と --vm-hang)の意味は公式ドキュメントでは説明されておらず、この回では気にしなくて構いません。
参考:Assign Memory Resources to Containers and Pods | Kubernetes
2 つ目の端末で、マニフェストを渡します。
$ kubectl apply -f memory-demo.yaml --context kind-kind
pod/memory-demo が作られたことを知らせる 1 行が出るはずです。1 つ目の端末には memory-demo の行が書き足されていき、STATUS 列は Pending、ContainerCreating を経て Running になるはずです。polinux/stress のイメージを初めて取得するときは、ContainerCreating のまましばらく止まって見えることがあります(第9回)。公式ドキュメントでは、この Pod は requests より多く limits より少ない、約 150 MiB のメモリを使って動いています。そのため RESTARTS 列は 0 のまま増えないはずです。
参考:Assign Memory Resources to Containers and Pods | Kubernetes、Pod Lifecycle | Kubernetes
公式ドキュメントは kubectl top pod でこの Pod のメモリの使用量を確かめています。kubectl top を使うには metrics-server というコンポーネントが要るので、この回では使いません。metrics-server は第35回で入れます。
参考:Assign Memory Resources to Containers and Pods | Kubernetes
続けて、2 つ目の端末で、この Pod の QoS クラスを取り出します。公式ドキュメントの QoS クラスのタスクのページと同じく、-o jsonpath で status.qosClass を表示します。
$ kubectl get pod memory-demo -o jsonpath='{.status.qosClass}{"\n"}' --context kind-kind
Burstable と表示されるはずです。メモリの requests(100Mi)と limits(200Mi)が等しくなく、CPU の requests と limits も無いので、Guaranteed の条件を満たしません。コンテナにメモリの requests があるので、Burstable の条件に当てはまります。
ノードの割り当て可能な量と requests の合計を見る
2 つ目の端末で、ノードを詳しく表示します。シングルノードのクラスタなので、名前を指定しなくても、表示されるのは 1 つのノードだけです。
$ kubectl describe nodes --context kind-kind
公式ドキュメントの例では、表示の一部は次のとおりです。Non-terminated Pods: の下の Pod の行は省きました。
見るところを次の表にまとめます。
| 見るところ | 表しているもの |
|---|---|
Capacity: | ノードの資源の全体の量(status.capacity) |
Allocatable: | ノードの資源のうち、Pod が使える量(status.allocatable) |
Non-terminated Pods: | ノードで動いている(終了していない)Pod の一覧。Pod ごとの CPU とメモリの requests と limits が並ぶ |
Allocated resources: | ノードの Pod の requests と limits の合計と読める(公式ドキュメントの例では、省いた 5 つの Pod の CPU の requests を足すと 680m になる) |
参考:Resource Management for Pods and Containers | Kubernetes、Node Status | Kubernetes
公式ドキュメントはこの例の出力から、CPU を 1.120 より多く、またはメモリを 6.23Gi より多く求める Pod は、このノードに収まらないと読めると説明しています。公式ドキュメントはこの値の求め方を書いていませんが、このメディアで計算すると次のとおりです。CPU では、Allocatable: の 1800m から Allocated resources: の 680m を引くと 1120m になり、公式ドキュメントの 1.120 と合います。メモリでは、7474992Ki はおよそ 7.13Gi で、そこから 920Mi を引くと、およそ 6.23Gi になります。表示の注記には、limits の合計は 100% を超えることがある(overcommit)とも書かれています。kube-scheduler がノードに収まるかを確かめるのは requests の合計なので、limits の合計はノードの量を超えることがあると考えられます。
参考:Resource Management for Pods and Containers | Kubernetes、Resource Management for Pods and Containers | Kubernetes
手元の表示は例と次の点が違うはずです。
Name:やCapacity:、Allocatable:の値は手元のクラスタのものになる(例の値とは違う)。kind のノードは手元のマシンの上で動くコンテナなので、Capacity:は手元のマシン(Docker に割り当てた分)の資源になると考えられるNon-terminated Pods:にはmemory-demoの行があり、メモリの requests に100Mi、limits に200Miが並ぶと考えられるNon-terminated Pods:にはkube-systemの Pod(第4回)の行も並ぶと考えられる。それらの requests もAllocated resources:の合計に入る- 例の
[ ... lines removed for clarity ...]の行は公式ドキュメントが省いた印で、手元ではその場所にほかの項目が表示される
括弧の中の割合が何に対する値かは、公式ドキュメントでは説明されていません。この回では割合ではなく値を見て、次の 2 点を確かめてください。
Allocated resources:のMemory Requestsに、memory-demoの100Miも足されていること(Non-terminated Pods:の各行の値と比べる)Allocatable:の値からAllocated resources:の requests を引くと、上の例と同じように、これから置く Pod に使える残りの量を見積もれること
メモリの limits を超える Pod を作る
以下はメモリの requests を 50Mi、limits を 100Mi にしたコンテナで、250 MiB のメモリを確保しようとする Pod のマニフェストです。公式ドキュメントのメモリのタスクのページの例から、前と同じく metadata.namespace: mem-example の行だけを省きました。memory-demo-2.yaml という名前で保存します。
apiVersion: v1
kind: Pod
metadata:
name: memory-demo-2
spec:
containers:
- name: memory-demo-2-ctr
image: polinux/stress
resources:
requests:
memory: "50Mi"
limits:
memory: "100Mi"
command: ["stress"]
args: ["--vm", "1", "--vm-bytes", "250M", "--vm-hang", "1"]参考:Assign Memory Resources to Containers and Pods | Kubernetes
公式ドキュメントによると、このコンテナは 250 MiB のメモリを確保しようとします。limits の 100 MiB を大きく超える量です。前と同じく、"250M" は Kubernetes の量の書き方ではなく stress への引数です。
参考:Assign Memory Resources to Containers and Pods | Kubernetes
2 つ目の端末で、マニフェストを渡します。
$ kubectl apply -f memory-demo-2.yaml --context kind-kind
pod/memory-demo-2 が作られたことを知らせる 1 行が出るはずです。1 つ目の端末には memory-demo-2 の行が書き足されていきます。公式ドキュメントでは kubectl get pod memory-demo-2 を何度か実行して、次のような表示を得ています。
参考:Assign Memory Resources to Containers and Pods | Kubernetes
公式ドキュメントは続けて同じコマンドを何度か実行し、コンテナが強制終了されては再起動されることを示しています。そのときの 2 つの表示は次のとおりです。どちらも先頭の 1 行は、公式ドキュメントが実行したコマンドです。
参考:Assign Memory Resources to Containers and Pods | Kubernetes
公式ドキュメントはこれを「コンテナが強制終了され、再起動され、また強制終了され、また再起動され……」と説明しています。STATUS 列が OOMKilled になり、RESTARTS 列が増えて Running に戻る、という移り変わりです。この回の手順は公式ドキュメントと次の点が違うので、手元の表示も違って見えるはずです。
| 違い | 手元の表示 |
|---|---|
kubectl get pod を繰り返す代わりに --watch で追っている | 先頭にコマンドの行は無い。表示は 1 つにまとまり、変化のたびに行が書き足される。memory-demo の行も並ぶ |
| 再起動を何度も繰り返す | 第9回で見た待ち時間が働くと、STATUS 列に CrashLoopBackOff が表示されることもあると考えられる |
| 時刻 | AGE 列の値は手元のものになる |
参考:Assign Memory Resources to Containers and Pods | Kubernetes、Pod Lifecycle | Kubernetes
「OOMKilled になったコンテナのその後」の図に当てはめると、STATUS 列の OOMKilled は Terminated の箱に、CrashLoopBackOff は Waiting の箱にあたると考えられます。図の箱に書いた理由(Reason)が STATUS 列に表示されているという見方です。
memory-demo-2 の RESTARTS 列が増えていくあいだも、memory-demo の行の RESTARTS 列は 0 のままのはずです。同じノードで動いていても、limits を超えたのは memory-demo-2 のコンテナだけだからです。
終了の理由と終了コードを確かめる
memory-demo-2 の RESTARTS 列が 1 以上になったら、2 つ目の端末で、1 つ前のコンテナの終了の理由と終了コードを取り出します。第9回と同じく、status.containerStatuses の最初の項目(この Pod の唯一のコンテナ memory-demo-2-ctr)の lastState.terminated を指定します。
$ kubectl get pod memory-demo-2 -o jsonpath='{.status.containerStatuses[0].lastState.terminated.reason}{"\n"}{.status.containerStatuses[0].lastState.terminated.exitCode}{"\n"}' --context kind-kind
参考:JSONPath Support | Kubernetes、Determine the Reason for Pod Failure | Kubernetes
1 行目に OOMKilled、2 行目に 137 が表示されるはずです。公式ドキュメントは kubectl get pod memory-demo-2 --output=yaml で Pod 全体を表示し、そのうちの次の部分を示して、コンテナがメモリ不足(OOM)で強制終了されたと説明しています。
参考:Assign Memory Resources to Containers and Pods | Kubernetes
続けて、Pod を詳しく表示します。
$ kubectl describe pod memory-demo-2 --context kind-kind
公式ドキュメントは表示のうち次の 2 行を示して、コンテナが起動と失敗を繰り返していると説明しています。
参考:Assign Memory Resources to Containers and Pods | Kubernetes
例の 2 行は Events: の欄の一部だと考えられます。行の先頭の ... は公式ドキュメントが省いた部分で、手元ではそこに Age や From などの列の値が表示されると考えられます。メッセージの文言も例とは違うことがあると考えられます。表示のほかの部分で見るところを、「OOMKilled になったコンテナのその後」で引用した例と、第8回で引用した例をもとに、次の表にまとめます。
| 見るところ | 表示されるはずのこと |
|---|---|
Limits:、Requests: | memory: 100Mi と memory: 50Mi。マニフェストに書いた値 |
State: | 実行中に表示したときは Running。再起動を待っているときは Waiting で、Reason: は CrashLoopBackOff になると考えられる |
Last State: | Terminated。Reason: は OOMKilled、Exit Code: は 137 |
Restart Count: | 再起動された回数。1 つ目の端末の RESTARTS 列と同じ値になると考えられる |
QoS Class: | Burstable(メモリの requests と limits が等しくないため) |
参考:Resource Management for Pods and Containers | Kubernetes、Debug Running Pods | Kubernetes、Pod Quality of Service Classes | Kubernetes
公式ドキュメントのメモリのタスクのページは、このあと kubectl describe nodes で、メモリ不足でコンテナが強制終了された記録(OOMKilling のイベント)がノードの表示にも出る例を示しています。手元の kind のクラスタで同じ記録が出るかどうかは、公式ドキュメントからはわかりません。この回では Pod の側の表示だけを確かめます。
参考:Assign Memory Resources to Containers and Pods | Kubernetes
片付け
1 つ目の端末で Ctrl+C を押して、Pod の監視を止めます。
2 つ目の端末で、作った 2 つの Pod を、マニフェストを指定して削除します。
$ kubectl delete -f memory-demo.yaml --context kind-kind
$ kubectl delete -f memory-demo-2.yaml --context kind-kind
kubectl delete は既定では、Pod が消えるまで待ってから終わります。Pod には終了までの猶予の時間(既定では 30 秒)があるので、終わるまで少しかかることがあります。少し待ってから、残っていないかを確かめます。
$ kubectl get pods --context kind-kind
Pod が Terminating で表示されていたら、少し待ってからもう一度実行します。第8回と同じく、見つからないことを知らせる 1 行が出れば、削除できています。
最後に、クラスタを削除し、消えたことを確かめます。
$ kind delete cluster
$ kind get clusters
kind delete cluster は存在しないクラスタを指定してもエラーを返しません。kind get clusters に kind が表示されなくなったことを確かめます。kubeconfig に kind-kind のコンテキストが残っていたら、第3回の片付けと同じく kubectl config delete-context kind-kind で消します。
注意点
requests と limits でつまずきやすい点と、後から変えにくい設定、バージョンで変わる点を、次の表にまとめます。
| 項目 | 内容 |
|---|---|
| requests は上限ではない | ノードに空きがあれば、コンテナは requests より多く使える。上限を決めるのは limits |
| kube-scheduler が見るのは requests | 実際の使用量が低くても、requests の合計がノードに収まらなければ、そのノードには置かれない。収まるかどうかの基準は、API リファレンスがスケジューリングに使える量とする Allocatable だと考えられる |
| CPU とメモリで、limits を超えようとしたときの結果が違う | CPU はスロットリングされるだけで、終了はさせられない。メモリは OOM で強制終了されることがある |
| メモリの単位の大文字と小文字 | M はメガバイト、m はミリバイト。メモリに 400m と書くと 0.4 バイトになる |
| limits だけを書くと requests も同じ値になる | アドミッションの仕組みで既定の requests が入らない場合、limits の値が requests にコピーされる |
| QoS クラスは作ったときに決まる | Pod が存在するあいだ変わらない。QoS クラスが変わるような in-place の変更は拒否される |
| 退避と OOM kill は別 | 退避では Pod ごと終了し、Failed になる。OOM で強制終了されたコンテナは、restartPolicy に従って同じ Pod の中で再起動できる |
| 直接作った Pod は退避させられると戻らない | 代わりの Pod を作るのはワークロードのリソースのコントローラー(第8回) |
| Pod 単位の requests と limits は v1.37 時点ではベータ | PodLevelResources は既定で有効。書けるのは cpu、memory、hugepages だけ |
参考:Resource Management for Pods and Containers | Kubernetes、Node | Kubernetes API Reference、Pod Quality of Service Classes | Kubernetes、Node-pressure Eviction | Kubernetes、Feature Gates | Kubernetes
requests の値は、kube-scheduler が Pod を置くかどうかを決める値です。公式ドキュメントのタスクのページは、requests を低めにすると置かれやすくなり、limits を requests より大きくするとバーストに対応できる、と説明しています。一方で、実際の使用量より requests を小さくしすぎると、ノードの資源が逼迫したときに、requests を超えて使っている Pod として、先に退避の候補になると考えられます。逆に大きくしすぎると、実際には使わない分までノードの Allocatable を取ってしまうので、Pod を置けるノードが見つかりにくくなると考えられます。
参考:Assign Memory Resources to Containers and Pods | Kubernetes、Node-pressure Eviction | Kubernetes
関連記事
Namespace ごとに requests と limits の既定値や範囲を決める LimitRange と、Namespace 全体の資源の合計に上限を設ける ResourceQuota については、以下の記事で解説しています。
LimitRange と ResourceQuota の違いNamespace ごとに資源の使い方を制限する LimitRange と ResourceQuota の違いを図で説明します。1 つずつの既定値と範囲を決める LimitRange と、Namespace 全体の合計を決める ResourceQuota が、どちらもアドミッションで働くことを押さえ、kind のクラスタで上限を超える Pod の作成が拒否される様子を試す手順を紹介します。この回で使わなかった kubectl top のための metrics-server と、負荷に合わせて Pod の数を自動で増減させる Horizontal Pod Autoscaler については、以下の記事で解説しています。
この回で見た OOMKilled のあとの再起動と、restartPolicy、CrashLoopBackOff の仕組みについては、以下の記事で解説しています。