第28章 Out of Resource (リソース不足) エラーの処理
28.1. 概要
このトピックでは、OpenShift Container Platform がメモリー不足 (OOM) やディスク領域不足の状況を防ぐためのベストエフォートの取り組みについて説明します。
ノードは、利用可能なコンピュートリソースが少ない場合に安定性を維持する必要があります。これは、メモリーやディスクなどの圧縮不可能なリソースを扱う場合にとくに重要になります。どちらかのリソースが消費されると、ノードは不安定になります。
管理者は、エビクションポリシーを使用してノードをプロアクティブにモニターし、ノードでコンピュートリソースおよびメモリーリソースが不足する状況を防ぐことができます。
このトピックでは、OpenShift Container Platform がリソース不足の状況に対処する方法についての情報を提供し、シナリオ例や推奨される対策について説明します。
swap メモリーがノードに対して有効にされる場合、ノードは MemoryPressure 状態にあるかどうかを検知できません。
メモリーベースのエビクションを利用するには、オペレーターは swap を無効にする必要があります。
28.2. エビクションポリシーの設定
エビクションポリシー により、ノードが利用可能なリソースが少ない状況で実行されている場合に 1 つ以上の Pod が失敗することを許可します。Pod の失敗により、ノードは必要なリソースを回収できます。
エビクションポリシーは、エビクションシグナルと、ノード設定ファイルまたはコマンドラインで設定される特定のエビクションしきい値の組み合わせです。エビクションは、 ノードがしきい値を超える Pod に対して即時のアクションを実行するハードエビクションか、またはノードがアクションを実行する前の猶予期間を許可するソフトエビクションのどちらかになります。
クラスターのノードを変更するには、ノード設定マップを必要に応じて更新します。node-config.yaml
ファイルは手動で変更しないようにしてください。
適切に設定されたエビクションポリシーを使用することで、ノードは、プロアクティブにモニターし、コンピュートリソースを完全に使い切る事態を防ぐことができます。
ノードによる Pod の失敗が生じる場合、Pod 内のすべてのコンテナーが停止し、PodPhase
は Failed に切り替わります。
ノードは、以下のようにディスクの不足状態を検出する際に nodefs
および imagefs
ファイルシステムのパーティションをサポートします。
nodefs
または rootfs
は、ノードがローカルディスクボリューム、デーモンログ、emptyDir など (/
を指定するファイルシステムなど) に使用するファイルシステムです。rootfs
には、openshift.local.volumes
(デフォルトは /var/lib/origin/openshift.local.volumes) が含まれます。
imagefs
は、コンテナーランタイムがイメージおよび個別のコンテナーの書き込み可能な層を保存するために使用するファイルシステムです。エビクションのしきい値は、imagefs
については 85% になります。imagefs
ファイルシステムはランタイムによって異なり、Docker の場合は使用しているストレージドライバーに応じます。
Docker の場合:
devicemapper
ストレージドライバーを使用している場合は、imagefs
はシンプールになります。Docker デーモンに
--storage-opt dm.basesize
フラグを設定してコンテナーの読み取り/書き込み層を制限できます。$ sudo dockerd --storage-opt dm.basesize=50G
-
overlay2
ストレージドライバーを使用している場合、imagefs
は/var/lib/docker/overlay2
が含まれるファイルシステムになります。
- オーバーレイドライバーを使用する CRI-O の場合、imagefs はデフォルトで /var/lib/containers/storage になります。
ローカルストレージの分離 (一時ストレージ) を使用せず、XFS クォータ (volumeConfig) を使用していない場合、Pod でローカルディスクの使用を制限することはできません。
28.2.1. ノード設定を使用したポリシーの作成
エビクションポリシーを設定するには、ノード設定ファイルを編集して eviction-hard
または eviction-soft
パラメーターでエビクションしきい値を指定します。
以下に例を示します。
例28.1 ハードエビクションについてのノード設定ファイルのサンプル
例28.2 ソフトエビクションについてのノード設定ファイルのサンプル
kubeletArguments: eviction-soft: 1 - memory.available<500Mi 2 - nodefs.available<500Mi - nodefs.inodesFree<100Mi - imagefs.available<100Mi - imagefs.inodesFree<100Mi eviction-soft-grace-period:3 - memory.available=1m30s - nodefs.available=1m30s - nodefs.inodesFree=1m30s - imagefs.available=1m30s - imagefs.inodesFree=1m30s
変更を有効するために OpenShift Container Platform サービスを再起動します。
# systemctl restart atomic-openshift-node
28.2.2. エビクションシグナルについて
以下の表にあるシグナルのいずれかに基づいてエビクションの意思決定をトリガーするようノードを設定することができます。エビクションシグナルは、しきい値と共にエビクションのしきい値に追加できます。
各シグナルの値は、ノード要約 API に基づいて Description 列で説明されています。
シグナルを表示するには、以下を実行します。
curl <certificate details> \ https://<master>/api/v1/nodes/<node>/proxy/stats/summary
ノードの状態 | エビクションシグナル | 値 | 説明 |
---|---|---|---|
|
|
|
ノードの利用可能なメモリーがエビクションしきい値を超えている。 |
|
|
|
ノードの root ファイルシステムまたはイメージファイルシステムのいずれかで利用可能なディスク領域がエビクションしきい値を超えている。 |
|
| ||
|
| ||
|
|
上記のシグナルのそれぞれは、リテラル値またはパーセンテージベースの値のいずれかをサポートします。パーセンテージベースの値は、各シグナルに関連付けられる合計容量との関連で計算されます。
スクリプトは kubelet が実行する一連の手順を使用し、cgroup から memory.available
値を派生させます。スクリプトは計算から非アクティブなファイルメモリー (つまり、非アクティブな LRU リストのファイルベースのメモリーのバイト数) を計算から除外します。非アクティブなファイルメモリーはリソースの不足時に回収可能になることが想定されます。
free -m
はコンテナーで機能しないため、free -m
のようなツールは使用しないでください。
OpenShift Container Platform はこれらのファイルシステムを 10 秒ごとにモニターします。
ボリュームおよびログを専用ファイルシステムに保存する場合、ノードはそのファイルシステムをモニターしません。
OpenShift Container Platform 3.4, の時点で、ノードはディスク不足に基づくエビクションの意思決定をトリガーする機能をサポートします。ディスク不足のために Pod をエビクトする前に、ノードはコンテナーおよびイメージのガべージコレクションも実行します。今後のリリースでは、ガべージコレクションは、純粋なディスクのエビクションベースの設定が優先されるために非推奨になります。
28.2.3. エビクションのしきい値について
しきい値をノード設定ファイルに追加することで、ノードのリソース回収をトリガーするエビクションしきい値を指定するようノードを設定することができます。
関連付けられた猶予期間とは別にエビクションのしきい値に達する場合、ノードは、ノードがメモリー不足またはディスク不足であることを示す状態を報告します。これにより、スケジューラーがリソース回収の試行期間中に Pod をノードにスケジュールすることを防ぐことができます。
ノードは、node-status-update-frequency
引数で指定される頻度で、ノードのステータス更新を継続的に報告します。これはデフォルトで 10s
(10 秒) に設定されています。
エビクションのしきい値は、しきい値に達する際にノードが即時にアクションを実行する場合にハード となり、リソース回収前の猶予期間を許可する場合はソフトになります。
ソフトエビクションは、特定レベルの使用状況をターゲットとしているものの、一時的な値の上昇を許容できる場合により一般的に使用されます。ソフトエビクションは、ハードエビクションのしきい値よりも低く設定することが推奨されますが、期間はオペレーターが固有に設定できます。システム予約もソフトエビクションのしきい値以上に設定する必要があります。
ソフトエビクションのしきい値は高度な機能になります。ソフトエビクションのしきい値の使用を試行する前にハードエビクションのしきい値を設定してください。
しきい値は以下の形式で設定されます。
<eviction_signal><operator><quantity>
-
eviction-signal
値は任意のサポートされるエビクションシグナルにすることができます。 -
operator
値は<
になります。 -
quantity
値は、Kubernetes で使用される数量表現と一致している必要があり、%
トークンで終了する場合はパーセンテージで表現されます。
たとえば、オペレーターが 10Gi メモリーのあるノードを持つ場合で、オペレーターは利用可能なメモリーが 1Gi を下回る場合にエビクションを導入する必要がある場合、メモリーのエビクションしきい値は以下のいずれかで指定することができます。
memory.available<1Gi memory.available<10%
ノードはエビクションしきい値の評価とモニターを 10 秒ごとに実行し、値を変更することはできません。これはハウスキープ処理の間隔になります。
28.2.3.1. ハードエビクションのしきい値について
ハードエビクションのしきい値には猶予期間がなく、しきい値を超えることが確認される場合には、ノードは関連付けられた不足状態にあるリソースを回収するために即時のアクションを実行します。ハードエビクションのしきい値に達する場合、ノードは正常な終了なしに Pod を即時に強制終了します。
ハードエビクションのしきい値を設定するには、「ポリシー作成のためのノード設定の使用」に示されるように、エビクションしきい値を eviction-hard
の下にあるノード設定ファイルに追加します。
ハードエビクションのしきい値が設定されたノード設定ファイルのサンプル
kubeletArguments: eviction-hard: - memory.available<500Mi - nodefs.available<500Mi - nodefs.inodesFree<100Mi - imagefs.available<100Mi - imagefs.inodesFree<100Mi
この例は一般的なガイドラインを示すためのもので、推奨される設定ではありません。
28.2.3.1.1. デフォルトのハードエビクションしきい値
OpenShift Container Platform は、eviction-hard
の以下のデフォルト設定を使用します。
... kubeletArguments: eviction-hard: - memory.available<100Mi - nodefs.available<10% - nodefs.inodesFree<5% - imagefs.available<15% ...
28.2.3.2. ソフトエビクションのしきい値について
ソフトエビクションのしきい値は、エビクションしきい値と管理者が指定する必要な猶予期間のペアを設定します。ノードは、猶予期間が経過するまではエビクションシグナルに関連付けられたリソースを回収しません。猶予期間がノード設定ファイルに指定されない場合、ノードは起動時にエラーを出して失敗します。
さらにソフトエビクションのしきい値に達する場合、オペレーターは Pod をノードからエビクトする際に使用する Pod 終了の猶予期間として許可される最長期間を指定できます。eviction-max-pod-grace-period
が指定されると、ノードは pod.Spec.TerminationGracePeriodSeconds
と maximum-allowed-grace-period の値の小さい方を使用します。これが指定されていない場合は、ノードは正常な停止なしに、Pod を即時に強制終了します。
ソフトエビクションのしきい値については、以下のフラグがサポートされています。
-
eviction-soft
: エビクションしきい値のセットです (例:memory.available<1.5Gi
)。対応する猶予期間を経過してからこの値に達すると、Pod のエビクションをトリガーします。 -
eviction-soft-grace-period
: エビクションの猶予期間のセットです (例:memory.available=1m30s
)。これは、Pod のエビクションをトリガーするまでソフトエビクションのしきい値が有効である期間に対応します。 -
eviction-max-pod-grace-period
: ソフトエビクションのしきい値に達する際の Pod の終了時に使用される最長で許可される猶予期間 (秒単位) です。
ソフトエビクションのしきい値を設定するには、「ポリシー作成のためのノード設定の使用」に示されるように、エビクションのしきい値を eviction-soft
の下にあるノード設定ファイルに追加します。
ソフトエビクションのしきい値が設定されたノード設定ファイルのサンプル
kubeletArguments: eviction-soft: - memory.available<500Mi - nodefs.available<500Mi - nodefs.inodesFree<100Mi - imagefs.available<100Mi - imagefs.inodesFree<100Mi eviction-soft-grace-period: - memory.available=1m30s - nodefs.available=1m30s - nodefs.inodesFree=1m30s - imagefs.available=1m30s - imagefs.inodesFree=1m30s
この例は一般的なガイドラインを示すためのもので、推奨される設定ではありません。
28.3. スケジューリング用のリソース量の設定
スケジューラーがノードを完全に割り当て、エビクションを防止できるようにするために、スケジューリングで利用できるノードリソースの数量を制御できます。
system-reserved
を、Pod のデプロイおよび system-daemon 用にスケジューラーで利用可能にするリソース量と等しくなるようにします。エビクションは、Pod が割り当て可能なリソースの要求量よりも多くのリソースを使用する場合に生じます。
ノードは 2 つの値を報告します。
-
Capacity
: マシンにあるリソースの量です。 -
Allocatable
: スケジューリング用に利用可能にされるリソースの量です。
割り当て可能なリソースの量を設定するには、以下を実行します。
適切なノード設定マップを編集して、
eviction-hard
またはeviction-soft
のsystem-reserved
パラメーターを追加するか、または変更します。kubeletArguments: eviction-hard: 1 - "memory.available<500Mi" system-reserved: - "memory=1.5Gi"
- 1
- このしきい値は、
eviction-hard
またはeviction-soft
のいずれかにできます。
変更を有効するために OpenShift Container Platform サービスを再起動します。
# systemctl restart atomic-openshift-node
28.4. ノードの状態変動の制御
ノードがソフトエビクトしきい値の上下で変動しているものの、関連付けられた猶予期間を超えていない場合、対応するノード状態は true と false の間で変動します。これにより、スケジュールの問題が生じる場合があります。
この変動を防ぐには、eviction-pressure-transition-period
パラメーターを設定して、ノードが不足状態からの切り換え前に待機する期間を制御します。
-
<resource_type>=<resource_quantity>
のペアのセットを使用して、パラメーターを編集するか、またはノード設定マップのkubeletArguments
セクションに追加します。
kubeletArguments: eviction-pressure-transition-period="5m"
+ ノードは、指定期間の指定された不足状態についてエビクションしきい値に達していないことを確認する場合に状態を false に戻します。
+
調整を行う前にデフォルト値 (5 分) を使用します。このデフォルト値のオプションには、システムを安定させ、安定した状態になる前にスケジューラーが新規 Pod をノードに割り当てないようにすることが意図されています。
変更を有効するために OpenShift Container Platform サービスを再起動します。
# systemctl restart atomic-openshift-node
28.5. ノードレベルのリソースの回収
エビクション条件が満たされる場合、ノードはシグナルが定義されたしきい値を下回るまで、不足状態にあるリソースを回収するプロセスを実行します。このプロセスでは、ノードはいずれの新規 Pod のスケジューリングもサポートしません。
ノードは、ホストシステムにコンテナーランタイム用の専用 imagefs
が設定されているかどうかに基づいて、エンドユーザー Pod のエビクト前にノードレベルのリソース回収を試行します。
Imagefs が設定されている場合
ホストシステムに imagefs
が設定されている場合:
nodefs
ファイルシステムがエビクションしきい値を満たす場合、ノードは以下の順番でディスク領域を解放します。- 実行されない Pod/コンテナーの削除
imagefs
ファイルシステムがエビクションのしきい値を満たす場合、ノードは以下の順番でディスク領域を解放します。- すべての未使用イメージの削除
Imagefs が設定されていない場合
ホストシステムに imagefs
がされていない場合:
nodefs
ファイルシステムがエビクションしきい値を満たす場合、ノードは以下の順番でディスク領域を解放します。- 実行されない Pod/コンテナーの削除
- すべての未使用イメージの削除
28.6. Pod エビクションについて
エビクションしきい値に達し、猶予期間を経過している場合、ノードはシグナルが定義されたしきい値を下回るまで Pod のエビクトのプロセスを実行します。
ノードは、エビクトする Pod を QoS (Quality of Service) に基づいてランク付けし、同じQoS レベルにある Pod については Pod のスケジューリング要求との関連で不足状態のリソースの消費量に基づいてランク付けします。
それぞれの QoS レベルには OOM スコープが設定されており、Linux out-of-memory ツール (OOM killer) はこのスコープを使用して強制終了する Pod を判別します。以下の「QoS および Out of Memory Killer について」を参照してください。
以下の表には、各 QoS レベルと関連付けられた OOM スコアが一覧表示されています。
QoS (Quality of Service) | 説明 |
---|---|
|
要求との関連で不足状態にあるリソースの最大量を消費する Pod が最初に失敗します。いずれの Pod もその要求を超えていない場合、ストラテジーは不足状態にあるリソースを最も多く消費する Pod をターゲットにします。 |
|
リソースの要求との関連で、不足状態にあるリソースの最大量を消費する Pod が最初に失敗します。いずれの Pod もその要求を超えていない場合、ストラテジーは不足状態にあるリソースを最も多く消費する Pod をターゲットにします。 |
|
不足状態にあるリソースの最大量を消費する Pod が最初に失敗します。 |
Guaranteed
Pod は、(ノード、docker、journald などの) システムデーモンが システム予約か、または kube 予約 の割り当てを使用して予約されている量よりも多くのリソースを消費しているか、またはノードに Guaranteed
Pod のみが残っているのでない限り、別の Pod のリソース消費が原因でエビクトされることはありません。
ノードに Guaranteed
Pod のみが残っている場合、ノードはノードの安定性に最も影響の少ない Guaranteed
Pod をエビクトし、他の Guaranteed
Pod に対する予想外の消費による影響を制限します。
ローカルディスクは BestEffort
リソースです。必要な場合は、ノードは DiskPressure
の発生時にディスクを回収するため、Pod を 1 度に 1 つずつエビクトします。ノードは QoS に基づいて Pod をランク付けします。ノードが inode の不足状態に対応している場合、ノードは QoS の最も低いレベルにある Pod を最初にエビクトして inode を回収します。ノードが利用可能なディスクの不足状態に対応している場合は、ノードはローカルディスクを最も多く消費する QoS 内の Pod をランク付けし、それらの Pod を最初にエビクトします。
28.6.1. QoS および Out of Memory Killer について
ノードによるメモリーの回収が可能になる前にシステムの OOM (Out of Memory) イベントが発生する場合、ノードの動作はこれに対応する OOM killer によって異なります。
ノードは、Pod の QoS に基づいて各コンテナーの oom_score_adj
値を設定します。
QoS (Quality of Service) | oom_score_adj 値 |
---|---|
|
-998 |
|
min(max(2, 1000 - (1000 * memoryRequestBytes) / machineMemoryCapacityBytes), 999) |
|
1000 |
ノードがシステムの OOM イベントが発生する前にメモリーを回収できない場合、oom_killer
は oom_score
を計算します。
% of node memory a container is using + `oom_score_adj` = `oom_score`
次にノードは、スコアの最も高いコンテナーを強制終了します。
スケジューリング要求に関連してメモリーを最も多く消費する、QoS の最も低いレベルにあるコンテナーが最初に失敗します。
Pod のエビクションとは異なり、Pod コンテナーが OOM が原因で失敗する場合、ノードはノードの再起動ポリシーに基づいてこのコンテナーを再起動できます。
28.7. Pod スケジューラーおよび OOR 状態について
スケジューラーは、追加の Pod をノードに配置する際にノードの状態を表示します。たとえば、ノードに以下のようなエビクションのしきい値がある場合は、以下のようになります。
eviction-hard is "memory.available<500Mi"
さらに利用可能なメモリーが 500Mi を下回る場合、ノードは Node.Status.Conditions
の MemoryPressure
の値を true として報告します。
ノードの状態 | スケジューラーの動作 |
---|---|
|
ノードがこの状態を報告する場合、スケジューラーは |
|
ノードがこの状態を報告する場合、スケジューラーは追加の Pod をノードに配置しません。 |
28.8. シナリオ例
以下のシナリオについて考えてみましょう。
オペレーター:
-
メモリー容量が
10Gi
のノードがある。 - システムデーモン (カーネル、ノードなど) のメモリー容量の 10% を予約する必要がある。
- システムの OOM の悪化または発生の可能性を軽減するため、メモリー使用率が 95% の時点で Pod をエビクトする必要がある。
この設定から、system-reserved
にはエビクションのしきい値でカバーされるメモリー量が含まれていることを読み取ることができます。
この容量に達するのは、一部の Pod による使用がその要求を超えるか、またはシステムによる使用が 1Gi
を超える場合のいずれかになります。
ノードに 10 Gi の容量があり、システムデーモン (system-reserved
) 用に 10% の容量を予約する必要がある場合、以下の計算を実行します。
capacity = 10 Gi system-reserved = 10 Gi * .1 = 1 Gi
割り当て可能なリソースの量は以下のようになります。
allocatable = capacity - system-reserved = 9 Gi
これは、デフォルトでスケジューラーはノードに対し、9 Gi のメモリーを要求する Pod をスケジュールすることを意味します。
エビクションをオンに設定して、エビクションのトリガーが利用可能なメモリーが 30 秒間で 10% の容量を下回ることをノードで確認される場合や、5% の容量を下回る場合には即時にトリガーされるようにする必要がある場合、スケジューラーで 8Gi が割り当て可能であることを確認できる必要があります。そのため、システム予約ではエビクションしきい値の大きい方の値がカバーされている必要があります。
capacity = 10 Gi eviction-threshold = 10 Gi * .1 = 1 Gi system-reserved = (10Gi * .1) + eviction-threshold = 2 Gi allocatable = capacity - system-reserved = 8 Gi
以下を適切なノード設定マップに追加します。
kubeletArguments: system-reserved: - "memory=2Gi" eviction-hard: - "memory.available<.5Gi" eviction-soft: - "memory.available<1Gi" eviction-soft-grace-period: - "memory.available=30s"
この設定により、スケジューラーは Pod が設定される要求を下回る量のメモリーを使用することを前提とし、メモリー不足を即時に発生させ、エビクションをトリガーする Pod をノードに配置しないようにします。
28.9. 推奨される対策
28.9.1. DaemonSet および Out of Resource (リソース不足) の処理
ノードが DaemonSet によって作成された Pod をエビクトすると、Pod は即時に再作成され、同じノードに再スケジュールされます。これが生じるのは、ノードが DaemonSet から作成された Pod とそれ以外のオブジェクトを区別することができないためです。
通常 DaemonSet は作成する Pod がエビクションの候補として特定されることを防ぐため、BestEffort
Pod を作成しません。DaemonSet が起動する適切な Pod は Guaranteed
Pod になります。