Deutsch| English

Konfigurierung Ihres LEMP Systems (Linux, nginx, MySQL, PHP-FPM) für maximale Leistung

Version 1.0
Author: Falko Timme <ft [at] falkotimme [dot] com>
Follow me on Twitter
Last edited 09/22/2012

Benutzen Sie nginx als Webserver, so sind Sie auf einenPerformanceboost und Geschwindigkeit aus. Nginx ist von Haus aus schnell, Sie können seine Leistung und die der Teile, die mit ihm zusammenarbeiten (wie PHP und MySQL) aber noch weiter optimieren. Hier finden Sie eine kleine Liste von Tipps und Tricks zur Leistungsoptimierung von LEMP. Diese Tricks funktionieren bei mir, sie könnten sich auf anderen Systemen aber auch weniger stark bemerkbar machen. Implementieren Sie sie nicht alle auf einmal, sondern einen nach dem anderen. Überprüfen Sie nach einer Implementierung, wie sich die Änderung auf Ihre Leistung auswirkt.

Für die Richtigkeit der Inhalte dieses Tutorials gebe ich keinerlei Garantie.

1 Reduzierung des Disk I/O durch mounten von Partitionen mit noatime und nodiratime

Fügen Sie noatime und nodiratime zu Ihren Mountoptionen in /etc/fstab hinzu:

vi /etc/fstab

# /etc/fstab: static file system information.
#
# Use 'blkid' to print the universally unique identifier for a
# device; this may be used with UUID= as a more robust way to name devices
# that works even if disks are added and removed. See fstab(5).
#
# <file system> <mount point> <type> <options> <dump> <pass>
proc /proc proc defaults 0 0
# / was on /dev/sda2 during installation
UUID=9cc886cd-98f3-435a-9830-46b316e2a20e / ext3 errors=remount-ro,noatime,nodiratime,usrjquota=quota.user,grpjquota=quota.group,jqfmt=vfsv0 0 1
# swap was on /dev/sda1 during installation
UUID=bba13162-121d-40a4-90a7-10f78a0097ae none swap sw 0 0
/dev/scd0 /media/cdrom0 udf,iso9660 user,noauto 0 0

#Parallels Shared Folder mount
none /media/psf prl_fs sync,nosuid,nodev,noatime,share,nofail 0 0

Mounten Sie die modifizierten Partitionen danach wieder wie folgt (stellen Sie sicher, dass Sie für jede Partition den richtigen Mountpunkt benutzen):

mount -o remount /

Mehr darüber können Sie in diesem Howto lesen: Reducing Disk IO By Mounting Partitions With noatime

2 Nginx tunen

2.1 worker_processes

Stellen Sie sicher, dass Sie die richtige Anzahl worker_processes in Ihrer /etc/nginx/nginx.conf verwenden. Diese sollte gleich der Anzahl von CPU Kernen ind er Ausgabe folgendes Befehls sein:

cat /proc/cpuinfo | grep processor

root@server1:~# cat /proc/cpuinfo | grep processor
processor : 0
processor : 1
processor : 2
processor : 3
processor : 4
processor : 5
processor : 6
processor : 7
root@server1:~#

In diesem Beispiel haben wir acht CPU Kerne, deshalb setzen wir

vi /etc/nginx/nginx.conf

[...]
worker_processes 8;
[...]

2.2 keepalive_timeout, sendfile, tcp_nopush, tcp_nodelay

Setzen Sie keepalive_timeout auf einen sinnvollen Wert, wie z.B. zwei Sekunden. Erlauben Sie sendfile, tcp_nopush und tcp_nodelay:

vi /etc/nginx/nginx.conf

[...]
http {
[...]
 sendfile on;
 tcp_nopush on;
 tcp_nodelay on;
 keepalive_timeout 2;
 types_hash_max_size 2048;
 server_tokens off;
[...]
}
[...]

2.3 File Cache

Erlauben Sie den Nginx File Cache:

vi /etc/nginx/nginx.conf

[...]
http {
[...]
 ##
 # File Cache Settings
 ##

 open_file_cache max=5000 inactive=20s;
 open_file_cache_valid 30s;
 open_file_cache_min_uses 2;
 open_file_cache_errors on;
[...]
}
[...]

2.4 Erlauben der Gzip Kompression

Hier können Sie mehr über Gzip Kompression erfahren: How To Save Traffic With nginx’s HttpGzipModule (Debian Squeeze)

vi /etc/nginx/nginx.conf

[...]
http {
[...]
 ##
 # Gzip Settings
 ##

 gzip on;
 gzip_static on;
 gzip_disable "msie6";
 gzip_http_version 1.1;
 gzip_vary on;
 gzip_comp_level 6;
 gzip_proxied any;
 gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript application/javascript text/x-js;
 gzip_buffers 16 8k;
[...]
}
[...]

2.5 Erlauben des SSL Session Caches

Falls Sie https Webseiten ausliefern, sollten Sie den SSL Session Cache aktivieren:

vi /etc/nginx/nginx.conf

[...]
http {
[...]
 ssl_session_cache shared:SSL:10m;
 ssl_session_timeout 10m;
 ssl_ciphers HIGH:!aNULL:!MD5;
 ssl_prefer_server_ciphers on;
[...]
}
[...]

2.6 Benutzen des FastCGI Caches

Haben Sie cachebaren PHP Inhalt, so können Sie den Nginx FastCGI Cache benutzen um diesen zu cachen. Fügen Sie dazu in Ihrer nginx.conf eine Zeile ähnlich dieser ein:

vi /etc/nginx/nginx.conf

[...]
http {
[...]
 fastcgi_cache_path /var/cache/nginx levels=1:2 keys_zone=microcache:10m max_size=1000m inactive=60m;
[...]
}
[...]

Das Cacheverzeichnis /var/cache/nginx muss existieren und von Nginx beschreibbar sein:

mkdir /var/cache/nginx
chown www-data:www-data /var/cache/nginx

(Druch Benutzung von tmpfs können Sie das Verzeichnis sogar direkt im Speicher Ihres Servers platzieren, was einen weiteren Geschwindigkeitsvorteil erbringt - schauen Sie sich dazu dieses Howto an: Storing Files/Directories In Memory With tmpfs).

Fügen Sie in Ihrer vHost Konfiguration folgenden Block zur location ~ .php$ {} Sektion hinzu (wann Inhalte gecached werden und wann nicht können Sie hier einstellen):

[...]
 # Setup var defaults
 set $no_cache "";
 # If non GET/HEAD, don't cache & mark user as uncacheable for 1 second via cookie
 if ($request_method !~ ^(GET|HEAD)$) {
 set $no_cache "1";
 }
 # Drop no cache cookie if need be
 # (for some reason, add_header fails if included in prior if-block)
 if ($no_cache = "1") {
 add_header Set-Cookie "_mcnc=1; Max-Age=2; Path=/";
 add_header X-Microcachable "0";
 }
 # Bypass cache if no-cache cookie is set
 if ($http_cookie ~* "_mcnc") {
 set $no_cache "1";
 }
 # Bypass cache if flag is set
 fastcgi_no_cache $no_cache;
 fastcgi_cache_bypass $no_cache;
 fastcgi_cache microcache;
 fastcgi_cache_key $scheme$host$request_uri$request_method;
 fastcgi_cache_valid 200 301 302 10m;
 fastcgi_cache_use_stale updating error timeout invalid_header http_500;
 fastcgi_pass_header Set-Cookie;
 fastcgi_pass_header Cookie;
 fastcgi_ignore_headers Cache-Control Expires Set-Cookie;
[...]

Der komplette location ~ .php$ {} Block sähe also folgendermaßen aus:

[...]
location ~ .php$ {

 # Setup var defaults
 set $no_cache "";
 # If non GET/HEAD, don't cache & mark user as uncacheable for 1 second via cookie
 if ($request_method !~ ^(GET|HEAD)$) {
 set $no_cache "1";
 }
 # Drop no cache cookie if need be
 # (for some reason, add_header fails if included in prior if-block)
 if ($no_cache = "1") {
 add_header Set-Cookie "_mcnc=1; Max-Age=2; Path=/";
 add_header X-Microcachable "0";
 }
 # Bypass cache if no-cache cookie is set
 if ($http_cookie ~* "_mcnc") {
 set $no_cache "1";
 }
 # Bypass cache if flag is set
 fastcgi_no_cache $no_cache;
 fastcgi_cache_bypass $no_cache;
 fastcgi_cache microcache;
 fastcgi_cache_key $scheme$host$request_uri$request_method;
 fastcgi_cache_valid 200 301 302 10m;
 fastcgi_cache_use_stale updating error timeout invalid_header http_500;
 fastcgi_pass_header Set-Cookie;
 fastcgi_pass_header Cookie;
 fastcgi_ignore_headers Cache-Control Expires Set-Cookie;

 try_files $uri =404;
 include /etc/nginx/fastcgi_params;
 fastcgi_pass unix:/var/lib/php5-fpm/web1.sock;
 fastcgi_index index.php;
 fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
 fastcgi_param PATH_INFO $fastcgi_script_name;
 fastcgi_intercept_errors on;
}
[...]

Dieser Block cached Seiten mit den Ausgabecodes 200, 301 und 302 für zehn Minuten.

Hier können Sie mehr zu diesem Thema nachlesen: Why You Should Always Use Nginx With Microcaching

2.7 Benutzung von FastCGI Buffern

Sie können in Ihrer vHost Konfiguration folgende Zeilen zur location ~ .php$ {} Sektion hinzufügen:

[...]
 fastcgi_buffer_size 128k;
 fastcgi_buffers 256 16k;
 fastcgi_busy_buffers_size 256k;
 fastcgi_temp_file_write_size 256k;
 fastcgi_read_timeout 240;
[...]

Die ganze location ~ .php$ {} Sektion könnte wie folgt aussehen:

[...]
location ~ .php$ {
 try_files $uri =404;
 include /etc/nginx/fastcgi_params;
 fastcgi_pass unix:/var/lib/php5-fpm/web1.sock;
 fastcgi_index index.php;
 fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
 fastcgi_param PATH_INFO $fastcgi_script_name;
 fastcgi_intercept_errors on;

 fastcgi_buffer_size 128k;
 fastcgi_buffers 256 16k;
 fastcgi_busy_buffers_size 256k;
 fastcgi_temp_file_write_size 256k;
 fastcgi_read_timeout 240;
}
[...]

2.8 Benutzung von memcached

nginx kann ganze Seiten direkt aus memcached auslesen. Ist Ihre Webanwendung also dazu fähig, ganze Seiten in memcached zu speichern, kann nginx diese Seiten auslesen. Eine Beispielkonfiguration (in Ihrem vHost) könnte folgendermaßen aussehen:

[...]
 location ~ .php$ {
 set $no_cache "";
 if ($query_string ~ ".+") {
 set $no_cache "1";
 }
 if ($request_method !~ ^(GET|HEAD)$ ) {
 set $no_cache "1";
 }
 if ($request_uri ~ "nocache") {
 set $no_cache "1";
 }
 if ($no_cache = "1") {
 return 405;
 }

 set $memcached_key $host$request_uri;
 memcached_pass 127.0.0.1:11211;
 default_type text/html;
 error_page 404 405 502 = @php;
 expires epoch;
 }

 location @php {
 try_files $uri =404;
 include /etc/nginx/fastcgi_params;
 fastcgi_pass unix:/var/lib/php5-fpm/web1.sock;
 fastcgi_index index.php;
 fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
 fastcgi_param PATH_INFO $fastcgi_script_name;
 fastcgi_intercept_errors on;
 }
[...]

Es ist wichtig, dass Ihre Webanwendung zum Speichern der Seiten den selben Schlüssel benutzt wie nginx um Sie von memcached abzuholen (in diesem Beispiel ist es $host$request_uri), andernfalls wird es nicht funktionieren.

Speichern Sie viele Daten in memcached müssen Sie sicherstellen, dass Sie memcached ausreichend RAM zugewiesen haben, zum Beispiel:

vi /etc/memcached.conf

[...]
# Start with a cap of 64 megs of memory. It's reasonable, and the daemon default
# Note that the daemon will grow to this size, but does not start out holding this much
# memory
-m 512
[...]

2.9 Browser statische Daten mit der expires Direktive cachen lassen

Dateien, die nicht oft geändert werden (wie Bilder, CSS, JS, etc.), können vom Browser des Besuchers mit Hilfe der expires Direktive gecached werden (siehe http://wiki.nginx.org/HttpHeadersModule#expires):

[...]
 location ~* .(jpg|jpeg|png|gif|ico)$ {
 expires 365d;
 }
[...]

2.10 Logging bei statischen Dateien deaktivieren

Normalerweise macht es keinen Sinn Zugriffe auf Bilder oder CSS im Zugriffslog zu protokollieren. Um Disk I/O zu minimieren können Sie deren Protokollierung deaktivieren, zum Beispiel folgendermaßen:

[...]
 location ~* .(jpg|jpeg|png|gif|ico)$ {
 log_not_found off;
 access_log off;
 }
[...]

3 PHP-FPM tunen

3.1 Benutzung eines PHP Opcode Caches wie Xcache oder APCse A PHP Opcode Cache Like Xcache Or APC

Stellen Sie sicher, dass Sie einen PHP opcode Cache wie Xcache oder APC installiert haben. Unter Debian/Ubuntu kann Xcache folgendermaßen installiert werden:

apt-get install php5-xcache

APC kann so installiert werden:

apt-get install php-apc

Installieren Sie jedoch nur einen von ihnen, nicht beide (entweder Xcache oder APC). Laden Sie PHP-FPM nach der Installation neu:

/etc/init.d/php5-fpm reload

3.2 Weisen Sie Xcache/APC ausreichend Arbeitsspeicher zu

Benutzen Sie eine Vielzahl von PHP Skrips, so sollten Sie den Xcache/APC zugewiesenen Arbeitsspeicher wahrscheinlich erhöhen. Im Falle von Xcache können Sie dies in der /etc/php5/conf.d/xcache.ini Datei tun:

vi /etc/php5/conf.d/xcache.ini

[...]
xcache.size = 512M
[...]

Dem entsprechend für APC:

vi /etc/php5/conf.d/apc.ini

[...]
apc.shm_size="512"
[...]

Laden Sie PHP-FPM nach dieser Modifikation neu:

/etc/init.d/php5-fpm reload

3.3 PHP-FPM Notfalleinstellungen

Dies ist eher eine Vorsorgeeinstellung als eine, die die Leistung betrifft: PHP-FPM kann sich selbst neustarten, wenn es aufhört zu funktionieren:

vi /etc/php5/fpm/php-fpm.conf

[...]
; If this number of child processes exit with SIGSEGV or SIGBUS within the time
; interval set by emergency_restart_interval then FPM will restart. A value
; of '0' means 'Off'.
; Default Value: 0
emergency_restart_threshold = 10

; Interval of time used by emergency_restart_interval to determine when
; a graceful restart will be initiated. This can be useful to work around
; accidental corruptions in an accelerator's shared memory.
; Available Units: s(econds), m(inutes), h(ours), or d(ays)
; Default Unit: seconds
; Default Value: 0
emergency_restart_interval = 1m

; Time limit for child processes to wait for a reaction on signals from master.
; Available units: s(econds), m(inutes), h(ours), or d(ays)
; Default Unit: seconds
; Default Value: 0
process_control_timeout = 10s
[...]

3.4 Benutzung des ondemand Prozessmanagers bei PHP >= 5.3.9

Benutzen Sie PHP >= 5.3.9 so können Sie den ondemand Prozessmanager in einem PHP-FPM Pool anstatt von static oder dynamic benutzen, dies wird einigen Arbeitsspeicher einsparen:

[...]
pm = ondemand
pm.max_children = 100
pm.process_idle_timeout = 5s
[...]

3.5 Benutzung von Unix Sockets anstatt von TCP Sockets

Um Netzwerkoverhead zu reduzieren sollten Sie Ihre Pools anweisen, Unix Sockets anstatt von TCP Sockets zu benutzen:

[...]
;listen = 127.0.0.1:9000
listen = /var/lib/php5-fpm/www.sock
listen.owner = www-data
listen.group = www-data
listen.mode = 0660
[...]

Ändern Sie dies, müssen Sie natürlich die location ~ .php$ {} Sektion in Ihrem nginx vHost anpassen, sodass der Socket benutzt wird (fastcgi_pass unix:/var/lib/php5-fpm/www.sock; anstatt von fastcgi_pass 127.0.0.1:9000;):

[...]
location ~ .php$ {
 try_files $uri =404;
 include /etc/nginx/fastcgi_params;
 ##fastcgi_pass 127.0.0.1:9000;
 fastcgi_pass unix:/var/lib/php5-fpm/www.sock;
 fastcgi_index index.php;
 fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
 fastcgi_param PATH_INFO $fastcgi_script_name;
 fastcgi_intercept_errors on;
}
[...]

3.6 Vermeiden Sie 502 Bad Gateway Fehler mit Sockets auf geschäftigen Seiten

Benutzen Sie Unix Sockets mit PHP-FPM, so könnten Sie 502 Bad Gateway Fehlern auf oft besuchten Seiten begegnen. Um dies zu vermeiden, erhöhen Sie die maximale Anzahl an erlaubten Verbindungen mit einem Socket. Öffnen Sie /etc/sysctl.conf

vi /etc/sysctl.conf

… und setzen Sie:

[...]
net.core.somaxconn = 4096
[...]

Benutzen Sie danach

sysctl -p

um die Änderungen geltend zu machen.

4 MySQL tunen

4.1 Optimierung Ihrer my.cnf

Sie sollten Skripts wie mysqltuner.pl oder tuning-primer.sh (oder beide) benutzen um herauszufinden, welche Einstellungen Sie in Ihrer my.cnf Datei anpassen sollten. Eine der wichtigsten Variablen ist query_cache_size, und, falls Sie InnoDB Tabellen benutzen, innodb_buffer_pool_size.

Folgendes ist eine Beispielkonfiguration eines Testservers mit 16 GB Arbeitsspeicher, ungefähr 30 Datenbanken mit 50% MyISAM Tabellen und 50% InnoDB Tabellen - sie hat sich bei Datenbankbasierten Testseiten als gut herausgestellt (diese wurden von einem Benchmarktool ausgereizt (ab)):

[...]
key_buffer = 256M

max_allowed_packet = 16M
thread_stack = 192K
thread_cache_size = 100

table_open_cache = 16384
table_definition_cache = 8192

sort_buffer_size = 256K

read_buffer_size = 128K

read_rnd_buffer_size = 256K

myisam_sort_buffer_size = 64M
myisam_use_mmap = 1
thread_concurrency = 10
wait_timeout = 30

myisam-recover = BACKUP,FORCE

query_cache_limit = 10M
query_cache_size = 1024M
query_cache_type = 1

join_buffer_size = 4M

log_slow_queries = /var/log/mysql/mysql-slow.log
long_query_time = 1

expire_logs_days = 10
max_binlog_size = 100M

innodb_buffer_pool_size = 2048M
innodb_log_file_size = 256M
innodb_log_buffer_size = 16M
innodb_flush_log_at_trx_commit = 0
innodb_thread_concurrency = 8
innodb_read_io_threads = 64
innodb_write_io_threads = 64
innodb_io_capacity = 50000
innodb_flush_method = O_DIRECT
innodb_file_per_table
innodb_additional_mem_pool_size = 256M
transaction-isolation = READ-COMMITTED

innodb_support_xa = 0
innodb_commit_concurrency = 8
innodb_old_blocks_time = 1000
[...]

Beachten Sie bitte: Benötigen Sie ACID Konformität müssen Sie innodb_flush_log_at_trx_commit auf 1 setzen. Hier können Sie mehr darüber herausfinden: http://dev.mysql.com/doc/refman/5.5/en/innodb-parameters.html#sysvar_innodb_flush_log_at_trx_commit.

innodb_io_capacity sollte nur auf einen hohen Wert gesetzt werden wenn Sie MySQL auf einer SSD benutzen. Benutzen Sie es auf einer normalen Festplatte, lassen Sie diese Zeile besser aus.

4.2 Benutzung einer SSD

Ein riesiger Leistungsboost ist möglich wenn Sie MySQL mit einer Solid State Disk (SSD) benutzen, da dies die Disk I/O stark verringert. Der einfachste Weg dies zu erreichen ist, das /var/lib/mysql Verzeichnis auf einer SSD zu mounten.

5 Caching von Webanwendungen

Viele Webanwendungen (wie z.B. WordPress mit dem WP Super Cache oder W3 Total Cache Plugins, Drupal mit dem Boost module, TYPO3 mit der nc_staticfilecache Erweiterung) bieten die Möglichkeit an, einen Full-Page-Cache anzulegen, welcher auf der Festplatte gespeichert wird und auf den nginx direkt zugreifen kann, sodass der gesamte PHP-MySQL Stapel umgangen wird. Dies bringt einen riesigen Leistungsschub.

Hier finden Sie Tutorials zu diesem Thema:

Sie können den Cache für statische Dateien sogar noch stärker beschleunigen, indem Sie ihn mit dem tmpfs direkt im Arbeitsspeicher des Servers platzieren:

Storing Files/Directories In Memory With tmpfs

Natürlich können Sie tmpfs auch für den nginx FastCGI Cache aus Kapitel 2.6 verwenden.

6 Links

Über den Autor

Falko Timme ist der Besitzer von Boost Your Site mit Timme Hosting - ultra-schnelles nginx-WebhostingTimme Hosting (ultra-schnelles nginx Webhosting). Er ist der Hauptzuständige für HowtoForge (seit 2005) und einer der Kernentwickler von ISPConfig (seit 2000), außerdem hat er zum O’Reilly Buch “Linux System Administration” beigetragen.
vgwort />

 

 

One Response to “Konfigurierung Ihres LEMP Systems (Linux, nginx, MySQL, PHP-FPM) für maximale Leistung”

  1. Martin Herbst Sagt:

    Auf meinem Web Server (Strato VServer mit AMD Opteron) liefert cat /proc/cpuinfo folgendes
    processor: 0
    siblings: 8
    core id: 0
    cpu cores: 8
    Daraus würde ich schliessen, das ein Prozessor mit 8 Cores ist.

Kommentar

Du musst eingeloggt um einen Kommentar zu hinterlassen.