Einführung

Als ich unsere Finanzanwendung auf Tomcat entwickelte, lernte ich schnell, dass es keine gute Idee war, Tomcat direkt im öffentlichen Internet zu betreiben. Es funktionierte anfangs, war aber fragil, schwer abzusichern und fast unmöglich, während der Bereitstellungen reibungslos zu warten.

Schließlich entschied ich mich, Nginx vor Tomcat zu setzen und Nginx alles erledigen zu lassen, was Tomcat nicht gut kann, insbesondere SSL-Terminierung, Wartungsmodus, Caching von statischen Dateien und das Verbergen interner Infrastrukturdetails.

Dieser Artikel erklärt die genaue Konfiguration, die ich heute in der Produktion verwende. Es ist kein allgemeiner Leitfaden. Er ist speziell für das Finanzsystem geschrieben, das ich bereitgestellt habe, und enthält echte Konfigurationen sowie die Gründe für jede Entscheidung.

Warum ich Nginx vor Tomcat benötigte

1. Eine saubere Wartungsseite, wenn Tomcat neu gestartet wird

In einem Finanzsystem müssen wir Tomcat häufig während der Bereitstellungen oder Wartungsarbeiten neu starten. Wenn Tomcat nicht verfügbar ist, sollte der Benutzer niemals eine rohe Tomcat 503-Seite sehen, die Serverinformationen preisgibt.

Mit Nginx sehen die Benutzer immer eine freundliche Wartungsseite, selbst wenn Tomcat komplett offline ist. Nginx fängt die 502- oder 503-Antwort ab und liefert eine statische HTML-Seite, die ich vorbereitet habe.

2. SSL ist auf Nginx viel einfacher

Die Konfiguration von SSL innerhalb von Tomcat ist unnötig kompliziert. Sie müssen Zertifikate in einen Java-Keystore umwandeln, den Connector verwalten und mit mehreren Formaten umgehen.

Mit Nginx ist SSL einfach. Ich benötige nur zwei Dateien, ein Zertifikat und einen privaten Schlüssel. Nginx beendet HTTPS, und Tomcat erhält nur unverschlüsseltes HTTP von localhost. Auch die Erneuerung von Zertifikaten und das Debuggen sind einfacher.

3. Tomcat ist von der Außenwelt vollständig verborgen

Aus Sicherheitsgründen möchte ich nicht, dass Tomcat seine Ports oder Serverinformationen an öffentliche Benutzer preisgibt. Tomcat hört nur auf 127.0.0.1:8080.

Wenn jemand versucht, meinen Server zu scannen, kann er nicht einmal erkennen, dass Tomcat existiert.

4. Große Header für Finanzgenehmigungen

Einige Genehmigungsoperationen im System senden eine lange Liste von Finanzanwendungs-IDs im Header oder in der Abfragezeichenfolge.

Um diese großen Header zu unterstützen, habe ich Nginx so konfiguriert, dass es größere Header-Puffer zulässt. Ohne dies würden einige Anfragen fehlschlagen, da die Standardgröße der Header zu klein ist.

5. Große Datei-Uploads

Benutzer laden Verträge, Rechnungen und gescannte Dokumente hoch. Ich habe ein sauberes Upload-Größenlimit in Nginx festgelegt, sodass große Dateien Tomcat nicht erreichen, es sei denn, sie bestehen zuerst die Nginx-Validierung.

6. Schnellere statische Inhalte (CSS, JS, Icons)

Obwohl Tomcat statische Dateien bedienen kann, ist Nginx viel schneller. Ich habe das Caching für statische Assets aktiviert, sodass Nginx sie direkt bedient, ohne Tomcat zu belästigen.

7. Benutzerdefinierte Timeouts für lang laufende Operationen

Einige Prozesse in einem Finanzsystem dauern länger, wie z.B. die Erstellung von Berichten oder die Batchgenehmigung.

Nginx gibt mir die volle Kontrolle über Proxy-Timeouts, sodass Benutzer keine unerwarteten 504-Fehler erhalten.

Die Nginx HTTPS-Konfiguration, die ich verwende

Ich halte diese Datei unter:

/etc/nginx/sites-available/myapp_https

Hier ist die tatsächliche Konfiguration, vereinfacht für den Artikel, aber dennoch genau.

server {
    listen 443 ssl;
    server_name finance.example.com;

    ssl_certificate     /opt/myapp/keystore/app.crt;
    ssl_certificate_key /opt/myapp/keystore/app.key;

    ssl_session_cache shared:SSL:1m;
    ssl_session_timeout 5m;
    ssl_ciphers HIGH:!aNULL:!MD5;
    ssl_protocols TLSv1.2;
    ssl_prefer_server_ciphers on;

    large_client_header_buffers 4 65k;
    client_max_body_size 100m;

    error_page 502 503 = @maintenance;
    error_page 404 = @errors;

    access_log /opt/myapp/nginx/logs/access.log;
    error_log  /opt/myapp/nginx/logs/error.log;

    location / {
        proxy_set_header Host                $host;
        proxy_set_header X-Forwarded-Host    $host;
        proxy_set_header X-Forwarded-Server  $host;
        proxy_set_header X-Forwarded-For     $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto   https;

        proxy_pass http://127.0.0.1:8080;

        proxy_buffering off;
        proxy_buffer_size 128k;
        proxy_buffers 100 128k;

        proxy_connect_timeout 180;
        proxy_send_timeout    180;
        proxy_read_timeout    180;
        send_timeout          180;

        location ~* \.(css|js|jpg|gif|ico)$ {
            proxy_cache cache;
            proxy_cache_key   $host$uri$is_args$args;
            proxy_cache_valid 200 301 302 30m;
            expires 30m;
            proxy_pass http://127.0.0.1:8080;
        }
    }

    location = / {
        return 301 /myapp/;
    }

    location @maintenance {
        root /opt/myapp/nginx/www;
        rewrite ^ /maintenance.html break;
    }

    location @errors {
        root /opt/myapp/nginx/www;
        rewrite ^ /404.html break;
    }
}

Das ist der Kern des Systems. Nginx kümmert sich um SSL, leitet den Verkehr an Tomcat weiter, bedient die Wartungsseite, wenn Tomcat offline ist, cached statische Inhalte und schützt das interne Netzwerklayout.

HTTP-Weiterleitung (Port 80)

Ich habe eine einfache Konfiguration hinzugefügt, die gesamten HTTP-Verkehr zu HTTPS umleitet.

/etc/nginx/sites-available/myapp_http
server {
    listen 80;
    server_name finance.example.com;
    return 301 https://$host$request_uri;
}

Ordnerstruktur

Ich halte die Nginx-bezogenen Dateien in einem speziellen Anwendungsordner:

/opt/myapp/
    keystore/
    nginx/
        logs/
        www/

Das macht es einfach, Zertifikate, Protokolle und statische Seiten zu verwalten.

Tomcat-Konfiguration, die die Einrichtung vervollständigt

Innerhalb von Tomcats server.xml binde ich Tomcat an localhost, sodass er nicht von außen zugänglich ist.

<Connector port="8080"
           address="127.0.0.1"
           maxHttpHeaderSize="65536"
           protocol="HTTP/1.1"
           connectionTimeout="20000"
           maxThreads="150"/>

Ich lasse Tomcat auch die echte Client-IP und das Protokoll verstehen:

<Valve className="org.apache.catalina.valves.RemoteIpValve"
       remoteIpHeader="X-Forwarded-For"
       protocolHeader="X-Forwarded-Proto"
       protocolHeaderHttpsValue="https"/>

Das stellt sicher, dass das System die korrekte Client-IP protokolliert und sich korrekt für HTTPS verhält.

Warum diese Einrichtung gut für ein Finanzsystem funktioniert

Nginx vor Tomcat zu setzen, hat das gesamte System viel professioneller und stabiler gemacht. Während der Bereitstellungen oder Neustarts stoßen die Benutzer niemals auf die typischen Tomcat 503-Seiten. Stattdessen sehen sie immer einen sauberen Wartungsbildschirm, was das System auch dann zuverlässig erscheinen lässt, wenn im Hintergrund gearbeitet wird.

Die Handhabung von SSL auf Nginx vereinfacht ebenfalls alles. Zertifikate sind einfacher zu verwalten, zu erneuern und zu debuggen, und Tomcat muss sich nicht mehr direkt mit HTTPS befassen. Ein weiterer Vorteil ist die Sicherheit. Da Tomcat nur auf localhost hört, ist es vollständig vor externem Verkehr verborgen, was eine ganze Kategorie von Scan- und Probeversuchen eliminiert.

Die Leistung verbessert sich ebenfalls. Statische Dateien wie CSS und JavaScript laden schneller, da Nginx sie cachen kann, und Uploads werden überprüft und begrenzt, bevor sie Tomcat überhaupt erreichen. Einige unserer Finanzoperationen beinhalten große Header oder lang laufende Aktionen, und Nginx gibt mir die volle Kontrolle über Puffergrößen und Timeouts, was diese Operationen reibungslos ablaufen lässt, ohne zufällige Fehler.

Selbst in Fällen, in denen Tomcat für einen Moment offline geht, reagiert die Seite dank der Wartungsseite weiterhin vorhersehbar. Im Laufe der Jahre hat sich diese Einrichtung als äußerst stabil erwiesen. Sie hat der Finanzanwendung geholfen, nahezu null Ausfallzeiten über viele Releases und Infrastrukturänderungen hinweg zu erreichen, und sie bewältigt weiterhin den Produktionsverkehr ohne Überraschungen.