はじめに
会社で PB 級の Hadoop クラスタを運用していますが、ある日から Datanode の CPU system (Kernel 内での CPU 使用率) が高騰し、Job が遅延するという症状が発現しました。Hadoop で CPU system 高騰というと、 Transparent HugePage 設定が有名ですが、そちらについては既に特定し、対策済みでした。 THP と Hadoop に関係については下記 Blog が詳しいです。
Transparent Huge Pages and Hadoop Workloads
Transparent Huge Pages and Hadoop Workloads
今回は THP ではなく、 "zone_reclaim_mode" の設定による性能劣化について、現象から原因特定に至るまでの経緯と、推奨する設定について解説します。
現象
観測された現象について簡単に箇条書きします。
- CPU user が 5% 程度の時でも CPU system が30% を超えるなど、 Kernel 内での CPU 使用率が異常に高かった
- CPU 使用率高騰により、いくつかの Job 実行時間が、問題発生前と比較して 1.5 倍に増えた
- 一部のマシンで発生し、他のモデルのマシンでは発生しなかった
perf による原因調査
今回は Profiling により、 "どの関数で CPU を使用しているのか" 、 "どの処理で問題の関数が呼ばれるのか(Call Graph)" を調査します。
perf での Profiling 取得
CPU system が高騰したタイミングを見計らい、下記コマンドでプロファイルを取得します。
perf record -F 99 -a -g -- sleep 30取得した結果を表示。Call Graph が取得できます。
perf report
結果の一部:
- 33.01% java [kernel.kallsyms] [k] _spin_lock_irq - _spin_lock_irq - 98.89% shrink_inactive_list shrink_mem_cgroup_zone shrink_zone zone_reclaim get_page_from_freelist - __alloc_pages_nodemask - 89.56% alloc_pages_current - 79.46% __page_cache_alloc - 99.76% grab_cache_page_write_begin ext4_da_write_begin generic_file_buffered_write __generic_file_aio_write generic_file_aio_write ext4_file_write do_sync_write vfs_write sys_write system_call_fastpath + 0x3fda40e6fd + 17.54% tcp_sendmsg + 1.75% __get_free_pages + 1.25% pte_alloc_one + 9.29% alloc_pages_vma + 1.15% kmem_getpages + 14.24% java [kernel.kallsyms] [k] _spin_lock + 4.75% java libjvm.so [.] SpinPause + 4.03% java perf-1947.map [.] 0x00007fd9550209cd + 2.64% java libsnappy.so.1.1.3 [.] snappy::internal::CompressFragment(char const*, unsigned long, char*, + 2.01% java libjvm.so [.] ParallelTaskTerminator::offer_termination(TerminatorTerminator*) + 1.84% java [kernel.kallsyms] [k] __isolate_lru_page + 1.58% init [kernel.kallsyms] [k] intel_idle ...
- CPU system を高騰させているのは "spin_lock*" 関数であること
- "spin_lock*" 関数は "メモリ回収処理" の延長で呼ばれていること
つまり、メモリが足りなくなったために、メモリ回収処理があまりに頻繁に呼ばれ、spin_lock のオーバヘッドが高騰したことが予想されます。さて、メモリ使用量について改めて観測すると、半分程度しか使用していません。にもかかわらずメモリ回収処理が頻繁に呼ばれるのは何故でしょうか...
Linux のメモリ解放に関連するパラメータを洗い出してみると、一つの気になるパラメータがありました。"zone_reclaim_mode" です。有効になっている場合、NUMA 環境で zone 毎のメモリ回収が積極的に行われるようになります。デフォルトでは無効のはずですが、今回の該当マシン(CentOS 6系)で調べてみると、なんと有効になっていました。
zone_reclaim_mode について詳細: https://www.kernel.org/doc/Documentation/sysctl/vm.txt
zone_reclaim_mode 無効設定の結果
なぜ zone_reclaim_mode が有効になっていたか
zone_reclaim_mode は default = 0 ですが、NUMA 環境における Node 間の距離(RECLAIM_DISTANCE) の値によっては、Kernel 起動時に 1 に修正されてしまうようです。
mm/pagealloc.c
その閾値は "20" とあります。3096 /* 3097 * If another node is sufficiently far away then it is better 3098 * to reclaim pages in a zone before going off node. 3099 */ 3100 if (distance > RECLAIM_DISTANCE) 3101 zone_reclaim_mode = 1;
57 /* 58 * If the distance between nodes in a system is larger than RECLAIM_DISTANCE 59 * (in whatever arch specific measurement units returned by node_distance()) 60 * then switch on zone reclaim on boot. 61 */ 62 #define RECLAIM_DISTANCE 20
有効になっていたマシンの NUMA Node 距離を調べると... 21
$ numactl --hardware ... node distances: node 0 1 0: 10 21 1: 21 10
無効になっていたマシンでは 20 でした。
要するに、ハードウェア構成によっては zone_reclaim_mode が自動的に有効になってしまう様です。
最新の Kernel ではどうなっているか
http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=4f9b16a64753d0bb607454347036dc997fd03b82
他のソフトウェアでの推奨値
- PostgreSQL: PostgreSQL, NUMA and zone reclaim mode on linux
- MongoDB: Configuring NUMA on Linux
まとめ
- Hadoop Datanode での CPU system 高騰原因を perf を使って調査した
- 原因: zone_reclaim_mode = 1 によるメモリ回収処理多発
- ハードウェア構成によってい zone_reclaim_mode のデフォルト設定は変わる
- Hadoop 含め、NUMA 環境でメモリを大量に使うソフトウェアで CPU system が高騰していた場合、 zone_reclaim_mode 設定を確認する