Gunicorn: DockerコンテナのGunicorn + Flask + Tensorflowが機能しない

作成日 2019年10月03日  ·  23コメント  ·  ソース: benoitc/gunicorn

こんにちは

小さなFlaskAPIが前にあるTensorFlow2.0プロジェクトがあるので、APIですでに行われているデータ前処理を使用してHTTP呼び出しを介してモデルにリクエストを送信できます。 DockerコンテナでFlask / TensorFlowアプリケーションを実行するためにGunicornを選択しました。 悲しいことに、Gunicornが作成するワーカープロセスは、Gunicornによって殺されるまでコンテナにハングアップします。 サーバーが起動せず、サーバーにリクエストを送信できません。 さらに、同じGunicornのセットアップは、私のホストマシンのDockerの外でも問題なく機能します。

Dockerログ(しばらくするとハングし、タイムアウトエラーが出力されます)

[2019-10-03 18:03:05 +0000] [1] [INFO] Starting gunicorn 19.9.0
[2019-10-03 18:03:05 +0000] [1] [INFO] Listening at: http://127.0.0.1:8000 (1)
[2019-10-03 18:03:05 +0000] [1] [INFO] Using worker: sync
[2019-10-03 18:03:05 +0000] [8] [INFO] Booting worker with pid: 8
2019-10-03 18:03:08.126584: I tensorflow/core/platform/cpu_feature_guard.cc:142] Your CPU supports instructions that this TensorFlow binary was not compiled to use: AVX2 FMA
2019-10-03 18:03:08.130017: I tensorflow/core/platform/profile_utils/cpu_utils.cc:94] CPU Frequency: 3392000000 Hz
2019-10-03 18:03:08.130306: I tensorflow/compiler/xla/service/service.cc:168] XLA service 0x55fbb23fb2d0 executing computations on platform Host. Devices:
2019-10-03 18:03:08.130365: I tensorflow/compiler/xla/service/service.cc:175]   StreamExecutor device (0): Host, Default Version

dockerfile:

FROM python

RUN pip install gunicorn

WORKDIR /usr/src/app

COPY requirements.txt ./
RUN pip install --no-cache-dir -r requirements.txt

COPY . .

EXPOSE 8000

CMD [ "gunicorn", "--chdir", "src", "api:app" ]

api.py:

from flask import Flask, request
import inference

app = Flask(__name__)

@app.route('/', methods=['GET', 'POST'])
def predict():
    if request.method == 'GET':
        return 'POST a json payload of {"imageBase64": "base64base64base64"} to this address to predict.'
    try:
        result = inference.run(request.json['imageBase64'])
        return result
    except Exception as e:
        return {'error': str(e)}, 500

if __name__ == "__main__":
    app.run()
else:
    print('\n * Server ready!')

inference.py

# Import packages
from __future__ import absolute_import, division, print_function, unicode_literals

import os
import tensorflow as tf
from tensorflow import keras
import PIL
import numpy as np
from io import BytesIO
import base64
import json

print("TensorFlow version is ", tf.__version__)

# Set variables
##########################################################################################
##########################################################################################

model_name = 'catsdogs'

base_dir = os.path.join(os.path.dirname(__file__), '..')
model_dir = os.path.join(base_dir, 'models')

##########################################################################################
##########################################################################################

# Load model
model = keras.models.load_model(os.path.join(model_dir, model_name + '.h5'))

# Load metadata
with open(os.path.join(model_dir, model_name + '_metadata.json')) as metadataFile:
    metadata = json.load(metadataFile)

# Split metadata
labels = metadata['training_labels']
image_size = metadata['image_size']

# Exported function for inference
def run(imgBase64):
    # Decode the base64 string
    image = PIL.Image.open(BytesIO(base64.b64decode(imgBase64)))

    # Pepare image
    image = image.resize((image_size, image_size), resample=PIL.Image.BILINEAR)
    image = image.convert("RGB")

    # Run prediction
    tensor = tf.cast(np.array(image), tf.float32) / 255.
    tensor = tf.expand_dims(tensor, 0, name=None)
    result = model.predict(tensor, steps=1)

    # Combine result with labels
    labeledResult = {}
    for i, label in enumerate(labels):
        labeledResult[label] = float(result[0][labels[label]])

    return labeledResult

私は何年もの間これに対する解決策を探してきましたが、何とか思い付くことができませんでした。どんな助けでも大歓迎です。

ありがとう!

Feedback Requested FeaturWorker FeaturIPC PlatforDocker

最も参考になるコメント

同じ問題がありました。 限り私は自分のログから推測できるように、それは次のようになりますtensorflow使用されるgevent 、あなたが使用することはできませんgeventで同時にgunicorn--workersフラグと--threadsフラグは私には何の違いもありませんが、 --worker-class=geventから--worker-class=gthreadすると問題が修正されました。 ありがとう@javabrett

全てのコメント23件

Dockerの設定により、コンテナーで使用可能な最大メモリが制限されていますか?

同じことを経験します。 しかし、Gunicornのせいではないと思います。 コンテナ内のbashシェルからpython3 api.pyを実行すると、同じエラーが発生します。

@tlaanemaaあなた@mackdelanyの言うこと

おい。 そのように消えてすみません。

私のセットアップではDockerのRAMが少し制限されていますが、制限を解除しても同じことが起こりました。

gunicornを使用せずにAPIファイルを実行して、レポートを返してみます。

ありがとう!

@tlaanemaaそれについて何かニュースはありますか?

@benoitc Heya
申し訳ありませんが、私は他のものに夢中になり、これをさらに進める時間がありませんでした。
今日はこれを突いてあなたに返事をしようと思います

そこで、コンテナにgunicornを入れずにアプリを実行してみましたが、うまくいきました。
以下は私のDockerfileのCMDビットです

作品:

CMD [ "python", "src/api.py" ]

ログ:

2019-12-02 11:40:45.649503: I tensorflow/core/platform/cpu_feature_guard.cc:142] Your CPU supports instructions that this TensorFlow binary was not compiled to use: AVX2 FMA
2019-12-02 11:40:45.653496: I tensorflow/core/platform/profile_utils/cpu_utils.cc:94] CPU Frequency: 2208000000 Hz
2019-12-02 11:40:45.653999: I tensorflow/compiler/xla/service/service.cc:168] XLA service 0x55f969cf6a40 executing computations on platform Host. Devices:
2019-12-02 11:40:45.654045: I tensorflow/compiler/xla/service/service.cc:175]   StreamExecutor device (0): Host, Default Version
TensorFlow version is  2.0.0
 * Serving Flask app "api" (lazy loading)
 * Environment: production
   WARNING: This is a development server. Do not use it in a production deployment.
   Use a production WSGI server instead.
 * Debug mode: off
 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)

動作しません:

CMD [ "gunicorn", "--chdir", "src", "api:app" ]

ログ:

[2019-12-02 11:39:22 +0000] [1] [INFO] Starting gunicorn 20.0.4
[2019-12-02 11:39:22 +0000] [1] [INFO] Listening at: http://127.0.0.1:8000 (1)
[2019-12-02 11:39:22 +0000] [1] [INFO] Using worker: sync
[2019-12-02 11:39:22 +0000] [9] [INFO] Booting worker with pid: 9
2019-12-02 11:39:24.041188: I tensorflow/core/platform/cpu_feature_guard.cc:142] Your CPU supports instructions that this TensorFlow binary was not compiled to use: AVX2 FMA
2019-12-02 11:39:24.046495: I tensorflow/core/platform/profile_utils/cpu_utils.cc:94] CPU Frequency: 2208000000 Hz
2019-12-02 11:39:24.047129: I tensorflow/compiler/xla/service/service.cc:168] XLA service 0x5623e18b5200 executing computations on platform Host. Devices:
2019-12-02 11:39:24.047183: I tensorflow/compiler/xla/service/service.cc:175]   StreamExecutor device (0): Host, Default Version

また、リポジトリを開いて、必要に応じて調べられるようにしました。
役に立つかもしれない

https://gitlab.com/tlaanemaa/image-classifier

Listening at: http://127.0.0.1:8000 (1)

問題は、gunicornがコンテナ内のローカルホストをリッスンしているため、外部からアクセスできないことでしょうか?

フラスコアプリが同じことをしていて、それが機能していたので、私はそうは思いません。
また、gunicornバージョンはテンソルフローバージョンをログに記録しません。これは、コードのログ行の前に問題が発生したことを示唆しています。 gunicornを使用せずに実行する場合は、フラスコを使用するだけで、ログに記録されます。
TensorFlow version is 2.0.0

デバッグレベルでは何と言っていますか?

@tlaanemaa Dockerデーモンネットワークはどのように構成されていますか? @CaselITからのコメントに

引数-b 0.0.0.0:8000 Gunicornを起動してみてください。

少なくともログからは、テンソルフローのインポート後に来るログ行にサーバーがヒットしないため、サーバーがまったく起動していないように見えるため、ネットワークに問題があるとは思いません。

それにもかかわらず、私はあなたの提案を試みましたが、それは私にエラーを与えます

CMD [ "gunicorn", "-b", "0.0.0.0:8000", "--chdir", "src", "api:app" ]

_ログ_

usage: gunicorn [OPTIONS] [APP_MODULE]
gunicorn: error: unrecognized arguments: -d

自分で試してみたい場合は、comtainerイメージがregistry.gitlab.com/tlaanemaa/image-classifierで入手でき

@tlaanemaa更新されたDockerfile 、イメージビルドコマンド、およびコンテナ実行コマンドを再投稿できますか?

@javabrettもちろん

  • Dockerfile: https ://gitlab.com/tlaanemaa/image-classifier/blob/master/Dockerfile
  • ビルドコマンド: docker build -t tlaanemaa/image-classifier .
  • コンテナはPortainerを介して実行されますが、悲しいことに、どのコマンドを使用してそれを実行するのかわかりません。 クレイジーなことは何も行われていません。標準的なもので、ポート8000​​が転送されます。

_投稿時のDockerfile:_

FROM python:3.7

RUN pip install gunicorn

WORKDIR /usr/src/app

COPY requirements.txt ./
RUN pip install --no-cache-dir -r requirements.txt

COPY . .

EXPOSE 8000

CMD [ "gunicorn", "-b", "0.0.0.0:8000", "--chdir", "src", "api:app" ]

Dockerの完全なログは何ですか?最終的に使用しているコマンドラインを貼り付けることができますか?

この問題のデバッグ中に見過ごせないことをしているのでない限り、今のところ、Portainerなしで実行できますか?

これは私にとってはうまくいきます、Mac2.1.0.5用のDockerデスクトップ:

docker build -t tlaanemaa/image-classifier .
docker run -it --rm -p 8000:8000 tlaanemaa/image-classifier

POSTリクエストを受け入れます。

実行して、完全な出力と結果を投稿してください。

私はそれを試しました、そしてそれは今働きます。
それを修正したのは-bフラグ

どうもありがとう!

今興味深いのは、POSTリクエストを実行すると、thposeは高速ですが、GETリクエストは非常に遅いということです。 しばらくしてGETリクエストを実行すると、これらは高速になりますが、POSTが非常に遅くなり、ワーカーがタイムアウトします。 そのPOSTに応答すると、POSTは再び高速になり、GETは低速になります。 速くできるようで、切り替わるまでに時間がかかります:D

これらは、ワーカーがタイムアウトしたためにGETが高速でPOSTが遅い場合のログです。

[2020-01-10 09:34:46 +0000] [1] [CRITICAL] WORKER TIMEOUT (pid:72)
[2020-01-10 09:34:46 +0000] [72] [INFO] Worker exiting (pid: 72)
[2020-01-10 09:34:47 +0000] [131] [INFO] Booting worker with pid: 131
TensorFlow version is  2.0.0
2020-01-10 09:34:48.946351: I tensorflow/core/platform/cpu_feature_guard.cc:142] Your CPU supports instructions that this TensorFlow binary was not compiled to use: AVX2 FMA
2020-01-10 09:34:48.951124: I tensorflow/core/platform/profile_utils/cpu_utils.cc:94] CPU Frequency: 2208000000 Hz
2020-01-10 09:34:48.951612: I tensorflow/compiler/xla/service/service.cc:168] XLA service 0x56481dbabd80 executing computations on platform Host. Devices:
2020-01-10 09:34:48.951665: I tensorflow/compiler/xla/service/service.cc:175]   StreamExecutor device (0): Host, Default Version

 * Server ready!

また、状況によっては、 * Server ready!ログがDockerログに記録されないように見えます。 それも誤解を招く可能性があります。 何が原因なのかわからない

Docker内の現在のサーバーは、シングル/同期スレッドで構成されます。これは、ビジー/ブロッキングを簡単に行うことができるため、これが表示されている可能性があります。 --workers=2 --threads=4 --worker-class=gthreadような引数を追加してみてください。

ありがとう@javabrett
それはそれを修正しました!

同じ問題がありました。 限り私は自分のログから推測できるように、それは次のようになりますtensorflow使用されるgevent 、あなたが使用することはできませんgeventで同時にgunicorn--workersフラグと--threadsフラグは私には何の違いもありませんが、 --worker-class=geventから--worker-class=gthreadすると問題が修正されました。 ありがとう@javabrett

やあ! geventの保守者であり、このプロジェクトの貢献者でもある私は、geventとgunicornがうまく連携していることを明確に述べることができます。 さまざまなライブラリが干渉する可能性がありますが、それはgunicornまたはgeventのどちらのせいでもありません。 そうでない場合は、新しい問題を開いてください。 ありがとう!

このページは役に立ちましたか?
0 / 5 - 0 評価