Gunicorn: OSError: [Errno 0] Erro

Criado em 8 mai. 2018  ·  30Comentários  ·  Fonte: benoitc/gunicorn

Estou executando o app
gunicorn -w 2 -b ' localhost: 8585 ' --timeout = 200 --certfile = crt.crt --keyfile = key.key service: app

E eu recebo o seguinte, mas nem sempre recebo essa resposta, a maioria das solicitações são tratadas corretamente, mas às vezes ocorre um erro

[2018-05-08 14:53:36 +0500] [11227] [ERROR] Socket error processing request.
Traceback (most recent call last):
  File "/usr/lib/python3/dist-packages/gunicorn/workers/sync.py", line 134, in handle
    req = six.next(parser)
  File "/usr/lib/python3/dist-packages/gunicorn/http/parser.py", line 41, in __next__
    self.mesg = self.mesg_class(self.cfg, self.unreader, self.req_count)
  File "/usr/lib/python3/dist-packages/gunicorn/http/message.py", line 153, in __init__
    super(Request, self).__init__(cfg, unreader)
  File "/usr/lib/python3/dist-packages/gunicorn/http/message.py", line 53, in __init__
    unused = self.parse(self.unreader)
  File "/usr/lib/python3/dist-packages/gunicorn/http/message.py", line 165, in parse
    self.get_data(unreader, buf, stop=True)
  File "/usr/lib/python3/dist-packages/gunicorn/http/message.py", line 156, in get_data
    data = unreader.read()
  File "/usr/lib/python3/dist-packages/gunicorn/http/unreader.py", line 38, in read
    d = self.chunk()
  File "/usr/lib/python3/dist-packages/gunicorn/http/unreader.py", line 65, in chunk
    return self.sock.recv(self.mxchunk)
  File "/usr/lib/python3.5/ssl.py", line 922, in recv
    return self.read(buflen)
  File "/usr/lib/python3.5/ssl.py", line 799, in read
    return self._sslobj.read(len, buffer)
  File "/usr/lib/python3.5/ssl.py", line 585, in read
    v = self._sslobj.read(len)
OSError: [Errno 0] Error
( FeaturSSL

Comentários muito úteis

Hmm, bem depois de alguma pesquisa adicional, parece que isso pode realmente ser um bug na maneira ssl biblioteca python https://bugs.python.org/issue31122

Todos 30 comentários

Na minha memória, esse erro acontece quando um cliente tenta se conectar sem SSL. Isso poderia ser o seu caso?

Vejo sua postagem sobre o outro problema que fechei. Minhas desculpas se meu comentário não é a causa.

Existe um padrão para o qual as solicitações falham dessa maneira?

@usmetanina que tipo de clientes se conectam ao Gunicorn também? Você tem alguma opção SSL usada explicitamente para se conectar a ele?

já está resolvido? @usmetanina , porque tenho exatamente o mesmo problema

@benoitc vejo @usmetanina é erro exata freqüentemente usando python3.6 e gunicorn 19.9.0 .

Eu uso as informações abaixo para iniciar o gunicorn com um aplicativo de frasco em execução em um contêiner do docker.

gunicorn --workers=3 --bind=0.0.0.0:8000 --config=gunicorn_config.py --preload main

O arquivo de configuração tem a seguinte aparência (domain-with-cert.com, claro, é um espaço reservado para o nome de domínio real):

workers = 3
bind = '0.0.0.0:443'
certfile = '/etc/letsencrypt/live/domain-with-cert.com/fullchain.pem'
keyfile = '/etc/letsencrypt/live/domain-with-cert.com/privkey.pem'

Qualquer opinião sobre depuração seria útil. Se precisar de mais informações, é só me avisar.

@willpatera , veja meu comentário:

Na minha memória, esse erro acontece quando um cliente tenta se conectar sem SSL. Isso poderia ser o seu caso?

@tilgovi eu vi o comentário acima. Tenho certeza de que o cliente está se conectando por SSL. Alguma sugestão de depuração?

@willpatera eu diria, ligue os logs de acesso e veja se você pode determinar qual solicitação causa o problema. Se você tiver um proxy reverso na frente do gunicorn, certifique-se de que ele tenha registros de acesso para que você possa ver qual solicitação causa um erro com o gunicorn, mesmo que o gunicorn nunca o registre.

@tilgovi Estou tendo os mesmos problemas. Tive que editar as seguintes informações um pouco, pois estavam incorretas:
O pedido que está sendo feito ao gunicorn é sempre exatamente o mesmo pedido (mas com um corpo diferente). Portanto, não há dúvida de que é https e não http.
O que eu noto é que sempre acontece quando a quantidade de pedidos está aumentando. Quando o servidor está ocupado, parece que há problemas para lidar com as solicitações de maneira adequada.

Talvez isso tenha a ver com os trabalhadores ou algo assim? Se você tiver alguma sugestão de configuração, eu gostaria de testá-la.

Oi pessoal, ainda estou procurando uma maneira de resolver isso. Atualmente, a única opção que temos é fazer o downgrade para HTTP simples, o que não é viável de forma alguma.

Eu testemunhei a mesma coisa. Tinha um servidor de produção executando Gunicorn + Flask (atrás de um balanceador de carga) que funcionou bem por meses, então, de repente, todas as solicitações geraram este erro até que reiniciei o Gunicorn:

[2019-11-21 07:27:36 +0000] [24245] [ERROR] Socket error processing request.
Traceback (most recent call last):
  File "/usr/local/lib/python3.6/dist-packages/gunicorn/workers/sync.py", line 134, in handle
    req = six.next(parser)
  File "/usr/local/lib/python3.6/dist-packages/gunicorn/http/parser.py", line 41, in __next__
    self.mesg = self.mesg_class(self.cfg, self.unreader, self.req_count)
  File "/usr/local/lib/python3.6/dist-packages/gunicorn/http/message.py", line 181, in __init__
    super(Request, self).__init__(cfg, unreader)
  File "/usr/local/lib/python3.6/dist-packages/gunicorn/http/message.py", line 54, in __init__
    unused = self.parse(self.unreader)
  File "/usr/local/lib/python3.6/dist-packages/gunicorn/http/message.py", line 193, in parse
    self.get_data(unreader, buf, stop=True)
  File "/usr/local/lib/python3.6/dist-packages/gunicorn/http/message.py", line 184, in get_data
    data = unreader.read()
  File "/usr/local/lib/python3.6/dist-packages/gunicorn/http/unreader.py", line 38, in read
    d = self.chunk()
  File "/usr/local/lib/python3.6/dist-packages/gunicorn/http/unreader.py", line 65, in chunk
    return self.sock.recv(self.mxchunk)
  File "/usr/lib/python3.6/ssl.py", line 997, in recv
    return self.read(buflen)
  File "/usr/lib/python3.6/ssl.py", line 874, in read
    return self._sslobj.read(len, buffer)
  File "/usr/lib/python3.6/ssl.py", line 633, in read
    v = self._sslobj.read(len)
OSError: [Errno 0] Error

Nada nos registros que precedem esses erros indica qual poderia ter sido o gatilho.

Isso aconteceu com o Gunicorn 19.9.0 em execução com 3 trabalhadores em um servidor de núcleo único.

Como esta é a primeira vez que vejo esse problema, não posso prometer que jamais o reproduzirei. No entanto, se houver qualquer tipo de registro ou outro código de diagnóstico que alguém queira que eu adicione em nosso servidor que possa fornecer algumas informações úteis caso isso aconteça novamente, sou todo ouvidos.

O seu LB chama um ponto de extremidade específico? Como responde ao pedido de LB?

Quando eu disse "Load Balancer", realmente deveria ter dito CDN ou camada de cache. Especificamente: é Amazon Cloudfront. Ele apenas encaminha as solicitações ao nosso servidor Gunicorn (em execução em uma instância EC2) e armazena os resultados em cache por um tempo.

hrm não deve amazon cloudfront encerrar a solicitação SSL para você? @ExplodingCabbage . Por que o gunicorn tem que ouvir no SSL por trás?

@benoitc Então, há duas camadas com SSL envolvidas na arquitetura. Membros do público se conectam ao nosso site por meio de nosso domínio do CloudFront sobre HTTPS e, em seguida, o CloudFront faz uma solicitação para o nosso nó de back-end executando Gunicorn, também usando HTTPS (com um nome de domínio e certificado diferente), armazena em cache o resultado e o veicula para o público.

Acho que talvez você esteja se perguntando qual é o objetivo de usar SSL para essa segunda solicitação interna. É certamente discutível, é inútil (embora possivelmente não - ele impede a Amazon de espionagem em nossas comunicações em sua rede interna, e também há razões regulatórias que não vou entrar em por que, devido ao setor de minha empresa, podemos precisar garantir que temos criptografia em todo o pipeline). Quer seja inútil ou não, nós o fazemos. ¯ \ _ (ツ) _ / ¯

será que o cloudfront está enviando ao seu endpoint uma solicitação HTTP simples? Se você tiver acesso aos logs do cloudfront, deverá ser capaz de vê-los.

@benoitc Não acho que o CloudFront expõe quaisquer logs que seriam úteis, mas tenho certeza de que ele não estava tentando se conectar por HTTP, uma vez que:

  • Nossa distribuição é configurada no console do CloudFront para se conectar à origem do Gunicorn por meio de "apenas HTTPS"
  • Gunicorn não está escutando na porta 80
  • Se eu tentar me conectar ao nosso servidor de back-end por HTTP (incluindo forçar HTTP na porta 443), ele não reproduz o OSError citado acima
  • Quando eu estava obtendo o OSError citado acima, reiniciar o Gunicorn no servidor de back-end corrigiu instantaneamente o problema, que aponta para algo errado na extremidade do Gunicorn, não na extremidade do Cloudfront

@ExplodingCabbage ok, darei uma olhada nele depois que o 20.0.1 for lançado. Uma última coisa, qual versão do Python você está usando?

3.6.8

Percebi que deixei de fora um detalhe da minha história acima: antes de reiniciar o Gunicorn, também atualizei o certificado SSL que o Gunicorn usa com LetsEncrypt. Não pensei em mencionar isso porque havia concluído erroneamente ontem que não havia nenhuma maneira de um certificado expirar no dia em que os erros começaram e que a atualização do certificado não tinha de fato sido relevante para corrigir o problema.

No entanto, ao verificar alguns logs, agora percebo que os erros na verdade começaram no dia em que um certificado anterior estava para expirar.

Ainda há algum mistério aqui e algum espaço potencial para melhorias (o que exatamente significa esse erro, e por que Gunicorn não pode dar uma mensagem mais útil?), Mas a narrativa que dei antes - na qual esse erro começou do nada sem causa aparente - não está certo. Eu acho que o CloudFront estava encerrando a conexão em resposta ao ver um certificado expirado do servidor Gunicorn, e que o Gunicorn, em vez de ser capaz de entender isso e relatá-lo de forma significativa, permite que um OSError sem mensagem borbulhe.

Peço desculpas por não ter meus patos em uma fila antes de reportar. Por outro lado, talvez isso torne mais fácil reproduzir essa exceção à vontade, se você quiser tentar lidar com o cenário de maneira mais elegante.

@ExplodingCabbage oh isso é bastante interessante, deve ser reproduzível em algum ponto então. Obrigado pelos detalhes adicionais!

Acabei de encontrar o mesmo problema de forma reproduzível e estou um tanto confiante de que é a consequência de algum tipo de esgotamento de recursos.

Para mim, foi desencadeado por esquecer um tempo limite em uma chamada de bloqueio e pedidos acumulando.

HTH

Olá! Estou enfrentando exatamente esse problema. Eu tenho um serviço gunicorn / flask em execução em um cluster ECS atrás de um balanceador de carga de rede. Alguns detalhes da versão:

python    - 3.7.4
gunicorn  - 19.9.0
flask     - 1.0.4

O serviço é capaz de responder às solicitações provenientes de um cliente usando TLS sem problemas, no entanto, meus logs estão inundados com OSErrors. Pelo que eu posso dizer, eles são resultantes das solicitações de verificação de integridade vindas do balanceador de carga (TCP).

Consegui reproduzir o erro localmente abrindo e fechando uma conexão TCP manualmente na porta de escuta (8000 neste caso):

$ nc -vz 127.0.0.1 8000
localhost [127.0.0.1] 8000 (irdmi) open

O que resultou no seguinte erro:

Traceback (most recent call last):
    File "/nix/store/nh3v0c2nipihwblkdn0mh2kqyv3jq9nz-python3-3.7.4-env/lib/python3.7/site-packages/gunicorn/workers/sync.py" line 134 in handle
        req = six.next(parser)
    File "/nix/store/nh3v0c2nipihwblkdn0mh2kqyv3jq9nz-python3-3.7.4-env/lib/python3.7/site-packages/gunicorn/http/parser.py" line 41 in __next__
        self.mesg = self.mesg_class(self.cfg, self.unreader, self.req_count)
    File "/nix/store/nh3v0c2nipihwblkdn0mh2kqyv3jq9nz-python3-3.7.4-env/lib/python3.7/site-packages/gunicorn/http/message.py" line 181 in __init__
        super(Request, self).__init__(cfg, unreader)
    File "/nix/store/nh3v0c2nipihwblkdn0mh2kqyv3jq9nz-python3-3.7.4-env/lib/python3.7/site-packages/gunicorn/http/message.py" line 54 in __init__
        unused = self.parse(self.unreader)
    File "/nix/store/nh3v0c2nipihwblkdn0mh2kqyv3jq9nz-python3-3.7.4-env/lib/python3.7/site-packages/gunicorn/http/message.py" line 193 in parse
        self.get_data(unreader, buf, stop=True)
    File "/nix/store/nh3v0c2nipihwblkdn0mh2kqyv3jq9nz-python3-3.7.4-env/lib/python3.7/site-packages/gunicorn/http/message.py" line 184 in get_data
        data = unreader.read()
    File "/nix/store/nh3v0c2nipihwblkdn0mh2kqyv3jq9nz-python3-3.7.4-env/lib/python3.7/site-packages/gunicorn/http/unreader.py" line 38 in read
        d = self.chunk()
    File "/nix/store/nh3v0c2nipihwblkdn0mh2kqyv3jq9nz-python3-3.7.4-env/lib/python3.7/site-packages/gunicorn/http/unreader.py" line 65 in chunk
        return self.sock.recv(self.mxchunk)
    File "/nix/store/azwzsm1pkbzjxpkiq88w68p4jdghgasl-python3-3.7.4/lib/python3.7/ssl.py" line 1056 in recv
        return self.read(buflen)
    File "/nix/store/azwzsm1pkbzjxpkiq88w68p4jdghgasl-python3-3.7.4/lib/python3.7/ssl.py" line 931 in read
        return self._sslobj.read(len)
OSError: [Errno 0] Error

Espero que isto ajude!

Hmm, bem depois de alguma pesquisa adicional, parece que isso pode realmente ser um bug na maneira ssl biblioteca python https://bugs.python.org/issue31122

Conforme mencionado por @shevisjohnson, se você executar "nc -vz hostname port_no", este erro aparecerá.
Podemos suprimir esse erro no arquivo de log usando o mecanismo de log abaixo.

$ cat logging_config.yml

version: 1

formatters:
  simple:
    format: " %(asctime)s || %(name)s || %(levelname)s || %(message)s"

  test_api:
    format: "[%(asctime)s] [%(process)s] [%(levelname)s] %(message)s"

handlers:

  console:
    class: logging.StreamHandler
    level: DEBUG
    formatter: simple
    stream: ext://sys.stdout

  test_api_file_handler:
     class: logging.handlers.RotatingFileHandler
     level: DEBUG
     formatter: test_api
     filename: logs/test.log
     maxBytes: 2000000000
     backupCount: 1
     encoding: utf8

loggers:

  test_api: 
    level: DEBUG
    handlers: [test_api_file_handler]
    propagate: 0

root:
  level: DEBUG
  handlers: [console]

Aqui está o arquivo python.

import logging
import yaml
from flask import Flask

app = Flask(__name__)

def logSetter(logger_name:str) -> logging:
    with open("logging_config.yml", 'r') as f:
        config = yaml.safe_load(f)
    logging.config.dictConfig(config)
    logger = logging.getLogger(logger_name)
    return logger

logger=logSetter(logger_name="test_api")

@app.route("/api/test")
def hello():
     app.logger.info("hey from api")
     return "Hello from Python!"

Espero que ajude.

Observamos intermitentemente vários aplicativos Gunicorn falhando com esse erro na produção durante a carga simultânea.

Demorou apenas um momento para chegar a uma reprodução confiável: usando hey para enviar 100 solicitações simultâneas para o Gunicorn mais recente (20.0.4) usando o trabalhador gthread :

$ hey -n 100 -c 100 https://127.0.0.1:8000

`` `
$ gunicorn app: app -k gthread --certfile = ... --keyfile = ...
...
[2020-07-11 19:10:58 +0000] [3628247] [ERROR] Solicitação de processamento de erro de soquete.
Traceback (última chamada mais recente):
return self._sslobj.read (len)
OSError: [Errno 0] Erro


Using a Debian 9 / Linux 4.14.67 based environment.

The WSGI app to reproduce need not be anything beyond:
```python
# app.py
def app(environ, start_response):
    start_response("200 OK", [])
    return ""

Caso isso ajude também!

Se a causa raiz for de fato https://bugs.python.org/issue31122 :

  • Houve uma correção enviada em 4 de março (python / cpython # 18772), mas ainda não foi reconhecida por um desenvolvedor principal. Talvez um mantenedor do Gunicorn deixando um comentário lá ou no BPO-31122 dizendo que está afetando os usuários do gunicorn ajudaria?
  • O Gunicorn ainda precisaria contornar isso para versões com suporte do Python anteriores ao lançamento dessa correção. Também vale a pena perguntar se há uma solução alternativa para o mesmo comentário.

Isso está afetando minha organização em produção também.

Percebi que a correção de bug caiu em ramos 3.8 e 3.9, mas eles estão considerando <= 3.7 EOL e ainda estamos presos no 3.6 por enquanto. Existe uma solução alternativa conhecida para esse problema no próprio gunicorn? Há algo planejado?

Estamos investigando o que poderia estar chamando o serviço para acionar isso, mas estou apenas tentando descobrir o que poderia ser feito, pois isso resulta em grandes picos de recursos nos nós afetados.

Além do comentário de jriddy sobre a não intenção de fazer backport antes do 3.8, se alguém mais estiver tendo esse problema, observe também que a correção foi definida para ser incluída no CPython 3.8.6 .

Tendo problemas para dizer exatamente de onde este traceback emana - no meu caso, usando gevent como servidor de aplicativo WSGI diretamente, então presumindo que é uma chamada de registro em algum lugar dentro de gevent / greenlet, mas não consigo encontrá-la ainda. Para Gunicorn, isso acontece aqui, para trabalhadores síncronos:

https://github.com/benoitc/gunicorn/blob/e636bf81989bb833d2b99104feb11e86c3f2c43a/gunicorn/workers/sync.py#L150

No caso do Gunicorn, se você está preocupado apenas com o ruído nas toras, pode ser capaz de fazer algo como:

import logging

class HandshakeFilter(logging.Filter):
    # example: https://docs.python.org/3/howto/logging-cookbook.html
    # I have not tested this
    def filter(self, record):
        return "socket error processing request" in record.msg.casefold()

logging.getLogger("gunicorn").addFilter(HandshakeFilter())

Problema de gevent relacionado: https://github.com/gevent/gevent/issues/1671

Esta página foi útil?
0 / 5 - 0 avaliações