Nginx stellt Inhalte vom falschen Proxy bereit

Nginx stellt Inhalte vom falschen Proxy bereit

Ich habe einen einzelnen Server, auf dem mehrere Rails-Sites gehostet werden. Alle Sites werden durch eindeutige Hostnamen identifiziert (alle Domänennamen werden derselben IP-Adresse zugeordnet). Sowohl Rails als auch nginx laufen in Docker-Containern. Ich verwende nginx 1.23.1, das in einem Docker-Image ausgeführt wird, das aus dem offiziellen Docker-Image erstellt wurde (ich habe nur certbot für die TLS-Zertifikatsverarbeitung hinzugefügt).

Nachdem ich vor Kurzem eine weitere Site hinzugefügt hatte, passierte etwas sehr Merkwürdiges. Direkt nach dem Start von nginx funktionierte alles wie erwartet: Der gesamte zurückgegebene Inhalt stimmt mit dem Hostnamen in der Anfrage überein. Doch nach ein paar Stunden stimmt der zurückgegebene Inhalt nicht mit dem angeforderten Hostnamen überein. Dies betrifft jedoch nur den Proxy-Inhalt; alle statischen Ressourcen werden basierend auf dem Hostnamen weiterhin korrekt bereitgestellt.

Wenn ich zum Beispiel anforderehttps://www.meaaa.com(alle Domänen hier sind Beispiele, keine echten Domänen), erhalte ich den HTML-Inhalt von bbb.me.com. Und da der Inhalt von bbb.me.com nach Stilen und Bildern fragt, die er bei bbb.me.com zu finden erwartet, antwortet der Server auf alle diese Anfragen mit 404 (weil statische Assets von derwww.meaaa.comDateien, da der Hostname der Anfragewww.meaaa.com).

Und wenn ich darum bittehttps://bbb.me.com, erhalte ich den HTML-Inhalt vonwww.meaaa.com. Auch hier wird erwartet, dass die im Markup angegebenen Vermögenswerte auswww.meaaa.com, aber da statische Assets entsprechend dem Hostnamen bbb.me.com in der Anfrage korrekt abgerufen werden, werden sie nicht gefunden.

Daher scheint der Upstream-Rails-Inhalt der beiden Sites die Plätze getauscht zu haben, während die statischen Assets korrekt bereitgestellt werden.

Ich verwende seit Jahren nicht-Docker-nginx mit mehreren Rails-Sites und habe das noch nie erlebt. Es geht nicht darum, einen undefinierten Host anzufordern; beide Hosts sind in der Konfiguration deklariert. Wenn ein Host nicht mehr erkannt wird, könnte ich davon ausgehen, dass der zurückgegebene Inhalt nur der Standardserver ist, aber tatsächlich werden beide erkannt, nur vertauscht. Die Tatsache, dass nur der Proxy-Inhalt und nicht die statischen Assets umgeschaltet werden, zeigt, dass beide Hostnamen erkannt werden.

Um die Symptome zusammenzufassen, zeigt curl Folgendes:

$ curl -s https://www.meaaa.com | grep '<title>'
  <title>BBB Site</title>

$ curl -s https://bbb.me.com | grep '<title>'
  <title>MEAAA Site</title>

Anfordernwww.meaaa.comstatische Assets einzeln über diewww.meaaa.comDer Hostname funktioniert einwandfrei, ebenso wie das Anfordern von bbb.me.com-Assets von bbb.me.com.

Ich habe auch sichergestellt, dass das Problem nicht bei Docker liegt. Innerhalb des Nginx-Containers kann ich jedes Backend curlen und den richtigen Inhalt erhalten:

$ curl -s http://aaa:3000 | grep '<title>'
  <title>MEAAA Site</title>

$ curl -s http://bbb:3000 | grep '<title>'
  <title>BBB Site</title>

Hier ist die Konfiguration für diewww.meaaa.comWebsite:

upstream aaa-rails {
    server aaa:3000;
}

server {
    server_name www.meaaa.com source.meaaa.com aaa.meinternal.com;
    root /var/www/aaa-rails/public;
    index index.html index.htm;

    location /cms {
      deny 172.22.188.2; # public network interface
      try_files $uri @app;
    }
    location / {
        try_files $uri/index.html $uri @app;
    }

    location @app {
        proxy_pass http://aaa-rails;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $http_host;
        proxy_set_header Origin $scheme://$http_host;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Forwarded-Port $server_port;
        proxy_set_header X-Forwarded-Host $host;
        proxy_redirect off;
    }

    location ~* ~/assets/ {
        try_files $uri @app;
    }

    listen 80;

    listen 443 ssl; # managed by Certbot
    ssl_certificate /etc/letsencrypt/live/www.meaaa.com/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/www.meaaa.com/privkey.pem; # managed by Certbot
    include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
}

Und hier ist die Konfiguration für die Site bbb.me.com:

upstream bbb-rails {
    server bbb:3000;
}

server {
    server_name bbb.me.com bbb-source.me.com bbb.meinternal.com;
    root /var/www/bbb-rails/public;
    index index.html index.htm;

    client_max_body_size 50m;

    location / {
        try_files $uri @app;
    }

    location @app {
        proxy_pass http://bbb-rails;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $http_host;
        proxy_set_header Origin $scheme://$http_host;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Forwarded-Port $server_port;
        proxy_set_header X-Forwarded-Host $host;
        proxy_redirect off;
    }

    location ~* ~/assets/ {
        try_files $uri @app;
    }

    listen 80;

    listen 443 ssl; # managed by Certbot
    ssl_certificate /etc/letsencrypt/live/bbb.me.com/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/bbb.me.com/privkey.pem; # managed by Certbot
    include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
}

Das Seltsamste für mich ist, dass ein Neustart von nginx das Problem behebt, aber nur vorübergehend. Ich glaube nicht, dass Caching stattfindet, und ich sehe keine Fehler in den nginx-Protokollen. Ich wäre für alle Vorschläge, wonach ich suchen sollte, sehr dankbar.

AKTUALISIEREN

Die beiden Sites, die die Plätze gewechselt haben, waren diejenigen, die HTTPS verwendeten. Ich habe mehrere andere so geändert, dass sie HTTPS verwenden, und jetzt sind drei davon in einer Art Round-Robin-Verfahren falsch: aaa anfordern, bbb erhalten; bbb anfordern, ccc erhalten; ccc anfordern, aaa erhalten. Zwei andere Sites reagieren einfach nicht. Es ist, als ob ein unvorhersehbares Ereignis dazu führt, dass nginx die Routing-Tabellen beschädigt, die es zum Bereitstellen von Proxy-Inhalten verwendet.

Da es sich derzeit um einen Produktionsserver handelt, starte ich nginx alle 60 Minuten neu. Ich versuche, einen Staging-Server als Duplikat des Produktionsservers einzurichten, in der Hoffnung, dass dort dasselbe Problem auftritt, sodass ich versuchen kann, das Problem zu lösen, ohne die Websites zum Absturz zu bringen.

Antwort1

Es stellte sich heraus, dass das Problem darauf zurückzuführen war, dass nginx jeden Backend-Namen nur einmal auflöst (beim Laden der Konfiguration) und danach davon ausgeht, dass sich die IP-Adresse nie ändern wird. Vielen Dank an Ángel von der nginx-Mailingliste, der darauf hingewiesen hat.

Da es sich bei jedem Back-End um eine Webanwendung handelt, muss ich in meinem Fall „logrotate“ täglich ausführen, damit die Protokolldateien rotiert und komprimiert werden und die entsprechende Rails-App neu gestartet wird, wenn die Protokolle groß genug werden.

Es kann leicht passieren, dass zwei oder mehr Rails-Apps mehr oder weniger gleichzeitig neu gestartet werden, aber es ist nicht garantiert, dass sie in genau der richtigen Reihenfolge neu gestartet werden, damit sie ihre vorherige IP-Adresse im internen Docker-Netzwerk zurückerhalten. Das erklärt, was passierte: Hin und wieder tauschten Rails-Apps IP-Adressen aus, aber nginx wusste nichts davon und leitete daher weiterhin Anfragen an die alten IP-Adressen weiter und stellte gleichzeitig statische Assets korrekt bereit. Ich simulierte dies, indem ich zwei Anwendungen stoppte und neu startete, und konnte das Problem genau reproduzieren. Außerdem hatte ich ein Skript hinzugefügt, das die Sites alle fünf Minuten überwachte, und die Protokolle zeigten, dass das Problem anscheinend immer ungefähr zur gleichen Zeit auftrat, was damit übereinstimmte, dass logrotate der Auslöser war.

Ángel wies darauf hin, dass man nginx nicht neu starten, sondern einfach die Konfiguration neu laden lassen kann, was für die Besucher der Websites weniger Unterbrechungen bedeuten sollte. Er wies auch darauf hin, dass man nginx zwingen kann, alle paar Minuten nach Namen zu suchen, anstatt nur einmal (siehehttps://forum.nginx.org/read.php?2,215830,215832#msg-215832).

Um das Problem zu lösen, habe ich Docker zumindest vorerst so konfiguriert, dass allen Containern eine feste IP-Adresse zugewiesen wird. Auf diese Weise kann nginx weiterhin Namen nur einmal auflösen. Dies hat jedoch einige Konsequenzen, da ich jetzt Befehle wie db:migrate oder asset:precompile nicht mit derselben Datei docker-compose.yml wie die laufende Rails-App ausführen kann (Sie erhalten die Fehlermeldung „Adresse wird verwendet“). Vorerst verwende ich docker compose execanstelle von run, aber dies scheint Auswirkungen auf den docker compose restartBefehl zu haben (Sie erhalten nach dem Ausführen von einige Sekunden lang die Meldung „Adresse wird verwendet“ exec). Wenn dies zu einem Problem wird, kehre ich möglicherweise zu nicht festen IP-Adressen zurück und lasse nginx seine Konfiguration als Teil des Logrotate-Prozesses neu laden.

verwandte Informationen