Gunicorn: OSFehler: [Errno 0] Fehler

Erstellt am 8. Mai 2018  ·  30Kommentare  ·  Quelle: benoitc/gunicorn

Ich führe die App aus
gunicorn -w 2 -b ' localhost:8585 ' --timeout=200 --certfile=crt.crt --keyfile=key.key service:app

Und ich bekomme folgendes, aber ich bekomme nicht immer eine solche Antwort, die meisten Anfragen werden korrekt behandelt, aber manchmal tritt ein Fehler auf

[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

Hilfreichster Kommentar

Hmm, nach einigen zusätzlichen Recherchen scheint dies tatsächlich ein Fehler in der Art und Weise zu sein, wie die Python-Bibliothek ssl zerlumpte EOFs unter Linux behandelt: https://bugs.python.org/issue31122

Alle 30 Kommentare

Aus meiner Erinnerung tritt dieser Fehler auf, wenn ein Client versucht, eine Verbindung ohne SSL herzustellen. Könnte das bei dir der Fall sein?

Ich sehe Ihren Beitrag zu dem anderen Problem, das ich geschlossen habe. Ich entschuldige mich, wenn mein Kommentar nicht die Ursache ist.

Gibt es ein Muster, nach dem Anfragen auf diese Weise fehlschlagen?

@usmetanina Welche Art von Clients verbinden sich auch mit Gunicorn? Haben Sie SSL-Optionen, die explizit verwendet werden, um eine Verbindung herzustellen?

ist das schon gelöst? @usmetanina , weil ich genau das gleiche Problem habe

@benoitc Ich sehe @usmetanina ‚genaue Fehler s häufig mit python3.6 und gunicorn 19.9.0 .

Ich verwende die folgenden Informationen, um gunicorn mit einer Flaschen-App zu starten, die in einem Docker-Container ausgeführt wird.

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

Die Konfigurationsdatei sieht so aus (domain-with-cert.com ist natürlich ein Platzhalter für den eigentlichen Domainnamen):

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'

Alle Gedanken zur Fehlersuche wären hilfreich. Wenn Sie weitere Informationen benötigen, lassen Sie es mich einfach wissen.

@willpatera , siehe mein Kommentar:

Aus meiner Erinnerung tritt dieser Fehler auf, wenn ein Client versucht, eine Verbindung ohne SSL herzustellen. Könnte das bei dir der Fall sein?

@tilgovi Ich habe den obigen Kommentar gesehen. Ich bin mir ziemlich sicher, dass der Client über SSL eine Verbindung herstellt. Irgendwelche Debugging-Vorschläge?

@willpatera Ich würde sagen, schalten Sie die Zugriffsprotokolle ein und sehen Sie, ob Sie feststellen können, welche Anfrage das Problem verursacht. Wenn Sie einen Reverse-Proxy vor gunicorn haben, stellen Sie sicher, dass dieser über Zugriffsprotokolle verfügt, damit Sie möglicherweise sehen können, welche Anforderung einen Fehler bei gunicorn verursacht, auch wenn gunicorn es nie protokolliert.

@tilgovi Ich habe die gleichen Probleme. Musste die folgenden Informationen etwas bearbeiten, da sie falsch waren:
Die Anfrage an gunicorn ist immer die gleiche Anfrage (aber mit einem anderen Körper). Es besteht also kein Zweifel, dass es sich um https und nicht um http handelt.
Was mir auffällt, ist, dass es immer passiert, wenn die Anzahl der Anfragen steigt. Wenn der Server beschäftigt ist, scheint er Probleme zu haben, die Anforderungen richtig zu verarbeiten.

Hat das vielleicht mit den Arbeitern zu tun oder so? Wenn Sie Konfigurationsvorschläge haben, würde ich diese gerne testen.

Hallo Leute, ich suche noch nach einer Lösung für das Problem. Derzeit ist die einzige Option, die wir haben, ein Downgrade auf reines HTTP, was überhaupt nicht machbar ist.

Ich habe dasselbe erlebt. Hatte einen Produktionsserver mit Gunicorn + Flask (hinter einem Load Balancer), der monatelang einwandfrei funktionierte, ergab plötzlich jede Anfrage diesen Fehler, bis ich Gunicorn neu startete:

[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

Nichts in den Protokollen vor diesen Fehlern weist darauf hin, was der Auslöser gewesen sein könnte.

Dies war mit Gunicorn 19.9.0, das mit 3 Arbeitern auf einem Single-Core-Server lief.

Da ich dieses Problem zum ersten Mal sehe, kann ich nicht versprechen, dass ich es jemals reproduzieren werde. Wenn es jedoch irgendeine Art von Protokollierung oder anderen Diagnosecode gibt, der möchte, dass ich auf unserem Server einfüge, der einige nützliche Informationen für den Fall liefert, dass dies erneut passiert, bin ich ganz Ohr.

Ruft Ihr LB einen bestimmten Endpunkt an? Wie reagiert sie auf die LB-Anfrage?

Wenn ich "Load Balancer" sagte, hätte ich wirklich CDN oder Caching-Layer sagen sollen. Konkret: Es ist Amazon Cloudfront. Es leitet nur Anfragen an unseren Gunicorn-Server (der auf einer EC2-Instanz läuft) weiter und speichert die Ergebnisse für eine Weile.

hrm sollte nicht amazon cloudfront die ssl-Anfrage für dich beenden? @Explodierender Kohl . Warum muss gunicorn auf ssl hinter sich hören?

@benoitc Es gibt also zwei Schichten mit SSL, die an der Architektur beteiligt sind. Mitglieder der Öffentlichkeit stellen eine Verbindung zu unserer Website über unsere CloudFront-Domain über HTTPS her, und dann stellt CloudFront eine Anfrage an unseren Backend-Knoten, auf dem Gunicorn ausgeführt wird, ebenfalls unter Verwendung von HTTPS (mit einem anderen Domainnamen und Zertifikat), speichert das Ergebnis im Cache und liefert es an die öffentlich.

Vielleicht fragen Sie sich, welchen Sinn es hat, SSL für diese zweite interne Anfrage zu verwenden? Es ist sicherlich fraglich, ob es sinnlos ist (obwohl es möglicherweise nicht - es verhindert, dass Amazon in unseren Kommunikationen in ihrem internen Netzwerk herumschnüffelt, und es gibt auch regulatorische Gründe, auf die ich nicht eingehen werde, warum wir angesichts der Branche meines Unternehmens möglicherweise sicherstellen müssen, dass wir Verschlüsselung entlang der Pipeline). Ob sinnlos oder nicht, wir machen es. ¯\_(ツ)_/¯

Könnte es sein, dass Cloudfront eine einfache HTTP-Anfrage an Ihren Endpunkt sendet? Wenn Sie Zugriff auf die Cloudfront-Protokolle haben, sollten Sie diese sehen können.

@benoitc Ich glaube nicht, dass CloudFront irgendwelche Protokolle

  • Unsere Distribution ist in der CloudFront-Konsole so konfiguriert, dass sie sich über "HTTPS only" mit dem Gunicorn-Ursprung verbindet.
  • Gunicorn hört nicht auf Port 80
  • Wenn ich versuche, eine Verbindung zu unserem Backend-Server über HTTP herzustellen (einschließlich des Erzwingens von HTTP auf Port 443), wird der oben zitierte OSError nicht reproduziert
  • Als ich den oben zitierten OSError erhielt, behob ein Neustart von Gunicorn auf dem Backend-Server sofort das Problem, das auf einen Fehler am Gunicorn-Ende hinweist, nicht am Cloudfront-Ende

@ExplodingCabbage ok Ich werde es mir ansehen, nachdem die 20.0.1 herausgekommen ist. Eine letzte Sache, welche Version von Python verwenden Sie?

3.6.8

Mir ist klar, dass ich ein Detail aus meiner obigen Geschichte ausgelassen habe: Vor dem Neustart von Gunicorn habe ich auch das SSL-Zertifikat aktualisiert, das Gunicorn mit LetsEncrypt verwendet. Ich hatte nicht daran gedacht, dies zu erwähnen, weil ich gestern fälschlicherweise zu dem Schluss gekommen war, dass ein Zertifikat am Tag des Fehlerbeginns auf keinen Fall abgelaufen wäre und die Zertifikatsaktualisierung für die Behebung des Problems tatsächlich nicht relevant gewesen sei.

Beim Überprüfen einiger Protokolle erkenne ich jedoch, dass die Fehler tatsächlich an dem Tag begannen, an dem ein vorheriges Zertifikat ablaufen sollte.

Hier gibt es immer noch ein Rätsel und ein potenzielles Verbesserungspotenzial (was genau bedeutet dieser Fehler und warum kann Gunicorn keine nützlichere Nachricht geben?), aber die Erzählung, die ich zuvor gegeben habe - in der dieser Fehler aus heiterem Himmel begann ohne ersichtlichen Grund - ist nicht richtig. Ich würde vermuten, dass CloudFront die Verbindung als Reaktion auf ein abgelaufenes Zertifikat vom Gunicorn-Server beendet hat, und dass Gunicorn, anstatt dies zu verstehen und sinnvoll zu melden, einen nachrichtenlosen OSError aufblähen lässt.

Ich entschuldige mich dafür, dass ich meine Enten nicht hintereinander habe, bevor ich mich melde. Auf der anderen Seite ist es vielleicht einfacher, diese Ausnahme nach Belieben zu reproduzieren, wenn Sie versuchen möchten, das Szenario eleganter zu handhaben.

@ExplodingCabbage oh das ist

Ich bin gerade reproduzierbar auf dasselbe Problem gestoßen und bin mir ziemlich sicher, dass es die Folge einer Art von Ressourcenerschöpfung ist.

Bei mir wurde es ausgelöst, indem ich eine Zeitüberschreitung bei einem blockierenden Anruf vergaß und sich Anfragen häuften.

HTH

Hallo! Ich habe genau dieses Problem. Ich habe einen Gunicorn/Flask-Dienst, der auf einem ECS-Cluster hinter einem Netzwerk-Load-Balancer ausgeführt wird. Einige Versionsdetails:

python    - 3.7.4
gunicorn  - 19.9.0
flask     - 1.0.4

Der Dienst kann problemlos auf Anfragen eines Clients reagieren, der TLS verwendet, aber meine Protokolle werden mit OSErrors überflutet. Soweit ich das beurteilen kann, resultieren diese aus den Health Check-Anfragen des Load Balancer (TCP).

Ich konnte den Fehler lokal reproduzieren, indem ich eine TCP-Verbindung manuell auf dem Listening-Port (in diesem Fall 8000) öffnete und schloss:

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

Was dazu führte, dass der folgende Fehler geworfen wurde:

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

Hoffe das hilft!

Hmm, nach einigen zusätzlichen Recherchen scheint dies tatsächlich ein Fehler in der Art und Weise zu sein, wie die Python-Bibliothek ssl zerlumpte EOFs unter Linux behandelt: https://bugs.python.org/issue31122

Wie von @shevisjohnson erwähnt, erscheint dieser Fehler, wenn Sie "nc -vz hostname port_no" ausführen.
Wir können diesen Fehler in der Protokolldatei unterdrücken, indem wir den folgenden Protokollierungsmechanismus verwenden.

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

Hier ist die Python-Datei.

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

Ich hoffe es hilft.

Wir haben zeitweise beobachtet, dass mehrere Gunicorn-Apps mit diesem Fehler in der Produktion unter gleichzeitiger Last fehlgeschlagen sind.

Es dauerte nur einen Moment, um eine zuverlässige Reproduktion zu finden: Verwenden Sie hey , um 100 gleichzeitige Anfragen an das neueste Gunicorn (20.0.4) mit dem gthread Worker zu senden:

$ 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] Socket-Fehler bei Verarbeitungsanfrage.
Traceback (letzter Anruf zuletzt):
return self._sslobj.read(len)
OSFehler: [Errno 0] Fehler


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

Falls das auch hilft!

Wenn die Ursache tatsächlich https://bugs.python.org/issue31122 ist :

  • Am 4. März wurde ein Fix eingereicht (python/cpython#18772), der jedoch noch immer nicht von einem Core-Entwickler bestätigt wurde. Vielleicht würde ein Gunicorn-Maintainer helfen, der dort oder auf BPO-31122 einen Kommentar hinterlässt, der sagt, dass es Gunicorn-Benutzer betrifft?
  • Gunicorn müsste dies noch für unterstützte Versionen von Python umgehen, die vor der Veröffentlichung dieses Fixes liegen. Lohnt es sich auch zu fragen, ob es in demselben Kommentar eine Problemumgehung gibt?

Dies wirkt sich auch auf meine Organisation in der Produktion aus.

Mir ist aufgefallen, dass der Bugfix in den Zweigen 3.8 und 3.9 gelandet ist, aber sie erwägen <= 3.7 EOL und wir bleiben vorerst immer noch bei 3.6. Gibt es derzeit in gunicorn selbst eine bekannte Problemumgehung für dieses Problem? Ist etwas geplant?

Wir untersuchen, was den Dienst so oft aufrufen könnte, um dies auszulösen, aber ich versuche nur herauszufinden, was getan werden könnte, da dies zu enormen Ressourcenspitzen auf den betroffenen Knoten führt.

Zusätzlich zu jriddys Kommentar, dass keine Absicht besteht, vor Version 3.8 zurück zu portieren, beachten Sie, dass der Fix , falls jemand anderes dieses Problem hat,

Ich habe Probleme, genau zu sagen, woher dieser Traceback stammt - in meinem Fall, indem ich gevent direkt als WSGI-App-Server verwende. Für Gunicorn passiert es hier, für Synchronarbeiter:

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

Wenn Sie sich im Fall von Gunicorn nur Sorgen um das Rauschen in Protokollen machen, können Sie Folgendes tun:

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

Zugehöriges gevent-Problem: https://github.com/gevent/gevent/issues/1671

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen