Ore no homepage
前回、最近割と暇とか書いたらなんかエラーが増えてきて対応しなきゃって事案が発生した。Rubyなアプリケーションの運用は今の会社で初めてなんだが、こういうトラブルに直面すると知見が増える。
いうてもやることは結構あるっちゃあるんだけどね。直近だとうるう秒対応とか…。俺んとこはntpd止めることにした。で、うるう秒経過後に起動する、と。ワケあって本番のOSがgentooだし、何が起きるかわからんので。AWS上にいるAmazon Linuxや、GCE上にいるUbuntuはslewモードで乗り切る。しかしまあマルチクラウドも考えものだな…。運用しづらい。個人的には今のGentooなインフラをAWSに移したいんだが、会社としてまだその判断はしないようだ。まあインフラ周り見てるの俺一人しかいねーしな…。
環境
- Ruby 2.0.0
- Rails 4.0.1
- Unicorn 4.6.3
- Octopus 0.8.1
- MySQL 5.6
デプロイ時や、unicornのワーカ自動再起動時(monitでunicornの使用メモリを監視していて閾値超えたらワーカを殺している。unicorn_worker_killerというgemがあるが、それと近いことをmonitにやらせている。)に、下記のようなエラーが出て、結果ユーザにHTTP 500が返ってしまっていた。
ERROR — : NoMethodError: undefined method `query’ for nil:NilClass: SELECT xxxxx
前から「なんかたまにエラー出るな…」くらいに思っていたんだが、おかげさまでユーザ数も増えてきてこのエラーが無視できなくなってきたので調査することにした。
原因と対策
原因はOctopusで、issueに対策が書いてあった。この業界、「ググる」能力は必須だが、「イシュる」能力も同じように必要だな…。己のRuby力の低さもあいまってここまでたどり着くのに結構時間がかかった。最初はActiveRecordを疑ったし。
https://github.com/tchandy/octopus/issues/59#issuecomment-3134239
対策はこのissueに書いてある通りなんだが、unicornでは通常fork前にActiveRecord::Base.connection.disconnect!してコネクションを切って、fork後にActiveRecord::Base.establish_connectionする。しかしOctopusを使っているときは、
before_forkでActiveRecord::Base.connection.disconnect!
としていたのを
ActiveRecord::Base.connection_proxy.instance_variable_get(:@shards).each { |shard, conn| conn.disconnect! }
とする。同じく
after_forkでActiveRecord::Base.establish_connection
としていたのを
ActiveRecord::Base.connection_proxy.instance_variable_get(:@shards).each { |shard, conn| conn.clear_reloadable_connections! }
とする必要がある。
どういうことか
以下は俺なりの理解。
Octopusを使うとスレーブへのコネクションだけでなくマスターへのコネクションもOctopusの管理下に置かれる。Octopus::Proxyを見ると、@shards(= HashWithIndifferentAccess.new)にshards.ymlの情報を突っ込み、最後に@shards[:master] ||= ActiveRecord::Base.connection_pool_without_octopusとしてマスタの情報も突っ込んでいる。すなわち各DBへの接続情報は@shardsに入っている。
ここで、このOctopus::Proxyは、Octopus::Modelに書かれたconnection_proxy経由で取れる。また、Octopus::ModelではActiveRecord::Base.extend(Octopus::Model)しているのでActiveRecord::Base.connection_proxyが呼ぶことができる。よってActiveRecord::Base.connection_proxy.instance_variable_get(:@shards)で@shardsにアクセスできるので、それをeachで回して、@shardsに格納されたDBごとのコネクションをとってきて、before_forkでコネクションを切る -> after_forkでコネクションを貼りなおすといった処理を行う。
このようにしてOctopusを使っているときは、DBごとのコネクションをケアをしてやる必要がある。という理解。
だいぶ減った
毎朝slackにドメインごとにデイリーのエラー数を通知するようにしてるんだが、
対応して1週間くらい経過観察していたんだがとりあえずはこれで良さそうだ。502はunicornのダウンと思うかもしれないが、これはまた別の問題(対応済み)。
おわり
Information
welcome!Top Posts
- - 開発支援系のサービスが充実しすぎて転職か廃業を考えた [33,464 views]
- - MySQL ibdata1が肥大化する理由(記事の意訳) [13,162 views]
- - vm.swappiness=0は重要 [11,358 views]
- - about [10,533 views]
- - serverspec インフラ層のテスト項目を考える [10,517 views]
- - MySQL バイナリログを使ったデータリカバリ [10,127 views]
- - Cassandraメモその1(インストール、データモデル、データ操作について) [7,805 views]
- - Mac OSX開発環境構築メモ、便利なツールとか [7,232 views]
- - chefを捨ててシェルスクリプトにした [6,827 views]
- - knifeクライアントの設定とchefの実行 [6,691 views]
Archives
- 2015年6月 (3)
- 2015年5月 (3)
- 2015年4月 (3)
- 2015年2月 (1)
- 2015年1月 (1)
- 2014年12月 (2)
- 2014年11月 (1)
- 2014年10月 (1)
- 2014年9月 (2)
- 2014年8月 (3)
- 2014年6月 (2)
- 2014年5月 (3)
- 2014年4月 (2)
- 2014年3月 (3)
- 2014年1月 (1)
- 2013年12月 (3)
- 2013年11月 (2)
- 2013年10月 (3)
- 2013年8月 (1)
- 2013年7月 (2)
- 2013年6月 (1)
- 2013年5月 (3)
- 2013年4月 (3)
- 2013年3月 (4)
- 2013年2月 (2)
- 2013年1月 (2)
- 2012年12月 (3)
- 2012年11月 (1)
- 2012年10月 (4)
- 2012年9月 (1)
- 2012年7月 (3)
- 2012年6月 (2)
- 2012年5月 (1)
- 2012年4月 (2)
- 2012年3月 (1)
- 2012年2月 (1)
- 2012年1月 (1)
- 2011年11月 (4)