PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : nginx - Was man wissen sollte


bl0bb
31.05.2012, 17:30
So, bekomme grad Lust was zu schreiben. Vielleicht hilft es dem einen oder anderen Quereinsteiger ;)

Wer es nicht weiß - nginx (http://nginx.org/) ist ein robuster, kleiner und performanter Webserver der einem Apache kaum (wenn überhaupt noch) nachsteht.
Gerade bei stark frequentierten Seiten, würde ich jedem zu nginx raten, wenn man nicht Unsummen in Hardware stecken möchte, da der Lastunterschied zwischen nginx und Apache doch gravierend ist. Auf meinem Server (im Schnitt etwa 35k Anfragen in der Minute) ging die Serverlast von ~70% runter auf ~22%. Ich denke das spricht für sich, wenn man bedenkt das auch die Verarbeitung der Anfragen und die Seitenauslieferung um einiges schneller vonstatten geht.


Jedoch sollte man nginx vor dem Liveeinsatz erst einmal auf einem (lokalen) Testserver kennenlernen.

Achtung:
Die hier gezeigten Beispiele (außer die Snippets), sollten auch als solche betrachtet werden. Einige Sachen habe ich willkürlich zusammengestellt, damit etwas da steht. Syntaktisch korrekt sind alle, aber ob die Kombination zuträglich ist, vermag ich nicht zu sagen.


nginx kompilieren

Ich verzichte auf die fertigen Packages, und stelle mir nginx lieber selber zusammen.

nginx herunterladen, entpacken und in das Source Verzeichnis wechseln:
wget http://nginx.org/download/nginx-1.2.0.tar.gz
tar xvfz nginx-1.2.0.tar.gz
cd nginx-1.2.0Jetzt wird nginx konfiguriert, d.h. man setzt die Pfade und wählt/schließt die Module aus, die man braucht.
./configure --helpzeigt einem alle verfügbaren Optionen an. Was welches Modul kann, kann man hier (http://wiki.nginx.org/Modules) nachlesen.

Beispiel
./configure
--prefix=/usr/share/nginx
--sbin-path=/usr/sbin
--conf-path=/etc/nginx/nginx.conf
--http-log-path=/var/log/nginx/access.log
--error-log-path=/var/log/nginx/error.log
--pid-path=/var/run/nginx/nginx.pid
--http-proxy-temp-path=/usr/share/nginx/tmp/proxy
--http-fastcgi-temp-path=/usr/share/nginx/tmp/fastcgi
--http-uwsgi-temp-path=/usr/share/nginx/tmp/uwsgi
--http-scgi-temp-path=/usr/share/nginx/tmp/scgi
--http-client-body-temp-path=/usr/share/nginx/tmp/client_body
--user=www-data
--group=www-data
--with-sha1-asm
--with-sha1=/usr/lib
--with-http_realip_module
--with-http_ssl_module
--with-http_perl_module
--with-cpu-opt=amd64
--with-http_flv_module
--with-http_gzip_static_module
--with-http_secure_link_module
--with-http_stub_status_module
--with-http_addition_module
--with-http_sub_moduleWer IPv6 fehlerfrei nutzen möchte, sollte mindestens die Version 1.1.19 nutzen.

Dann noch
make && make installund schon sollte alles durchrattern. Wenn es Fehlermeldungen gibt, einfach schaun welche Pakete man noch installieren muss.


init.d

Das Startskript wird automatisch erstellt, zu finden unter /etc/init.d. Falls nicht, ich habe mal eins angehangen, ggf. müssen die Pfade angepasst werden.
Ändert man während des laufenden Betriebes etwas an den Configs, reicht ein
/etc/init.d/nginx reloadzu (weicher Restart). Damit bekommen die User nichts mit.


Konfig

Unter /etc/nginx findet man alle Konfigdateien. Man kann auch selber welche erstellen, die man später per include einbinden kann.
Nach alle Änderungen muss ein reload/restart erfolgen. Um Fehler zu vermeiden, kann man vorher ein configtest machen.

Ach und wer nach einer optimalen Konfiguration sucht - vergesst es, so etwas gibt es nicht!
Es gibt für einige Parameter Richtlinien, die sich an der vorhandenen Hardware und den Zugriffen orientieren. Diese sollte man beherzigen. Die Optimierung muss jedoch im Livebetrieb erfolgen.

mime.types
Merkt man das ein Inhalt nicht korrekt ausgeliefert wird, sollte man den MIME Typ hier hinzufügen. nginx übernimmt dann den Rest.
Der Aufbau ist Content-Type Extension.

Beispiel
types {
...
image/gif gif;
...
}

fastcgi_params oder fastcgi.conf
Diese Datei sollte man um globale Parameter erweitern, damit man diese nicht immer extra bei den vHosts einfügen muss. Natürlich können diese auch in einem vHost überschrieben werden. Mittels include kann man die Datei später einbinden. Welche Parameter man festlegen will, ist jedem selber überlassen. Alle Parameter haben in der Regel einen Defaultwert, die man an die Frequentierung seines Servers anpassen sollte.

Beispiel
...
# PHP only, falls PHP mit --enable-force-cgi-redirect kompiliert wurde
fastcgi_param REDIRECT_STATUS 200;
# FastCGI Response-Header Puffer
fastcgi_buffer_size 256k;
# Antworten unter 1MB bleiben im RAM, Rest wird auf die Platte geschrieben | 4k/8k/16k -> getconf PAGESIZE
fastcgi_buffers 256 4k;
# alle Antworten bleiben im RAM
fastcgi_max_temp_file_size 0;
# Timeout zum FastCGI Server
fastcgi_connect_timeout 60;
# Timeout, wenn in der Zeit nichts geschrieben wurde
fastcgi_send_timeout 180;
# Timeout, wenn in der Zeit nichts gelesen wurde
fastcgi_read_timeout 180;
# FastCGI Fehler werden von nginx ausgegeben (error_page)
fastcgi_intercept_errors on;
...Die verfügbaren Parameter kann man hier (http://wiki.nginx.org/HttpFastcgiModule) nachlesen. Und hier (http://wiki.nginx.org/FcgiExample) noch ein include Beispiel.

deny.conf
Ich weiß gar nicht mehr, ob das überhaupt eine Defaultkonfig ist. Jedenfalls kann man sich eine Datei erstellen, in der man einzelne IPs adden kann, die nginx später blocken soll. Wie jede andere Konfig muss diese später via include eingebunden werden. Ich selber nutze für so etwas lieber iptables, aber jedem das seine.

Beispiel
deny 123.123.123.123
deny 90.0.0.2
...
proxy_params
Kann man sehen wie fastcgi_params, nur das es sich hier um die Parameter des Proxymoduls dreht. Wird nur benötigt, wenn man einen Reverse Proxy braucht, oder nginx als solchen nutzen möchte. Hier (http://wiki.nginx.org/HttpProxyModule) findet man alle Parameter aufgeschlüsselt.

nginx.conf
Nun zum eigentlichen Herzstück. Diese Konfig gliedert sich in drei Teile:
CoreModule, EventsModule und HttpCoreModule
# CoreModule
...
# EventsModule Block
events {
...
}
# HttpCoreModule Block
http {
...
}Das Core Modul (http://wiki.nginx.org/CoreModule) beinhaltet die Eigenschaften um die Basisfunktionalitäten von nginx zu steuern.

Beispiel
# CoreModule
# PID File
pid /var/run/nginx.pid;
# jede Sekunde ein gettimeofday() syscall
timer_resolution 1000ms;
# verwendeter Nutzer
user www-data;
# auf einen CPU (Core) kommt ein worker_process
worker_processes 4;
# maximale Anzahl der gleichzeitig geöffneten Dateien - /etc/security/limits.conf muss ebenfalls angepasst werden, sonst hat diese Einstellung keinen Effekt
worker_rlimit_nofile 50000;
# jeden worker_process einem CPU (Core) zuweisen
worker_cpu_affinity 0001 0010 0100 1000;Das EventsModule (http://wiki.nginx.org/EventsModule) kontrolliert die Verbindungsbehandlung von nginx.

Beispiel
# EventsModule Block
events {
# soviele Verbindungen wie möglich akzeptieren
multi_accept on;
# maximale Anzahl gleichzeitiger Verbindungen (max clients = worker_processes * worker_connections)
worker_connections 20000;
}

Zuletzt noch das HttpCoreModule (http://wiki.nginx.org/HttpCoreModule). Dieses kontrolliert alles rund um die HTTP Behandlung.

Beispiel
# HttpCoreModule Block
http {
# mime.types inkludieren
include /etc/nginx/mime.types;
# deny.conf inkludieren
include /etc/nginx/deny.conf;

# Timeout, wenn der Client nach 5s nichts geschickt hat
client_body_timeout 5;
# max. 5s auf den Client Request Header warten
client_header_timeout 5;
# falls zu niedrig kommt der Status 413, z.B. bei einem Uploadformular
client_max_body_size 25M;
# default mime, wenn in der mime.types nichts anderes definiert ist
default_type application/octet-stream;
# braucht --with-http_gzip_static_module
# gzip Kompression aktivieren
gzip on;
# gzip für alte IEs deaktivieren
gzip_disable "MSIE [1-6]\.(?!.*SV1)";
# alle Antworten für einen Proxy komprimieren
gzip_proxied any;
# mime types die komprimiert gesendet werden sollen
gzip_types text/plain text/html text/css text/xml application/x-javascript application/xml application/xml+rss text/javascript;
# aktivieren von Vary im Antwortheader
gzip_vary on;
# ungültige Headerinformationen werden ignoriert
ignore_invalid_headers on;
# gleichzeitige Anfragen über keep-alive Verbindung
keepalive_requests 4;
# keep-alive Verbindung nach 5s schließen | keep-alive header 5s
keepalive_timeout 5 5;
# nginx Default minimieren, niemand braucht eine so lange URI (falls doch sollte man den Code optimieren)
large_client_header_buffers 2 2k;
# Client liest nichts in 10s, Verbindung schließen
send_timeout 10;
# Datentransfer beschleunigen, falls Sendfile() Kernel Primitive existiert
sendfile on;
# nginx Version auf Fehlerseiten / im Server Header nicht anzeigen
server_tokens off;
# Antwort in einem Paket schicken, wenn sendfile aktiviert
tcp_nopush on;
# Pakete schicken, Antwort später verarbeiten
tcp_nodelay on;

# eigene Konfigdateien einbinden
include /etc/nginx/conf.d/*.conf;
# vHosts einbinden
include /etc/nginx/sites-enabled/*;
}


vHosts

Einen vHost kann man auch in der nginx.conf definieren, indem man einfach alles in den HttpCoreModule (http://wiki.nginx.org/HttpCoreModule) Block reinschreibt. Da dies allerdings recht schnell unübersichtlich werden kann, sollte man es doch lieber lassen ;)
Zuerst einmal legt man eine Datei my_vHost in /etc/nginx/sites-available an. Um den vHost später auch zu aktivieren, einfach einen Softlink von der Datei in das Verzeichnis /etc/nginx/sites-enabled legen. Den Rest macht dann die Anweisung include /etc/nginx/sites-enabled/*; im HttpCoreModule Block.

Beispiel
# vHost Block - http Verbindung
server {
# Dieser vHost lauscht am Port 80. Man kann auch noch die Server-IP voranstellen: ServerIP:80. Der default_server Parameter wird gleich erläutert.
listen 80 default_server;
# Der eingehende Request muss an "example.com", eine entsprechende Subdomain, oder an "www.example.com" gerichtet sein.
server_name example.com *.example.com

# Hat man mehrere vHosts konfiguriert und aktiviert, wird der Host Header des Requests mit allen Servernamen verglichen.
# Wird kein Servername gefunden, wird nach dem entsprechendem Port gesucht.
# Vorzugsweise wird dann der vHost mit dem "default_server" Parameter genommen.
# Ist dieser Parameter nicht gesetzt, wird einfach der zuerst passende vHost genommen um den Request zu verarbeiten.
# Man sollte seine vHosts also genau definieren um Quark zu vermeiden.

# Pfade der Logdateien
access_log /var/log/nginx/meine_seite.de.access.log;
error_log /var/log/nginx/meine_seite.de.error.log;

# Content-Type Charset setzen
charset utf-8;

# Fehlerseiten definieren (Status -> Seite)
error_page 404 /404.html;
error_page 500 502 503 504 /50x.html;

# Mittels location können, in Abhängigkeit von der Request-URI, verschiedene Direktiven konfiguriert werden.
# Einfach bei den einzelnen Direktiven (der Module) auf "Context: location" achten.
# Diese location trifft auf alle URIs zu. Jeder Request landet hier, wenn keine andere location Direktive definiert ist.
location / {
# Index definieren
index index.php index.html index.htm;
# Einträge alá "xyz.php not found" werden nicht mehr in die error_log geschrieben
log_not_found off;
# Access Log deaktivieren
access_log off;

# usw. (wenn man denn möchte)
}

# PHP Seiten verarbeiten (URI endet auf .php)
location ~* \.php$ {
# Zero-day exploit defense
# http://forum.nginx.org/read.php?2,88845,page=3
try_files $uri = 404;

# SCRIPT_NAME und PATH_INFO Variablen setzen
fastcgi_split_path_info ^(.+\.php)(/.+)$;
# FastCGI Config laden
include fastcgi_params;
# SCRIPT_FILENAME definieren
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
# FastCGI Server Socket setzen
fastcgi_pass unix:/var/run/php5-fpm.socket;
# hat bei mir in Extremfällen zum Absturz des FastCGI Servers geführt, wenn es auf off geschalten war
fastcgi_ignore_client_abort on;
}

# Pfad zum www Verzeichnis für diesen vHost
root /path/to/www
}


location Syntax

Die location Direktive ist speziell dafür da, um Aufrufe gezielt zu verarbeiten, umzuleiten oder zu blocken.

location [=|~|~*|^~|@] uri { … }


uri => Kann ein String Literal oder eine RegEx sein.
= => uri ist ein String Literal und muss mit der Request-URI exakt übereinstimmen.
Falls dies der Fall ist, werden keine weiteren location Direktiven ausgeführt.
~ => uri ist eine RegEx, es wird zwischen Groß- und Kleinschreibung unterschieden.
~* => uri ist eine RegEx, es wird nicht zwischen Groß- und Kleinschreibung unterschieden.
^~ => uri ist ein String Literal und zudem Präfix von der Request-URI.
@ => Ist eine "named" location und wird nicht während der normalen Verarbeitung verwendet. Sie ist nur für intern umgeleitete Anfragen zuständig, z.B. error_page, try_files.


Verarbeitungsreihenfolge

1. Direktiven mit = werden zuerst untersucht. Bei einem Treffer wird die Suche gestoppt.
2. Danach folgen Direktiven mit einem String Literal als uri. Nutzt die Direktive ^~ und es gibt einen Treffer, wird die Suche gestoppt.
3. Dauert die Suche an, werden alle RegEx uri's, in der Reihenfolge in der sie definiert wurden, untersucht.
4. Gibt es bei 3. eine Übereinstimmung wird diese genommen und die Suche beendet. Andernfalls wird die bestmögliche Übereinstimmung aus 2. genommen.

Hat man viele location Direktiven konfiguriert, sollte man die Anordnung der Verarbeitungsreihenfolge anpassen. Diese Optimierung kann je nach Frequentierung doch einiges ausmachen.

Beispiele
server {
...
# Request-URI ist "/bla.php"
location = /bla.php {
# ändere die Fehlerseite für den Status 404, wenn bla.php aufgerufen wird
error_page 404 @errorpage;
}

location @errorpage {
# Definiere die Fehlerseite, wenn die named location aufgerufen wird.
# OK, das Beispiel ist jetzt ziemlich sinnlos, aber man kann ja auch einen Redirect nehmen, oder andere hübsche Sachen anstellen
error_page 404 /404_new.html;
}

# Request-URI ist /images/hotlink.gif
location ^~ /images {
# gib einen 403 zurück, wenn die Datei im images Ordner liegt
return 403;
}

# Request-URI ist irgendeine PHP
location ~* \.php$ {
# mach was, wenn die Request-URI mit ".php" endet
}

# alle anderen Request-URIs fallen hier rein
location / {
...
}
...
}


rewrite Syntax

Ab und zu braucht man auch mal eine Umleitung.

rewrite regex replacement [flag]


regex => Eine RegEx halt...
replacement => Ersetzungen in der URI die mittels regex spezifiziert wurden. Falls replacement mit http(s) beginnt, erfolgt eine sofortige Umleitung und der Prozess wird beendet.
flag
- last -> Beendet den aktuellen rewrite und startet den Prozess neu.
- break -> Beendet den aktuellen rewrite, und beendet die aktuelle location Direktive. Es werden keine weiteren Rewrites ausgeführt.
- redirect -> Leitet die Anfrage temporär um (Status 302), wenn replacement mit http(s) beginnt.
- permanent -> Leitet die Anfrage dauerhaft um (Status 301), wenn replacement mit http(s) beginnt.


Beispiel
server {
# dauerhafte Umleitung von http://www.example.com / http://subdomain.example.com auf http://example.com
server_name .example.com;

rewrite ^ http://example.com$request_uri? permanent;
# oder
rewrite ^(.*) http://example.com$1 permanent;
}

OK, das war es dann auch schon. Mit den Sachen, kann man den Webserver ohne weiteres zum Laufen bringen.
Natürlich war das nur eine Einführung bzw. ein grober Überblick wenn man so will.
Für eine optimale Konfiguration kommt man nicht um das nginx Wiki (http://wiki.nginx.org/) herum.

Zum Schluß noch ein paar Snippets, die dem ein oder anderem sicherlich helfen werden ;)


Snippets

Direktzugriff auf das Verzeichnis LockedFolder sperren
location ^~ /LockedFolder/ {
deny all;
allow 127.0.0.1;
return 403;
}

.htaccess für das Verzeichnis LockedFolder
location ^~ /LockedFolder/ {
auth_basic "Restricted";
auth_basic_user_file /path/to/.htpasswd;
autoindex on;
allow all;
}

Hotlink Schutz für alle Bilder
location ~* \.(jpe?g|png|bmp|gif)$ {
# prüfen ob der Referer gültig ist, falls nicht gib 404 zurück
# blocked = Referer ist maskiert
# none = Referer fehlt
# server_names = definierte server_names
valid_referers blocked none server_names;
if ($invalid_referer) {
return 404;
}
}
...
# muss unterhalb des Hotlink Schutzes gesetzt werden, da dieser sonst nicht greift
root /path/to/www

bestimmte Dateitypen mit maximalen Expire Header ausliefern
location ~* \.(css|js)$ {
expires max;
}

Announce auf anderen Port umleiten
server {
listen 80;
server_name example.com;
rewrite ^/announce.php$ http://example.com:81/announce.php permanent;
}

HTTPS vHost
server {
listen 443;
server_name example.com;

ssl on;
ssl_certificate /path/to/.crt;
ssl_certificate_key /path/to/.key;
ssl_client_certificate /path/to/.pem;
ssl_protocols SSLv2 SSLv3 TLSv1;
ssl_ciphers ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP;
ssl_prefer_server_ciphers on;

...
}

DoS Schutz
# kann für jede location extra definiert werden
http {
...
# Zone "dosprotection" mit 10MB um die Sessions zu verwalten - 30 Requests pro Sekunde sind erlaubt, alles darüber wird geblockt
# muss bei einer stark frequentierten Seite ausbalanciert werden, da sonst einige User geblockt werden können
limit_req_zone $binary_remote_addr zone=dosprotection:10m rate=30r/s;
# ab v1.1.18 limit_conn_zone sonst limit_zone
# Zone "slimits" und deren Größe für die Speicherung der Sessions wird definiert
limit_conn_zone slimits $binary_remote_addr 10m;
# maximale simultane Verbindungen in der Zone "slimits" pro Session - alles darüber bekommt einen 503
limit_conn slimits 20;
...
location / {
...
# User darf 30 Requests die Sekunde haben, wenn es mal mehr sein soll, dann können es auch maximal 60 sein.
# Liegt der erlaubte Requestanzahl mehrere Sekunden lang über 30, wird der Überschuss aufaddiert, bis 60 erreicht ist.
# Danach gibt es für den User einen 503er.
limit_req zone=dosprotection burst=60;
}
...
}

Reverse Proxy (z.B. sicherer Serverstandort als Proxy, billiger Server für Inhalte)
server {
listen 1.2.3.4:80;
server_name example.com;

location / {
proxy_pass http://5.6.7.8:80;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host 5.6.7.8;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
...
}
}

IPv6 aktivieren
# IPv4 & IPv6
listen [::]:80;

# nur IPv6
listen [::]:80 ipv6only=on;

tantetoni2
31.05.2012, 19:12
fein fein ;)

bl0bb
31.05.2012, 19:30
Ich seh grad, in dem rewrite Beispiel, da muss

rewrite ^(.*) http://example.com$1 permanent;

hin. Das $1 fehlt, k.A. warum. Wenn ich auf Ändern gehe, wird es angezeigt.