オーバーコミット


随分とご無沙汰しております。エージェンテック年寄衆の一人クリシマです。大丈夫。ワタクシはまだ生きています。そう簡単にくたばる訳には参りません!!相変わらず毎日元気に自分の脚で6階のオフィスまで階段をのぼっております

さて、今回は趣を変えて若干技術的な話をば。

障害発生!

当社のABook製品はクラウドサービスもご用意しておりますが、お客様のご指定場所(物理的・地理的な場所もありますし、クラウドの事もあります)にABook製品をインストールしてご利用いただくオンプレミスの設置サービスもやっております。
まぁ、出前みたいなものですよね。へいっ!来々軒ですっ!!(全国の来々軒さん、ゴメンナサイ。)

そんなオンプレミスのサーバにて先日、ある障害が起こりました。

出前イメージ

そのサーバではかなり大きめのRAMを搭載しているのですが、メモリ・インテンシブな処理が走るサーバでしたので搭載RAMの2/3程のヒープをアプリケーションサーバ・プロセスに割り当てました。これだけなら問題ないのですが。

このアプリケーションサーバ・プロセスはある種の処理を子プロセスであるシェルスクリプトへ委譲する造りとなっていて、メモリ増設後にそのシェルスクリプトのプロセス起動に失敗する障害が発生しました。これはヤバイ!

オーバーコミットの話

しかし。この現象は既視感のある事案です。今まで直接遭遇したことはありませんでしたが、理論的に起こりうる事であり、情報としてはどこかで見たことがある事案です。そう。Linuxカーネルのオーバーコミット問題です。

ビハインド・ザ・シーン!!(ルー大柴風に)

Linuxの任意のプロセスは、子プロセスを作成して任意のプログラムイメージでその子プロセスのメモリを「上書き」して実行できます。この処理は二段階に分かれており、最初にforkして子プロセスを生成し、次いで子プロセス中でexec処理にて新しい実行イメージをロードして新しい処理を開始します。

fork処理は簡単に言えば「細胞分裂」のようなものです。

この時、使用メモリ(各プロセスの仮想メモリの和)が見かけは2倍に増えるのですが、実際にはすぐに子プロセス用に物理メモリまたはRAM(Linuxカーネルの専門用語で言うと、ページフレーム)が割り当てられるわけではありません。
Linuxではfork処理は裏側でcloneシステムコールによって処理されていて、cloneは新しいプロセス用の仮想メモリを割り当てて、その参照先をすべて親プロセスのページフレーム(=物理メモリ・RAMの事ね)に割り当てます。ですので、fork直後は子プロセスのメモリの「中身」は親プロセスと全く同一であり、物理メモリ・RAMの消費量もほぼ親プロセスのものと同じで、それぞれのプロセスの処理が進むにつれて「違い」が生じる時点で子プロセス用に新しいページフレームが割り当てられていきます。

このような動作を一般的に「コピー・オン・ライト(Copy On Write, COW)」と呼びます。
書き込み処理が「実際に」発生する時点で初めて、もとの情報(今の場合はページフレーム)をコピーするからです。時間的コストのかかる処理はそれが実際に必要となるまで後回し。それがコンピュータの世界の常識なのです。

ですので、各プロセスの仮想メモリの総和は物理メモリ・RAMの実装量を簡単に超えることができます。この状態をメモリの「オーバーコミット」と呼びます。

オーバーコミットの処理方針

Linuxカーネルのオーバーコミットへの対処方針を決定する重要なカーネルパラメータがあります。
それは”/proc/sys/vm/overcommit_memory”です。proc – process information pseudo-filesystemの/proc/sys/vm/overcommit_memoryに関する解説を見ると、このパラメータの値は0,1または2の値をとります。

値0 : 帰納的なオーバーコミット(デフォルト値)
値1 : 常にオーバーコミット。チェック無し。
値2 : 常にチェック。オーバーコミット無し。

値2に設定した場合、以下の計算式により計算された制限値までが許可されることになります。

コミット制限値 = (RAM総量 – Huge_TLB総量) * オーバーコミット率 / 100 + スワップ総量

現実的な対処

では、実際に障害では何が起こっていたのか?

RAMの2/3の仮想メモリを割り当てたアプリケーションサーバのプロセスが子プロセスを作ってシェルスクリプトを実行するためにforkするとき、見かけ上RAMの2/3 * 2 = 4/3倍のメモリが必要となります。いきなりこれだけで激しいオーバーコミット状態となります。
この時、先の/proc/sys/vm/overcommit_memoryのデフォルト値は0ですから、カーネルによるオーバーコミットの「検閲」が入ります。どのようなロジックなのかといった詳細は不明ですし知る必要もないのですが、激しいオーバーコミット状態ですのでカーネルは子プロセス用の仮想メモリ割当を拒否したようです。
どうも肌感覚としては、搭載RAMの半分までのプロセスであるならば、子プロセスをforkしても仮想メモリを割り当ててくれるようです。しかし、同時並行的にforkすればやはり仮想メモリ割当をしてもらえない子プロセスが出てくる事は容易に想像できます。

巨大なメモリフットプリントを有するアプリケーションサーバプロセスをforkして子プロセスを作るのはどうなのよ!?という根源的かつアーキテクチャ的なハナシは脇に置いといて。実際の造りとしてそうなっているのですから、対処しなければなりません。
どうするか?値2に設定してコミット制限値を定める事も出来るのですが、後日また同様な問題で対応を迫られるケースがあるかもしれません。そしてアプリケーションサーバプロセスが起動するシェルスクリプトはアプリケーションサーバ自体よりもずっと小さなメモリフットプリントですので、オーバーコミット後のRAM残量不足に伴うOOMキラー攻撃を受ける事は考えにくい。ですので、値1、つまり常にオーバーコミットを許容する設定とするのが最も妥当な設定であると判断できます。

ちなみに、このオーバーコミット問題、アプリケーションサーバだけの問題では御座いません。
巷でよく目にするのはRedis。Redisはそのアーキテクチャ上、マルチスレッドではなく子プロセスを生成してデータ処理を行います。この時親Redisプロセスが膨大なデータを握っていると、そのメモリフットプリントが肥大化しているので、子プロセスをforkする時にこのオーバーコミット問題をもろに受けることになります。ですので、Redis界隈でもこの「常にオーバーコミット」となる設定を実施するケースが多々あるようです。

苦しむ技術者イメージ

オーバーコミット設定の実施

設定そのものは簡単です。OS再起動も不要です。カーネルの設定内容は即時反映される筈ですが、可能であるならば念のためアプリケーションサーバプロセスの再起動(これによりアプリケーションサーバのPIDが変わるので、カーネルによる「追跡」を確実に振り切ることが出来る。)を実施しておくと良いでしょう。

/etc/sysctl.confファイルに以下の行を追記します。

vm.overcommit_memory = 1

/etc/sysctl.confの設定内容を反映させるために、rootユーザでsysctlコマンドを実行します。

# sysctl -p

設定内容を確認します。

# sysctl -a|grep overcommit_memory
sysctl: reading key "net.ipv6.conf.all.stable_secret"
sysctl: reading key "net.ipv6.conf.default.stable_secret"
sysctl: reading key "net.ipv6.conf.eth0.stable_secret"
sysctl: reading key "net.ipv6.conf.eth1.stable_secret"
sysctl: reading key "net.ipv6.conf.lo.stable_secret"
vm.overcommit_memory = 1
#

または:

# cat /proc/sys/vm/overcommit_memory
1
#

これでオーバーコミット設定が即時反映され、かつOS再起動後もオーバーコミット設定が残るようになりました。念のためここでアプリケーションサーバを再起動しておくと良いでしょう。

おわりに

システムリソースの増設はよくある話だと思いますが、それと同時に今まで起こった事がないような障害に見舞われる事があるんだなぁと。

当社は自社アプリケーションABookのベンダですが、自社アプリケーション以外の事でも結構「何でも屋」的な対応をしなければならないシチュエーションが往々にしてあり、我々は日々奮闘・精進している次第であります。

免責について

本記事にて記載される情報は「ありのまま」にて提供されるものであり、弊社および執筆者個人はその実施を教唆するものでは御座いません。また、本記事内容の正確性および有効性ならびに当該情報に基づき実施された行為に係るすべての結果および当該結果より生じたもしくは生じるであろう一切の債務に関しましても、弊社および執筆者個人は一切の責任を負いません。

本記事情報を活用される場合には、実施者は前記免責条項に関して同意したものとみなします。

おしらせ

メルマガ登録などよろしくお願いします!皆さんからの質問やコメントもお待ちしております!

コメント

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