発端
2021/04/18
ふと気がつくとherokuで運用中のrailsアプリが亡くなっていました。
原因探るの面倒だったので再起動しました。
その後も割と頻繁に死亡していたので、herokuのダッシュボードを確認しました。
すると...
🧟Memory Usage 100%🧟
なんかメモリが爆上げしているご様子
そして多数のapp crashのログがちらほら
環境
heroku
web: Free Dynos
worker: Free Dynos
Ruby: 2.7.3
Rails: 5.2.5
対応したこと
herokuがrubyでのメモリ爆上げ状態の対策をまとめてくださったので、そちらを参考に対応しました。
ありがとうheroku、いいプラットフォームです。
調べてみるとSidekiqで非同期処理を動かしていたのが怪しいみたいな感じでした。
起動時の Worker 数の超過
起動時のWorkerを1にしました。
# config/puma.rb
workers Integer(ENV['WEB_CONCURRENCY'] || 2)
# CLI
$ heroku config:set WEB_CONCURRENCY=1
引用すると
2 つの Puma Worker が原因で、standard-1x dyno が提供できる量を超える RAM が使用されます。これが発生した場合でも、スレッドを使用して、スループットを増大させることができます。または、dyno サイズをアップグレードして、実行する Worker の数を増やすことができます。
2つで死が近づくっぽいですね
経過に伴うWorker数の超過
メモリが増加して上限を迎える場合(今回の私の感じで)Puma Worker Killerでプロセスを再起動することでメモリの増加を抑制できます。
gem "puma_worker_killer"
PumaWorkerKiller.enable_rolling_restart
Worker が再起動すると、数秒間リクエストに応答できないので、繰り返しの再起動がトリガーされると、エンドユーザーは、アプリケーション全体のスループットが低下したときに減速を経験することがあります。再起動すると、スループットは通常に戻ります。
このアプリケーションは非同期処理ではリアルタイム性を特に求めてなかったのでプロセスには容赦なく消えていただきました。
Puma Worker Killerはgemで、指定した条件になったらWorkerプロセスを切るしてくれるイイやつです。
Unicornにも似たようなことができるgemがあったと思います。
環境変数MALLOC_ARENA_MAX=2を指定する
https://qiita.com/kazutosato/items/4ab561bf5cc03da75e72
↑こちらの記事に自分と全く同じ状況の方がいたので参考にさせていただきました。
Herokuでは、上記の2の環境変数MALLOC_ARENA_MAX=2はデフォルトになりました。ただし、2019年9月24日より前に作られたアプリケーションでは自分で設定する必要があります。
らしいです。私のアプリケーションこれより前に作ってた気がしています。
起動時のメモリの超過
こちらは調査で終わったやつですが一応。
derailedというベンチマークのgemで起動時のメモリ使用量をチェックしました。
gem 'derailed', group: :development
$ bundle exec derailed bundle:mem
これでボトルネックとなりそうなgemを消すという対策です
が、Hobby Dynosの512MBを食いつぶすようなものは見つからず...
TOP: 67.2734 MiB
jemallocでのビルド
なんか公式のものよりメモリに優しいアロケータらしい
$ heroku buildpacks:add --index 1 https://github.com/gaffneyc/heroku-buildpack-jemalloc.git
$ git push heroku master
$ heroku config:set JEMALLOC_ENABLED=true
結果
↑の右の方のダウン(メモリ消費が0になった)の後に対応しました
増え続けていはいない気がするけど、なんか期待してたのと違う
とりあえず要経過観察です
オチ
unicorn起動してたwwwwwwwwwww
Procfileでガッツリunicorn起動していました...
なんの為に私はpumaの設定をせっせと書き換えていたのだろうか....
unicornのgemもけしました
無事に平和な感じになってそうです