ElasticBeanstalkで大きめのデータをPOSTするとInternalServerError(500)が発生する
AWSのElastic BeanstalkでRubyのアプリケーションを動かしていたところ、ある程度大きなデータ(リクエストのContent-Lengthが大きい)をPOSTするとInternal Server Error (HTTPエラーコード 500)になる不具合が発生した。
アプリケーションログにはエラーはおろかアクセスすら記録されていないし何事かと思ってたらPassengerの方のログにエラーが記録されていた。
# /var/app/support/logs/passenger.log 2013/xx/xx xx:xx:xx [crit] 1745#0: *170726 open() "/tmp/passenger-standalone.1667/client_body_temp/0000000003" failed (2: No such file or directory), client: xxx.xxx.xxx.xxx, server: _, request: "POST /xxx/xxx HTTP/1.1", host: "www.example.com", referrer: "http://www.example.com/xxx"
Passengerのことはまだよく知らないのだけれどどうやら大きなデータをPOSTされたときに作られる一時ファイルの読み込みに失敗しているようだ。
サーバにログインして /tmp の中を見たけれど passenger.x.x.xxxxx というディレクトリはあったが確かに passenger-standalone.xxx というディレクトリは無い。
エラーメッセージで検索かけて見つかったのが次のページ。
Issue Uploading Files from Rails app hosted on Elastic Beanstalk - Stack Overflow
なるほど。cronで /tmp の中を掃除するときにPassengerに必要なファイルまで消してしまっているということか。
BeanstalkのWebコンソールからアプリケーションをRestartさせたら /tmp に passenger-standalone.xxx ディレクトリができていた。
これがtmpwatchで消されているようだ(親ディレクトリごと削除されていることを考えると上記のPassengerのエラーは読み込みではなく書き込み時のエラーかも)。
解決方法
本来はAmazon側で解決すべき問題だけどそれを待ってもいられない。
上記のページにあるように /etc/cron.daily/tmpwatch で /tmp 内のPassenger関連ディレクトリを除外するようにした。
/etc/cron.daily/tmpwatch #! /bin/sh flags=-umc /usr/sbin/tmpwatch "$flags" -x /tmp/.X11-unix -x /tmp/.XIM-unix \ -x /tmp/.font-unix -x /tmp/.ICE-unix -x /tmp/.Test-unix \ -X '/tmp/hsperfdata_*' -X '/tmp/passenger*' 10d /tmp /usr/sbin/tmpwatch "$flags" 30d /var/tmp for d in /var/{cache/man,catman}/{cat?,X11R6/cat?,local/cat?}; do if [ -d "$d" ]; then /usr/sbin/tmpwatch "$flags" -f 30d "$d" fi done
ただ、今のインスタンスを直接いじってもインスタンスが変わったときに戻ってしまう。
なので「Custom AMIが必要か…」と思っていたら .ebextensions 以下の設定ファイルで対応可能だった。
EC2 インスタンス上のソフトウェアのカスタマイズ - AWS Elastic Beanstalk
アプリケーションのソースコードに以下のファイルを追加。
# .ebextensions/overwrite_cron_tmpwatch.config files: "/etc/cron.daily/tmpwatch": mode: "000755" owner: root group: root content: |+ #! /bin/sh flags=-umc /usr/sbin/tmpwatch "$flags" -x /tmp/.X11-unix -x /tmp/.XIM-unix \ -x /tmp/.font-unix -x /tmp/.ICE-unix -x /tmp/.Test-unix \ -X '/tmp/hsperfdata_*' -X '/tmp/passenger*' 10d /tmp /usr/sbin/tmpwatch "$flags" 30d /var/tmp for d in /var/{cache/man,catman}/{cat?,X11R6/cat?,local/cat?}; do if [ -d "$d" ]; then /usr/sbin/tmpwatch "$flags" -f 30d "$d" fi done commands: delete_backup_file: command: "rm /etc/cron.daily/tmpwatch.bak" ignoreErrors: true
rmしているのはfilesでファイルを作成するときに元のファイルがバックアップされるため。
これでなんとかなったけど、大きなデータかつデプロイ直後には発生しない不具合とかわかりにくいので勘弁願いたい。