4.4. コンテナーメモリーとリスク要件を満たすためのクラスターメモリーの設定
クラスター管理者は、以下を実行し、クラスターがアプリケーションメモリーの管理を通じて効率的に動作するようにすることができます。
- コンテナー化されたアプリケーションコンポーネントのメモリーおよびリスク要件を判別し、それらの要件を満たすようコンテナーメモリーパラメーターを設定する
- コンテナー化されたアプリケーションランタイム (OpenJDK など) を、設定されたコンテナーメモリーパラメーターに基づいて最適に実行されるよう設定する
- コンテナーでの実行に関連するメモリー関連のエラー状態を診断し、これを解決する
4.4.1. アプリケーションメモリーの管理について リンクのコピーリンクがクリップボードにコピーされました!
まず Red Hat OpenShift Service on AWS によるコンピュートリソースの管理方法の概要をよく読んでから次の手順に進むことを推奨します。
各種のリソース (メモリー、cpu、ストレージ) に応じて、Red Hat OpenShift Service on AWS ではオプションの 要求 および 制限 の値を Pod の各コンテナーに設定できます。
メモリー要求とメモリー制限について、以下の点に注意してください。
メモリーリクエスト
- メモリー要求値が指定されている場合、Red Hat OpenShift Service on AWS スケジューラーに影響します。スケジューラーは、コンテナーのノードへのスケジュール時にメモリー要求を考慮し、コンテナーの使用のために選択されたノードで要求されたメモリーをフェンスオフします。
- ノードのメモリーが使い切られると、Red Hat OpenShift Service on AWS はメモリー使用がメモリー要求を最も超過しているコンテナーのエビクションを優先します。深刻なメモリー枯渇の場合、ノード OOM キラーが同様のメトリックに基づいてコンテナー内のプロセスを選択して強制終了することがあります。
- クラスター管理者は、メモリー要求値に対してクォータを割り当てるか、デフォルト値を割り当てることができます。
- クラスター管理者は、クラスターのオーバーコミットを管理するために開発者が指定するメモリー要求の値を上書きできます。
メモリー制限
- メモリー制限値が指定されている場合、コンテナーのすべてのプロセスに割り当て可能なメモリーにハード制限を指定します。
- コンテナーのすべてのプロセスで割り当てられるメモリーがメモリー制限を超過する場合、ノードの OOM (Out of Memory) killer はコンテナーのプロセスをすぐに選択し、これを強制終了します。
- メモリー要求とメモリー制限の両方が指定される場合、メモリー制限の値はメモリー要求の値よりも大きいか、これと等しくなければなりません。
- クラスター管理者は、メモリーの制限値に対してクォータを割り当てるか、デフォルト値を割り当てることができます。
-
最小メモリー制限は 12 MB です。
Cannot allocate memory
Pod イベントのためにコンテナーの起動に失敗すると、メモリー制限は低くなります。メモリー制限を引き上げるか、これを削除します。制限を削除すると、Pod は制限のないノードのリソースを消費できるようになります。
4.4.1.1. アプリケーションメモリーストラテジーの管理 リンクのコピーリンクがクリップボードにコピーされました!
Red Hat OpenShift Service on AWS でアプリケーションメモリーをサイジングする手順は以下の通りです。
予想されるコンテナーのメモリー使用の判別
必要時に予想される平均およびピーク時のコンテナーのメモリー使用を判別します (例: 別の負荷テストを実行)。コンテナーで並行して実行されている可能性のあるすべてのプロセスを必ず考慮に入れるようにしてください。たとえば、メインのアプリケーションは付属スクリプトを生成しているかどうかを確認します。
リスク選好 (risk appetite) の判別
エビクションのリスク選好を判別します。リスク選好のレベルが低い場合、コンテナーは予想されるピーク時の使用量と安全マージンのパーセンテージに応じてメモリーを要求します。リスク選好が高くなる場合、予想される平均の使用量に応じてメモリーを要求することがより適切な場合があります。
コンテナーのメモリー要求の設定
上記に基づいてコンテナーのメモリー要求を設定します。要求がアプリケーションのメモリー使用をより正確に表示することが望ましいと言えます。要求が高すぎる場合には、クラスターおよびクォータの使用が非効率となります。要求が低すぎる場合、アプリケーションのエビクションの可能性が高くなります。
コンテナーのメモリー制限の設定 (必要な場合)
必要時にコンテナーのメモリー制限を設定します。制限を設定すると、コンテナーのすべてのプロセスのメモリー使用量の合計が制限を超える場合にコンテナーのプロセスがすぐに強制終了されるため、いくつかの利点をもたらします。まずは予期しないメモリー使用の超過を早期に明確にする ("fail fast" (早く失敗する)) ことができ、次にプロセスをすぐに中止できます。
一部の Red Hat OpenShift Service on AWS クラスターでは制限値を設定する必要があります。制限に基づいて要求を上書きする場合があります。また、一部のアプリケーションイメージは、要求値よりも検出が簡単なことから設定される制限値に依存します。
メモリー制限が設定される場合、これは予想されるピーク時のコンテナーのメモリー使用量と安全マージンのパーセンテージよりも低い値に設定することはできません。
アプリケーションが調整されていることの確認
適切な場合は、設定される要求および制限値に関連してアプリケーションが調整されていることを確認します。この手順は、JVM などのメモリーをプールするアプリケーションにおいてとくに当てはまります。残りの部分では、これを説明します。
4.4.2. Red Hat OpenShift Service on AWS の OpenJDK 設定について リンクのコピーリンクがクリップボードにコピーされました!
デフォルトの OpenJDK 設定はコンテナー化された環境では機能しません。そのため、コンテナーで OpenJDK を実行する場合は常に追加の Java メモリー設定を指定する必要があります。
JVM のメモリーレイアウトは複雑で、バージョンに依存しており、この詳細はこのドキュメントでは説明しません。ただし、コンテナーで OpenJDK を実行する際のスタートにあたって少なくとも以下の 3 つのメモリー関連のタスクが主なタスクになります。
- JVM 最大ヒープサイズを上書きする。
- JVM が未使用メモリーをオペレーティングシステムに解放するよう促す (適切な場合)。
- コンテナー内のすべての JVM プロセスが適切に設定されていることを確認する。
コンテナーでの実行に向けて JVM ワークロードを最適に調整する方法はこのドキュメントでは扱いませんが、これには複数の JVM オプションを追加で設定することが必要になる場合があります。
4.4.2.1. JVM の最大ヒープサイズを上書きする方法について リンクのコピーリンクがクリップボードにコピーされました!
OpenJDK は、デフォルトで、使用可能なメモリーの最大 25% (コンテナーのメモリー制限を認識します) をヒープメモリーに使用します。このデフォルト値は控えめな値であり、適切に設定されたコンテナー環境ではこの値を使用すると、コンテナーに割り当てられたメモリーの 75% がほとんど使用されないことになります。コンテナーレベルでメモリー制限が課されるコンテナーコンテキストでは、JVM がヒープメモリーに使用する割合を 80% などかなり高く設定する方が適しています。
ほとんどの Red Hat コンテナーには、JVM の起動時に更新された値を設定して OpenJDK のデフォルトを置き換える起動スクリプトが含まれています。
たとえば、OpenJDK コンテナーの Red Hat ビルドのデフォルト値は 80% です。この値は、JAVA_MAX_RAM_RATIO
環境変数を定義することで異なるパーセンテージに設定できます。
その他の OpenJDK デプロイメントの場合、次のコマンドを使用してデフォルト値の 25% を変更できます。
例
java -XX:MaxRAMPercentage=80.0
$ java -XX:MaxRAMPercentage=80.0
4.4.2.2. JVM で未使用メモリーをオペレーティングシステムに解放するよう促す方法について リンクのコピーリンクがクリップボードにコピーされました!
デフォルトで、OpenJDK は未使用メモリーをオペレーティングシステムに積極的に返しません。これは多くのコンテナー化された Java ワークロードには適していますが、例外として、コンテナー内に JVM と共存する追加のアクティブなプロセスがあるワークロードの場合を考慮する必要があります。それらの追加のプロセスはネイティブのプロセスである場合や追加の JVM の場合、またはこれら 2 つの組み合わせである場合もあります。
Java ベースのエージェントは、次の JVM 引数を使用して、JVM が未使用のメモリーをオペレーティングシステムに解放するように促すことができます。
-XX:+UseParallelGC -XX:MinHeapFreeRatio=5 -XX:MaxHeapFreeRatio=10 -XX:GCTimeRatio=4 -XX:AdaptiveSizePolicyWeight=90
-XX:+UseParallelGC
-XX:MinHeapFreeRatio=5 -XX:MaxHeapFreeRatio=10 -XX:GCTimeRatio=4
-XX:AdaptiveSizePolicyWeight=90
これらの引数は、割り当てられたメモリーが使用中のメモリー (-XX:MaxHeapFreeRatio
) の 110% を超え、ガベージコレクター (-XX:GCTimeRatio
) での CPU 時間の 20% を使用する場合は常にヒープメモリーをオペレーティングシステムに返すことが意図されています。アプリケーションのヒープ割り当てが初期のヒープ割り当て (-XX:InitialHeapSize
/ -Xms
で上書きされる) を下回ることはありません。詳細は、Tuning Java’s footprint in OpenShift (Part 1)、Tuning Java’s footprint in OpenShift (Part 2)、および OpenJDK and Containers を参照してください。
4.4.2.3. コンテナー内のすべての JVM プロセスが適切に設定されていることを確認する方法について リンクのコピーリンクがクリップボードにコピーされました!
複数の JVM が同じコンテナーで実行される場合、それらすべてが適切に設定されていることを確認する必要があります。多くのワークロードでは、それぞれの JVM に memory budget のパーセンテージを付与する必要があります。これにより大きな安全マージンが残される場合があります。
多くの Java ツールは JVM を設定するために各種の異なる環境変数 (JAVA_OPTS
、GRADLE_OPTS
など) を使用します。適切な設定が適切な JVM に渡されていることを確認するのが容易でない場合もあります。
JAVA_TOOL_OPTIONS
環境変数は常に OpenJDK によって考慮され、JAVA_TOOL_OPTIONS
に指定された値は、JVM コマンドラインに指定される他のオプションによって上書きされます。デフォルトでは、Java ベースのエージェントイメージで実行されるすべての JVM ワークロードでこれらのオプションがデフォルトで使用されるように、Red Hat OpenShift Service on AWS Jenkins Maven エージェントイメージは次の変数を設定します。
JAVA_TOOL_OPTIONS="-Dsun.zip.disableMemoryMapping=true"
JAVA_TOOL_OPTIONS="-Dsun.zip.disableMemoryMapping=true"
この設定は、追加オプションが要求されないことを保証する訳ではなく、有用な開始点になることを意図しています。
4.4.3. Pod 内でのメモリー要求および制限の検索 リンクのコピーリンクがクリップボードにコピーされました!
Pod 内からメモリー要求および制限を動的に検出するアプリケーションでは Downward API を使用する必要があります。
手順
MEMORY_REQUEST
とMEMORY_LIMIT
スタンザを追加するように Pod を設定します。以下のような YAML ファイルを作成します。
Copy to Clipboard Copied! Toggle word wrap Toggle overflow 以下のコマンドを実行して Pod を作成します。
oc create -f <file_name>.yaml
$ oc create -f <file_name>.yaml
Copy to Clipboard Copied! Toggle word wrap Toggle overflow
検証
リモートシェルを使用して Pod にアクセスします。
oc rsh test
$ oc rsh test
Copy to Clipboard Copied! Toggle word wrap Toggle overflow 要求された値が適用されていることを確認します。
env | grep MEMORY | sort
$ env | grep MEMORY | sort
Copy to Clipboard Copied! Toggle word wrap Toggle overflow 出力例
MEMORY_LIMIT=536870912 MEMORY_REQUEST=402653184
MEMORY_LIMIT=536870912 MEMORY_REQUEST=402653184
Copy to Clipboard Copied! Toggle word wrap Toggle overflow
メモリー制限値は、/sys/fs/cgroup/memory/memory.limit_in_bytes
ファイルによってコンテナー内から読み取ることもできます。
4.4.4. OOM の強制終了ポリシーについて リンクのコピーリンクがクリップボードにコピーされました!
Red Hat OpenShift Service on AWS は、コンテナーのすべてのプロセスのメモリー使用量の合計がメモリー制限を超えるか、またはノードのメモリーを使い切られるなどの深刻な状態が生じる場合にコンテナーのプロセスを強制終了する場合があります。
プロセスが OOM (Out of Memory) によって強制終了される場合、コンテナーがすぐに終了する場合があります。コンテナーの PID 1 プロセスが SIGKILL を受信する場合、コンテナーはすぐに終了します。それ以外の場合、コンテナーの動作は他のプロセスの動作に依存します。
たとえば、コンテナーのプロセスは、SIGKILL シグナルを受信したことを示すコード 137 で終了します。
コンテナーがすぐに終了しない場合、OOM による強制終了は以下のように検出できます。
リモートシェルを使用して Pod にアクセスします。
oc rsh <pod name>
# oc rsh <pod name>
Copy to Clipboard Copied! Toggle word wrap Toggle overflow 以下のコマンドを実行して、
/sys/fs/cgroup/memory/memory.oom_control
で現在の OOM kill カウントを表示します。grep '^oom_kill ' /sys/fs/cgroup/memory/memory.oom_control
$ grep '^oom_kill ' /sys/fs/cgroup/memory/memory.oom_control
Copy to Clipboard Copied! Toggle word wrap Toggle overflow 出力例
oom_kill 0
oom_kill 0
Copy to Clipboard Copied! Toggle word wrap Toggle overflow 以下のコマンドを実行して、Out Of Memory (OOM) による強制終了を促します。
sed -e '' </dev/zero
$ sed -e '' </dev/zero
Copy to Clipboard Copied! Toggle word wrap Toggle overflow 出力例
Killed
Killed
Copy to Clipboard Copied! Toggle word wrap Toggle overflow 以下のコマンドを実行して、
/sys/fs/cgroup/memory/memory.oom_control
の OOM kill カウンターの増分を表示します。grep '^oom_kill ' /sys/fs/cgroup/memory/memory.oom_control
$ grep '^oom_kill ' /sys/fs/cgroup/memory/memory.oom_control
Copy to Clipboard Copied! Toggle word wrap Toggle overflow 出力例
oom_kill 1
oom_kill 1
Copy to Clipboard Copied! Toggle word wrap Toggle overflow Pod の 1 つ以上のプロセスが OOM で強制終了され、Pod がこれに続いて終了する場合 (即時であるかどうかは問わない)、フェーズは Failed、理由は OOMKilled になります。OOM で強制終了された Pod は
restartPolicy
の値によって再起動する場合があります。再起動されない場合は、レプリケーションコントローラーなどのコントローラーが Pod の失敗したステータスを認識し、古い Pod に置き換わる新規 Pod を作成します。Pod のステータスを取得するには、次のコマンドを使用します。
oc get pod test
$ oc get pod test
Copy to Clipboard Copied! Toggle word wrap Toggle overflow 出力例
NAME READY STATUS RESTARTS AGE test 0/1 OOMKilled 0 1m
NAME READY STATUS RESTARTS AGE test 0/1 OOMKilled 0 1m
Copy to Clipboard Copied! Toggle word wrap Toggle overflow Pod が再起動されていない場合は、以下のコマンドを実行して Pod を表示します。
oc get pod test -o yaml
$ oc get pod test -o yaml
Copy to Clipboard Copied! Toggle word wrap Toggle overflow 出力例
Copy to Clipboard Copied! Toggle word wrap Toggle overflow 再起動した場合は、以下のコマンドを実行して Pod を表示します。
oc get pod test -o yaml
$ oc get pod test -o yaml
Copy to Clipboard Copied! Toggle word wrap Toggle overflow 出力例
Copy to Clipboard Copied! Toggle word wrap Toggle overflow
4.4.5. Pod エビクションについて リンクのコピーリンクがクリップボードにコピーされました!
Red Hat OpenShift Service on AWS は、ノードのメモリーが使い切られると、そのノードから Pod をエビクトする場合があります。メモリー消費の度合いによって、エビクションは正常に行われる場合もあれば、そうでない場合もあります。正常なエビクションは、各コンテナーのメインプロセス (PID 1) が SIGTERM シグナルを受信してから、プロセスがすでに終了していない場合は後になって SIGKILL シグナルを受信することを意味します。正常ではないエビクションは各コンテナーのメインプロセスが SIGKILL シグナルを即時に受信することを示します。
エビクトされた Pod のフェーズは Failed になり、理由は Evicted になります。この場合、restartPolicy
の値に関係なく再起動されません。ただし、レプリケーションコントローラーなどのコントローラーは Pod の失敗したステータスを認識し、古い Pod に置き換わる新規 Pod を作成します。
oc get pod test
$ oc get pod test
出力例
NAME READY STATUS RESTARTS AGE test 0/1 Evicted 0 1m
NAME READY STATUS RESTARTS AGE
test 0/1 Evicted 0 1m
oc get pod test -o yaml
$ oc get pod test -o yaml
出力例
... status: message: 'Pod The node was low on resource: [MemoryPressure].' phase: Failed reason: Evicted
...
status:
message: 'Pod The node was low on resource: [MemoryPressure].'
phase: Failed
reason: Evicted