Installation pas à pas d’un serveur de blog WordPress sur Debian Squeeze
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 fraiche de Debian Squeeze (version stable 6.0.2) 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):
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:
chmod a+x /etc/init.d/firewall.sh
update-rc.d firewall.sh defaults 20
./firewall.sh
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:
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:
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:
Qui donne la valeur 4 sur ma Dedibox (4 coeurs/CPU).
Puis dans la section html du même fichier, il faut fixer les variables suivantes:
keepalive_timeout 5;
tcp_nodelay off;
tcp_nopush on;
On relance le serveur pour prendre en compte la configuration:
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:
/etc/init.d/php5-fpm restart
Optimiser un peu celle-ci en modifiant quelques variables dans le fichier /etc/mysql/my.cnf:
query_cache_limit = 2M
query_cache_size = 32M
et relancer le daemon pour que la configuration soit prise en compte:
On passe ensuite à la phase de création de la base de données nommée wordpress accessible par utilisateur/motdepasse:
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 version francisée de WordPress disponible sur le site officiel:
wget http://fr.wordpress.org/wordpress-3.2.1-fr_FR.tar.gz
tar zxvf wordpress-3.2.1-fr_FR.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:
index index.php;
# Rewrite rules
if (!-e $request_filename) {
rewrite ^(.+)$ /index.php?q=$1 last;
}
}
Ne pas oublier de relancer NGinx pour prendre en compte cette modification:
Installation du plugin WordPress W3 Total Cache
J'utilise W3 Total Cache depuis maintenant 1 an et je suis très satisfait des performances de ce plugin WordPress qui optimise le temps de chargement et augmente les performances des blogs. L'installation se fait via l'interface Web d'administration de WordPress (menu Extensions > Ajouter).
Une fois installé et activé il faut se rendre dans le menu Performance > General setting de la page d'administration de WordPress puis cliquer sur le bouton Compatibility check pour vérifier que les pré-requis sont installés, notamment:
Memcache extension: Installed
Ensuite on configure WordPress pour utiliser Memcached en activant toutes le fonctions de cache (CDN mis à part) et en utilisant le démon Memcached. Exemple de configuration pour les pages:
Dans le menu Page cache settings j'active toutes les fonctions sauf pour les pages 404:
Dans le menu suivant (Minify settings), j'active toutes les fonctions. Cela à pour but d'optimiser les pages, css et js avant d'être envoyés vers les lecteurs (attention, certains JS n'aiment pas beaucoup ces optimisations, à tester donc...).
Enfin, dans le menu Browser Cache setting, j'active toutes les fonctions et les expires header lifetime à:
- 3600 secondes pour JS / CSS
- 3600 secondes pour le HTML
- 2592000 secondes pour les autres fichiers
A l'heure actuelle, le trafic généré par mon blog ne nécessite pas l'utilisation de caches réseau répartis (CDN). Mais c'est une optimisation à prendre en compte pour les "gros" sites avec des visiteurs venant d'un peu partout sur la planète.
Ne pas oublier de désactiver le "Preview" qui rend caduque toutes les configurations quand il est actif (Enable).
Le plugin devrait produire un fichier nommé nginx.conf à la racine de votre blog (c'et à dire dans /var/www/wordpress). Ce fichier remplace en fait le fichier .htaccess dans le monde Apache. Nous allons devoir le mettre dans la configuration de notre serveur NGinx.
Afin de clarifier cette configuration j'ai créé l'arborescence suivante:
mkdir /etc/nginx/sites-available
mv /etc/nginx/sites-enabled/default-site /etc/nginx/sites-available/
cp /etc/nginx/sites-available/default-site /etc/nginx/sites-available/wordpress
ln -s /etc/nginx/sites-available/wordpress /etc/nginx/sites-enabled/wordpress
Nous allons ensuite mettre le fichier de configuration généré par W3 Total Cache dans le répertoire global puis le renommer avec un nom plus explicite (wordpress-w3-total-cache.conf):
Note: de part l'utilisation d'un permalink utilisant l'extension .html ( /%year%/%monthnum%/%postname%.html), j'ai du éditer ce fichier et supprimer les extensions .html et .htm sur la ligne suivante:
J'ai ensuite créé le fichier 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 global/php-fpm.conf:
location ~ \\.php$ {
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
Pour finalement modifier le fichier de configuration du site (/etc/nginx/site-enabled/wordpress) pour prendre en compte tout cela:
listen 80;
server_name www.mondomaine.com;
location / {
root /var/www/wordpress;
index index.php index.html index.htm;
# Rewrite rules
if (!-e $request_filename) {
rewrite ^(.+)$ /index.php?q=$1 last;
}
# Security
include global/security.conf;
# W3 Total Cache
include global/wordpress-w3-total-cache.conf;
# PHP-FPM
include global/php-fpm.conf;
}
}
On redémarre le serveur Nginx pour appliquer la configuration:
A ce stade, on peut faire quelques tests de performances avec Apache Bench (disponible dans le paquet Debian apache2-utils):
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.
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:
.host = "127.0.0.1";
.port = "8080";
}
sub vcl_recv {
# Rules for all requests
# Only the blog URL is cached
#if (req.http.host ~ "(www.mondomaine.com)") { set req.backend = blog; }
#else { return (pass); }
# Compatiblity with Apache log
remove req.http.X-Forwarded-For;
set req.http.X-Forwarded-For = client.ip;
# Post requests will not be cached
if (req.request == "POST") {
return (pass);
}
# Normalize encoding/compression
if (req.http.Accept-Encoding) {
if (req.http.Accept-Encoding ~ "gzip") { set req.http.Accept-Encoding = "gzip"; }
elsif (req.http.Accept-Encoding ~ "deflate") { set req.http.Accept-Encoding = "deflate"; }
else { remove req.http.Accept-Encoding; }
}
# Remove has_js and Google Analytics __* cookies.
if (req.http.cookie) {
set req.http.Cookie = regsuball(req.http.Cookie, "(^|;s*)(__[a-z]+|has_js)=[^;]*", "");
# Remove a ";" prefix, if present.
set req.http.Cookie = regsub(req.http.Cookie, "^;s*", "");
# Remove empty cookies.
if (req.http.Cookie ~ "^s*$") {
unset req.http.Cookie;
}
}
# Serve the page
unset req.http.vary;
# If I am logged in to wordpress, I DO NOT WANT TO SEE cached pages
if ( req.url ~ "^/wp-(login|admin)" || req.http.Cookie ~ "wordpress_logged_in_" ) {
return (pass);
} else {
# If I'm just a regular visitor
# If the request is static
if (req.url ~ ".(jpeg|jpg|png|gif|ico|js|css|txt|gz|zip|lzma|bz2|tgz|tbz|html|htm)$") {
# Remove the cookie and make the request static
unset req.http.cookie;
return (lookup);
}
# Try to lookup in the cache
return (lookup);
}
# Cookie ? Not cacheable by default
if (req.http.Authorization || req.http.Cookie) {
return (pass);
}
# if host header is empty return 404
error 404 req.http.host;
return (lookup);
}
sub vcl_fetch {
#if (req.http.host ~ "(www.mondomaine.com)") {
# Do not cache POST requests
if (req.request == "POST") {
return (hit_for_pass);
}
# If the request is static
if (req.url ~ ".(jpeg|jpg|png|gif|ico|js|css|txt|gz|zip|lzma|bz2|tgz|tbz|html|htm)$") {
# Cache it, and make it last 2 hours
set beresp.ttl = 7200s;
# Make the request static by removing any cookies set by those static files
unset beresp.http.set-cookie;
# Deliver the cached object
return (deliver);
}
# If I am logged in to wordpress, I DO NOT WANT TO SEE cached pages
if (req.http.cookie ~ "wordpress_logged_in") {
return (hit_for_pass);
} else {
# Cache anything for 2 minutes. When the cache expires it will be cached again and again, at the time of the request
set beresp.ttl = 120s;
return (deliver);
}
#}
}
sub vcl_deliver {
# Secure the header
remove resp.http.Via;
remove resp.http.X-Varnish;
remove resp.http.Server;
remove resp.http.X-Powered-By;
}
Puis la manière dont le daemon va se lancer dans le fichier /etc/default/varnish:
NFILES=131072
MEMLOCK=82000
DAEMON_OPTS="-a :80 \\
-T localhost:6082 \\
-f /etc/varnish/default.vcl \\
-S /etc/varnish/secret \\
-p thread_pool_add_delay=2 \\
-p thread_pools=4 \\
-p thread_pool_min=200 \\
-p thread_pool_max=4000 \\
-p cli_timeout=25 \\
-p session_linger=100 \\
-s malloc,512M"
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):
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:
/etc/init.d/nginx restart
/etc/init.d/varnish restart
Pour que le plugin W3 Total Cache puisse interagir avec Varnish, il suffit d'activer la fonction suivante:
Le site devrait fonctionner normalement mais avec des performances boostées. Par exemple, le même test Apache Bench donne les résultats suivants:
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.