貧弱な環境でそれっぽいことをしようとしたら unicorn がロックしてたのでメモ
個人レベルで運用している某Rais製サービスにて,大量アクセス(といってもちょっと強めのF5連打くらい)があったときに nginx が 「504 gateway timeout 」やら 「 502 bad gateway 」を吐いてサービス停止することがありました.
nginx や unicorn に精通しておらず,何が原因がわからないまま,サービス停止時は再起動を行うという対処でごまかしていましたが,このたび原因を究明できた(とおもう)のでメモしておきます.
システムアーキテクチャ
タイトルにある "それっぽい" というのは,フロントエンド層とAPI層とDB層をわけた構成です.(これはいわゆる3層アーキテクチャといっていんですかね?) 趣味開発なので,そこまでする必要はなかったのですが,将来マルチデバイス対応を考えるとロジックをAPI層でやらせたかったし,加えて,どこからか好奇心がわいてきてそのようなアーキテクチャを採用しました.
通常,これらの層ごとにサーバを用意すると思うのですが,趣味開発ということもあり経済的に厳しかったので,1つのサーバに全部載せで運用していました.( = 貧弱な環境).
このような感じです.
nginx + unicorn に関する自分の見解
Rails のデプロイ環境には,nginxとunicornを採用しました.ここ周辺の知識に自信がなかったので,まずは"王道パターン"ということで採用しました.
私の認識ですと,unicorn は,1プロセスは1リクエストしかさばけず,unicorn のプロセスより多い数のリクエストがあるとキューとして溜まっていくと考えています.(誤っていたらご指摘ください)
貧弱環境での挙動
これを,さきほどの貧弱環境で動作させますと,同じサーバ内ですがフロント層からAPI層へのアクセスをわざわざHTTPを使うことになります. つまり1アクセスあると2つのunicornプロセスを使うということですね.
デッドロック!
通常であれば,この構成でも非効率ではありますが徐々にアクセスを処理していく...と思っていたのですが,1. のアクセスが同時にすべてのunicornをつかんだときにデッドロックが起きるんですね. はい,これが原因不明のサービス停止の原因だったと思います.
暫定対応
というわけで,暫定対応としまして unicorn を 2つのグループにわけて起動しまして,片方をフロント層専用,もう片方を API層専用としました.これで,同時につかんだとしても処理が流れていくと思います.
※図の「API専用」と「Front専用」は逆でした...すみません
今後の課題
いまはフロント側から2つ以上のAPIを叩くことがないシンプルなサービスですが,この状況が変わればまたここでデッドロックが発生しそうです.フロント層とAPI層を別サーバで運用するのが手っ取り早いかな...