Gunicorn: OSError : [Errno 0] Erreur

Créé le 8 mai 2018  ·  30Commentaires  ·  Source: benoitc/gunicorn

je lance l'application
gunicorn -w 2 -b ' localhost:8585 ' --timeout=200 --certfile=crt.crt --keyfile=key.key service:app

Et j'obtiens ce qui suit, mais je n'obtiens pas toujours une telle réponse, la plupart des demandes sont traitées correctement, mais parfois une erreur se produit

[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

Commentaire le plus utile

Hmm, bien après quelques recherches supplémentaires, il semble que cela puisse en fait être un bogue dans la façon dont la bibliothèque python ssl gère les EOF en lambeaux sur Linux : https://bugs.python.org/issue31122

Tous les 30 commentaires

De mémoire, cette erreur se produit lorsqu'un client essaie de se connecter sans SSL. Serait-ce le cas pour vous ?

Je vois votre message sur l'autre sujet que j'ai fermé. Mes excuses si mon commentaire n'est pas la cause.

Existe-t-il un modèle auquel les requêtes échouent de cette façon ?

@usmetanina quel type de clients se connecte également à Gunicorn ? Avez-vous des options SSL utilisées explicitement pour vous y connecter ?

c'est déjà résolu ? @usmetanina , car j'ai exactement le même problème

@benoitc Je vois @usmetanina « est une erreur exacte fréquemment en utilisant python3.6 et gunicorn 19.9.0 .

J'utilise les informations ci-dessous pour démarrer gunicorn avec une application de flacon s'exécutant dans un conteneur Docker.

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

Le fichier de configuration ressemble à ceci (domain-with-cert.com est bien sûr un espace réservé pour le nom de domaine réel) :

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'

Toute réflexion sur le débogage serait utile. Si vous avez besoin de plus d'informations, faites le moi savoir.

@willpatera , voir mon commentaire :

De mémoire, cette erreur se produit lorsqu'un client essaie de se connecter sans SSL. Serait-ce le cas pour vous ?

@tilgovi J'ai vu le commentaire ci-dessus. Je suis à peu près sûr que le client se connecte via SSL. Des suggestions de débogage ?

@willpatera Je dirais, activez les journaux d'accès et voyez si vous pouvez déterminer quelle demande est à l'origine du problème. Si vous avez un proxy inverse devant gunicorn, assurez-vous qu'il dispose de journaux d'accès afin que vous puissiez peut-être voir quelle requête provoque une erreur avec gunicorn même si gunicorn ne l'enregistre jamais.

@tilgovi J'ai les mêmes problèmes. J'ai dû modifier un peu les informations suivantes car elles étaient incorrectes :
La demande qui est faite à gunicorn est toujours exactement la même demande (mais avec un corps différent). Il ne fait donc aucun doute qu'il s'agit de https et non de http.
Ce que je remarque, c'est que cela se produit toujours lorsque le nombre de demandes augmente. Lorsque le serveur est occupé, il semble avoir du mal à traiter correctement les demandes.

Peut-être que cela a à voir avec les travailleurs ou quelque chose comme ça? Si vous avez des suggestions de configuration, je serais ravi de les tester.

Salut les gars, je cherche toujours un moyen de résoudre ce problème. Actuellement, la seule option dont nous disposons est de rétrograder en HTTP simple, ce qui n'est pas du tout faisable.

J'ai été témoin de la même chose. J'avais un serveur de production exécutant Gunicorn + Flask (derrière un équilibreur de charge) qui fonctionnait correctement pendant des mois, puis soudain, chaque requête a généré cette erreur jusqu'à ce que je redémarre 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

Rien dans les journaux précédant ces erreurs n'indique ce que le déclencheur aurait pu être.

C'était avec Gunicorn 19.9.0 exécuté avec 3 travailleurs sur un serveur monocœur.

Comme c'est la première fois que je vois ce problème, je ne peux pas promettre que je le reproduirai un jour. Cependant, s'il existe un type de code de journalisation ou de diagnostic que quelqu'un aimerait que j'ajoute sur notre serveur qui pourrait fournir des informations utiles au cas où cela se reproduirait, je suis tout ouïe.

Votre LB appelle-t-il un point de terminaison spécifique ? Comment répond-il à la requête LB ?

Quand j'ai dit "Load Balancer", j'aurais vraiment dû dire CDN ou couche de mise en cache. Plus précisément : c'est Amazon Cloudfront. Il transmet simplement les demandes à notre serveur Gunicorn (s'exécutant sur une instance EC2) et met en cache les résultats pendant un certain temps.

hrm ne devrait-il pas amazon cloudfront terminer la demande ssl pour vous ? @ExplodingCabbage . Pourquoi gunicorn doit écouter sur ssl derrière ?

@benoitc Donc, il y a deux couches avec SSL impliquées dans l'architecture. Les membres du public se connectent à notre site Web via notre domaine CloudFront via HTTPS, puis CloudFront fait une demande à notre nœud principal exécutant Gunicorn, également en utilisant HTTPS (avec un nom de domaine et un certificat différents), met en cache le résultat et le sert au Publique.

Je suppose que vous vous demandez peut-être quel est l'intérêt d'utiliser SSL pour cette seconde demande interne? On peut certainement soutenir que c'est inutile (bien que peut-être pas - cela empêche Amazon d'espionner nos communications dans leur réseau interne, et il y a aussi des raisons réglementaires pour lesquelles je n'expliquerai pas pourquoi, étant donné le secteur de mon entreprise, nous devrons peut-être nous assurer que nous avons chiffrement tout au long du pipeline). Que ce soit inutile ou non, nous le faisons. \_(ツ)_/¯

se pourrait-il que cloudfront envoie à votre point de terminaison une simple requête HTTP ? Si vous avez accès aux journaux cloudfront, vous devriez pouvoir les voir.

@benoitc Je ne pense pas que CloudFront expose des journaux qui seraient utiles, mais je suis sûr qu'il n'essayait pas de se connecter via HTTP, car :

  • Notre distribution est configurée dans la console CloudFront pour se connecter à l'origine Gunicorn en "HTTPS uniquement"
  • Gunicorn n'écoute pas sur le port 80
  • Si j'essaie de me connecter à notre serveur principal via HTTP (y compris en forçant HTTP sur le port 443), il ne reproduit pas l'OSError cité ci-dessus
  • Lorsque j'obtenais l'erreur OSE citée ci-dessus, le redémarrage de Gunicorn sur le serveur principal a instantanément résolu le problème, ce qui indique que quelque chose ne va pas du côté de Gunicorn, pas du côté de Cloudfront.

@ExplodingCabbage ok, j'y

3.6.8

Je me rends compte que j'ai omis un détail de mon histoire ci-dessus : avant de redémarrer Gunicorn, j'ai également mis à jour le certificat SSL que Gunicorn utilise avec LetsEncrypt. Je n'avais pas pensé à le mentionner car j'avais conclu à tort hier qu'il n'y avait aucun moyen qu'un certificat ait expiré le jour où les erreurs ont commencé et que la mise à jour du certificat n'avait en fait pas été pertinente pour résoudre le problème.

Cependant, en vérifiant certains journaux, je me rends compte maintenant que les erreurs ont en fait commencé le jour où un certificat précédent devait expirer.

Il y a encore un peu de mystère ici, et une marge d'amélioration potentielle (que signifie exactement cette erreur, et pourquoi Gunicorn ne peut-il pas donner un message plus utile ?), mais le récit que j'ai donné auparavant - dans lequel cette erreur a commencé à l'improviste sans cause apparente - n'est pas juste. Je suppose que CloudFront mettait fin à la connexion en réponse à la vue d'un certificat expiré du serveur Gunicorn, et que Gunicorn, plutôt que de pouvoir le comprendre et le signaler de manière significative, laisse apparaître une erreur OSE sans message.

Je m'excuse de ne pas avoir aligné mes canards avant de signaler. D'un autre côté, cela facilitera peut-être la reproduction à volonté de cette exception si vous voulez essayer de gérer le scénario avec plus d'élégance.

@ExplodingCabbage oh c'est assez intéressant, ça devrait être reproductible à un moment donné alors. Merci pour les détails supplémentaires !

Je viens de rencontrer le même problème de manière reproductible et je suis un peu confiant que c'est la conséquence d'une sorte d'épuisement des ressources.

Pour moi, cela a été déclenché par l'oubli d'un délai d'attente sur un appel bloquant et les demandes s'accumulent.

HTH

Salut! Je rencontre exactement ce problème. J'ai un service gunicorn/flask exécuté sur un cluster ECS derrière un équilibreur de charge réseau. Quelques spécificités de version :

python    - 3.7.4
gunicorn  - 19.9.0
flask     - 1.0.4

Le service est capable de répondre aux demandes provenant d'un client utilisant TLS sans problème, mais mes journaux sont inondés d'erreurs OSE. Pour autant que je sache, ceux-ci résultent des demandes de vérification de l'état provenant de l'équilibreur de charge (TCP).

J'ai pu reproduire l'erreur localement en ouvrant et fermant une connexion TCP manuellement sur le port d'écoute (8000 dans ce cas) :

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

Ce qui a donné lieu à l'erreur suivante :

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

J'espère que cela t'aides!

Hmm, bien après quelques recherches supplémentaires, il semble que cela puisse en fait être un bogue dans la façon dont la bibliothèque python ssl gère les EOF en lambeaux sur Linux : https://bugs.python.org/issue31122

Comme mentionné par @shevisjohnson, si vous exécutez "nc -vz hostname port_no", cette erreur apparaît.
Nous pouvons supprimer cette erreur dans le fichier journal en utilisant le mécanisme de journalisation ci-dessous.

$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]

Voici le fichier 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!"

J'espère que ça aide.

Nous avons observé par intermittence plusieurs applications Gunicorn échouer avec cette erreur en production alors qu'elles étaient sous charge simultanée.

Il n'a fallu qu'un instant pour arriver à une reproduction fiable : en utilisant hey pour envoyer 100 requêtes simultanées au dernier Gunicorn (20.0.4) en utilisant le gthread worker :

$ 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] [ERREUR] Demande de traitement d'erreur de socket.
Traceback (appel le plus récent en dernier) :
return self._sslobj.read(len)
OSError : [Errno 0] Erreur


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 ""

Au cas où cela aiderait aussi !

Si la cause première est en fait https://bugs.python.org/issue31122 :

  • Un correctif a été soumis le 4 mars (python/cpython#18772), mais il n'a toujours pas été reconnu par un développeur principal. Peut-être qu'un responsable de Gunicorn laissant un commentaire là-bas ou sur BPO-31122 disant que cela affecte les utilisateurs de Gunicorn aiderait?
  • Gunicorn devra toujours contourner ce problème pour les versions prises en charge de Python antérieures à la publication de ce correctif. Cela vaut également la peine de demander s'il existe une solution de contournement dans ce même commentaire ?

Cela affecte également mon organisation en prod.

J'ai remarqué que la correction de bogues a atterri dans les branches 3.8 et 3.9, mais ils envisagent <= 3.7 EOL et nous sommes toujours un peu bloqués sur 3.6 pour le moment. Existe-t-il actuellement une solution de contournement connue à ce problème dans gunicorn lui-même ? Y a-t-il quelque chose de prévu ?

Nous examinons ce qui pourrait tant appeler le service pour déclencher cela, mais j'essaie simplement de comprendre ce qui pourrait être fait, car cela entraîne d'énormes pics de ressources sur les nœuds affectés.

En plus du commentaire de jriddy concernant l'absence d'intention de rétroporter avant la version 3.8, si quelqu'un d'autre rencontre ce problème, notez également que le correctif est défini pour être inclus dans CPython 3.8.6 .

Avoir du mal à dire exactement d'où émane ce retraçage - dans mon cas, en utilisant directement gevent comme serveur d'applications WSGI, donc en supposant qu'il s'agit d'un appel de journalisation quelque part dans gevent/greenlet, mais je ne peux pas le trouver pour le moment. Pour Gunicorn, cela se passe ici, pour les travailleurs synchrones :

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

Dans le cas de Gunicorn, si vous êtes simplement préoccupé par le bruit dans les journaux, vous pourrez peut-être faire quelque chose comme :

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())

Problème gevent connexe : https://github.com/gevent/gevent/issues/1671

Cette page vous a été utile?
0 / 5 - 0 notes