Hallo allerseits!
Heute möchten wir darüber sprechen, wie das Hotelreservierungsservice-Team von
Ostrovok.ru das Problem des Wachstums von Mikroservices gelöst hat, dessen Aufgabe es ist, Informationen mit unseren Lieferanten auszutauschen. Über seine Erfahrung berichtet der
unsterbliche DevOps-Teamleiter in Ostrovok.ru.
Der Microservice war zunächst klein und führte folgende Funktionen aus:
- eine Anfrage von einem lokalen Dienst annehmen;
- eine Anfrage an einen Partner stellen;
- normalisieren Sie die Antwort;
- Geben Sie das Ergebnis an den Abfragedienst zurück.
Mit der Zeit wuchs der Service jedoch zusammen mit der Anzahl der Partner und Anfragen an sie.
Als der Service wuchs, traten verschiedene Probleme auf. Verschiedene Lieferanten stellen ihre eigenen Regeln auf: Jemand begrenzt die maximale Anzahl von Verbindungen, jemand beschränkt Kunden auf weiße Listen.
Infolgedessen mussten wir die folgenden Probleme lösen:
- Es ist wünschenswert, mehrere feste externe IP-Adressen zu haben, damit Sie diese Partnern zum Hinzufügen zu weißen Listen zur Verfügung stellen können.
- haben einen einzigen Pool von Verbindungen zu allen Lieferanten, so dass bei der Skalierung unseres Microservices die Anzahl der Verbindungen minimal bleibt,
- Beenden Sie SSL und halten Sie
keepalive
an einem Ort, wodurch die Belastung der Partner selbst verringert wird.
Sie dachten lange nicht nach und fragten sich sofort, was sie wählen sollten: Nginx oder Haproxy.
Zuerst schwang das Pendel in Richtung Nginx, da ich mit seiner Hilfe die meisten Probleme im Zusammenhang mit HTTP / HTTPS löste und immer mit dem Ergebnis zufrieden war.
Das Schema war einfach: Es wurde eine Anfrage an unseren neuen Proxyserver auf Nginx mit einer Domäne der Form
<partner_tag>.domain.local
. In Nginx gab es eine
map
, in der
<partner_tag>
der Adresse des Partners entsprach. Eine Adresse wurde von
map
und
proxy_pass
wurde an diese Adresse
proxy_pass
.
Hier ist eine Beispielkarte, mit der wir die Domain analysieren und den Upstream aus der Liste auswählen:
Und so sieht "
snippet.d/upstreams_map
" aus:
“one” “http://one.domain.net”; “two” “https://two.domain.org”;
Hier haben wir
server{}
:
server { listen 80; location / { proxy_http_version 1.1; proxy_pass $upstream_address$request_uri; proxy_set_header Host $upstream_host; proxy_set_header X-Forwarded-For ""; proxy_set_header X-Forwarded-Port ""; proxy_set_header X-Forwarded-Proto ""; } }
Alles ist cool, alles funktioniert. Es ist möglich, diesen Artikel zu beenden, wenn nicht für eine Nuance.
Bei Verwendung von proxy_pass wird die Anforderung in der Regel unter Verwendung des HTTP / 1.0-Protokolls ohne
keepalive
direkt an die gewünschte Adresse gesendet und sofort nach Abschluss der Antwort geschlossen. Selbst wenn wir
proxy_http_version 1.1
nichts ohne Upstream (
proxy_http_version ).
Was zu tun ist? Der erste Gedanke ist, alle Lieferanten in den Upstream zu bringen, wobei der Server die Adresse des Lieferanten ist, den wir benötigen, und in der
map
"tag" "upstream_name"
behalten.
Fügen Sie eine weitere
map
um das Schema zu analysieren:
Und erstellen Sie
upstreams
mit Tag-Namen:
upstream one { keepalive 64; server one.domain.com; } upstream two { keepalive 64; server two.domain.net; }
Der Server selbst wurde geringfügig geändert, um das Schema zu berücksichtigen und anstelle der Adresse den Namen des Upstreams zu verwenden:
server { listen 80; location / { proxy_http_version 1.1; proxy_pass $upstream_scheme$upstream_prefix$request_uri; proxy_set_header Host $upstream_host; proxy_set_header X-Forwarded-For ""; proxy_set_header X-Forwarded-Port ""; proxy_set_header X-Forwarded-Proto ""; } }
Großartig. Die Lösung funktioniert, fügen Sie die
keepalive
Direktive in jedem Upstream hinzu, setzen Sie
proxy_http_version 1.1
, - jetzt haben wir einen Verbindungspool und alles funktioniert wie es sollte.
Dieses Mal können Sie den Artikel definitiv beenden und Tee trinken gehen. Oder nicht?
Während wir Tee trinken, kann einer der Lieferanten die IP-Adresse oder die Adressgruppe unter derselben Domain (hi, Amazon) ändern, wodurch einer der Lieferanten auf dem Höhepunkt unserer Teeparty abfällt.
Was tun? Nginx hat eine interessante Nuance: Während des Neuladens kann es die Server im
upstream
an neue Adressen nüchtern machen und Datenverkehr auf sie übertragen. Im Allgemeinen auch eine Lösung. Werfen Sie
cron reload nginx
alle 5 Minuten und trinken Sie weiterhin Tee.
Trotzdem schien es mir eine mittelmäßige Entscheidung zu sein, und so begann ich, Haproxy schief anzusehen.
Haproxy kann DNS-
dns resolvers
angeben und den
dns cache
konfigurieren. Daher aktualisiert Haproxy den
dns cache
wenn die darin enthaltenen Einträge abgelaufen sind, und ersetzt Adressen für Upstream, wenn sie sich geändert haben.
Großartig! Jetzt liegt es an den Einstellungen.
Hier ist ein kurzes Konfigurationsbeispiel für Haproxy:
frontend http bind *:80 http-request del-header X-Forwarded-For http-request del-header X-Forwarded-Port http-request del-header X-Forwarded-Proto capture request header Host len 32 capture request header Referer len 128 capture request header User-Agent len 128 acl host_present hdr(host) -m len gt 0 use_backend %[req.hdr(host),lower,field(1,'.')] if host_present default_backend default resolvers dns hold valid 1s timeout retry 100ms nameserver dns1 1.1.1.1:53 backend one http-request set-header Host one.domain.com server one--one.domain.com one.domain.com:80 resolvers dns check backend two http-request set-header Host two.domain.net server two--two.domain.net two.domain.net:443 resolvers dns check ssl verify none check-sni two.domain.net sni str(two.domain.net)
Diesmal scheint alles so zu funktionieren, wie es sollte. Das einzige, was ich an Haproxy nicht mag, ist die Komplexität der Konfigurationsbeschreibung. Sie müssen viel Text erstellen, um einen vorgelagerten Text hinzuzufügen. Aber Faulheit ist der Motor des Fortschritts: Wenn Sie nicht dasselbe schreiben möchten, schreiben Sie einen Generator.
Ich hatte bereits eine Karte von Nginx mit dem Format
"tag" "upstream"
, daher habe ich beschlossen, sie als Grundlage zu verwenden, ein Haproxy-Backend zu analysieren und basierend auf diesen Werten zu generieren.
Jetzt müssen wir nur noch einen neuen Host in nginx_map hinzufügen, den Generator starten und die fertige Haproxy-Konfiguration erhalten.
Das ist wahrscheinlich alles für heute. Dieser Artikel bezieht sich eher auf den einleitenden Artikel und widmete sich dem Problem der Auswahl einer Lösung und ihrer Integration in die aktuelle Umgebung.
Im nächsten Artikel werde ich ausführlicher darauf eingehen, auf welche Fallstricke wir bei der Verwendung von Haproxy gestoßen sind, welche Metriken sich für die Überwachung als nützlich erwiesen haben und welche genau im System optimiert werden sollten, um die maximale Leistung der Server zu erzielen.
Vielen Dank für Ihre Aufmerksamkeit, wir sehen uns!