Es wird mal wieder Zeit etwas über NginX zu schreiben. In meinem heutigen Artikel widme ich mich dem Thema „Caching“. Durch ein Kundenprojekt darauf angestoßen habe ich mich mit verschiedenen Möglichkeiten auseinander gesetzt um einer PHP Anwendung auf die Sprünge zu helfen. Im Falle des Kundensystems war NginX als Reverse Proxy vor einem Apachen eine sehr gute Wahl. Der heutige Ansattz geht allerdings in eine andere Richtung zumal NginX als Webserver für WordPress bereits konfiguriert ist und Varnish das Caching übernehmen wird.
Dieser Artikel wird nicht auf Feinheiten bei der Konfiguration eingehen. Damit muss ich jeder selbst auseinander setzen. Vielmehr soll diese Anleitung einen Weg vorzeigen, der sequentiell abgearbeitet werden kann und der am Ende ein tolles Ergebnis liefert.
Ein alter Bekannter
Die ist nicht der erste Artikel zum Thema, wahrscheinlich auch nicht der Letzte. Das heutige Szenario sieht NginX aber nicht in der Rolle des schnellen Webservers. Denn obwohl sich der „Kleine“ im Vergleich zum „Großen“ (Apachen) bei der Auslieferung von WordPress als durchaus schneller erweist, ist der Bench dennoch nicht weltbewegend.
WordPress + NginX
Die Quelle für diese quick & dirty Konfiguration ist die Seite von Nginx selbst [1].
# Upstream to abstract backend connection(s) for php
# Use either or: socket or ip as defined in the php-fpm configuration
upstream php_backend {
server unix:/tmp/php-cgi.socket;
server 127.0.0.1:9000;
}
server {
## Your website name goes here.
server_name domain.tld;
## Your only path reference.
root /var/www/wordpress;
## This should be in your http block and if it is, it's not needed here.
index index.php;
location = /favicon.ico {
log_not_found off;
access_log off;
}
location = /robots.txt {
allow all;
log_not_found off;
access_log off;
}
location / {
# This is cool because no php is touched for static content
try_files $uri $uri/ /index.php;
}
location ~ \.php$ {
#NOTE: You should have "cgi.fix_pathinfo = 0;" in php.ini
include fastcgi.conf;
fastcgi_intercept_errors on;
fastcgi_pass php_backend;
}
location ~* \.(js|css|png|jpg|jpeg|gif|ico)$ {
expires max;
log_not_found off;
}
}
Damit ist NginX in der Lage WordPress zu servieren. Wie der Konfiguration entnommen werden kann, übergibt Nginx php-Requests an den Upstream ‚php_backend‘. Was im Umkehrschluss wiederum bedeutet, dass PHP-FPM bereits installiert und konfiguriert ist. Mit den aktuellen Distributionen (wie z.B. Ubuntu 12.04 LTS) kann dies mittels apt-get install php5-fpm
geschehen. Ältere Distributionen (inkl. 10.04 LTS) liefern noch kein Paket. Hier helfen backports, PPA, oder einfach selbst Hand anlegen.
In einem älteren Artikel finden sich hierzu weitere Informationen, die hilfreich sein können.
PHP-FPM Pool
Angelehnt an genannten älteren Artikel hier eine mögliche PHP Konfigurationsdatei. Diese muss an einigen Stellen an die eigenen Bedürfnisse angepasst werden. Zum Beispiel wird sich der Pfad /var/lib/php5/sockets
auf keinem System finden lassen (wurde von Hand angelegt). Ebenso sind die Pfade zu den Logdateien zu kontrollieren.
[wp]
;listen = 127.0.0.1:9000
listen = /var/lib/php5/sockets/wp.socket
;listen.backlog = -1
listen.allowed_clients = 127.0.0.1
listen.owner = www-data
listen.group = www-data
listen.mode = 0666
user = www-data
group = www-data
pm = dynamic
pm.max_children = 50
pm.start_servers = 15
pm.min_spare_servers = 5
pm.max_spare_servers = 35
pm.max_requests = 500
;pm.status_path = /status
;ping.path = /ping
;ping.response = pong
;request_terminate_timeout = 0
;request_slowlog_timeout = 0
slowlog = /usr/local/php/var/log/wp.log.slow
;rlimit_files = 1024
;rlimit_core = 0
;chroot =
chdir = /
;catch_workers_output = yes
;env[HOSTNAME] = $HOSTNAME
;env[PATH] = /usr/local/bin:/usr/bin:/bin
;env[TMP] = /tmp
;env[TMPDIR] = /tmp
;env[TEMP] = /tmp
php_admin_value[sendmail_path] = /usr/sbin/sendmail -t -i -f y@ou.tld
php_flag[display_errors] = off
php_admin_value[error_log] = /usr/local/php/var/log/fpm-php.wp.log
php_admin_flag[log_errors] = on
php_admin_value[memory_limit] = 128M
php_admin_value[apc.shm_size] = 64M
Funktioniert doch. Benchmark
Funktionieren alleine genügt uns nicht – das kann ja jeder! Um die Performance von WordPress zu steigern verbauen wir einen HTTP-Accelerator: Varnish.
An dieser Stelle könnte man die Anleitung mit Benchmarks auflockern, die zeigen wo die Reise hingeht. Gängiges Benchmarking Werkzeug ist hier ab
– Apache Benchmark [2]. Mit nachfolgendem Befehl lässt sich ein Vorabbild der Situation machen:
ab -k -c 10 -n 1000 http://www.domain.tld/
Die einzelnen Parameter erklärt dabei der Aufruf von ab
an der Kommandozeile. Interessant ist dabei der Vergleich vorher – nachher bzgl. der Werte Requests per second
Es sollte dabei darauf geachtet werden, dass der Aufruf der gleiche bleibt. Zudem sei an dieser Stelle darauf hingewiesen, dass man sich mit dem Thema Benchmarking immer sehr kritisch auseinander setzen muss. Warum beschreibt Kristian Lyngstøl[3] in seinem Blog.
Damit dieser Artikel jetzt Sinn macht und um das beeindruckende Ergebnis zu veranschaulichen, zunächst die VORHER Werte. Dabei liefert NginX die Seite aus mit obiger Konfiguration.
Requests per second: 32.92 [#/sec] (mean)
Wobei es sich um einen Server mit 1 AMD Phenom(tm) II X6 1055T Processor handelt, 8GB RAM und Ubuntu 10.04 LTS als OS.
Ein anderes Bench-Werkzeug ist httperf
. Der Aufruf gestaltet sich etwas schwerer als der von ab
.
httperf --rate 2000 --num-conns=10000 --num-calls 20 \
--burst-length 20 --server localhost --port 80 --uri /
httperf
liefert auch hier anschauliche Daten, ähnlich ab
. Diese sind aber nicht weiter wichtig. Warum?
Benchmark Fazit
Wir brauchen für diesen Artikel einen Benchmark – ansonsten lässt sich das Ergebnis unserer Arbeit nicht messen. Es sei jedoch auf darauf hingewiesen, dass wir unsere Webseite … lokal benchen. Zudem haben angesprochene Tools Eigenschaften, die sie nicht perfekt machen. So läuft httperf
beispielsweise single threaded. Es kann zwar mehrere gleichzeitige Verbindungen aufbauen, aber nur in einem einzigen Thread. Zudem stösst httperf
an interne Limits: . Dies lässt sich umgehen, wenn man das Tool selbst kompiliert.
Es sind schlicht synthetische Ergebnisse, die man kritisch interpretieren muss.
Was und wo ist nun Varnish?
Varnish is a web application accelerator. You install it in front of your web application and it will speed it up significantly.
Ausgehend von einer neuen Distribution kann einfach der Paketmanager angeschmissen werden, z.b. apt
oder yum
. Wer es mit etwas älterem zu tun hat, z.B. Ubuntu 10.04 LTS, der installiert „varnish“ wie folgt:
curl http://repo.varnish-cache.org/debian/GPG-key.txt | apt-key add -
echo "deb http://repo.varnish-cache.org/ubuntu/ lucid varnish-3.0" >> /etc/apt/sources.list
apt-get update
apt-get install varnish
Für andere Distributionen gibt es hier entsprechende Anleitungen.
Damit ist Varnish zunächst unangepasst installiert und bereit lauffähig. Um ihn in das obige Konstrukt einzubauen, sind einige Anpassungen notwendig.
Konfiguration Varnish
In einem ersten Schritt wird /etc/default/varnish
angepasst.
#...
START=yes
#...
DAEMON_OPTS="-a :80 \
-T localhost:6082 \
-f /etc/varnish/newfile.vcl \
-S /etc/varnish/secret \
-s malloc,256m"
# ...
START=yes
lässt Varnish nach einem reboot wieder starten. Die DAEMON_OPTS
, die bereits vorhanden sind werden in -a
angepasst. :80
definiert den Port an dem Varnish auf Anfragen hören wird. In vorderster Front ist dies der HTTP-Port.
Die zweite Anpassung ist die Zuweisung einer neuen .vcl
Datei. In dieser newfile.vcl
wird der WordPress spezifische Code geschrieben, der Varnish das Handling von WordPress ermöglicht.
Ein großes Augenmerk sei dabei auf den Anfang gerichtet, denn der Block backend default
bestimmt von wo Varnish Inhalte bezieht.
backend default {
.host = "127.0.0.1";
.port = "8888";
}
sub vcl_recv {
if (req.http.Accept-Encoding) {
#revisit this list
if (req.url ~ "\.(gif|jpg|jpeg|swf|flv|mp3|mp4|pdf|ico|png|gz|tgz|bz2)(\?.*|)$") {
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 ~ "\.(gif|jpg|jpeg|swf|css|js|flv|mp3|mp4|pdf|ico|png)(\?.*|)$") {
unset req.http.cookie;
set req.url = regsub(req.url, "\?.*$", "");
}
if (req.url ~ "\?(utm_(campaign|medium|source|term)|adParams|client|cx|eid|fbid|feed|ref(id|src)?|v(er|iew))=") { set req.url = regsub(req.url, "\?.*$", "");
}
if (req.http.cookie) {
if (req.http.cookie ~ "(wordpress_|wp-settings-)") {
return(pass);
} else {
unset req.http.cookie;
}
}
}
sub vcl_fetch {
if (req.url ~ "wp-(login|admin)" || req.url ~ "preview=true" || req.url ~ "xmlrpc.php") {
return (hit_for_pass);
}
if ( (!(req.url ~ "(wp-(login|admin)|login)")) || (req.request == "GET") ) {
unset beresp.http.set-cookie;
}
if (req.url ~ "\.(gif|jpg|jpeg|swf|css|js|flv|mp3|mp4|pdf|ico|png)(\?.*|)$") {
set beresp.ttl = 365d;
}
}
sub vcl_deliver {
# multi-server webfarm? set a variable here so you can check
# the headers to see which frontend served the request
# set resp.http.X-Server = "server-01";
if (obj.hits > 0) {
set resp.http.X-Cache = "HIT";
} else {
set resp.http.X-Cache = "MISS";
}
}
Die Varnish Konfiguration wird in C-Code übersetzt und zurück in Varnish gelinkt. Fehler in der Konfiguration werden dabei direkt beim Start des Caches ausgegeben.
Konfiguration Nginx
Mit Varnish in der ersten Reihe muss Nginx zwangsläufig in die Zweite. Der Block
backend default {
.host = "127.0.0.1";
.port = "8888";
}
der Varnish-Konfiguration hat dies bereits angedeutet. Die Virtuellen Hosts in Nginx werden so angepasst, dass der Port 8888 in der listen
Direktive steht.
Ende
Das ist tatsächlich schon alles. Die Server sind zu starten, bzw. die Konfigurationen neu einzulesen.
service nginx restart && service varnish start
Bench…immer wieder bench
Der Vollständigkeit halber testen wir das System noch ein mal mit
ab -k -c 10 -n 1000 http://www.domain.tld/
und schauen uns das Ergebnis an:
Requests per second: 9664.95 [#/sec] (mean)
Beeindruckende Steigerung, wenn auch unter Berücksichtigung obiger Hinweise bzgl. Benchmarking.
[1] http://wiki.nginx.org/WordPress
[2] Die Installation von ab erfolgt mit dem Package apache2-utils
. Der Apache Webserver muss dazu nicht installiert werden.
[3] http://kly.no/me.html
Schreibe einen Kommentar
Du musst angemeldet sein, um einen Kommentar abzugeben.