エンジニアの原です。
弊社内のいくつかのPythonを使用したWebサービスでは、アプリケーションサーバーとしてGunicorn を使用しています。
先日サービス全体には大きな影響がなかったものの、そのGunicornのバグを踏み、一部のサービスが不安定になったことがあったのでその事例紹介をします。
環境
- Python 3.4.2
- Gunicorn 19.6.0
- Nginx 1.12.0
現象
特に負荷のかかる時間帯にもかかわらず、任意のWebサーバーのnginxからクライアントに502レスポンスが返されていました。
nginxのログ的に、Gunicornがリクエストハンドリング用に生成しているUNIXドメインソケットが喪失したようです。
nginxのupstreamとして稼働しているGunicornのログには、以下のようなスタックトレースが出力されていました。*1
level:ERROR time:2017-02-14 00:00:00 (JST) process:100001 name:gunicorn.error message:Exception in worker process Traceback (most recent call last): File "/var/www/example/venv/default/lib/python3.4/site-packages/gunicorn/arbiter.py", line 557, in spawn_worker worker.init_process() File "/var/www/example/venv/default/lib/python3.4/site-packages/gunicorn/workers/gthread.py", line 109, in init_process super(ThreadWorker, self).init_process() File "/var/www/example/venv/default/lib/python3.4/site-packages/gunicorn/workers/base.py", line 132, in init_process self.run() File "/var/www/example/venv/default/lib/python3.4/site-packages/gunicorn/workers/gthread.py", line 240, in run s.close() File "/var/www/example/venv/default/lib/python3.4/site-packages/gunicorn/sock.py", line 123, in close os.unlink(self.cfg_addr) FileNotFoundError: [Errno 2] No such file or directory: '/var/run/sockets/gunicorn.sock'
CPU稼働率、メモリ使用率等のシステムリソースには特異点はありませんでした。
原因
同様の問題に関するissueがありました。
issue内のコメントでも言及されていますが、原因は19.6.0で入ったこの変更だと思われます。
該当箇所は以下です。
- def close(self, locked=False): - if self.parent == os.getpid() and not locked: - os.unlink(self.cfg_addr) + def close(self): + os.unlink(self.cfg_addr) + super(UnixSocket, self).close()
本来ならば、UNIXドメインソケットはarbiterプロセスのみによってファイルunlinkされるはずですが、workerプロセスによってunlinkされるようになっていました。
このcloseメソッド内ではプロセスIDからarbiterプロセスであるかの判定が必要なのですが、その判定が当該コミットで削除されています。
どうやらcloseメソッドを呼ぶプロセス自体がarbiterプロセスのみになるため削除したらしいのですが、19.6.0ではまだworkerプロセスでcloseメソッドが呼ばれてしまっているようです。
対策
この現象は、GunicornをUNIXドメインソケットでバインドさせた際に発生する問題なので、
UNIXドメインソケットではなくループバックアドレスに未使用ポートでGunicornをlistenさせることで解決しました。
- bind = 'unix:/var/run/sockets/gunicorn.sock' + bind = '127.0.0.1:8080'
なお、このバグは2017/3/4にリリースされたGunicorn v19.7.0で修正されています。
closeメソッド自体が削除され、別の場所でunlinkされるようになったようです。
バージョンアップしたいところでしたが、内部の実装が大きく変わっていたため、緊急性と安定性を鑑みてバージョンアップは見送りました。
まとめ
UNIXドメインソケットを使用する場合は、v19.6.0以外のバージョンを使用するようにしましょう。
非常にニッチなテーマで恐縮ですが、誰かの助けになれば幸いです。
*1:ログ日時、ファイルパスなどは実際のものとは異なります