Teil Zwei meiner Beitragsreihe: Nginx – Varnish – Apache – WordPress.

Varnish als Cache und WordPress Beschleuniger

Das Varnish Projekt behauptet von sich selbst: „Varnish makes you websites fly„.
Diese Aussage kann ich bestätigen!

Meine Umsetzung und Erfahrungen möchte ich im Folgenden aufführen und damit sowohl die Aussage des Varnish Projektes, als auch meine eigene zu belegen.

Varnish ist ein Web-Beschleuniger / Reverse HTTP Caching Proxy für dynamische Webseiten und Content Management Systeme wie z.B. WordPress. Varnish kann somit als Caching Layer vor jeden anderen, HTTP sprechenden, Server installiert werden.
Nach Aussagen des Varnish-Cache-Projektes werden mit Varnish ausgelieferte Webseiten um den Faktor 300x – 1000x beschleunigt.

Um dieses Statement zu zu prüfen, oder vielleicht auch zu wiederlegen, habe ich eigene Testergebnisse weiter unten in diesem Artikel bereitgestellt.

Ein Vorteil, den Varnish gegenüber anderen Web-Beschleunigern besitzt, ist auf jeden Fall seine flexible, jedoch auch spezielle „VCL Sprache“. Die damit entstehenden VCL Skripte sind in „C“ kompilierte „Shared Objects“, die direkt in Varnish eingelinkt werden. Das ist sicherlich ein Grund für den Performance Gewinn, der durch den Einsatz von Varnish entsteht.
Die VCL Sprache ist recht einfach zu erlernen, so dass eine grundlegend-lauffähige Varnish-Installation simpel umzusetzen ist.
Wie immer erhöht die Komplexität der Aufgabe / Konfiguration den Lern- und Zeitaufwand.

Wie so oft ist es zu Beginn sinnvoll, sich über die Aktualität von eingesetzter Software Gedanken zu machen; das gilt auch für Varnish auf Ubuntu 14.04.
Ubuntu 14.04. liefert Varnish per se in einer Version 3.0.5-2 aus, auf der offiziellen Varnish Projektseite ist jedoch schon eine stabile Version 4.0.3-2 verfügbar. Hier gilt ebenso wie für meine Nginx Installation: bei einer Neuinstallation bitte das aktuellste stabile Paket benutzen, da einige mit Ubuntu 14.04 ausgelieferten Versionen oftmals veraltet sind.

Die beiden wichtigsten Unterschiede zwischen Varnish in der Version 3 und Varnish 4 sind aus meiner Sicht:

  1. Varnish 4 separiert Backend- und Client-Threads
  2. dadurch ergeben sich teilweise Anpassungen in der Varnish eigenen VCL Sprache

Der erste Punkt ist ein eindeutiger Schritt in Richtung Performance, der zweite Punkt sollte bei einem Update auf Varnish 4 dringend beachtet werden.
Beispiel:

  • mit Varnish 3.x muss der „sub vcl_recv“ Block mit „return (lookup)“ abgeschlossen werden
  • mit Varnish 4.x muss der „sub vcl_recv“ Block mit „return (hash)“ abgeschlossen werden

Installation von Varnish 4 auf Ubuntu 14.04 Trusty

Das Varnish Cache Repository bindet man wie folgt ein:

apt-get install apt-transport-https
curl https://repo.varnish-cache.org/GPG-key.txt | apt-key add -
echo "deb https://repo.varnish-cache.org/ubuntu/ precise varnish-4.0" >> /etc/apt/sources.list.d/varnish-cache.list
apt-get update
apt-get install varnish

Ehe man den Varnish Dienst nun startet, sind in der Datei /etc/default/varnish mindestens folgende Angaben zu machen:

  • Ports
  • Default Konfigurationsdatei
  • Größe des Caches im Speicher

gemacht werden. Also z.B. :

DAEMON_OPTS="-a localhost:6081 
-T localhost:6082 
-f /etc/varnish/default.vcl 
-S /etc/varnish/secret 
-s malloc,512m"

Nach einem /etc/init.d/varnish start oder service varnish start lauscht der Varnish Cache mit den o.g. Angaben auf Port 6081.

Varnish für WordPress konfigurieren

Beispielhaft habe ich meine WordPress-Varnish-Konfigurationsdatei auf GitHub bereitgestellt.

Abseits der dort eingebauten WordPress spezifischen Einstellungen sind folgende allgemeinen Konfigurationen sicherlich sinnvoll:

  • Angabe der VCL Sprachversion
  • Backendkonfiguration
  • Cache-Purging absichern

Das sieht dann wir folgt aus:

# Marker to tell the VCL compiler that this VCL has been adapted to the
# new 4.0 format.
vcl 4.0;
# Default backend definition. Set this to point to your content server.
backend default {
.host = "127.0.0.1";
.port = "8080";
}
acl Purge {
# For now, I'll only allow purges coming from localhost
"127.0.0.1";
"localhost";
"91.250.113.206";
}

Meine, auf GitHub bereitgestellte Varnish 4.0 Konfigurationsdatei „default.vcl„, erhebt keinerlei Anspruch darauf „die WordPress optimierte Varnish Konfigurationsdatei“ zu sein.
Nein, sie ist eher ein Extrakt aus vielen anderen, online verfügbaren, Varnish Konfigurationen für WordPress und der Gegenüberstellung dieser Informationen mit der offiziellen Varnish 4.0 Dokumentation.
Diese Varnish default.vcl ist nicht als final anzusehen, da die Optimierung von Varnish für mich ein sich evolutionierender Prozeß ist. Diesen monitore und visualisiere ich mit  Icinga2 Monitoring und Graphite, um Optimierungspotential mehr als nur punktuell aufzudecken.

Folgende  WordPress spezifischen Einstellungen möchte ich an dieser Stelle herausheben:

sub vcl_recv {
# Don't cache logged-in users or authors
if (req.http.Cookie ~ "wp-postpass_|wordpress_logged_in_|comment_author|PHPSESSID") {
return(pass);
}
# don't cache these special pages, e.g. urls with ?nocache or comments, login, regiser, signup, ajax, etc.
if (req.url ~ "nocache|wp-admin|wp-(comments-post|login|signup|activate|mail|cron).php|preview=true|admin-ajax.php|xmlrpc.php|bb-admin|server-status|control.php|bb-login.php|bb-reset-password.php|register.php") {
return(pass);
}
# Unset the header for static files and cache them
if (req.url ~ ".(css|flv|gif|htm|html|ico|jpeg|jpg|js|mp3|mp4|pdf|png|swf|xml|webp|txt)(?.*|)$") {
# Remove the query string
set req.url = regsub(req.url, "?.*$", "");
unset req.http.Cookie;
unset req.http.User-Agent;
unset req.http.Vary;
}
# Http header Cookie
# Remove some cookies (if found).
# Partially from https://www.varnish-cache.org/docs/4.0/users-guide/increasing-your-hitrate.html#cookies
if (req.http.Cookie && !(req.url ~ "wp-(login|admin)")) {
# 1. Append a semi-colon to the front of the cookie string.
set req.http.Cookie = ";" + req.http.Cookie;
# 2. Remove all spaces that appear after semi-colons.
set req.http.Cookie = regsuball(req.http.Cookie, "; +", ";");
# 3. Match the cookies we want to keep, adding the space we removed
# previously, back. (1) is first matching group in the regsuball.
set req.http.Cookie = regsuball(req.http.Cookie, ";(SESS[a-z0-9]+|NO_CACHE)=", "; 1=");
# 4. Remove all other cookies, identifying them by the fact that they have
# no space after the preceding semi-colon.
set req.http.Cookie = regsuball(req.http.Cookie, ";[^ ][^;]*", "");
# 5. Remove all spaces and semi-colons from the beginning and end of the
# cookie string.
set req.http.Cookie = regsuball(req.http.Cookie, "^[; ]+|[; ]+$", "");

# 6. Remove has_js and Google Analytics __* cookies.
set req.http.Cookie = regsuball(req.http.Cookie, "(^|;s*)(_[_a-z]+|has_js)=[^;]*", "");

# 7. Remove a ";" prefix, if present.
set req.http.Cookie = regsub(req.http.Cookie, "^;s*", "");

if (req.http.Cookie == "") {
# If there are no remaining cookies, remove the cookie header. If there
# aren't any cookie headers, Varnish's default behavior will be to cache
# the page.
unset req.http.Cookie;
}
else {
# If there are any cookies left (a session or NO_CACHE cookie), do not
# cache the page. Pass it on to Apache directly.
return (pass);
}
}

}

Zuerst teile ich Varnish mit, keine Requests für eingeloggte User, und auch keine WordPress spezifischen Backend Urls zu cachen.
Danach wage ich mich an das, nicht zu unterschätzende Thema:  „Cookies und Caching„.
Dabei gilt es „die harmlosen Request-Cookies“ und die von „der Anwendung benötigten Cookies“ zu differenzieren und herauszufiltern. Nur so kann der Webinhalt optimal gecacht werden, ohne die Funktionalität der Anwendung zu gefährden.
Google Tracking Cookies, oder „wp-test“ Cookies können sicherlich eliminiert werden, ebenso alle Session Cookies.
Auch soll Varnish Bilder in jeglicher Form cachen.

Alle meine WordPress Instanzen laufen mit dieser Konfiguration wirklich performant und stabil, wobei es sicherlich noch Verbesserungspotential gibt. Dieses Potential werde ich in den kommenden Wochen herausarbeiten und meine Erkenntnisse wieder hier teilen.

Varnish und WordPress: Erkenntnisse und Testergebnisse

Meine Erkenntnis: ja, Varnish bringt Webseiten zum fliegen!

Aber: die Komplexität des Setups wird durch die mangelnde HTTPS Unterstützung von Varnish nicht gerade einfacher.
Möchte man SSL verschlüsselte Webseiten  ausliefern, muss HTTPS vor dem Varnish – mit Nginx, Pound etc. –  terminiert werden. Damit z.B., WordPress (PHP) basierende Webseiten ausgeliefert werden können, muss ein Webserver hinter dem Varnish laufen. Somit müssen drei Layer bedient, konfiguriert und aufeinander abgestimmt werden.

Fakt ist: Varnish skaliert sehr gut, und nimmt dem dahinterliegenden Webserver sehr viel Last ab.
Ich habe, mit einem Apache-Performance-Test, versucht dies beispielhaft auf einer meiner WordPress-Installationen aufzuzeigen.

Der Apache-Performance-Test wurde, von einem meiner RootServer,  mit 50 Requests (-n 50) mit 1o konkurrierenden Usern (-c 10)  gegen meine 365-Tage-Fotoprojektseite (auf einem anderen RootServer) gestartet:

ab -n 50 -c 10 -k -H „Accept-Encoding: gzip, deflate“ -t 60 http://www.365-tage-fotoprojekte.de/365-tage-fotoprojekt/

Apache-Performance-Test ohne Varnish

Time taken for tests:60.114 seconds
Complete requests:325
Request per second:5.4 (mean)
Transfer rate:36.29 KBytes/sec
Connection times (median):1773 ms
Apache Server Load (average):6.60

Apache-Performance-Test mit Varnish

Time taken for tests:27.672 seconds
Complete requests:50000
Request per second:1806.90 (mean)
Transfer rate:12226.20 KBytes/sec
Connection times (median):27 ms
Apache Server Load (average):0.52

Sieht man sich die gegenübergestellten Zahlen an, so kommt man unweigerlich zu einem einzigen Schluß:

Yes, they can: Varnish makes you websites fly

Den Benefit, WordPress über Varnish auszuliefern, sieht man sehr schön in den Performance-Kurven, die Loadimpact schreibt. Loadimpact ist ein „On demand Lasttest Tool„. Das schöne an Loadimpact:  nach einer Registrierung kann das „Free pricing Modul“ genutzt werden. Die Möglichkeiten sind damit natürlich recht begrenzt, aber die Tendenzen in den Lastkurven ist sehr gut zu erkennen.

Man würde beim Einsatz von Varnish bei der Auslieferung von WordPress folgendes erwarten:

Anfangs eine höhere Auslieferungszeit (bei nicht vorgewärmtem Cache), die mit zunehmender Test-Dauer und ansteigender Useranzahl kleiner wird, um danach auf einem relativ konstanten minimalen Wert bis zum Ende des Lasttests bleibt.

Genau dieses Verhalten zeigt die folgende Lastkurve:

Dabei sind die Zugriffszeiten nicht repräsentativ (für  das deutsche Publikum), da die Loadimpact-Tests von weltweit verteilten Standorten ausgehen. Auch die Antwortzeiten kann man nicht direkt in Relation zu dem vorher durchgeführten Apache Performance Tests setzen: bei Loadimpact werden bei jedem Testdurchlauf 55 Url’s geladen, der Apache Performance Test lädt immer nur eine einzelne Url.