Proxmox/KVMでHuge Pagesを最適化:データベース本来のパフォーマンスを引き出す

Virtualization tutorial - IT technology blog
Virtualization tutorial - IT technology blog

午前2時、4KBがボトルネックになった時

ある日の午前2時、Proxmox上のRedisとPostgreSQLのクラスタと格闘していた時のことです。RAMの空き容量は十分でCPU負荷も低かったのですが、アプリケーションのレイテンシが異常に高くなっていました。ハイスペックなハードウェアにもかかわらず、システムが「カクつく」ような動作をしていました。perf/proc/meminfoで調査した結果、主な原因はTLB(Translation Lookaside Buffer)ミスの過多であることに気づきました。

デフォルトでは、Linuxは4KBsのページ単位でメモリを管理します。32GBのRAMを搭載した仮想マシン(VM)の場合、システムは800万以上のページを扱う必要があります。CPUがデータにアクセスするたびに、この膨大なページテーブルを検索しなければなりません。このテーブルがキャッシュ(TLB)の容量を超えると、CPUは「データがどこにあるか」を調べるためだけに、絶えずRAMにアクセスせざるを得なくなります。Huge Pagesは、ページサイズを2MBや1GBに拡大することで、この無駄を解消するために生まれました。

私のホームラボ環境では、現在12個のVMとコンテナが稼働しており、本番環境にデプロイする前のテスト場となっています。データベースのような負荷の高いワークロードでは、Huge Pagesを使用しない場合、メモリ管理のためだけにCPUパフォーマンスの10〜15%を浪費しているというのが実情です。

3つのメモリ管理手法:どれを選ぶべきか?

1. 標準ページ (4KB) – 馴染み深いが負荷が高い

小規模なプロセスには柔軟で適したデフォルト設定です。しかし、数十GBのRAMを積んだVMでは、ページ数が多すぎてCPUが検索時に息切れし、大きな負担となります。

2. 透明なHuge Pages (THP) – 便利だがリスクあり

Linuxが必要に応じて4KBのページを自動的に2MBにまとめます。一見良さそうですが、本番環境では突然の「ラグ」が発生しがちです。khugepagedプロセスがページの再構成を行う際にI/Oブロックを引き起こすため、OracleやRedisのエキスパートは真っ先にこの機能をオフにすることを推奨しています。

3. 静的なHuge Pages – 着実で確実

私が推奨する選択肢です。起動時に一定量のRAMをHuge Pagesとして固定的に割り当てます。このRAMは完全に「専有」され、スワップアウトされることはありません。KVM仮想化において、最も安定した高いパフォーマンスを提供します。

メリットとデメリットの検討

  • メリット:
    • TLBミスが顕著に減少。CPUはテーブル検索ではなくロジックの処理に集中できるようになります。
    • メモリが物理RAMにロック(固定)されます。スワップに追い出されることがないため、極めて低いレイテンシが保証されます。
    • ページテーブルが大幅に軽量化されるため、カーネルのオーバーヘッドが減少します。
  • デメリット:
    • Huge Pagesに割り当てたRAMは「戻ってこない」リソースとなります。VMが使い切らなくても、他のプロセスが借用することはできません。
    • 設定を安定して反映させ、メモリの断片化を避けるためにホストの再起動が必要です。
    • RAM Ballooning(VMメモリの動的伸縮)機能が無効になります。

なぜProxmoxでStatic Huge Pagesを選ぶのか?

小規模なホスティングであれば、リソースのオーバーコミットができるRAM Ballooningは便利です。しかし、エンタープライズ向けのデータベースでは、安定性が最優先です。私は柔軟性を犠牲にしてでも、ページフォールトによるRedisのラグを排除したいと考えます。128GBのRAMを搭載したサーバーであれば、通常64GB〜80GBをHuge Pagesとして固定的に割り当てます。

詳細な導入手順

以下は、現在最も一般的で導入しやすい2MBのHuge Pagesを設定する手順です。

ステップ 1: ハードウェア情報の確認

まず、以下のコマンドでCPUがHuge Pagesをサポートしているか確認します:

grep -i hugepages /proc/meminfo

ステップ 2: カーネルパラメータの設定

起動時にメモリを割り当てるようGrubを編集します。例として、20GBのRAMをHuge Pages(2MBタイプ)として割り当てる場合、10240ページになります。

nano /etc/default/grub

GRUB_CMDLINE_LINUX_DEFAULT行にパラメータを追加します:

# 2MB x 10240ページ = 20GBを割り当て
GRUB_CMDLINE_LINUX_DEFAULT="quiet default_hugepagesz=2M hugepagesz=2M hugepages=10240"

Grubを更新してサーバーを再起動します:

update-grub
reboot

ステップ 3: 結果の確認

再起動後、以下のコマンドでRAMが「予約」されているか確認します:

grep Huge /proc/meminfo

HugePages_Freeの値が、先ほど設定した10240に近い数値になっていれば成功です。

ステップ 4: 仮想マシン(VM)への適用

次に、Proxmoxに対して、通常のRAMではなくHuge PagesからVMへメモリを割り当てるように設定します。

CLIを使うと素早く設定できます:

# 100をあなたのVM IDに置き換えてください
qm set 100 -hugepages 2

重要な注意点:設定を有効にするには、VMを一度完全にシャットダウンしてから起動(パワーサイクル)する必要があります。

ステップ 5: 最終確認

VMの稼働中に、ホスト側で再度確認します:

grep HugePages_Free /proc/meminfo

もしFreeの値がVMのメモリ容量分だけ減少していればおめでとうございます。あなたのシステムは今、高速なメモリ上で快適に動作しています。

実地でのちょっとした注意点

64GB以上の非常に大きなVMの場合、1GBのHuge Pages(hugepagesz=1G)の方が効果的です。ただし、これにはより厳格なハードウェアサポートが必要です。

また、Huge Pagesを使用している場合、一部の古いQEMUバージョンではRAMを含むスナップショットでエラーが発生することがあります。インフラの変更後は、バックアップ手順を常に念入りにテストしてください。

システムの最適化は魔法ではなく、カーネルとハードウェアがどのように動作するかを理解することです。この深夜の試行錯誤が、皆さんのシステムをよりスムーズにする助けになれば幸いです!

Share: