Installation pas à pas d’un serveur de blog WordPress sur Debian Squeeze
Date: 25/08/2011 | Catégories: Blog,Open-source,Planet-libre,Systeme,Web | Tags: debian,memcached,nginx,php-fpm,varnish,w3 total cache,wordpress
Je viens de passer le cap et de m'abonner à un serveur dédié chez Online.net. Mon choix s'est porté vers une Dedibox DC. J'ai longtemps hésité avec une OVH Kimsufi 16G mais le fait que la Dedibox propose en standard deux disques fonctionnant en RAID1 à fait la différence (avec l'âge ou privilégie la sécurité à la performance...).
Avant de migrer mon blog sur ce nouveau serveur (il est actuellement hébergé chez un VPS Gandi 4 parts), j'ai profité de disposer d'un serveur tout neuf pour valider une procédure complète d'installation et d'optimisation d'un blog WordPress sur un serveur Debian Squeeze en utilisant le "stack" Web suivant: Varnish + Nginx + MemCached.
L'objectif de ce billet est de partager cette procédure avec vous.
Introduction
Nous allons donc détaillé l'installation d'un blog WordPress sur une installation toute fraîche de Debian Squeeze (version stable) pré-installé dans mon cas par Online.net avec un minimum de logiciels (pas d'Apache ni d'autres serveurs Web). Vous l'avez compris, pour suivre la procédure suivante, il faut s'assurer qu'aucun serveur Web n'est installé sur votre machine.
Post installation du système
J'ai pris l'habitude de créer des scripts de post installation pour les OS (desktop et server) que j'utilise. Dans le cas qui nous intéresse je vais donc utiliser le script: squeezeserverpostinstall.sh.
Pour le télécharger puis le lancer, il suffit de saisir les commandes suivantes à partir d'un compte administrateur ou directement en root):
wget --no-check-certificate https://raw.github.com/nicolargo/debianpostinstall/master/squeezeserverpostinstall.sh chmod a+x squeezeserverpostinstall.sh ./squeezeserverpostinstall.sh
Comme le serveur est directement connecté à Internet et à moins d'être très joueur, je vous conseille de configurer quelques règles de Firewall. J'ai mis à disposition un jeu de règles assez facile à modifier en éditant le fichier /etc/init.d/firewall.sh. Pour le télécharger et l'installer:
wget --no-check-certificate -O /etc/init.d/firewall.sh https://raw.github.com/nicolargo/debianpostinstall/master/firewall.sh chmod a+x /etc/init.d/firewall.sh update-rc.d firewall.sh defaults 20 service firewall start
Note: par défaut les ports SSH entrant et HTTP et DNS sortant sont autorisés.
Pour modifier ces listes, il suffit de configurer les variables suivantes dans le fichier /etc/init.d/firewall.sh:
# Services that the system will offer to the network TCP_SERVICES="22" # SSH only UDP_SERVICES="" # Services the system will use from the network REMOTE_TCP_SERVICES="80 443" # web browsing REMOTE_UDP_SERVICES="53" # DNS
A ce stade, vous devriez avoir un serveur à jour et sécurisé. Passons donc à l'étape suivante.
Installation de Nginx + PHP-FPM + Memcached
C'est actuellement une des combos les plus performantes pour héberger des serveurs Web basées sur PHP (ce qui est le cas du CMS WordPress). Pour effectuer simplement et rapidement ces logiciels, j'utilise un script maison: nginxautoinstall.sh. Il faut donc saisir les commandes suivantes:
wget --no-check-certificate https://raw.github.com/nicolargo/debianpostinstall/master/nginxautoinstall.sh chmod a+x nginxautoinstall.sh ./nginxautoinstall.sh
Le script va installer la dernière version stable de Nginx puis le daemon PHP-FPM qui permet de booster les performances des scripts PHP et enfin le gestionnaire de cache mémoire MemCached (note: le script fonctionne également sur Debian Lenny 5).
Pour adapter les performances de Nginx à votre CPU, il faut modifier la variable worker_processes le fichier /etc/nginx/nginx.conf. Pour obtenir la valeur optimale pour votre système, vous pouvez lancer la commande suivante:
cat /proc/cpuinfo | grep processor | wc -l
Qui donne la valeur 4 sur ma Dedibox (4 coeurs/CPU). On édite le fichier nginx.conf de la manière suivante:
user www-data; # Set this value to 1 or N with N = N-Core worker_processes 4; worker_rlimit_nofile 8192; events { # max_clients = worker_processes * worker_connections worker_connections 1024; # Only for Linux 2.6 or > use epoll; # Accept as many connections as possible multi_accept on; } http { # Mime types include mime.types; default_type application/octet-stream; # Log format set_real_ip_from 127.0.0.1; real_ip_header X-Forwarded-For; log_format main '$remote_addr - $remote_user [$time_local] $status ' '"$request" $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"'; # Hide the Nginx version number server_tokens off; # Some tweeks... sendfile on; tcp_nodelay on; #tcp_nopush on; # Timeouts #keepalive_timeout 10 10; keepalive_timeout 65; client_body_timeout 30; client_header_timeout 30; send_timeout 30; client_max_body_size 8M; reset_timedout_connection on; # Gzip module configuration gzip on; gzip_disable "MSIE [1-6].(?!.*SV1)"; gzip_vary on; gzip_comp_level 3; gzip_proxied any; gzip_buffers 16 8k; include /etc/nginx/conf.d/*.conf; include /etc/nginx/sites-enabled/*; }
On relance le serveur pour prendre en compte la configuration:
service nginx restart
Installation du langage PHP
WordPress use et abuse du langage PHP, une installation optimisé du moteur permettant d'exécuter le code est donc nécessaire. Personnellement j'utilise l'implémentation PHP5 FPM qui est réputé pour ses performances. Elle est installé par défaut avec mon script d'auto-install de Nginx.
La configuration est stocké dans le répertoire /etc/php5/fpm. Mon fichier php-fpm.conf ressemble à cela:
[global] pid = /var/run/php5-fpm.pid error_log = /var/log/php5-fpm.log emergency_restart_interval = 1m process_control_timeout = 10s include=/etc/php5/fpm/pool.d/*.conf
Avec WordPress comme CMS, je vous encourage à stocker les fichiers de sessions dans une base NoSQL de type Redis afin de ne pas se retrouver avec des milliers de petits fichiers dans le répertoire /tmp. Pour cela, il suffit d'ajouter les lignes suivantes dans le fichier /etc/php5/fpm/php.ini:
session.save_handler = "redis" session.save_path = "tcp://127.0.0.1:6379?weight=1"
Dernière étape et non des moindres, la configuration du pool de processus PHP-FPM que vous allez dédier à votre blog WordPress (par exemple /etc/php5/fpm/pool.d/www.conf dans mon cas):
[www] user = www-data group = www-data listen = 127.0.0.1:9000 pm = dynamic pm.max_children = 5 pm.start_servers = 2 pm.min_spare_servers = 1 pm.max_spare_servers = 3 pm.status_path = /status request_terminate_timeout = 120s rlimit_files = 65535 chdir = /
On peut relancer PHP
service php5-fpm restart
et passer à l'étape suivante, la base de donnée...
Installation de MySQL
A l'heure actuelle, WordPress utilise une base de donnée MySQL pour fonctionner (je préférerai PgSQL mais bon...). Il faut donc installer le serveur de base de donnée MySQL:
apt-get install mysql-server php5-mysql service php5-fpm restart
Optimiser un peu celle-ci en modifiant quelques variables dans le fichier /etc/mysql/my.cnf:
query_cache_type = 1 query_cache_limit = 2M query_cache_size = 32M
et relancer le daemon pour que la configuration soit prise en compte:
service mysql restart
On passe ensuite à la phase de création de la base de données nommée wordpress accessible par utilisateur/motdepasse:
# mysql -u root -p mysql> create database wordpress; Query OK, 1 row affected (0.00 sec) mysql> GRANT ALL PRIVILEGES ON wordpress.* TO "utilisateur"@"localhost" IDENTIFIED BY "motdepasse"; Query OK, 0 rows affected (0.00 sec) mysql> FLUSH PRIVILEGES; Query OK, 0 rows affected (0.00 sec) mysql> exit
Installation du CMS WordPress
J'utilise la dernière version stable de WordPress disponible sur le site officiel:
cd /var/www wget http://wordpress.org/latest.tar.gz tar zxvf latest.tar.gz cp wordpress/wp-config-sample.php wordpress/wp-config.php chown -R www-data:www-data /var/www/wordpress
Ensuite on configure la base de donnée dans le fichier wordpress/wp-config.php:
... define('DB_NAME', 'wordpress'); define('DB_USER', 'utilisateur'); define('DB_PASSWORD', 'motdepasse'); define('WP_CACHE', true); ...
Il suffit ensuite de finaliser l'installation de WordPress en pointant un navigateur Web vers http://votredomaine.com/wordpress/wp-admin/install.php.
Si vous avez changé la structure du permalink (par exemple chez moi c'est /%year%/%monthnum%/%postname%.html), il faut modifer la configuration Nginx, plus précisément la section "Location /" dans le fichier /etc/nginx/sites-enabled/default-site:
server{ listen 80; server_name blog.nicolargo.com; root /var/www/blog.nicolargo.com; index index.php index.html index.htm; access_log /var/log/nginx/blog.access_log; error_log /var/log/nginx/blog.error_log; # Security include global/security.conf; location / { # This is cool because no php is touched for static content. # include the "?$args" part so non-default permalinks doesn't break when using query string try_files $uri $uri/ /index.php?$args; } # PHP-FPM include global/php-fpm.conf; # STATICS FILES location ~* \.(js|css|png|jpg|jpeg|gif|ico)$ { expires max; log_not_found off; } }
avec les fichiers inclus suivants:
Securité /etc/nginx/global/security.conf:
location = /favicon.ico { log_not_found off; access_log off; } location = /robots.txt { allow all; log_not_found off; access_log off; } location ~ /\. { deny all; access_log off; log_not_found off; }
et PHP-FPM /etc/nginx/global/php-fpm.conf:
# PHP scripts -> PHP-FPM server listening on 127.0.0.1:9000 location ~ \.php$ { # The following line prevents malicious php code to be executed through some uploaded file (without php extension, like image) # This fix shoudn't work though, if nginx and php are not on the same server, other options exist (like unauthorizing php execution within upload folder) # More on this serious security concern in the "Pass Non-PHP Requests to PHP" section, there http://wiki.nginx.org/Pitfalls try_files $uri =404; # PHP # NOTE: You should have "cgi.fix_pathinfo = 0;" in php.ini fastcgi_pass 127.0.0.1:9000; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; include fastcgi_params; fastcgi_param QUERY_STRING $query_string; fastcgi_param REQUEST_METHOD $request_method; fastcgi_param CONTENT_TYPE $content_type; fastcgi_param CONTENT_LENGTH $content_length; fastcgi_intercept_errors on; fastcgi_ignore_client_abort off; fastcgi_connect_timeout 60; fastcgi_send_timeout 180; fastcgi_read_timeout 180; fastcgi_buffers 4 256k; fastcgi_buffer_size 128k; #fastcgi_buffers 256 16k; #fastcgi_buffer_size 16k; fastcgi_busy_buffers_size 256k; fastcgi_temp_file_write_size 256k; }
Ne pas oublier de relancer NGinx pour prendre en compte cette modification:
service nginx restart
Installation du plugin WP Super Cache
Après quelques années sous d'utilisation de W3 Total Cache et la mise en place de mon proxy/cache Varnish, j'ai choisi d'utiliser le plugin WP Super Cache qui s'occupe de cacher les pages demandées par les utilisateurs non identifiées (donc pas 90% du trafic) afin que PHP et MySQL ne soit pas appelé lors de ces requêtes.
Il est également possible de précharger la mise en cache des pages les plus visitées et de les mettre automatiquement à jour de manière régulière.
Une fois installé et activé il faut se rendre dans le menu de configuration du plugin et de cliquer sur l'onglet "Easy" et "Mise en cache Activée (Recommandé)".
A ce stade, on peut faire quelques tests de performances avec Apache Bench (disponible dans le paquet Debian apache2-utils):
[cce lang="bash"]
Requests per second: 219.53 [#/sec] (mean) (options -t 30 -c 5)
[/cce]
ou avec le service en ligne Load Impact qui permet de simuler gratuitement jusqu'à 50 utilisateurs simultanés sur votre site:
On voit bien que les page du blog se chargent rapidement (environ 500ms) même avec 50 utilisateurs simultanés.
Puis arriva Varnish...
J'ai mis à jour ma configuration de Varnish+Nginx pour WordPress dans le billet suivant. Vous pouvez le suivre en lieu et place du chapitre qui suit...
Vous savez tout le bien que je pense de Varnish. Nous allons donc maintenant ajouter cet accélérateur de site Web dans notre configuration. Il est à noter que cette étape est optionnelle. Vous pouvez tout à fait rester avec la configuration du chapitre précédent qui offre déjà de belles performances.
On commence par installer la dernière version de Varnish en utilisant le dépôt officiel.
apt-get install curl curl http://repo.varnish-cache.org/debian/GPG-key.txt | apt-key add - echo "deb http://repo.varnish-cache.org/debian/ $(lsb_release -s -c) varnish-3.0" >> /etc/apt/sources.list.d/varnish.list apt-get update apt-get install varnish
La version 3 de Varnish apporte certaines modifications au niveau de la syntaxe des fichiers de configuration. Si vous avez donc une config fonctionnelle en version 2, je vous conseille de lire cette page pour l'adapter.
On commence par éditer le fichier de configuration /etc/varnish/default.vcl:
Puis la manière dont le daemon va se lancer dans le fichier /etc/default/varnish:
Enfin on reconfigure NGinx pour ne plus écouter sur le port 80 (c'est Varnish qui va prendre le relais) mais sur le port 8080. Il suffit de changer la deuxième ligne du fichier /etc/nginx/sites-enabled/wordpress:
... listen 8080; ...
On n'oublie pas d'ouvrir les port au niveau du Firewall (fichier /etc/init.d/firewall.sh):
# Services that the system will offer to the network TCP_SERVICES="22 80" # SSH, Web UDP_SERVICES="" # Services the system will use from the network REMOTE_TCP_SERVICES="25 80 443" # Mail, Web browsing REMOTE_UDP_SERVICES="53" # DNS ...
On relance les services:
service firewall.sh restart service nginx restart service varnish restart
Afin pour que Varnish soit prévenu quand un billet est modifié, il faut installer et activier le plugin Varnish HTTP purge.
Le site devrait fonctionner normalement mais avec des performances boostées. Par exemple, le même test Apache Bench donne les résultats suivants:
Requests per second: 9425.03 [#/sec] (mean) (options -t 30 -c 5)
A comparer avec 220 requêtes par secondes sans Varnish...
On voit même une amélioration du temps de chargement des pages (300ms vs 500ms) qui reste constant avec Load Impact:
Conclusion
On arrive à la fin de ce (trop ?) long billet. Le sujet est vaste et il y a sûrement des améliorations à faire dans la configuration que je viens de vous présenter. Les commentaires ci-dessous sont fait pour partager vos expériences.
Je vous signale également que je regroupe tout les billets sur l'hébergement sur la page suivante.