Es war mal wieder soweit, der Amazon EC2 Server von Rätsel-Contest und Krupion war auch mit den aktuell installierten Caching Systemen in Drupal und Boost an seine Grenzen gestoßen. Grund dafür ist natürlich der ständig zunehmende Load und das mitlerweile doch recht hohe Benutzeraufkommen. Da reichte das einfach Caching auf DB Basis nicht mehr aus. Auch wenn die Inhalte mit Boost statisch aus dem Dateisystem geliefert werden, wird dazu immer noch der Apache benötigt. Das erzeugt natürlich deutlich überflüssige Lasten. Sinnvoller wäre es doch, statische Files zu cachen und diese ohne Apache, z.B. über einen deutlich schnelleren Server auszuliefern, ohne dass der Apache aktiv werden muss. Die Lösung dafür schien mir Varnish als Referse-Proxy zu sein. Varnish in Kombination mit der Drupal-Distribution Pressflow schienen mir die perfekte Lösung. Da Pressflow 100% Kompatibel mit Drupal ist, konnte das laufende Portal einfach mit Varnish "überschrieben" werden. Und auch ohne Varnish zuvor installiert zu haben lief die Seite einfach weiter. Laut Pressflow sollte auch das schon deutlich die Performance verbessern, da diese Distribution für PHP5 und MySQL optimiert ist.
WICHTIG: Bevor Sie damit beginnen, sollten Sie sich bewusst sein, dass Sie alle Seiten über Varnish laufen lassen müssen, wenn Sie dieses auf dem Server installieren, da Varnish auf Port 80 listen muss und somit das Listen auf Port 80 des Apache nicht mehr möglich ist. Dieser Dient dann nur noch als Backend, dazu aber später mehr.
Nun aber zum Wesentlichen. Nachdem wir Drupal mit der aktuellen Pressflowinstallation geupdated haben fügen wir in die settings.php folgenden Code ein:
$conf = array(
'reverse_proxy' => TRUE,
'reverse_proxy_addresses' => array(
'127.0.0.1', // Reverse proxy host A, es kann mehrere geben
),
);
Dann installieren wir Varnish auf unserem Debian Lenny Server.
apt-get install varnish
installiert uns die aktuelle Distribution. ACHTUNG: Sollten Sie eine alte Debian Etch Version einsetzen, sollten Sie Varnish manuell herunterladen und kompilieren, da diese sonst nicht aktuell ist.
Anschließend müssen zwei Dateien bearbeitet werden:
nano /etc/default/varnish
in dieser Datei habe ich lediglich folgenden Abschnitt eingestellt:
DAEMON_OPTS="-a :80 \
-T localhost:6082 \
-f /etc/varnish/default.vcl \
-S /etc/varnish/secret \
-p thread_pools=4 \
-p thread_pool_max=1500 \
-p listen_depth=2048 \
-p lru_interval=1800 \
-h classic,169313 \
-p connect_timeout=600 \
-p max_restarts=6 \
-s malloc,1G"
Das bedeutet folgendes: varnish listened auf Port 80 (daher wird der Apache gleich noch auf einen anderen Port 8080 geleget), -T gibt den Port des Controling-Interfaces an, -S die Konfigurationsdatei (bearbeiten wir gleich noch), -p connect_timeout setzt das Timeout für Connections höher um 503 Errors zu vermeiden bei hoher Last.
Varnish kann den Cache im Dateisystem aufbauen oder im Speicher. Im Speicher ist natürlich deutlich schneller daher entscheiden wir uns für diese Variante. Wir wollen aber auch, dass sich Varnish nicht mit Speicher "totfrisst" und anderen Anwendungen kein RAM mehr zur Verfügung steht, oder es eine Endlos-SWAP-Schleife gibt die den Server in die Knie zwingt. Daher begrenzen wir den maximal zur Verfügung stehenden Speicher für Varnish auf 1GB mit -s malloc,1G
Nun bearbeiten wir die Datei
nano /etc/varnish/default.vcl
wir legen zunächst das Defauult Backend fest. Dorthin leitet Varnish die Anfragen weiter, wenn diese nicht durch den Cache beantwortet werden können. Das Backend kann natürlich auch auf einem anderen Server liegen, das ist bei uns aber (noch) nicht der Fall.
backend default {
.host = "127.0.0.1";
.port = "8080"; //Apache muss auf diesem Port Listen!
.connect_timeout = 600s;
.first_byte_timeout = 600s;
.between_bytes_timeout = 600s;
}
Nun definieren wir den Requesthandler:
sub vcl_recv {
#has_js und Google Analytic entfernen
set req.http.Cookie = regsuball(req.http.Cookie, "(^|;\s*)(__[a-z]+|has_js)=[^;]*", "");
#";" prefix entfernen wenn vorhanden
set req.http.Cookie = regsub(req.http.Cookie, "^;\s*", "");
#Leere Cookies entfernen
if (req.http.Cookie ~ "^\s*$") {
unset req.http.Cookie;
}
#domainspezifische Einstellungen
if (req.url ~ "^/sites/SITE1.de/*" || req.url ~ "^/sites/SITE2.de/*" || req.url ~ "^/sites/SEITE3.de/*" ) {
unset req.http.Cookie;
}
#Them JS und CSS von Drupal Cachen
if (req.url ~ "^/modules/.*\.(js|css)\?") {
unset req.http.Cookie;
}
#beliebige weitere URLs explizit Cachen
if (req.url ~ "^/BLA/BLUB/FOO*")
{
unset req.http.Cookie;
}
#Andere URLs explizit NICHT CACHEN!
#Pass Cronjobs and server-status
if (req.url ~ "cron.php") {
return (pass);
}
if (req.url ~ ".*/server-status$") {
return (pass);
}
}
sub vcl_hash { if (req.http.Cookie) { set req.hash += req.http.Cookie; } }
Das wars. Wie man sieht, lässt sich mit der Sprache VCL alles bis ins kleinste Detail steuern. Mit
return (pass);
lässt Varnish den Request zum Backend durch. Mit
unset req.http.Cookie;
wird gecached.
Das waren alle Einstellungen, die wir für eine Pressflow Drupal-Spezifische Varnish Konfiguration vornehmen müssen.
Nun loggen wir uns in Drupal als Admin ein und stellen den Cache passend ein. Pressflow bietet uns dazu eine neue Cacheoption an - ("Extern") - unter /admin/settings/performance. Wir setzen also den Radiobutton auf "Extern" und Drupal weiß, dass durch eine Reverseproxy gecached wird.
Sobald wir Varnish neu starten, versucht es auf Port 80 zu listen um die Browseranfragen noch vor dem Apache abzubekommen. Das macht Sinn. Allerdings darf der Apache nun nicht mehr auf Port 80 listen sondern, wie in unserem Beispiel, auf Port 8080.
Also legen wir die Ports all unserer vhosts auf 8080 und starten den Apache neu.
/etc/init.d/apache2 restart
Nun starten wir Varnish neu
/etc/init.d/varnish restart
und prüfen mit
ps ax | grep varnish
ob Varnish korrekt läuft. Sollte das nicht so sein, schauen wir in der /var/log/syslog nach, ob ein Fehler aufgetreten ist. Sollte das nicht der Fall sein, ist auch dort die erfolgreiche Startmeldung von Varnish zu sehen.
Nun sollte schon alles funktionieren. Ob es auch wiklich klappt, kann man entweder im Header der Browseranfrage (bzw. der Serverantwort "via Varnish") sehen oder mit folgenden Befehlen:
varnishtop -b -i TxURL
zeigt alle Requests, die zum Backend durchgelassen wurden.
varnishhist
zeigt ein Histogramm an, "|" bedeutet hit, also durch Varnish-Cache beantwortet, "#" bedeutet nicht gecached, also zum Drupal-Backend weitergeleitet.
Um nun auch noch die Backendanfragen deutlich performanter abarbeiten zu können, sollte man noch APC installieren.
Das geht auf eine Debian System mit
pecl install apc
Das war schon von Debian Seite aus. Damit APC verwendet wird, muss in der settings.php nun folgender Code ergänzt werden (nach der Ergänzung die ich oben beschrieben habe)
$conf['cache_inc'] = './sites/all/modules/authcache/authcache.inc'; //wenn man noch authcache verwendet $conf['cacherouter'] = array( 'default' => array( 'engine' => 'apc', 'server' => array(), 'shared' => TRUE, 'prefix' => 'DOMAIN-de', //eigentlich nur wichtig bei Multisite installationen 'path' => 'sites/default/files/filecache', //wenn man Filecache statt RAM Cache verwenden wiill 'static' => FALSE, 'fast_cache' => TRUE, ), 'cache_form' => array( 'engine' => 'db', //WICHTIG: sonst funktionieren die AJAX Formulare nicht bekannter Fehler "...coult not be found in form-cache..." ), );
Am besten nochmal den Apache neustarten (siehe oben) und schon sind wir fertig.
Das Ergebnis:
Deutlich bessere Performance (natürlich auch in Abhängigkeit der Cacheeinstellungen). Die Seiten werden nun locker mit 10 facher Geschwindigkeit ausgeliefert und der Server Load ist um das 10 Fache reduziert. Wie das geht? Ja, wir hatten teilweise Serverloads von über 30, das hat sich nun dank Varnish und Pressflow Drupal auf 1-3 eingependelt.
Die Seite ist so schnell, dass es uns sogar die User danken:
Bei Fragen, gerne per Email oder über die Kommentarfunktion melden.
P.S. ich bin immer noch sehr begeistert über die neue Performance!










apt-get varnish
sollte es nicht eher
apt-get install varnish
heissen?
das ist natürlich richtig, vielen Dank für den Hinweise
Hi,
danke für die gute Anleitung.
Wenn ich das richtig verstehe, helfen aber alle Performance Optimierungen, die man mit Hilfe eines Reverse Proxy anstellt, jedoch nur für anonymen Traffic, richtig? Wenn ich eine Seite habe, auf der die Nutzer fast immer eingeloggt sind, bringt mir Varnish keinen großen Vorteil denke ich, oder?
Nein das ist so nicht ganz richtig. Sie können in der Datei default.vcl z.B. einstellen, dass manche Requests nie an das Backend weitergereicht werden können (das machen wir z.B. mit Bildern bzw. mit Seiten die für alle User gleich aussehen), sondern direkt aus dem Varnish-Cache beantwortet werden
if (req.url ~ "^/modules/.*\.(js|css)\?") {
unset req.http.Cookie;
}
z.B cached immer, für alles JS und CSS Dateien aus dem modules Ordner.
Ich hoffe, das hilft Ihnen weiter.
Wirklich ein interessanter Ansatz, den ich derzeit mangels eigenem Root-Server leider nur im Hinterkopf behalten kann. Pressflow muss ich mir jedoch unbedingt einmal ansehen, kenne ich bisher nur dem Namen nach.
Hallo,
danke für das Tutorial. Varnish läuft jetzt bei mir allerdinsg gibt es Probleme mit meinem Statistiktool - die Referer werden nicht mehr korrekt angezeigt auch dei IPs/Hosts nicht.
Der Pfad des Statistiktools wird auf jeder php-Seite über www.domain.de/statit4/statit.php aufgerufen.
Ich habe nun in die default.vcl
if (req.url ~ "statit.php*") {pass;
}
eingegeben aber es ändert sich nichts.
Im Header wird der Pfad so aufgerufen
GET /statit4/statit.php?st_id=1&st_w=1280&st_h=1024&st_c=24&st_ref=&st_dat=%2F
Hättest Du vielleicht eine Tip wie ich das Problem lösen könnte?
Danke und Gruß
Matthias
Ich würde einmal zwei Dinge versuchen:
if (req.url ~ "statit.php*") {return (pass); //statt nur pass;
}
und einmal
if (req.url ~ "^statit4/statit.php*") {return (pass); //statt nur pass;
}
Das müsste auf jeden Fall funktionieren, ich habe gerade in einer unserer Varnish Konfigurationen geschaut, so haben wir es umgesetzt.
Hallo Herr Pistner,
nochmals danke für Ihre Hilfe. Ich werde ihre Vorschläge am Wochenende ausprobieren.
Funktionieren ihre Beispiele "nur" wenn die statit4.php-Datei per Include eingebunden wird oder auch wenn dies über einen Javascriptaufruf geschieht?
Der Javascript-Aufruf (im Footer jeder Datei) würde so aussehen.
Reicht dann auch
if (req.url ~ "statit.php*") {
return (pass); //statt nur pass;
}
oder
if (req.url ~ "^statit4/statit.php*") {
return (pass); //statt nur pass;
}
oder müsste man das dann anders schreiben?
Vielen Dank für Ihre Mühe
Schlöne Grüße
Matthias
Der Javacriptcode wurde wohl "verschluckt" - hier ist er nochmal.
<script type="text/javascript"><!--
statit=new Image(3,2);
statit.src="http://www.domain.net/statit4/statit.php?st_id=1&st_w="+screen.width+"&st_h="+screen.height+"&st_c="+screen.colorDepth+"&st_ref="+encodeURIComponent(document.referrer)+"&st_dat="+encodeURIComponent(window.location.pathname+window.location.search);
//-->
</script>
Schöne GRüße Matthias
Das funktioniert auch, wenn der Aufruf über ein JS Request stattfindet. Würde das Script "nur" per Include im PHP Code eingebunden sein, ist es für Varnish ja gar nicht "sichtbar" und hat dadurch keinen Effekt. So wie Sie es oben geschrieben haben sollte es funktionieren und der Request im JS Code sieht für mich auch gut aus.
Sonst helfe ich gerne nochmal weiter, sollte es nicht funktionieren.
Kommentar hinzufügen