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.
$ apt-get update && apt-get upgrade
$ apt-get dist-upgrade
$ apt-get autoremoveJetzt 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.
$ adduser demoAnschließend erteilen wir dem erstellten Benutzer sudo-Rechte.
$ visudo# User privilege specification
root ALL=(ALL:ALL) ALL
demo ALL=(ALL:ALL) ALLAus Sicherheitsgründen ist es in diesem Zuge ratsam das Anmelden des Root-Benutzers zu verbieten und den SSH Port zu ändern. Finden und ersetzen.
$ nano /etc/ssh/sshd_configPort 1234 PermitRootLogin no$ service ssh restartEin guter Zeitpunkt das Verzeichnis anzulegen, aus dem später die Webseite ausgeliefert werden soll und der Ordnerstruktur die korrekten Besitzrechte zu erteilen.
$ mkdir -p /var/www/demo.local
$ chown -R demo /var/wwwNun kann man sich abmelden und mit dem neuem Benutzer erneut via SSH einloggen.
ssh -p 1234
demo@domain.localnginx
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.
$ sudo apt-add-repository ppa:nginx/development
$ sudo apt-get update
$ sudo apt-get install nginx
$ sudo service nginx startHHVM
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.
$ 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.list
$ sudo apt-get update
$ sudo apt-get install hhvmUm 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.
$ sudo /usr/share/hhvm/install_fastcgi.shMan 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.
$ sudo update-rc.d hhvm defaultsDatenbank: MariaDB
Als Datenbankserver kommt MariaDB zum Einsatz, für die korrekte Version ist es auch hier nötig die Repository manuell hinzuzufügen.
$ sudo apt-get install software-properties-common
$ sudo apt-key adv --recv-keys --keyserver hkp://keyserver.ubuntu.com:80 0xcbcb082a1bb943db
$ sudo add-apt-repository 'deb http://ams2.mirrors.digitalocean.com/mariadb/repo/10.1/ubuntu trusty main'
$ sudo apt-get update
$ sudo apt-get install mariadb-serverNachdem 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.
$ mysql_secure_installationFertig? 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.
$ mysql -u root -pMariaDB [(none)] CREATE DATABASE demo; MariaDB [(none)] exitWordPress & 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.
$ cd /var/www/demo.local
$ wget http://de.wordpress.org/latest-de_DE.tar.gz
$ tar -xvzf latest-de_DE.tar.gz
$ mv wordpress/* .
$ rm -rf wordpress
$ rm -rf latest-de_DE.tar.gzDie 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.
$ nano /etc/nginx/nginx.confDie 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.
server_tokens off; gzip_vary on; gzip_proxied any; gzip_comp_level 6;
gzip_buffers 16 8k;
gzip_http_version 1.1;
gzip_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.
$ nano /etc/nginx/sites-enabled/defaultAuch 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.
server {
listen 80;
server_name demo.local;
root /var/www/demo.local;
index index.php index.html;
include hhvm.conf;
location / {
try_files $uri $uri/ /index.php?$args;
}
location ~* \.(js|css|png|jpg|jpeg|gif|ico|woff2|woff|ttf|svg|otf)$ {
expires 30d;
add_header Pragma public;
add_header Cache-Control "public";
}
}„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.
$ sudo ufw allow 1234
$ sudo ufw allow 80
$ sudo ufw allow 443
$ sudo ufw enableDer Server antwortet nun nur auf Anfragen auf korrespondierenden Ports: dem benutzerdefiniertem SSH Port (1234), HTTP (80) und HTTPS (443).
Fertig.