フルボッコのリソース制限値(limit)の修正をした話

Linux

はじめに

清水です。先日JMeterクラスタのフルボッコ(fullbok)で負荷試験を行う機会がありました。

その際にフルボッコの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など設定が自動化された環境における設定時の注意点などが身に付いたかと思います。リソース制限値の設定方法としては複数あるので、対象となるプロセスに適した方法で設定しないといけない点、また対象となるプロセスがどのタイミングで起動するか(そしてそれ以前にリソース制限値の変更ができているか)を抑える点が重要だと感じました。