MapR Hadoopディストリビューションにおいて、メモリがどのように割り当てられているかは一見わかりにくいので、図を使いながら詳細を解説していきましょう。なお、説明はMapR 5.0のYARN構成がベースになっています。YARNアプリケーションのメモリ割り当ての説明以降は、どのHadoopディストリビューションでも同じなので広く参考になると思います。
MapRのサービスのメモリ割り当て
MapRでは各ノードで管理や処理を担うプロセスを「サービス」として定義しています。一般的なHadoopにも存在するYARNのResourceManagerやNodeManagerといったサービスもあれば、HDFSの代わりにファイルシステムの機能を提供するMapR-FSやCLDBのようなサービスや、NFSサーバ機能を提供するNFSサービス、Web UI機能を提供するMapR Control Systemサービスなどが存在します。
その中で、すべてのサービスのライフサイクルを管理する役割を提供するWardenというサービスがあります。いわばサービスをまとめて管理するサービスです。Wardenサービスの重要な役割の一つに、各サービスにどれだけメモリリソースを割り当てるかを管理する機能があります。
Wardenはクラスタ起動時に物理メモリ容量を取得し、設定ファイルをもとに各サービスが利用可能なメモリ容量を計算し、サービスごとに割り当てていきます。設定ファイルの一つ/opt/mapr/conf/warden.confを見ながら、例としてZooKeeperサービスに割り当てられるメモリがどのように計算されるかを見ていきます。
service.command.zk.heapsize.percent=1 service.command.zk.heapsize.max=1500 service.command.zk.heapsize.min=256
上記はZooKeeperのメモリ割り当てを記述している部分ですが、原則としてノードの物理メモリに対するpercentの割合がそのサービスに割り当てられます。ただし、上限としてmax、下限としてminの値が決められていますので、その範囲を外れる場合は上限または下限に値が調整されます。
例として、物理メモリがそれぞれ192GB、64GB、16GBの3つのパターンについて見てみましょう。
物理メモリが64GBのノードの場合は64GB×1%=655MBがZooKeeperに割り当てられます。一方、192GBのノードの場合は192GB×1%=1966MBになりますが、これは上限の1500MBを超えていますので、割り当て値は1500MBになります。同様に、16GBのノードの場合は16GB×1%=164MBで下限を下回るため、割り当て値は256MBになります。
上記の計算がすべてのサービスに対して適用されます。ノードによってインストールされるサービスが異なるため、各ノードでのメモリ割り当ても異なる結果になります。
MapR 特有のコアサービスに関しての設定は/opt/mapr/conf/warden.confファイルに記述されています。この中に、OSに割り当てるメモリの設定があります。この項目は実際にはサービスではありませんが、OSの稼働に必要なメモリ容量は計算上確保しておく必要があるため存在しています。また、MapR-FSサービスの設定には割合と下限の項目はありますが、上限はありません。
以下に、パラメータとデフォルト値の一覧を示します。
/opt/mapr/conf/warden.conf
パラメータ | デフォルト値 | 説明 |
---|---|---|
service.command.cldb.heapsize.percent | 8 | CLDBに割り当てる物理メモリの割合(%) |
service.command.cldb.heapsize.max | 4000 | CLDBが利用可能な最大メモリ容量(MB) |
service.command.cldb.heapsize.min | 256 | CLDBが最低限確保するメモリ容量(MB) |
service.command.mfs.heapsize.percent | 35 | MapR-FSに割り当てる物理メモリの割合(%) |
service.command.mfs.heapsize.maxpercent | 85 | 余剰メモリ容量がある場合にMapR-FSが利用可能な最大の物理メモリの割合(%)。YARN環境では使われない |
service.command.mfs.heapsize.max | 512 | MapR-FSが最低限確保するメモリ容量(MB) |
service.command.webserver.heapsize.percent | 3 | MapR Control Systemに割り当てる物理メモリの割合(%) |
service.command.webserver.heapsize.max | 750 | MapR Control Systemが利用可能な最大メモリ容量(MB) |
service.command.webserver.heapsize.min | 512 | MapR Control Systemが最低限確保するメモリ容量(MB) |
service.command.nfs.heapsize.percent | 3 | NFSに割り当てる物理メモリの割合(%) |
service.command.nfs.heapsize.max | 1000 | NFSが利用可能な最大メモリ容量(MB) |
service.command.nfs.heapsize.min | 64 | NFSが最低限確保するメモリ容量(MB) |
service.command.os.heapsize.percent | 10 | OSに割り当てる物理メモリの割合(%) |
service.command.os.heapsize.max | 4000 | OSが利用可能な最大メモリ容量(MB) |
service.command.os.heapsize.min | 256 | OSが最低限確保するメモリ容量(MB) |
service.command.warden.heapsize.percent | 1 | Wardenに割り当てる物理メモリの割合(%) |
service.command.warden.heapsize.max | 750 | Wardenが利用可能な最大メモリ容量(MB) |
service.command.warden.heapsize.min | 64 | Wardenが最低限確保するメモリ容量(MB) |
service.command.zk.heapsize.percent | 1 | ZooKeeperに割り当てる物理メモリの割合(%) |
service.command.zk.heapsize.max | 1500 | ZooKeeperが利用可能な最大メモリ容量(MB) |
service.command.zk.heapsize.min | 256 | ZooKeeperが最低限確保するメモリ容量(MB) |
YARNコンポーネントのサービスやHadoopエコシステムのサービスに関しては、/opt/mapr/conf/conf.d/ディレクトリ以下のwarden.<サービス名>.confという名前の、サービスごとの個別ファイルに設定があります。MapRコアサービスとは異なり、メモリ割り当てのデフォルト値は記載されていません。ノード上のメモリ割り当てを厳密に管理したい場合には、明示的に下記のパラメータを指定する必要があります。
/opt/mapr/conf/conf.d/warden.<サービス名>.conf
パラメータ | 説明 |
---|---|
service.heapsize.percent | サービスに割り当てる物理メモリの割合(%) |
service.heapsize.max | サービスが利用可能な最大メモリ容量(MB) |
service.heapsize.min | サービスが最低限確保するメモリ容量(MB) |
サービス名と対応するパッケージ名は下表を参照してください。
サービス名 | 説明 | パッケージ名 |
---|---|---|
drill-bits | Drillサービス | mapr-drill |
gateway | MapR Gatewayサービス | mapr-gateway |
hue | Hueサービス | mapr-hue |
httpfs | HttpFSサービス | mapr-httpfs |
hbasethrift | HBase Thriftサービス | mapr-hbasethrift |
hbase-rest | HBase REST Gatewayサービス | mapr-hbase-rest |
historyserver | YARN Job History Serverサービス | mapr-historyserver |
hivemeta | Hive Metastoreサービス | mapr-hivemetastore |
hs2 | HiveServer2サービス | mapr-hiveserver2 |
nodemanager | YARN Node Managerサービス | mapr-nodemanager |
oozie | Oozieサービス | mapr-oozie |
resourcemanager | YARN Resource Managerサービス | mapr-resourcemanager |
spark-master | Spark Masterサービス | mapr-spark-master |
MapRサービス用メモリの積み上げ
このように各サービスで個別に計算した割り当てメモリサイズを積み上げて、サービス用割り当て容量の合計サイズが求められます。
ここで、CLDBおよびZooKeeperサービスが稼働するノードのみに適用する例外があります。もしどちらかのサービスがそのノードに構成されていると、1500MB(ただしサービス用割り当て容量の合計が6GB未満であれば、その25%)がマージンとして確保されます。これは、CLDBまたはZooKeeperはクラスタの稼働のための重要なプロセスであり、万が一にもメモリ不足による停止もしくは応答の中断が発生することを避け、余裕を持たせるためです。
さて、物理メモリ容量から、サービス用割り当て容量とCLDB/ZooKeeper用のマージンを差し引いた残りは、YARNアプリケーションに提供されることになります。一般的なHadoopでは、YARNアプリケーション用のメモリサイズはパラメータyarn.nodemanager.resource.memory-mb (yarn-site.xml)にてあらかじめ定義しておく必要がありますが、MapRの場合は上記の通りWardenが計算した結果が、yarn.nodemanager.resource.memory-mbパラメータとしてYARN NodeManagerに自動的に引き渡されます。
具体的な計算例を見ていきましょう。64GB (=65536MB)の物理メモリを搭載したマシンがある場合、各サービスに割り当てられるメモリは次の通りになります。
サービス名 | percent | min | max | 計算結果 |
---|---|---|---|---|
cldb | 8 | 256 | 4000 | 4000 |
mfs | 35 | 512 | - | 22937 |
webserver | 3 | 512 | 750 | 750 |
nfs | 3 | 64 | 1000 | 1000 |
os | 10 | 256 | 4000 | 4000 |
warden | 1 | 64 | 750 | 655 |
zk | 1 | 256 | 1500 | 655 |
サービス合計 | 33997 | |||
cldb/zkマージン | 1500 | |||
YARNアプリケーション用メモリ | 30039 |
この場合、30039MBがYARNアプリケーションに割り当てられることになります。
MapRサービスで実際に使われるメモリ容量
ところで、Wardenで計算されたサービスのメモリ割り当ては、実際に使用されるメモリ容量に反映されるのでしょうか。実は、Wardenの設定がサービス起動時にプロセスに反映されるサービスと、反映されないサービスの2通りがあります。
例えばMapR-FSサービスは、Warden設定から計算された割り当て容量がMapR-FSプロセスに渡され、実際にOSのコマンドで確認すると設定されたサイズでメモリが確保されていることがわかります。一方、NodeManagerサービスは、Warden設定が計算上利用されますが、JVM起動時の最大ヒープサイズの指定にはyarn-env.shの環境変数YARN_NODEMANAGER_HEAPSIZEが使用されます。
ということで、実際に使われるメモリ容量が指定されているプロパティを表としてまとめました。ちなみにJVMで動作するサービスの場合、指定されたメモリ容量がJVMの最大ヒープサイズとして設定されますが、JVMはメモリが必要になったときに初めて物理メモリがマップされるため、常にすべての容量が確保されるわけではありません。念のため。
サービス名 | プロパティ | デフォルト値 | ファイル名 |
---|---|---|---|
cldb | Warden設定 | ||
mfs | Warden設定 | ||
webserver | Warden設定 | ||
nfs | Warden設定 | ||
os | 環境による | ||
warden | JVMのデフォルト値(物理メモリの1/4) | ||
zk | JVMのデフォルト値(物理メモリの1/4) | ||
historyserver | HADOOP_JOB_HISTORYSERVER_HEAPSIZE | 1000 | mapred-env.sh |
nodemanager | YARN_NODEMANAGER_HEAPSIZE | 1000 | yarn-env.sh |
resourcemanager | YARN_RESOURCEMANAGER_HEAPSIZE | 1000 | yarn-env.sh |
drill | DRILL_MAX_DIRECT_MEMORY | 8G | drill-env.sh |
DRILL_HEAP | 4G |
YARNアプリケーションのメモリ割り当て
YARNアプリケーションに対するメモリ割り当てのしくみも結構込み入っているため、正確に把握している人は意外に少ないような気がします。YARNフレームワークは汎用的なリソース管理のしくみを持っていますが、YARN上で動作する代表的なアプリケーションの1つであるMapReduce (MRv2)を例として、メモリが割り当てられる流れを見ていきます。
関連するパラメータは以下の通りです。
/opt/mapr/hadoop/hadoop-2.7.0/etc/hadoop/yarn-site.xml
パラメータ | デフォルト値 | 説明 |
---|---|---|
yarn.nodemanager.resource.memory-mb | Wardenによる計算 | YARNコンテナに割り当てることのできるメモリ容量(MB) |
yarn.scheduler.minimum-allocation-mb | 1024 | メモリ要求に対しYARNスケジューラが割り当てる最小メモリサイズ(MB)。CapacitySchedulerの場合、割り当てるメモリサイズの単位にもなる |
yarn.scheduler.increment-allocation-mb | 1024 | (FairSchedulerのみ)メモリ要求に対しYARNスケジューラが割り当てるメモリサイズの単位(MB) |
yarn.scheduler.maximum-allocation-mb | 8192 | メモリ要求に対しYARNスケジューラが割り当てる最大メモリサイズ(MB) |
/opt/mapr/hadoop/hadoop-2.7.0/etc/hadoop/mapred-site.xml
パラメータ | デフォルト値 | 説明 |
---|---|---|
yarn.app.mapreduce.am.resource.mb | 1536 | MRv2のApplicationMasterのコンテナのメモリサイズ(MB) |
mapreduce.map.memory.mb | 1024 | MRv2のMap Taskのコンテナのメモリサイズ(MB) |
mapreduce.reduce.memory.mb | 3072 | MRv2のReduce Taskのコンテナのメモリサイズ(MB) |
NodeManagerが起動する際には、まずパラメータyarn.nodemanager.resource.memory-mbが利用できるメモリ容量として読み込まれます。前述の通り、MapRの場合はWardenが自動計算したサイズが使われます。
MRv2アプリケーションの実行時には、アプリケーションの処理を行うYARNコンテナに必要なメモリサイズが、ResourceManagerのYARNスケジューラに対して要求されます。MRv2のApplicationMasterを実行するコンテナのメモリサイズはyarn.app.mapreduce.am.resource.mb、Map Taskを実行するコンテナのメモリサイズはmapreduce.map.memory.mb、Reduce Taskを実行するコンテナのメモリサイズはmapreduce.reduce.memory.mbで指定された値が読み込まれ、要求が行われます。
割り当てるメモリサイズはFairSchedulerの場合yarn.scheduler.increment-allocation-mb、CapacitySchedulerの場合yarn.scheduler.minimum-allocation-mbの倍数である必要があり、要求されたメモリサイズがこれらのパラメータのちょうど倍数になっていない場合には、要求を受けたYARNスケジューラは、最も近い倍数に繰り上げたサイズを割り当てます。また、メモリサイズが下限のyarn.scheduler.minimum-allocation-mbおよび上限のyarn.scheduler.maximum-allocation-mbで指定される範囲に収まっているかを確認し、範囲を外れる場合には上限または下限に値を調整します。これらは「リソースの正規化」と呼ばれます。
見方を変えると、yarn.app.mapreduce.am.resource.mb、mapreduce.map.memory.mb、mapreduce.reduce.memory.mbの値がyarn.scheduler.increment-allocation-mbやyarn.scheduler.minimum-allocation-mbの倍数になっていない場合には必要以上の容量がコンテナに割り当てられることになるため、メモリ利用効率を最大にするためにはこれらの倍数にあわせることが重要になります。
YARNスケジューラは、各ノードのNodeManagerが管理する利用可能なリソースの中から、上記の通り計算されたリソースをコンテナに割り当て、処理が実行されます。
YARNコンテナで実際に使われるメモリ容量
MapReduceアプリケーション用に設定するメモリ関連の項目には、他にも次のようなパラメータが存在します。これらはJVMで利用する最大ヒープサイズを指定していますが、なぜコンテナ用のメモリサイズとJVMの最大ヒープサイズの2種類の設定項目があるのでしょうか。
/opt/mapr/hadoop/hadoop-2.7.0/etc/hadoop/mapred-site.xml
パラメータ | デフォルト値 | 説明 |
---|---|---|
yarn.app.mapreduce.am.command-opts | -Xmx1024m | MRv2のApplicationMasterのJVM最大ヒープサイズ |
mapreduce.map.java.opts | -Xmx900m | MRv2のMap TaskのJVM最大ヒープサイズ |
mapreduce.reduce.java.opts | -Xmx2560m | MRv2のReduce TaskのJVM最大ヒープサイズ |
その理由は、JVMのヒープサイズはJVMの世代別GCで扱われる領域のうち、Eden、Survivor、Old領域のみを対象としており、それ以外に使われるメモリ容量は別に管理する必要があるためです。
それ以外に使用される可能性があるメモリとしては、Permanent領域(クラス/メソッド/static変数)、Cヒープ(JVM自体の内部使用メモリ)、スレッドスタック(メソッドの呼び出し階層情報/変数)、JNI呼び出しのネイティブコード、Forkされた子プロセスのメモリ、などがあります。これらが知らないうちにノード内のメモリを使い尽くしてしまうことを防ぐため、各ノードに存在するcontainer-executorというプロセスがYARNコンテナで利用されるメモリを管理・監視しています。JVM自体もcontainer-executorがForkする子プロセスであるため、container-executorはプロセスツリーをもとに使用メモリ容量を監視して、各コンテナに割り当てられたメモリサイズに収まっているかを確認し、超過していればJVMプロセスおよびその子プロセスを強制的に終了させます。
では、コンテナ用のメモリサイズに対してJVMの最大ヒープサイズをどれくらいに設定しておけばよいか、ということですが、アプリケーションコードにも依存しますが、だいたいコンテナ用のメモリサイズの75〜80%程度にしておけば問題ないケースが多いです。
以上、YARNアプリケーションとしてMapReduceの例で説明をしましたが、TezやSparkなどでは異なるパラメータの設定があります。ただ、YARNスケジューラの部分の考え方は変わりませんので、参考にしていただければと思います。