Tutorial: Server Setup mit nginx, HHVM und WordPress
Speed up all PHP things!
Wer PHP-basierte Webseiten ausliefern möchte, hat mittlerweile eine Menge Möglichkeiten, viele werden den Wechsel von Apache zu nginx in Kombination mit PHP-FPM bereits hinter sich haben, welcher bereits einen ansehnlichen Performance-Vorteil gebracht hat.
Durch den Einsatz von HHVM und dessen JIT-Compiler (vs. konventionellem PHP Interpreter) lässt sich im Zusammenspiel mit nginx ein weiterer großer Schritt in Sachen Performance machen. Ein netter Nebeneffekt ist die Tatsache, dass sich auch starkfrequentierte Anwendungen auf vergleichsweise günstiger Hardware betreiben lassen.
Wie man solch einen Server zusammenbaut, gibt es im nachfolgendem Tutorial, kurz und bündig samt beispielhafter WordPress Instanz. Das ganze passiert bei DigitalOcean auf einer Ubuntu 14.04 LTS Maschine.
Vorbereitung
Nach dem Hochfahren einer neuen Maschine sorgt man erst einmal routinemäßig dafür, dass das System und Abhängigkeiten auf dem neusten Stand sind.
1$ apt-get update && apt-get upgrade2$ apt-get dist-upgrade3$ apt-get autoremove
Jetzt legt man einen neuen Benutzer an und setzten das Passwort um die späteren Aktionen nicht mit dem Root-Benutzer auszuführen zu müssen. Alternativ zum Passwort kann hier auch mit SSH Keys gearbeitet werden.
1$ adduser demo
Anschließend erteilen wir dem erstellten Benutzer sudo
-Rechte.
1$ visudo
1# User privilege specification2root ALL=(ALL:ALL) ALL3demo ALL=(ALL:ALL) ALL
Aus Sicherheitsgründen ist es in diesem Zuge ratsam das Anmelden des Root-Benutzers zu verbieten und den SSH Port zu ändern. Finden und ersetzen.
1$ nano /etc/ssh/sshd_config
1Port 1234 PermitRootLogin no
1$ service ssh restart
Ein guter Zeitpunkt das Verzeichnis anzulegen, aus dem später die Webseite ausgeliefert werden soll und der Ordnerstruktur die korrekten Besitzrechte zu erteilen.
1$ mkdir -p /var/www/demo.local2$ chown -R demo /var/www
Nun kann man sich abmelden und mit dem neuem Benutzer erneut via SSH einloggen.
1ssh -p 12342demo@domain.local
nginx
Die Installation von nginx folgt jetzt. Da HHVM Änderungen an der Konfiguration von nginx automatisch vornehmen kann, wird HHVM erst anschließend installiert. Es wird empfohlen den Mainline-Branch (heißt development, etwas verwirrend) zu nutzen…
In NGINX nomenclature, “stable” means that no new features are added (the feature set is stable). Only major bugfixes are committed to that version. In general, you should deploy the NGINX mainline branch at all times.
…dafür fügt man entsprechendes Repository dem Paketmanager hinzu.
1$ sudo apt-add-repository ppa:nginx/development2$ sudo apt-get update3$ sudo apt-get install nginx4$ sudo service nginx start
HHVM
Kommen wir endlich zu HHVM. Im HVVM GitHub Repository finden sich zahlreiche Prebuilt Packages, auch für Ubuntu 14.04, so brauchen wir es nicht selbst kompilieren und sind ready to roll, nachdem die Paketquelle hinzugefügt wurde.
1$ wget -O - http://dl.hhvm.com/conf/hhvm.gpg.key | sudo apt-key add - echo deb http://dl.hhvm.com/ubuntu trusty main | sudo tee /etc/apt/sources.list.d/hhvm.list2$ sudo apt-get update3$ sudo apt-get install hhvm
Um HHVM mit nginx nutzen zu können muss die mitgelieferte FastCGI Installation ausgeführt werden, welche auch die angesprochenen Änderungen an nginx vornimmt und die Schnittstelle zu nginx darstellt.
1$ sudo /usr/share/hhvm/install_fastcgi.sh
Man schaltet Server zwar nicht so oft ab, aber um HHVM nicht manuell nach jedem Systemstart ausführen zu müssen, werfen wir HHVM in den Autostart von Ubuntu.
1$ sudo update-rc.d hhvm defaults
Datenbank: MariaDB
Als Datenbankserver kommt MariaDB zum Einsatz, für die korrekte Version ist es auch hier nötig die Repository manuell hinzuzufügen.
1$ sudo apt-get install software-properties-common2$ sudo apt-key adv --recv-keys --keyserver hkp://keyserver.ubuntu.com:80 0xcbcb082a1bb943db3$ sudo add-apt-repository 'deb http://ams2.mirrors.digitalocean.com/mariadb/repo/10.1/ubuntu trusty main'4$ sudo apt-get update5$ sudo apt-get install mariadb-server
Nachdem MariaDB installiert ist, wird noch für etwas mehr Sicherheit gesorgt. Beim Ausführen des nächsten Befehls werden wird man aufgefordert das MySQL Root Passwort zu ändern, welches zuvor gesetzt wurde, diese wird mit „Nein“ beantwortet, die restlichen Abfragen werden mit „Ja“ abgearbeitet.
1$ mysql_secure_installation
Fertig? Dann ist alles bereit eine neue Datenbank anzulegen, hierfür begibt man sich in die SQL Shell und tut genau dies. Vergebenes Passwort für den Root-Benutzer des Datenbankservers bereit halten.
1$ mysql -u root -p
1MariaDB [(none)] CREATE DATABASE demo; MariaDB [(none)] exit
WordPress & Konfiguration
Zu guter Letzt brauchen man natürlich noch WordPress an sich, die „Installation“ ist relativ einfach und mit wenigen Anweisungen ausgeführt, ohne einen SFTP Client zu starten.
1$ cd /var/www/demo.local2$ wget http://de.wordpress.org/latest-de_DE.tar.gz3$ tar -xvzf latest-de_DE.tar.gz4$ mv wordpress/* .5$ rm -rf wordpress6$ rm -rf latest-de_DE.tar.gz
Die WordPress Installation wird wie gewohnt im Browser fertiggestellt.
Nun müssen noch ein paar Anpassungen an der Konfiguration von nginx vornehmen, damit die Sachen so ausgeliefert werden, wie sie sollen.
1$ nano /etc/nginx/nginx.conf
Die folgenden Zeilen sind standardmäßig auskommentiert, die Option server_tokens
versteckt bei Fehlermeldungen des Servers die verwendete nginx-Version (Sicherheit und so) und da wir die gzip Kompression unbedingt verwenden möchten, ändern wir dies ebenfalls. Sofern Grafiken im SVG Format eingebunden werden sollen, fügt man zusätzlich image/svg+xml
den gzip_types
hinzu.
1server_tokens off; gzip_vary on; gzip_proxied any; gzip_comp_level 6;2gzip_buffers 16 8k;3gzip_http_version 1.1;4gzip_types … image/svg+xml;
Im letzten Schritt der Konfiguration von nginx geht es an den Server-Block, welcher dafür verantwortlich ist, HTTP Anfragen entgegenzunehmen und letztendlich die Antwort zur gesendeten Anfrage an den Client zu senden. nginx liest hierzu Konfigurationsdateien ein die dem Schema /etc/nginx/conf.d/*.conf
und /etc/nginx/sites-enabled/*
entsprechen, dies ist in der nginx.conf
einstellbar. Der Einfachheit halber wird der bereits vorgegebenen Server-Block bearbeitet.
1$ nano /etc/nginx/sites-enabled/default
Auch hier finden sich wieder zahlreich auskommentierte Anweisungen, die nicht benötigen werden, der Übersicht halber kann man nicht verwendete Server-Blöcke löschen. Am Ende sollte die Konfiguration wie folgt aussehen.
1server {2 listen 80;3 server_name demo.local;4 root /var/www/demo.local;56 index index.php index.html;78 include hhvm.conf;910 location / {11 try_files $uri $uri/ /index.php?$args;12 }1314 location ~* \.(js|css|png|jpg|jpeg|gif|ico|woff2|woff|ttf|svg|otf)$ {15 expires 30d;16 add_header Pragma public;17 add_header Cache-Control "public";18 }1920}
„Static“ durch Caching
Bonuspunkte gibt es für das Caching von dynamischen Anfragen, insbesondere bei WordPress lässt sich hier die Response Time verringern, in dem bei bereits im Cache liegende Seiten direkt an den Client gesendet werden, statt die Anfrage erst über HHVM und Datenbank zu schicken.
Die Auswahl an Möglichkeiten für die Umsetzung sind mehr als zahlreich, neben dem FastCGI-Cache von nginx gibt es – natürlich – diverse WordPress Plugins.
Mit Cachify lässt sich relativ einfach das gewünschte Ziel erreichen, nach der Installation und Wahl der Caching-Methode ergänzt man den zuvor genannten Server-Block um entsprechende Anweisungen und ist damit eigentlich schon fertig.
Ich bin derzeit noch am Experimentieren welche Konfiguration die beste Performance bringt, doch dabei geht es um die letzten Millisekunden. Grundsätzlich kann ich als solide Basis das Cachify Plugin von Sergej empfehlen, welches im Gegensatz zu anderen Plugin-Lösungen ohne großen Overhead daherkommt und sich auf das Wichtigste konzentriert, dies kann man leider nicht von vielen Plugins behaupten. An dieser Stelle noch mal ein Dank an Sergej für die großartige Arbeit in der WordPress Community.
Firewall
Als abschließende Übung machen wir den Server noch etwas sicherer, in dem wir die bei Ubuntu mitgelieferte Firewall aktivieren.
1$ sudo ufw allow 12342$ sudo ufw allow 803$ sudo ufw allow 4434$ sudo ufw enable
Der Server antwortet nun nur auf Anfragen auf korrespondierenden Ports: dem benutzerdefiniertem SSH Port (1234), HTTP (80) und HTTPS (443).
Fertig.