フルボッコのリソース制限値(limit)の修正をした話
はじめに
清水です。先日JMeterクラスタのフルボッコ(fullbok)で負荷試験を行う機会がありました。
- お手軽JMeterクラスター 〜 フルボッコ編|アドカレ2013 : CFn #1 | Developers.IO
- GitHub - classmethod-aws/fullbok: GUI JMeter cluster on EC2
その際にフルボッコのJMeter Slave(Amazon Linux)について、リソース制限(limit)の値が意図したものとなるよう修正を行いました。本エントリではその作業についてまとめてみたいと思います。具体的には、フルボッコはCloudFromationを使ってEC2インスタンスの構築、設定を行います。その過程で起動時にJMeterのプロセスを起動するように/etc/rc.localに処理を追記する箇所があります。この/etc/rc.localのJMeterプロセス起動の直前にulimitコマンドを実行するよう記載することで、意図したリソース制限の値でJMeterのプロセスが起動するようになりました。なお実際の修正内容はこちらのPull requestでフルボッコに取り込んでいただきました。
修正作業自体はフルボッコを対象としていましたが内容をまとめてみることで、Linuxにおけるulimitコマンドまたは設定ファイル変更でのリソース制限値の変更、またCloudFromationテンプレート内でのEC2インスタンス設定時の方法、設定順序などについても知識を整理する機会となりました。
フルボッコとリソース制限
フルボッコ(fullbok)はJMeterクラスタを構築するCloudFormationテンプレートです。JMeter MasterはWindows、JMeter SlaveはLinux(Amazon Linux)で構成されています。JMeter Slaveはスタック作成時に台数を指定可能ですが、1台のインスタンスでなるべく多くの負荷をかけられるよう、Linuxのリソース制限の値を別途設定しています。
具体的にはMax open filesは65536、Max processesは8192という制限値にしています。Max open filesについては、Linuxではネットワークソケットもファイルとして扱われます。JMeterで同時実行スレッド数を大きくすると、結果としてネットワークソケットも多く使い、ファイルを多く開いている状態となるため、大きめの値としています。またMax processesではプロセス数の上限を変更しています。こちらもJMeterで同時実行スレッド数をを大きくする場合は、JMeter(Java)のスレッド数 = OS上のプロセス数の上限を大きくしておく必要があります。
変更前のコードでは以下が該当箇所となっていました。
"files" : { "/etc/security/limits.conf" : { "content" : { "Fn::Join" : ["", [ "# /etc/security/limits.conf\n", "#\n", "#Each line describes a limit for a user in the form:\n", "#\n", "#\n", "#<domain> <type> <item> <value>\n", "#\n", "\n", "#* soft core 0\n", "#* hard rss 10000\n", "#@student hard nproc 20\n", "#@faculty soft nproc 20\n", "#@faculty hard nproc 50\n", "#ftp hard nproc 0\n", "#@student - maxlogins 4\n", "\n", "\n", "* hard nofile 65536\n", "* soft nofile 65536\n", "* hard nproc 8192\n", "* soft nproc 8192\n", "\n", "\n", "\n", "# End of file\n" ]]}, "mode" : "000644", "owner" : "root", "group" : "root" } },
"05-nproc_limit" : { "command" : "sed -i -e \"s/ nproc 1024/ nproc 8192/\" /etc/security/limits.d/90-nproc.conf" }
事の発端 90-nproc.confの廃止
今回の事の本端ですが、フルボッコのスタック作成後にJMeter Slaveインスタンスのログを確認したところ、以下のようなエラーが発生していました。
Command 05-nproc_limit (sed -i -e "s/ nproc 1024/ nproc 8192/" /etc/security/limits.d/90-nproc.conf) failed
先に示していますリソース制限値の変更箇所である05-nproc_limitの箇所でのエラーと推測できます。確認したところ2014/09/10を境に90-nproc.confがインストールされなくなったようです。JMeter SlaveであるAmazon Linuxでrpm -q --changelog pamを実行した出力に記載がありました(該当箇所のみ抜粋します)。
* Wed Sep 10 2014 Ben Cressey <bcressey@amazon.com> - do not install 90-nproc.conf
フルボッコのリソース制限値の確認
90-nproc.confの廃止に伴うエラーの対策ですが、nproc(=Max processes)については/etc/security/limits.confでも設定の記載があるので、こちらが有効になっていれば05-nproc_limtの設定自体が不要になるかと考えました。このため/etc/security/limits.confで設定してあるものが実際に反映されているかの確認を行いました。
実際に確認してみると、こちらもAmazon Linuxのバージョンアップに伴い設定方法が変わったためなのか、JMeterに関連するプロセスについては/etc/security/limits.confの設定が反映されておらず、意図したとおりのリソース制限値とはなっていませんでした。
実際のこの段階でのリソース制限値についての確認です。まずはJMeterに関係するプロセスIDを確認します。
[ec2-user@ip-10-0-0-224 ~]$ ps aux | grep jmeter root 7827 0.0 0.0 115200 1468 ? S 02:03 0:00 /bin/sh /opt/apache-jmeter-2.10/bin/jmeter-server root 7831 0.0 0.0 115200 1504 ? S 02:03 0:00 /bin/sh /opt/apache-jmeter-2.10/bin/jmeter -Djava.rmi.server.hostname=ip-10-0-0-224.ap-northeast-1.compute.internal -Dserver_port=1099 -s -j jmeter-server.log root 7833 0.1 2.1 1937460 36420 ? Sl 02:03 0:02 java -server -XX:+HeapDumpOnOutOfMemoryError -Xms1G -Xmx1G -XX:NewSize=256m -XX:MaxNewSize=512m -XX:MaxTenuringThreshold=2 -XX:PermSize=64m -XX:MaxPermSize=128m -XX:+CMSClassUnloadingEnabled -Dsun.net.inetaddr.ttl=0 -jar /opt/apache-jmeter-2.10/bin/ApacheJMeter.jar -Djava.rmi.server.hostname=ip-10-0-0-224.ap-northeast-1.compute.internal -Dserver_port=1099 -s -j jmeter-server.log ec2-user 7980 0.0 0.0 110448 904 pts/0 S+ 02:34 0:00 grep --color=auto jmeter
これからプロセスID 7827, 7831, 7833のそれぞれの/proc/プロセスID/limitsファイルを確認します。意図した制限値になっていなかったことがわかります。
[ec2-user@ip-10-0-0-224 ~]$ cat /proc/7827/limits Limit Soft Limit Hard Limit Units Max cpu time unlimited unlimited seconds Max file size unlimited unlimited bytes Max data size unlimited unlimited bytes Max stack size 8388608 unlimited bytes Max core file size 0 unlimited bytes Max resident set unlimited unlimited bytes Max processes 13067 13067 processes Max open files 1024 4096 files Max locked memory 65536 65536 bytes Max address space unlimited unlimited bytes Max file locks unlimited unlimited locks Max pending signals 13067 13067 signals Max msgqueue size 819200 819200 bytes Max nice priority 0 0 Max realtime priority 0 0 Max realtime timeout unlimited unlimited us [ec2-user@ip-10-0-0-224 ~]$ cat /proc/7831/limits Limit Soft Limit Hard Limit Units Max cpu time unlimited unlimited seconds Max file size unlimited unlimited bytes Max data size unlimited unlimited bytes Max stack size 8388608 unlimited bytes Max core file size 0 unlimited bytes Max resident set unlimited unlimited bytes Max processes 13067 13067 processes Max open files 1024 4096 files Max locked memory 65536 65536 bytes Max address space unlimited unlimited bytes Max file locks unlimited unlimited locks Max pending signals 13067 13067 signals Max msgqueue size 819200 819200 bytes Max nice priority 0 0 Max realtime priority 0 0 Max realtime timeout unlimited unlimited us [ec2-user@ip-10-0-0-224 ~]$ cat /proc/7833/limits Limit Soft Limit Hard Limit Units Max cpu time unlimited unlimited seconds Max file size unlimited unlimited bytes Max data size unlimited unlimited bytes Max stack size 8388608 unlimited bytes Max core file size 0 unlimited bytes Max resident set unlimited unlimited bytes Max processes 13067 13067 processes Max open files 4096 4096 files Max locked memory 65536 65536 bytes Max address space unlimited unlimited bytes Max file locks unlimited unlimited locks Max pending signals 13067 13067 signals Max msgqueue size 819200 819200 bytes Max nice priority 0 0 Max realtime priority 0 0 Max realtime timeout unlimited unlimited us [ec2-user@ip-10-0-0-224 ~]$
リソース制限値の修正
リソース制限値の修正ですが、結果としては以下の修正を行ないました。
@@ -309,6 +312,7 @@ "sysctl -p\n", + "echo -e 'ulimit -n 65536\nulimit -u 8192\n' >>/etc/rc.local\n", "echo \"/opt/apache-jmeter-2.10/bin/jmeter-server | logger -p daemon.info &\" >>/etc/rc.local\n", "# All is well so signal success\n",
実際のPull requestはこちら。
/etc/rc.localにjmeter-serverの起動設定を書き込む直前に、Max open filesを65536、Max processesを8192にするulimitコマンドを記載しています。rc.localにてjmeter-serverが起動する際、直前でulimitコマンドが実行されリソース制限値が意図したものとなり、その後のjmeter-server起動時にもこの設定値は引き継がれます。結果としてJMeter関連のプロセスでリソース制限値が意図したものとなります。
実際の確認結果は以下となります。
[ec2-user@ip-10-0-0-187 ~]$ ps aux | grep jmeter root 7807 0.0 0.0 115204 1468 ? S 06:03 0:00 /bin/sh /opt/apache-jmeter-3.1/bin/jmeter-server root 7813 0.0 0.0 115204 1532 ? S 06:03 0:00 /bin/sh /opt/apache-jmeter-3.1/bin/jmeter -Djava.rmi.server.hostname=ip-10-0-0-187.ap-northeast-1.compute.internal -Dserver_port=1099 -s -j jmeter-server.log root 7866 0.0 2.6 1941980 46660 ? Sl 06:03 0:10 java -server -XX:+HeapDumpOnOutOfMemoryError -Xms1G -Xmx1G -XX:MaxTenuringThreshold=2 -XX:PermSize=64m -XX:MaxPermSize=128m -XX:+CMSClassUnloadingEnabled -Dsun.net.inetaddr.ttl=0 -jar /opt/apache-jmeter-3.1/bin/ApacheJMeter.jar -Djava.rmi.server.hostname=ip-10-0-0-187.ap-northeast-1.compute.internal -Dserver_port=1099 -s -j jmeter-server.log ec2-user 31326 0.0 0.0 110452 900 pts/0 S+ 10:00 0:00 grep --color=auto jmeter [ec2-user@ip-10-0-0-187 ~]$ cat /proc/7807/limits Limit Soft Limit Hard Limit Units Max cpu time unlimited unlimited seconds Max file size unlimited unlimited bytes Max data size unlimited unlimited bytes Max stack size 8388608 unlimited bytes Max core file size 0 unlimited bytes Max resident set unlimited unlimited bytes Max processes 8192 8192 processes Max open files 65536 65536 files Max locked memory 65536 65536 bytes Max address space unlimited unlimited bytes Max file locks unlimited unlimited locks Max pending signals 13385 13385 signals Max msgqueue size 819200 819200 bytes Max nice priority 0 0 Max realtime priority 0 0 Max realtime timeout unlimited unlimited us [ec2-user@ip-10-0-0-187 ~]$ cat /proc/7813/limits Limit Soft Limit Hard Limit Units Max cpu time unlimited unlimited seconds Max file size unlimited unlimited bytes Max data size unlimited unlimited bytes Max stack size 8388608 unlimited bytes Max core file size 0 unlimited bytes Max resident set unlimited unlimited bytes Max processes 8192 8192 processes Max open files 65536 65536 files Max locked memory 65536 65536 bytes Max address space unlimited unlimited bytes Max file locks unlimited unlimited locks Max pending signals 13385 13385 signals Max msgqueue size 819200 819200 bytes Max nice priority 0 0 Max realtime priority 0 0 Max realtime timeout unlimited unlimited us [ec2-user@ip-10-0-0-187 ~]$ cat /proc/7866/limits Limit Soft Limit Hard Limit Units Max cpu time unlimited unlimited seconds Max file size unlimited unlimited bytes Max data size unlimited unlimited bytes Max stack size 8388608 unlimited bytes Max core file size 0 unlimited bytes Max resident set unlimited unlimited bytes Max processes 8192 8192 processes Max open files 65536 65536 files Max locked memory 65536 65536 bytes Max address space unlimited unlimited bytes Max file locks unlimited unlimited locks Max pending signals 13385 13385 signals Max msgqueue size 819200 819200 bytes Max nice priority 0 0 Max realtime priority 0 0 Max realtime timeout unlimited unlimited us
リソース制限値を修正する際にハマったことなど
以上で修正は無事完了したのですが、ここからは修正時に試行錯誤した記録をまとめておこうと思います。
/etc/security/limits.confの設定が効いていなかった件
修正前のテンプレートのコードにも/etc/security/limits.confへの設定の記載はあったのですが、ここの/etc/security/limits.confでの設定については、PAM認証を通る条件下でしか有効にならない、ということがわかりました。
以下の2つのサイトの解説が詳しく、参考にさせていただきました。
JMeter関連のプロセスは先に記載したとおり/etc/rc.localから起動されます。この場合はPAM認証を通らず/etc/security/limits.confに記載した設定は有効にならない、ということになります。
/etc/sysconfig/initでの変更は再起動時のみに有効となった
PAM認証を通らないプロセスのリソース制限値の変更方法として、先ほど紹介しました2つのサイトに以下の方法がありました。
- /etc/initscriptファイルにulimitコマンドを記載
- CentOS/RHELなどだと5系のUpstartが使われる以前の環境までで有効
- /etc/sysconfig/initファイルにulimitコマンドを記載
- CentOS/RHELなどで6系のUpstartが使われている環境で有効
どちらもファイルに記載されたコマンドがOS起動時に実行されるため、そこにulimitコマンドを記載することでリソース制限値を変更することができます。今回のフルボッコの環境(Amazon Linuxをyum updateで最新版にしたもの)でも後者の/etc/sysconfig/initファイルに実行コマンドを記載すると起動時に実行されることが確認できました。
しかしこちらの方法ですが、フルボッコのスタックを作成後にJMeter Slaveのインスタンスを一度再起動すれば有効になるものの、スタック作成直後の環境では有効になりませんでした。おそらくスタック作成時にCloudFormationテンプレートで処理される/etc/sysconfig/initファイルの書き込み、ならびにJMeter関連プロセスの起動などの内容が、/etc/sysconfig/initファイルに記載された内容の実行よりも後になってしまうためではないかと推測しています。
CloudFormationテンプレート内EC2インスタンスの設定項目の実行順序
/etc/sysconfig/initファイルでの設定が"スタック作成直後は無効だが再起動すると有効"となったことを受け、CloudFormationテンプレートに記載したJMeterプロセスの起動、設定ファイルに記載したことの反映処理(コマンド実行等)などの順序に着目することとなりました。(それまではCloudFormationテンプレートを見て、なんとなくUserDataとMetadataの箇所で設定している、という認識しかありませんでした…)
ポイントをまとめると、まずはUserDataに記載された処理が実行されます。そしてUserData内に記載されたcfn-initコマンドが実行されると、MetadataのAWS::CloudFormation::Initの内容が処理される、となります。詳細は下記のドキュメントに記載がありました。
まとめ
フルボッコのリソース制限値を修正した作業についてまとめてみました。作業当初はリソース制限自体の理解が怪しかった点もありますが、設定や確認方法、またCloudFormationなど設定が自動化された環境における設定時の注意点などが身に付いたかと思います。リソース制限値の設定方法としては複数あるので、対象となるプロセスに適した方法で設定しないといけない点、また対象となるプロセスがどのタイミングで起動するか(そしてそれ以前にリソース制限値の変更ができているか)を抑える点が重要だと感じました。