VMware ESXにおけるメモリ管理(8) - バルーニング

VMware ESXにおけるメモリ管理』シリーズ
(1) - 序:他のリソースとの違いはなに?
(2) - 仮想化インフラにおけるメモリ管理って?
(3) - メモリに関する仮想化支援機能(Intel EPT/VPID, AMD RVI/Tagged TLB)
(4) - メモリを割り当てるのは簡単だが、回収するのは難しい
(5) - 透過的ページ共有
(6) - Dynamic Memory on Hyper-V 実装編
(7) - Dynamic Memory on Hyper-V 設定編 +α
…の続きです。

戻って参りました、ESX(^_^;)。では、さっそく。今回のお題はバルーニングです。
バルーニングは(5)で取り上げた透過的ページ共有とは全く異なるメモリ管理手法です。透過的ページ共有はゲストOSに対しては透過的に(つまりは仮想マシン上のゲストOS側からは意識されないかたちで)機能する、「メモリ資源を節約するための仕組み」でしたが、バルーニングはゲストOS側が意識するかたちで「メモリ資源を回収するための仕組み」といえます。
仮想マシンの実体は「VMkernel上で実行されたプロセスである」という意味では、お互いに並列的に実行されている存在ですが、仮想マシン上に導入されているゲストOSから見ると、同一のESXホスト上で実行されているとしてもお互いを意識することはありません(更にいえば、意識する必要がないように実装されています)。そのため、VMkernelが仮想マシンに対して物理メモリページを割り当てることはできても、ゲストOSが確保している物理メモリページを返してくれることは「ゲストOS側の仕組みとしては(標準では)用意されていない」ことになります*1。これではメモリページは一方的に割り当てられていくだけとなり、メモリを効率的に使用することはできません。ESXホストとして使用するサーバには潤沢なメモリを搭載することが望ましいことは確かですが、仮想マシンに対して割り当てられたメモリサイズの合計がESXホストの物理メモリサイズを超えるオーバーコミット状態であったとしても各仮想マシンは可能な限りパフォーマンスを維持した動作が継続できる仕組みが用意されているべきであり、そのために用意された仕組みが、バルーニングです。バルーニングはゲストOSに割り当てたが使われていないメモリ空間を確認するための機能となります*2
ESXにおけるバルーニングは、VMware Toolsに含まれる形でゲストOS上に導入されるデバイスドライバ「バルーンドライバ」として実装されています。バルーンドライバはゲストOSに構成される外部インターフェイス(ネットワークアダプタや各種ポート類)は使用せずに、HypervisorであるVMkernelとの間ではゲストOSからも隠蔽されたプライベートチャネルを用いて連携動作し、回収するメモリサイズを決定します。VMkernelにおいてメモリリソースの回収が必要となった場合、指示を受けたバルーンドライバはゲストOS上のメモリ空間において風船を膨らませるかのようにメモリ空間をアロケートします。なお、バルーンドライバが動作するためには、ゲストOS内にスワップ領域が用意されている必要がありますので注意が必要です。

バルーンドライバはメモリ空間の回収のためのアロケート動作を開始する前にゲストOSのメモリ使用状況を確認し、回収するメモリページ数をVMkernelと調整します(よって、事実上この時点でこの仮想マシンから回収できるであろうメモリサイズをVMkernel側も把握することとなります)。バルーンドライバがメモリ空間をアロケートしようとした際に、ゲストOSは未割り当てのメモリ空間を割り当てますが、未割り当てのメモリ空間がなかった場合は、ゲストOSが判断して「スワップアウトしてもよいと判断したメモリページ」を「ゲストOS側が用意したスワップ領域にスワップアウトさせて」バルーンドライバの要求に応えることになります。上記の図では、アプリケーションが使用している左側の2つのメモリページはそのままに、右側の「回収できる」メモリページをバルーンドライバに対して割り当てます。メモリページのアロケートを受けたバルーンドライバは、ゲストOSから割り当てられたメモリページがスワップアウトされてしまわないように、つまりは「ピンをさしておくように」(上図例) して確保します。その上で、バルーンドライバはVMkernelに対して確保したメモリページに関する情報を通知し、通知を受けたVMkernelは対象となるメモリページを回収します。その間、対象となるメモリページはバルーンドライバによって「ピン留め」された状態となっているため、他の用途に用いられるために読み書きが行われたりページアウトしてしまうようなことはないことが保証されます*3ので、「安全な回収」が行われます。また、バルーンドライバを用いたメモリの回収においては、メモリページのデータを保持するためにゲストOS側が持っているスワップ機能をそのまま使用しますので、別途メモリページの割り当てなどを行う必要がないという点も仕組みをシンプルにすることができるという意味でよいところです。もしバルーンドライバに対するメモリのアロケート処理の中で「ゲストOSにおけるスワップ領域にスワップアウトしたメモリページ」に対するアクセスが発生した場合には、VMkernelは新規メモリページの割り当てとまったく同じ仕組みの中で処理を行います。
バルーンドライバを使って「ゲストOSが認識するかたちで」メモリを回収することは、VMkernelにとってもゲストOSにとってもメリットのあるやり方です。まずVMkernelにとってのメリットは、ゲストからメモリを回収してくる仕組み(どのメモリページを回収して良いのかどうか判断する仕組み)を自身で実装する必要がなくなる点です。もちろん、回収されるメモリページをバルーンドライバと連携して管理する機能は必要となりますが、具体的に「どのメモリページを回収すればよいのか」については(バルーンドライバのメモリアロケート要求に応えるという形で)ゲストOS側で判断されますので、VMkernel自身が判断を行う必要性はありません。続いてゲストOS側にとってのメリットは、「ゲストOS自身が管理するスワップ領域を使用したメモリデータの待避が行われている」点です。ゲストOSのメモリI/Oパフォーマンスにおいて最も悪いといえる状況は、「物理メモリページに格納されているとゲストOSが判断していたデータに対するアクセスが、実際には(ゲストOSからはみえない階層における処理として)スワップアウトしていてディスクアクセスが必要となり、パフォーマンス的にも負荷的にも高い状態となってしまう」ような状況となることです*4。VMkernelによって仮想マシン起動時に用意されるスワップファイルを使用したスワップアウトはまさに「ゲストOSは把握していない」スワップを生み出すこととなりますので、最後の手段であり、これが使われるような運用を行うべきではありません。
ゲストOSの中で管理されているスワップ領域であったとしても、スワップアウトはパフォーマンスに甚大な影響を与えることになるわけですが、ゲストOSが「このメモリページはスワップアウトさせてしまっても良い」と判断したメモリページをスワップアウトする方が、VMkernel側が問答無用で使用状況などを考慮せずにスワップアウトさせてしまうよりもパフォーマンスに与える影響は限定されます。
次回は、禁断の最終兵器、Hypervisor Swappingについて書きたいと思います。

*1:ゲストOSは物理マシンにおいて、全てのリソース資源を占有的に管理するように作られているのですから、誰かにリソースを渡す仕組みは一切持っていないのは当たり前といえば当たり前ですね

*2:逆に言えば、オーバーコミットしていなければバルーンドライバによるメモリページの回収は動作しません

*3:当然ながら、バルーンドライバ自身はメモリの確保は行ってもそこを使うわけではない

*4:メモリアクセスに比較すると、ディスクアクセスは1000倍ぐらい遅くなる場合もある