Am Beispiel von Suse Linux Enterprise Server 10 möchte ich kurz zeigen, wie die Konfiguration eines performanten Webservers mit nginx für statische Inhalte und Apache für dynamische Inhalte aussehen kann.
Vorteil dieser Lösung ist der geringere Footprint von nginx (wie z.B. auch lighty) im Vergleich zu Apache. Statische Dokumente werden schneller augeliefert und benötigen deutlich weniger Hauptspeicher. Bei mehr als 150 gleichzeitgen connections im Produktivbetrieb bleibt bei mir die CPU-Auslastung von nginx sehr gering (<1%). Zudem wird die einfache und bewährte Konfiguration von Apache für die dynamsichen Inhalte genutzt.
Nachteilig ist der erhöhte Administrationsaufwand sowie die Möglichkeit einer zusätzlichen Sicherheitslücke mit nginx.
Sinnvoll ist ebenfalls die Einrichtung von apache als worker (optimal als fgcid, falls php notwendig ist). Im Standard ist überlicherweise prefork konfiguriert, je nach Distribution lässt sich dies umstellen (in einem weiteren Blogeintrag gehe ich auf die Details für SLES ein).
Installation (bezogen aus SLES 10):
Paket nginx-stable-xxx.rpm vom opensuse Repository herunterladen. Nginx (wie auch lighty) sind leider nicht in SLES 10 als Paket vorhanden. Alternativ kann natürlich nginx selbst kompiliert werden.
nginx-Paket installieren mit rpm -ivh nginx-stable-xxx.rpm und alle Abhängigkeiten, falls vorhanden, auflösen.
Da Apache und nginx beide auf Port 80 hören möchten, muss einer der beiden umkonfiguriert werden. In unserem Beispiel Apache. Hierzu gibt es zwei Möglichkeiten:
- Nginx auf Port ip-adresse:80 und Apache auf 127.0.0.1:80 (es wird also Apache gesagt, dass er nur lokal auf 127.0.0.1 horchen soll)
- Nginx auf Port 80 und Apache z.B. auf Port 81. Der Apache Port spielt keine grosse Rolle, er sollte nur nicht belegt sein (herauszufinden über “netstat -an |grep 81″ unter Linux)
Ich habe mich im Beispiel für 2. entschieden.
Dazu muss in der httpd.conf (bei SLES in /etc/apache2/listen.conf) folgende Änderung gemacht werden:
Listen 81
Alle Anfragen, die nginx nicht beantworten kann (z.B. php-Scripts) werden über das Proxy-Modul von nginx an Apache weitergeleitet.
Die Konfiguration von nginx hierzu:
#user nobody;
user wwwrun www;
#worker_processes 1;
worker_processes 2;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
server {
listen 80;
access_log off;
server_tokens off;
location / {
proxy_pass http://127.0.0.1:81;
include /etc/nginx/proxy.conf;
}
location ~* ^.+.(jpe?g|xml|flv|gif|png|ico|css|zip|tgz|gz|rar|bz2|doc|xls|exe|ppt|tar|mid|midi|wav|bmp|rtf|js|swf|avi|mp3)$ {
expires 4h;
root /bla/blubb/webroot;
}
}
}
Es wird die Proxy-Konfiguration /etc/nginx/proxy.conf gelesen, die folgendermassen aussieht:
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
client_max_body_size 10m;
client_body_buffer_size 128k;
proxy_connect_timeout 90;
proxy_send_timeout 90;
proxy_read_timeout 90;
proxy_buffers 32 4k;
Natürlich sollten beide Konfigurationsdateien angepasst werden.
Als Tipp: Mit
if (!-f $request_filename) {
proxy_pass http://127.0.0.1:81;
break;
}
kann z.B. getestet werden, ob eine statische Datei vorhanden ist. Falls nicht, wird an Apache weitergeleitet.
Theoretisch sollte nach der Änderung und einem Restart beider Server alles funktionieren. Wichtig ist, dass beide Server denselben Benutzer für das webroot benutzen (bei SLES wwwroot im Standard), deshalb die Änderung “user wwwrun www;”.
Die Dokumentation der einzelnen nginx-Module kann hier eingesehen werden:
http://wiki.nginx.org/NginxModules
Auf die Umstellung des Apache auf Worker mit fcgid gehe ich in einem weiteren Blogeintrag ein, sonst wird es zu unübersichtlich…
März 14, 2010 um 21:58
Danke für die Anleitung, eine Frage hierzu: Wieso lässt Du den Apache nicht auf alle anfragen reagieren und als proxy agieren der dann statische anfragen zu nginx weiterleitet?
März 15, 2010 um 7:35
Hi Harty, nginx benutzt deutlich weniger Hauptspeicher, deshalb lasse ich die Verbindungen von nginx verwalten. Dadurch wird das System deutlich entlastet.
Juni 21, 2010 um 11:25
Danke für die toll Anleitung, Marc!
Wie sieht’s denn aus, wenn ich VirtualHosts verwende? Kann man da ein Switch in die Konfiguration einbauen, so nach dem Motto:
location ‘www.test.de’ {
root /var/www/dir1
}
location ‘www.test2.de’ {
root /var/www/dir2
}
…
Juni 21, 2010 um 19:01
Hallo, ja, nur nicht mit location sondern über einen separaten server-Block, dann die Domain als server_name konfigurieren, z.B.
http {
server {
listen 80;
server_name http://www.domain1.com;
access_log logs/domain1.access.log main;
location / {
index index.html;
root /var/www/domain1.com/htdocs;
}
}
server {
listen 80;
server_name http://www.domain2.com;
access_log logs/domain2.access.log main;
location / {
index index.html;
root /var/www/domain2.com/htdocs;
}
}
Siehe
http://wiki.nginx.org/NginxVirtualHostExample
Oktober 20, 2010 um 21:28
hallo mscheel,
ich mags nochmal gern sagen: echt klasse dein tutorial!
habe das jetzt nochmal gemacht, aber nun habe ich ein kleines problem und wollte dich fragen, ob du hierfür eine lösung hättest. ich hoste auf einen server mehrere seiten mit hilfe von virtualhosts.
nun ist das blöd wenn ich einfach nicht von nginx bearbeitbare anfragen an “http://127.0.0.1:81″ weiterleite, denn dann kann apache die domains nicht unterscheiden..
liebe grüße
Oktober 21, 2010 um 6:30
Hi, dafür gibt es den nginx-Befehl proxy_set_header in Verbindung mit “Host”. Damit wird der Header übergeben und apache kann dann unterscheiden.
Beispiel:
server {
server_name example.com;
proxy_pass http://127.0.0.1:8000;
proxy_set_header Host $host;
}
Apache virtual host:
ServerName example.com
…
Alternativ könntest Du Apache auf verschiedenen Ports horchen lassen, der obige Weg sollte aber besser sein.