Unicornのダウンタイムなしの再起動に挑戦(したが失敗)した話
目次
はじめに
最近、Railsアプリケーションを運用する中で、Unicornの再起動をダウンタイムなしで行えないかという話があった。環境変数の更新も含めて安全に再起動したいとのこと。
だが、実際にやってみると落とし穴がありうまくはいかなかった。今回はその試行錯誤の記録を書く。
Unicornとは?
UnicornはRailsのアプロケーションサーバーの一種。メモリを効率的に使いつつ、フォークした子プロセスでアプリを動かす。

やりたかったこと
・envファイルの更新(dotenv導入済)
・アプリの再起動(環境変数が反映された状態で)
・サービスを止めない
試した手順と落とし穴
方法1 kill -USR2 での再起動
kill -USR2 `cat tmp/pids/unicorn.pid`
新プロセスが立ち上がるまで旧プロセスを停止しないようにすることで、ダウンタイムを無くしたい。
→しかし、 PIDが切り替わるが、環境変数が更新されない。dotenvの読み込みも反映されず。
kill -USR2 で何が起きているのか?
- 親プロセス(master)がUSR2シグナルを受け、自分のコピーを新・親プロセスとしてフォークする。
- 新プロセスが設定ファイルやコードを読み直し、新たな子プロセスを作成。
- 古い親プロセスはunicorn.oldbinにPIDを移して残る。子プロセスが順次交代していく。
.envを読み込む処理はプロセス起動時に行われる。kill -URS2では「親プロセスそのもの」は再起動されず、古い親プロセスからフォークされるのみ。環境変数は古い親プロセスから引き継がれてしまう。新しい子プロセスも新・親プロセスの環境変数を継承するだけになるため、環境変数は古いもののままだ。
そもそも親プロセス起動時に環境変数を読み込んでおく必要があるようだ。
方法2 dotenvで起動時にenvを読み込む
というわけでapplication.rb に Dotenv.load を追記してみた。
→ 構造の問題か、試した環境では動かず。他に問題があるかと調べてみる。
さらに調べてみるとDotenvをproductionで使うべきではないのかも、という記事を発見。
Dotenvはproductionで使わないほうがよいのではという話
この記事によるとDotenv.overloadというコードで環境変数書き換えができるが、変数削除時に「削除されたこと」が反映されないようだ。古い環境変数が残ることがあるのは、運用上の問題がある。
この方法もenv情報の書き換えに難があると知る。どうも、Unicorn単体で環境変数を反映しつつダウンタイムなしで再起動することが難しいようだ。
二つの方法を試した結論
やはり安全に環境変数を反映するにはUnicorn自体の完全停止→再起動が必要であると分かった。
終わりに
この経験から学んだのは、Unicornは堅実なプロセス制御を提供してくれる反面、環境変数の扱いについては注意が必要だということだ。
特に、dotenvなどの仕組みと併用する場合、アプリケーションの設計段階で「再起動時の挙動」を明確にしておくことが、運用の安定性を左右する。
今後同じような構成を作ろうとした時には、こういったサービスを止めないという観点からも設計を作っていきたいと思う。例えば、1台ずつ順番に更新していくローリングアップデートや、同じ構成の環境を用意して切り替えるブルー・グリーンデプロイメントなどが考えられる。
今回の案件では環境的な問題もあり一時サービス停止を容認するとしたが、他に方法がないか今後も考えていきたいと思う。
補足
https://jamielinux.com/blog/zero-downtime-unicorn-restart-when-using-rbenv
このような方法があるようだと教えられ、こちらも試してみた。
shimファイルを噛ませて変更点を読み込ませ、Unicornを再起動するようだ。
残念ながら現在使用している環境だと古いPIDを保存したファイルが変更されず、正しい挙動でUSR2コマンドを終えられないためか失敗してしまった。別環境であれば稼働するかもしれないため、お試しください。
コメントを残す