Webserver-Kombination mit Nginx Varnish und Apache unter SLES 11 SP2

Admin

=Webserver-Kombination mit Nginx Varnish und Apache2=

Wir möchten die Geschwindigkeit des Apache2 Servers erhöhen und absichern, in dem wir Varnish und einen Nginx-Server davor schalten.

=Vorbereitung=

Da unter SLES 11 SP2 keine Pakete für Nginx und Varnish standardmäßig bereit stehen, müssen wir diese aus einen zusätzlichen Repository holen.

http://ftp5.gwdg.de/pub/opensuse/repositories/server:/http/SLE_11_SP2/x86_64/

Zudem benötigen wir zusätzlich das Apache Modul mod_rpaf (reverse proxy add forward). Das Apache Modul mod_rpaf schafft Abhilfe wenn ein Reverse Proxy/Web Beschleuniger wie Varnish oder Squid zum Einsatz kommen und dadurch die falsche Client IP Adresse in den Apache Logs aufscheint. Da der Reverse Proxy in der Regel vor den Apache Webserver geschalten wird, scheinen ohne diesem Modul alle Zugriffe mit der IP des Proxyservers auf. Dies ist vorallem auch für IP-basierte HTTP Authentisierungen (z.B. "Allow from 192.168.1.1") ein Problem, da diese dann nicht mehr korrekt funktionieren.

Der Reverse Proxy setzt in der Regel den Header "X-Forwarded-For" mit der IP Adresse des tatsächlichen Clients. Das Modul mod_rpaf übernimmt die IP des Headers und setzt diese als tatsächliche Client IP für den Apache Request. Überprüft wird dabei, ob der Apache Request auch tatsächlich von der IP des Proxies kommt.

Dadurch funktioniert einerseits die IP-basierte HTTP Authentication, anderseits enthalten die Apache Logs wieder die richtigen IP Adressen. Man sollte sich jedoch bewusst sein, dass die Apache Logs nur mehr einen kleinen Teil aller Zugriffe enthalten. Die meisten Zugriffe werden in der Regel ja durch den Reverse Proxy/HTTP Accelerator abgefangen.

ftp://ftp.pbone.net/mirror/ftp5.gwdg.de/pub/opensuse/repositories/home:/litewhatever/SLE_10/x86_64/

=Varnish=

Installation Varnish
Nun können wir Varnish mit den abhängigen Paketen installieren.

yast -i varnish

Konfiguration Varnish
Hierzu bearbeiten wir folgende Konfigurationsdatei.

/etc/varnish/vcl.conf

Als erstes definieren wir ein Backend.

backend default { .host = "localhost"; .port = "8000"; }

Als nächstes definieren wir eine Liste mit den erlaubten Hosts.

acl purge { "localhost"; }

Dann stellen wir die vcl_recv Methode ein, die wird aufgerufen, wenn eine Anfrage empfangen wurde.

sub vcl_recv { set req.grace = 6h; if (req.request == "PURGE") { if(!client.ip ~ purge) { error 405 "Not allowed."; }  #     purge("req.url ~ ^" req.url "$ && req.http.host == "req.http.host); }   if (req.url ~ "\.(jpg|png|gif|gz|tgz|bz2|lzma|tbz)(\?.*|)$") { remove req.http.Accept-Encoding; } elsif (req.http.Accept-Encoding ~ "gzip") { set req.http.Accept-Encoding = "gzip"; } elsif (req.http.Accept-Encoding ~ "deflate") { set req.http.Accept-Encoding = "deflate"; } else { remove req.http.Accept-Encoding; }   if (req.url ~ "wp-(login|admin)") { return (pass); }   if (req.request != "GET" && req.request != "HEAD") { return (pass); }   unset req.http.cookie; if (req.url ~ "\.(jpeg|jpg|png|gif|ico|swf|js|css|txt|gz|zip|rar|bz2|tgz|tbz|html|htm|pdf|pls|torrent)(\?.*|)$") { unset req.http.Authenticate; unset req.http.POSTDATA; set req.request = "GET"; set req.url = regsub(req.url, "\?.*$", ""); } }


 * Wir haben req.grace auf 6 Stunden gesetzt, das bedeutet, dass wenn der Cache ausläuft und das Backend von Varnish ist nicht erreichbar, wird der Kopie des Caches bis 6 Stunden genutzt. das erste if Statement prüft, ob die Anfrage vom Typ PURGE ist. Wenn es der Fall ist schaut es ob der Anfrager in der Zugriffsliste ist. Ist dieser in der Liste, säubert er die Anfrageseite. Mehr später dazu.
 * Die nächste if/else/else Anweisung behandelt die Kodierung.
 * Als nächstes schauen wir ob die URL wp-login oder wp-admin beinhaltet, ist das der Fall sagen wir Varnish das wir in das backend gehen sollen und die Funktion vcl_recv verlassen soll.
 * Dann prüfen wir das die Anfrage auch nicht vom Typ GET oder HEAD ist, wenn das auch nicht der Fall ist, geht es zum Backend und wir verlassen vcl_recv.
 * Als nächstes löschen wir alle Cookies, was für nötig ist, da Varnish keinen Content cachen kann, wenn Cookies existieren.
 * Die letzte If-Anweisung prüft ob die URL eine Erweiterung für statischen Inhalt hat. Dann werden alle HTTP Auth und POST Daten entfernt, setzt die Anfrage auf den Typ GET und entfernt alle QUERY_STRING-Inhalte aus der URL, wenn es ein statischer Inhalt ist.

Als nächstes kommt vcl_pipe und vcl_pass.

sub vcl_pipe { set bereq.http.connection = "close"; if (req.http.X-Forwarded-For) { set bereq.http.X-Forwarded-For = req.http.X-Forwarded-For; } else { set bereq.http.X-Forwarded-For = regsub(client.ip, ":.*", ""); } }

sub vcl_pass { set bereq.http.connection = "close"; if (req.http.X-Forwarded-For) { set bereq.http.X-Forwarded-For = req.http.X-Forwarded-For; } else { set bereq.http.X-Forwarded-For = regsub(client.ip, ":.*", ""); } }

Diese Methoden sind identisch und lassen einfach unsere X-Forwarded-For headers durch, welches in Nginx und Apache zum richtigen loggen von IP-Adressen in den access Logs verwendet wird.

sub vcl_fetch { set beresp.ttl = 1h; set req.grace = 6h; # if (req.url ~ "wp-(login|admin)") { #   return (pass); # }    unset beresp.http.set-cookie; if (req.url ~ "\.(jpeg|jpg|png|gif|ico|swf|js|css|txt|gz|zip|rar|bz2|tgz|tbz|html|htm|pdf|pls|torrent)$") { set beresp.ttl = 24h; } }

Diese Methode liefert Inhalt, welcher an Varnish zurückgeliefert wird, an Nginx zurück.


 * Als erstes setzen wir die TTL des Caches auf 1 Stunde.
 * Wir setzen wiederum die Frist wie in vcl_recv
 * Wieder schauen wir ob die URL wp-login oder wp-admin beinhaltet, ist das der Fall sagen wir Varnish das wir in das backend gehen sollen und die Funktion vcl_recv verlassen soll.
 * Dann entfernen wir die Set-Cookie Header
 * und schließlich setzen wir die TTL auf 24 Stunden vom Cache sofern wir in der URL eine Erweiterung für statischen Inhalt finden.

Und Last but not Least die vcl_deliver, welche einfach einige X-Cache Header Informationen zum Debuggen hinzufügt.

sub vcl_deliver { if (obj.hits > 0) { set resp.http.X-Cache = "HIT"; set resp.http.X-Cache-Hits = obj.hits; } else { set resp.http.X-Cache = "MISS"; } }

Varnish ist nun konfiguriert.

=Nginx=

Installation Nginx
Nun können wir Nginx mit den abhängigen Paketen installieren.

yast -i nginx

Konfiguration Nginx
Wir erstellen für den Proxyteil die Datei /etc/nginx/proxy_params mit folgendem Inhalt.

proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; client_max_body_size 100M; client_body_buffer_size 1m; proxy_intercept_errors on; proxy_buffering on; proxy_buffer_size 128k; proxy_buffers 256 16k; proxy_busy_buffers_size 256k; proxy_temp_file_write_size 256k; proxy_max_temp_file_size 0; proxy_read_timeout 300;

Danach benötigen wir noch eine Konfigurationsdatei für unseren Server, in diesem Fall testserver.conf.

upstream varnish { server localhost:6081; server localhost:8000 backup; } server { listen      134.76.28.192:80 default; server_name vm45.mpdl.mpg.de; location / { #proxy_pass varnish; proxy_pass http://localhost:8000; proxy_set_header Host $http_host; proxy_set_header X-Forwarded-Host $http_host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header HTTPS ""; proxy_hide_header X-Varnish; proxy_hide_header Via; } }

Wenn wir zusätzlich noch für HTTPS den Server konfigurieren möchte, fügen wir folgendes noch hinzu in die Konfiguration.

server { listen      192.168.178.28:443; server_name www.myshop.com; ssl                 on; ssl_certificate     server.crt; ssl_certificate_key server.key; ssl_session_timeout 10m; ssl_protocols       SSLv3 TLSv1; ssl_ciphers         ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM; ssl_prefer_server_ciphers  on; location / { proxy_pass varnish; proxy_set_header Host $http_host; proxy_set_header X-Forwarded-Host $http_host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header HTTPS "on"; proxy_hide_header X-Varnish; proxy_hide_header Via; } }

Eine weitere Konfiguration könnte so aussehen.

server { listen 80; server_name example.com; access_log /var/log/nginx/access.example.com.log; gzip on; gzip_disable msie6; gzip_static on; gzip_comp_level 9; gzip_proxied any; gzip_types text/plain text/css application/x-javascript text/xml application/xml application/xml+rss text/javascript; location / { proxy_redirect off; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_pass_header Set-Cookie; proxy_pass http://localhost:6081; } }

Eine kurze Beschreibung zu diesere Konfiguration.


 * Auf welchem Port soll der Server horchen und wie ist die Domain
 * gzip aktiviert gzip
 * gzip_disable sagt Nginx das er keine gzip Kopression für IE6 nehmen soll
 * gzip_static ist aktiv um statischen Inhalt zu komprimieren (jpeg, gif etc)
 * gzip_comp_level bestimmt das Kompressionslevel 1-9 (höher = mehr komprimiert)
 * gzip_proxied ist gesetzt, um jeglichen durchgeleiteten Inhalt zu komprimieren.
 * und schließlich setzen wir dieArten von Dateien dievon gzip erfasst werden sollen.
 * Als nächsten setzen wir unser Standort
 * deaktivieren Proxy-Weiterleitungen
 * setzen Host, X-Real-IP und X-Forwarded-For Headers
 * setzen die Set-Cookie Header zurück
 * und leiten die Verbindung um auf Varnish.

=Apache2=

Konfiguration Apache2
Da nun der Nginx Server auf Port 80 lauschen soll, muss der Apache2 Server auf einem anderen Port läuschen, da sonst beide Webserver sich im Wege stehen. Wir ändern den Port in diesem Fall auf Port 8000.

Öffne /etc/apache2/listen.conf und ändere folgende Stelle und speichere die Datei.

Listen 80 --> Listen 8000

Zudem müssen alle Vhost-Konfigurationen angepast werden

NameVirtualHost *.8000 VirtualHost *:8000

=Los gehts=

rcapache2 restart && rcvarnish restart && rcnginx restart