究極のt2.nanoインスタンス設定
EC2の中で最小インスタンスタイプである「t2.nano」はCPUが1コアでメモリは512MBです。「ARIGATO.ES」はこの最小インスタンスで稼働しています。この最小インスタンスの上にAmazon Linux + MySQL + Apache + PHP + WordPress + Mediawiki + Zabbixを稼働させています。当初はElasticsearchも入れていましたがメモリ環境が厳しくなってきたのでSquidとともにNTT PCコミュニケーションズのWebARENAへ移行しました。WebARENAも最小インスタンスで1コア/512MBです。
SquidとElasticsearchを移行したのですがここのところ立て続けにメモリ不足でOSが「Enable Wait」のような状態に陥るようになりました。「t2.micro」にインスタンスタイプを変更すると一応再現性が無くなるので根本原因はやはりメモリが少ないことのようです。だからと言って簡単に「t2.micro」にアップグレードすることも出来ません。そこでグーグル先生その他の協力を(勝手に)得て最小インスタンスである「t2.nano」での安定稼働を目指した設定変更を行いました。
Zabbixで監視しているとまずCPUロードが高くなり通常は1以下であるはずの数値がみるみる大きくなりあっという間に2桁になります。そうなると処理は遅延し待ちが待ちを呼ぶような状態となります。プロセス数もどんどん増えていき少ない実メモリはすぐに無くなってスワップファイルへ書き出しが始まります。この頃になるとoom-killerと呼ばれるOut Of Memoryからの回復処置が取られます。ここでターゲットにされるプロセスがmysqldやhttpdです。Kernelを生き残らせるための苦肉の策です。ですが既にFile I/Oの遅延が始まっておりKillしたプロセスすら終了しない状態となります。運が良ければ長い時間をかけて処理が進み復活することもありますがほとんどの場合はコマンドも受け付けない状態となりAWS Consoleからの停止・開始を必要とします。
そもそもどうして少ないリソースではやりくり出来ないほどのプロセスが上がってしまうのか。それはApacheのデフォルト設定にあります。以下がpreforkの既定値です。普通のインスタンスタイプなら特に問題はありませんが「t2.nano」には致命的です。この設定では物理メモリをすぐに食い尽くしてしまいスワップへ逃げるためのI/O地獄へまっしぐらです。
StartServers 8 # 最初に起動する子プロセスの数 MinSpareServers 5 # 待機する子プロセスの最小数 MaxSpareServers 20 # 待機する子プロセスの最大数 ServerLimit 256 # MaxClientsに設定可能な上限値 MaxClients 256 # 生成する子プロセスの最大数 MaxRequestsPerChild 4000 # それぞれの子プロセスが扱うリクエスト数の制限数
「t2.nano」ではこのぐらい(↓)極端に小さくします。そうしないとスワップが頻繁に発生しすぐにoom-killerが発動してしまいます。この設定の肝はプロセスの数よりもリクエスト数の制限を極端に小さくすることです。これは一度起動したプロセスが何回のhttpリクエストを処理したらプロセスが再起動するかをコントロールします。これが大きいままだと何度もリクエストを処理するためにメモリ割当が発生しあたかもメモリリークしたかのような状況に陥ります。そこで30という非常に小さい値にすることで一度のリクエストが終了したらプロセスが再起動するぐらいの設定に持っていきます。プロセスを再起動するということはメモリを一旦解放するということなので繰り返しリクエストを処理する際にメモリを食い続けるということが防げるようです。
StartServers 4 MinSpareServers 2 MaxSpareServers 10 ServerLimit 128 MaxClients 128 MaxRequestsPerChild 30
google-botやmsn-botが来た時にはかなり苦しい状況となりますが以前のように止まることは無くなりました。これでしばらく様子を見て上手くいったらこのまま安定運用化したいと思います。ちなみに512MBの実メモリに対してスワップファイルは2GBの大きさです。アンバランスは承知の上ですが「t2.nano」で運用するにはこのぐらいの極端な設定が必要です。次はMySQLのメモリ設定です。
innodb_buffer_pool_size=256M innodb_log_file_size=32M innodb_log_buffer_size=8M max_connections = 50
php.iniは「memory_limit = 64M」と設定しています。ただしこのままではWordPressの管理画面が実行出来ません。そこで「wp-admin」フォルダ下だけ「.htaccess」を使って「memory_limit = 96M」としています。