FlaskアプリケーションをGunicornで起動時のエラー対応

問題の概要

Gunicorn + Nginx + Flaskでアプリケーションをデプロイした際、プロジェクトは正常に起動するものの、APIアクセス時にエラーが発生しました。

Traceback (most recent call last):
  File "/usr/local/lib/python3.6/dist-packages/gunicorn/workers/sync.py", line 135, in handle
    self.handle_request(listener, req, client, addr)
  File "/usr/local/lib/python3.6/dist-packages/gunicorn/workers/sync.py", line 176, in handle_request
    respiter = self.wsgi(environ, resp.start_response)
TypeError: __call__() takes from 1 to 2 positional arguments but 3 were given

ただし、Flaskの開発用サーバー(runserver)で起動すると問題が発生しませんでした。

原因の特定

Flaskの開発用サーバーで起動した場合、Flaskアプリケーション自体が直接起動されるため、問題なくアクセスが可能です。

一方でGunicornはWSGIアプリケーションを処理するため、Flaskアプリケーションそのものではなく、誤ってflask-scriptのインスタンスが渡されていた可能性があります。

使用していたコード

以下のような構成でFlaskアプリケーションとflask-scriptを使っていました。

from projects import APP, db
from flask_migrate import Migrate, MigrateCommand
from flask_script import Manager

manager = Manager(APP)
migrate = Migrate(app=APP, db=db)
manager.add_command('db', MigrateCommand)

if __name__ == '__main__':
    manager.run()

このコードでは、manager.run()flask-scriptの起動ポイントとなっています。

しかし、GunicornはWSGIインターフェースを通してFlaskアプリケーションとやり取りを行うため、flask-scriptのインスタンスでは対応できません。

解決方法

Gunicornが起動する際には、Flaskアプリケーション自体(APP)を渡す必要があります。

したがって、以下の起動コマンドを使用します。

gunicorn -D -w 3 -t 300 -b 0.0.0.0:5000 manage:APP

ログ設定を追加する場合、以下のように記述します。

gunicorn -D --error-logfile=/logs/gunicorn.log --pid=/logs/gunicorn.pid --access-logfile=/logs/access.log -w 3 -t 300 -b 0.0.0.0:5000 manage:APP

補足:flask-scriptの仕組み

python manage.py runserverを実行すると、flask-scriptにより内部で以下のような処理が行われます。

def run(self, commands=None, default_command=None):
    if commands:
        self._commands.update(commands)

    argv = list(text_type(arg) for arg in sys.argv)
    if default_command is not None and len(argv) == 1:
        argv.append(default_command)

    try:
        result = self.handle(argv[0], argv[1:])
    except SystemExit as e:
        result = e.code

    sys.exit(result or 0)

この処理により、flask-scriptrunserverコマンドを認識し、最終的にFlaskアプリケーションのapp.run()が呼び出されます。

したがって、flask-scriptはあくまでコマンドラインインターフェースとして機能し、Gunicornが利用するWSGIアプリケーションとは異なる点に注意が必要です。

タグ: flask Gunicorn WSGI Python Webアプリケーション

7月24日 02:29 投稿