Olá
Eu tenho um projeto TensorFlow 2.0 que tem uma pequena API Flask na frente para que eu possa fazer solicitações ao modelo por meio de chamadas HTTP com pré-processamento de dados já feito na API. Escolhi o Gunicorn para executar meu aplicativo Flask / TensorFlow em um contêiner do docker. Infelizmente, o processo de trabalho que Gunicorn cria fica suspenso no contêiner até ser morto por Gunicorn. O servidor nunca é inicializado e não posso fazer solicitações a ele. Além disso, a mesma configuração do Gunicorn funciona perfeitamente fora do docker, em minha máquina host.
Registros do Docker (ele simplesmente fica pendurado e imprime um erro de tempo limite após um longo tempo)
[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
Eu procuro por uma solução para isso há anos e não consegui encontrar nada, qualquer ajuda seria muito apreciada.
Obrigado!
A configuração do Docker está limitando a memória máxima disponível para o contêiner?
Experimentando o mesmo. Eu não acho que Gunicorn seja o culpado. Recebo o mesmo erro ao executar python3 api.py
de um shell bash no contêiner.
@tlaanemaa você pode confirmar o que @mackdelany diz?
Ei. Desculpe por desaparecer assim.
Minha configuração está limitando um pouco a RAM do Docker, mas a mesma coisa aconteceu mesmo quando removi a limitação.
Vou tentar executar o arquivo api sem gunicorn e relatar de volta.
Obrigado!
@tlaanemaa alguma notícia sobre isso?
@benoitc Heya
Desculpe, fui levado com outras coisas e não tive tempo para ir mais longe com isso.
Vou tentar cutucar isso hoje e retorno para você
Tentei executar o aplicativo sem gunicorn no contêiner e funcionou.
Abaixo está o bit CMD do meu Dockerfile
Trabalho:
CMD [ "python", "src/api.py" ]
Histórico:
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)
Não funciona:
CMD [ "gunicorn", "--chdir", "src", "api:app" ]
Histórico:
[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
Além disso, abri o repositório para que você possa pesquisar se quiser.
Pode ser útil
Listening at: http://127.0.0.1:8000 (1)
O problema pode ser que o gunicorn está escutando localhost dentro de um contêiner, de forma que não pode ser alcançado de fora?
Acho que não porque o aplicativo Flask estava fazendo o mesmo e estava funcionando.
Além disso, a versão do gunicorn não registra a versão do tensorflow, o que sugere que o problema ocorre antes dessa linha de log no código. Ao executar sem gunicorn, apenas frasco, então ele registra isso.
TensorFlow version is 2.0.0
o que diz no nível de depuração?
@tlaanemaa, como a rede daemon do Docker está configurada? Por comentário de @CaselIT , parece provável que seu cliente não seja capaz de alcançar a porta Gunicorn pela rede Docker.
Você pode tentar iniciar o Gunicorn com o arg -b 0.0.0.0:8000
?
Não acho que o problema esteja na rede porque parece, pelo menos a partir dos logs, que o servidor não está iniciando, pois nunca atinge as linhas de log que vêm após a importação de tensorflow
Mesmo assim tentei sua sugestão, mas me deu um erro
CMD [ "gunicorn", "-b", "0.0.0.0:8000", "--chdir", "src", "api:app" ]
_Registro_
usage: gunicorn [OPTIONS] [APP_MODULE]
gunicorn: error: unrecognized arguments: -d
Se você quiser experimentar, a imagem do comtainer está disponível em registry.gitlab.com/tlaanemaa/image-classifier
@tlaanemaa, você pode repassar seu Dockerfile
, comando de compilação de imagem e comando de execução de contêiner atualizados?
@javabrett Claro
docker build -t tlaanemaa/image-classifier .
_Dockerfile no momento da postagem: _
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" ]
qual é o log completo do docker, você pode colar a linha de comando que ele finalmente está usando?
A menos que ele esteja fazendo algo que não pode ser abandonado durante a depuração desse problema, você pode executá-lo sem o Portainer por enquanto?
Isso funciona para mim, Docker Desktop for Mac 2.1.0.5:
docker build -t tlaanemaa/image-classifier .
docker run -it --rm -p 8000:8000 tlaanemaa/image-classifier
Aceita solicitações de POST
.
Execute e publique a produção e o resultado completos.
Eu tentei e funciona agora.
Poderia ter sido o sinalizador -b
que o corrigiu?
Muito obrigado!
O que é interessante agora é que, quando faço solicitações POST, as solicitações são rápidas, mas as solicitações GET são muito lentas. Depois de fazer solicitações GET, elas ficam mais rápidas, mas o POST fica muito lento e o trabalho expira. Depois de responder a esse POST, os POSTs são rápidos novamente e os GETs são lentos. Parece que ele pode fazer um rápido e leva tempo para mudar: D
estes são os registros quando GET é rápido e POST é lento porque o trabalhador atinge o tempo limite:
[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!
Além disso, em algumas situações, o log * Server ready!
não parece passar pelos logs do docker. Isso também poderia ser enganoso. Não tenho certeza do que está causando isso
O servidor atual em seu Docker será configurado single / sync threaded, o que será trivial para tornar ocupado / bloqueado, então é provável que você esteja vendo isso. Tente adicionar alguns argumentos como --workers=2 --threads=4 --worker-class=gthread
.
Obrigado @javabrett
Isso resolveu tudo!
Tive o mesmo problema. Tanto quanto posso adivinhar com meus próprios registros, parece que tensorflow
está usando gevent
, e você não pode usar gevent
ao mesmo tempo em gunicorn
. Os sinalizadores --workers
e --threads
não fazem nenhuma diferença para mim, mas mudar de --worker-class=gevent
para --worker-class=gthread
corrigiu o problema para mim. Obrigado @javabrett
Oi! Como mantenedor do gevent e contribuidor deste projeto, posso afirmar categoricamente que gevent e gunicorn funcionam bem juntos. Várias bibliotecas podem interferir, mas isso não é culpa do gunicorn ou do gevent. Abra um novo problema se esse não for o seu caso. Obrigado!
Comentários muito úteis
Tive o mesmo problema. Tanto quanto posso adivinhar com meus próprios registros, parece que
tensorflow
está usandogevent
, e você não pode usargevent
ao mesmo tempo emgunicorn
. Os sinalizadores--workers
e--threads
não fazem nenhuma diferença para mim, mas mudar de--worker-class=gevent
para--worker-class=gthread
corrigiu o problema para mim. Obrigado @javabrett