Einige Hintergrundinformationen:
Ich habe mit dem vpc von Amazon ein mäßig komplexes Netzwerk erstellt. Es ist ein dreistufiges Netzwerk in zwei Verfügbarkeitszonen. Jede Schicht hat ein Subnetz in Zone a und Zone b. Die Präsentationsebene befindet sich oben, eine Anwendungsebene in der Mitte und eine Kernebene unten.
Alle Sicherheitsgruppen und ACLs für die Subnetze erlauben derzeit ALLEN eingehenden und ausgehenden Datenverkehr, um die Oberfläche des Problems zu reduzieren.
Die Routing-Tabelle der Präsentationsschicht verweist den gesamten Datenverkehr auf ein Internet-Gateway. Das NAT-Gateway befindet sich in einem getrennten Subnetz und verweist den gesamten Datenverkehr auf das Internet-Gateway.
Meine Anwendung besteht aus zwei Komponenten, einer Benutzeroberfläche (React.js) und einer API (Node / Express). Sie werden als Docker-Images bereitgestellt. Vor jedem steht ein klassischer Load Balancer.
Die UI-ELB ist mit dem Internet verbunden und befindet sich in der Präsentationsschicht. Sie leitet den Datenverkehr von 80/443 an Port 8080 weiter und ist meiner App-ec2 zugeordnet, die sich im Subnetz der Anwendungsschicht befindet.
Meine API verfügt über einen internen Load Balancer. Der API-ELB befindet sich in der Anwendungsschicht (im selben Subnetz wie der app-ec2) und nimmt den Datenverkehr auf Port 80/443 auf und leitet ihn an den api-ec2 im Core auf Port 3000 weiter.
Beide Load Balancer entladen das Zertifikat, bevor sie Datenverkehr an ihre Instanzen weiterleiten.
Ich habe meine beiden Load Balancer in Route53 als Alias verknüpft und in den Anwendungen durch ihre hübsche URL ( https://app.website.com ) referenziert . Jeder Load Balancer besteht die definierten Integritätsprüfungen und meldet alle verwendeten ec2-Instanzen.
Zuletzt habe ich auf der API cors mit dem Paket cors nodejs aktiviert.
Hier ist ein schnelles und schmutziges Diagramm meines Netzwerks.
Das Problem:
Die APP-ELB leitet mich erfolgreich zur Anwendung weiter. Wenn die App jedoch versucht, eine GET-Anforderung an die API-ELB zu senden, sendet sie zuerst eine OPTIONS-Anforderung, die mit dem Fehlercode 408 übereinstimmt.
Wo es komisch wird
Einige der seltsamsten Dinge, denen ich beim Debuggen begegnet bin, sind:
- Ich kann SSH in die App-ec2-Instanz und kann eine erfolgreiche Curl gegen die API-ELB ausführen. Ich habe viele ausprobiert und alle funktionieren. Einige Beispiele sind:
curl -L https://api.website.com/system/healthcheck
undcurl -L -X OPTIONS https://api.website.com/system/healthcheck
. Es werden immer die gewünschten Informationen zurückgegeben. - Ich habe die gesamte Anwendung aus meinem Netzwerk in einen öffentlichen Standard-VPC verschoben und sie funktioniert wie vorgesehen.
- Ich habe die api-ec2 alle Netzwerkanforderungen an die Konsole schreiben. Während die Healthcheck-Anforderungen angezeigt werden, werden keine Anforderungen von app-ec2 angezeigt. Dies lässt mich glauben, dass der Verkehr nicht einmal die API erreicht.
Wirklich das Größte, was mich völlig ratlos macht, ist, dass das Einrollen des internen API-Elbs funktioniert, die Axios-Anforderung an dieselbe exakte URL jedoch nicht. Das ergibt für mich überhaupt keinen Sinn.
Was ich versucht habe
Ich habe ursprünglich viel Zeit damit verbracht, mit ACL-Regeln und Sicherheitsgruppen zu spielen, weil ich dachte, ich hätte etwas falsch gemacht. Schließlich sagte ich nur "Scheiß drauf" und öffnete alles, um zu versuchen, dieses Stück aus der Gleichung herauszunehmen.
Ich habe viel zu viel Zeit damit verbracht, mit Cors auf meiner API zu spielen. Irgendwann auf der Konfiguration landen, die ich jetzt habe, das ist der Standard- app.use(cors())
Rückruf, der vom cors-Knotenpaket bereitgestellt wird. Ich habe auch das app.options('*', cors())
empfohlen, was in der Dokumentation empfohlen wird.
Ich habe alles unter der Sonne googelt, aber speziell, ob ich mit den Elbs einige spezielle benutzerdefinierte Header definieren muss? Kann aber scheinbar nichts finden. Außerdem hat es gut funktioniert, als ich meine App aus dem Netzwerk entfernt habe.
Ich bin sicher, ich habe viele andere Dinge ausprobiert, aber diese scheinen am relevantesten zu sein. Was vermisse ich? Mir ist klar, dass dies möglicherweise ein sehr vages und weit gefasstes Thema und ein enormer Beitrag ist, aber ich schätze jeden Einblick und Ihre Zeit beim Lesen!
curl -X OPTIONS 127.0.0.1...
auf app-ec2? NurOPTIONS
ist kaputt? Die ELBs sind "Classic", nicht "Application", richtig? Können alle Instanzen über NAT korrekt auf das Internet zugreifen, zcurl ipv4.icanhazip.com
. (Ja, ich frage nach einem Grund, der dunkel erscheinen mag.)Antworten:
Was Sie also tatsächlich haben, ist Folgendes:
Da sich Ihre API-ELB in einer privaten Zone befindet, kann über das Internet nicht auf sie zugegriffen werden.
Ihr Frontend in React.js wird nur im Browser des Benutzers ausgeführt und nicht auf den UI-Servern. Diese Server dienen nur statischen Dateien.
Sie haben zwei Möglichkeiten: Konfigurieren Sie Ihre Frontend-Server so, dass API-Aufrufe an die API-ELB umgeleitet werden, oder aktualisieren Sie die API-ELB einfach so, dass sie mit dem Internet verbunden ist.
Die übliche Gefahr von JavaScript-Apps besteht darin, zu vergessen, dass sie im Browser des Benutzers und nicht wie bei einer JEE-Anwendung auf den Frontend-Servern ausgeführt werden.
quelle
Dies klingt nach einem asymmetrischen oder n-Pfad-Routing-Problem. Folgendes passiert wahrscheinlich:
Maschine A unter der IP-Adresse 192.168.1.1 initiiert eine [SYN] -Anforderung über die LB unter 192.168.1.10. Die LB überträgt dann die Nutzlast an Maschine B unter 192.168.1.2, sodass die Nutzlast jetzt die Quelle: 192.168.1.1 und das Ziel: 192.168.1.2 (früher 192.168.1.10) hat.
Was passiert nun, wenn 192.168.1.2 mit einem [SYN, ACK] antwortet? Was passieren sollte , ist, dass Maschine B über den Load Balancer auf Maschine A reagiert- In der Regel aufgrund einer Standardroute oder eines Standardgateways auf dem Server, das den Datenverkehr über die LB weiterleitet. In diesem Fall befindet sich der Computer jedoch im selben Subnetz, sodass die Route / das Gateway nicht verwendet wird und die Routing-Tabelle vom Server ignoriert wird. Dies bedeutet, dass, wenn der Server antwortet, die [SYN, ACK] für Maschine A von einer anderen IP als der IP stammt, mit der Maschine A die Anforderung initiiert hat - es wurde jedoch eine Quell-IP von 192.168.1.10 (LB) erwartet Es wird ein [SYN, ACK] von 192.168.1.2 (Maschine B) angezeigt, und daher kann die LB in diesem Szenario keine Verbindung mit Maschine B herstellen, da die Antwort an das falsche Gerät gesendet wurde.
Der Grund, warum dies für externen Datenverkehr funktioniert, liegt in Ihrer Standardroute - die Antworten an alle anderen werden über die ELB geleitet. Die ELB erkennt, dass sie eine Verbindung initiiert hat, fängt die Antwort automatisch ab und tauscht die Quelle von 192.168.1.2 zurück auf 192.168.1.10.
Für eine Lösung dieses Problems können Sie also einen einarmigen Lastausgleich implementieren (auch als Lastausgleich auf einem Stick bezeichnet). Dazu wird ein Quell-NAT auf der inneren Schnittstelle des Load Balancers verwendet (nehmen Sie also an, Sie hatten die äußere Schnittstelle 192.168.1.10 auf Ihrem Load Balancer und 192.168.1.11 auf der inneren Schnittstelle). Dies lässt den gesamten Datenverkehr aus Sicht von Maschine B aus 192.168.1.11 kommen, wodurch Ihr Verbindungsproblem gelöst werden sollte.
Es scheint jedoch, dass Ihre AWS ELB SNAT nicht unterstützt. Sie müssen daher entweder Ihre Hosts und ELB in verschiedenen Subnetzen platzieren oder SNATs wie die Virtual Edition von F5 verwenden, die stündlich oder BYOL erhältlich ist. Beachten Sie jedoch die Verbindungsbeschränkungen bei SNATing. Wenn Sie mehr als 30.000 gleichzeitige Verbindungen benötigen, wird der SNAT-Port erschöpft und Sie müssen einen SNAT-Pool verwenden. .
Daher besteht Ihre beste Lösung (aus Kostengründen und um zukünftige Probleme zu vermeiden) darin, sicherzustellen, dass sich Client und Server in unterschiedlichen Subnetzen befinden.
Die beste Möglichkeit zur Bestätigung besteht darin, tcpdump auf dem Verbindungshost und / oder dem Back-End-Server zu verwenden und nach Antworten zu suchen, die direkt zum / vom Back-End-Server kommen, anstatt den Load Balancer zu durchlaufen. Sie können dann Ihre Dump-Datei in WireShark laden, um genau herauszufinden, was los ist.
quelle