Dans les près-requis, on peut citer :
*Savoir utiliser [mercurial|http://mercurial.selenic.com/]
*Être root sur la machine
Dans un premier temps, nous allons voir la configuration du serveur, Nginx
!!!!Configuration du serveur
Rien de bien compliqué, la [documentation|http://wiki.nginx.org/Main] est très complète.
Voici __ma__ configuration du fichier @@nginx.conf@@ (situé dans @@/etc/nginx@@).
///
user nginx nginx;
worker_processes 1;
error_log /var/log/nginx/error.log;
#error_log /var/log/nginx/error.log notice;
#error_log /var/log/nginx/error.log info;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
use epoll;
}
http {
include mime.types;
default_type application/octet-stream;
#log_format main ‘$remote_addr – $remote_user [$time_local] « $request » ‘
# ‘$status $body_bytes_sent « $http_referer » ‘
# ‘ »$http_user_agent » « $http_x_forwarded_for »‘;
#access_log /var/log/nginx/access.log main;
log_format main ‘$remote_addr – $remote_user [$time_local] « $request » ‘
‘$status $body_bytes_sent « $http_referer » ‘
‘ »$http_user_agent »‘;
access_log /var/log/nginx/access.log main;
sendfile on;
#tcp_nopush on;
server_tokens off;
#keepalive_timeout 0;
keepalive_timeout 65;
#gzip on;
server {
listen 80;
server_name errements.net;
charset utf-8;
autoindex on;
index index.html;
# favicon.ico
location = /favicon.ico {
log_not_found off;
access_log off;
}
location / {
root /srv/www/htdocs/bornem/;
}
#error_page 404 /404.html;
# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /srv/www/htdocs/;
}
# Users directory support
location ~ ^/~(.+?)(/.*)?$ {
alias /home/$1/public_html$2;
}
}
}///
Quelques explications concernant ce fichier.
* Les journaux (d’erreurs et d’accès) seront stockés dans le dossier @@/var/log/nginx@@.
* Le numéro du processus sera stocké dans le fichier @@/var/run/nginx.pid@@.
* L’affichage du numéro de version du serveur est désactivé dans les en-têtes ??HTTP|HyperText Transfer Protocol??, la directive __server_tokens__ est sur __off__.
__Remarque :__ On peut analyser ces en-têtes avec le module [Requests|http://docs.python-requests.org/en/latest/index.html]. (Il faut faire tourner le serveur, et lancer l’interpréteur __Python__) :
Voici le cas où la directive __server_tokens__ est sur __on__ (valeur par défaut).
///olivier@bornem:~ $ python
Python 2.7.2 (default, Aug 19 2011, 20:41:43) [GCC] on linux2
Type « help », « copyright », « credits » or « license » for more information.
>>> import requests
>>> r = requests.get(« http://errements.net/ »)
>>> r.headers
{‘date’: ‘Sat, 21 Jan 2012 09:41:08 GMT’, ‘transfer-encoding’: ‘chunked’, ‘connection’: ‘keep-alive’, ‘content-type’: ‘text/html; charset=utf-8’, ‘server’: ‘nginx/1.0.10’}///
La valeur __server__ vaut __nginx/1.0.10__ (où __1.0.10__ représente le numéro de version).
Si l’on désactive cette directive (@@server_tokens off;@@), on obtient ceci :
///>>> r = requests.get(« http://errements.net/ »)
>>> r.headers
{‘date’: ‘Sat, 21 Jan 2012 09:54:32 GMT’, ‘transfer-encoding’: ‘chunked’, ‘connection’: ‘keep-alive’, ‘content-type’: ‘text/html; charset=utf-8’, ‘server’: ‘nginx’}///
Cette fois-ci, la valeur de __serveur__ vaut __nginx__.
* L’absence de [favicon|http://fr.wikipedia.org/wiki/Favicon] $$Il s’agit d’une « petite image », qui s’affiche dans la barre du navigateur, lorsque l’on consulte une page ??HTML|HyperText Markup Language??.$$, à la racine du site, n’est pas « stockée » dans les »logs ».
///[…]
# favicon.ico
location = /favicon.ico {
log_not_found off;
access_log off;
}
[…]///
* La racine du serveur est située dans @@/srv/www/htdocs/bornem/@@. Pour les utilsateurs, elle est située dans @@$HOME/public_html@@ (cette fonctionnalité est connue sous l’appellation [UserDir|http://wiki.nginx.org/UserDir]).
///[…]
# Users directory support
location ~ ^/~(.+?)(/.*)?$ {
alias /home/$1/public_html$2;
}
[…]///
Nous pouvons maintenant lancer le serveur.
!!!Démarrer Nginx
Sous openSUSE, les programmes pour lancer les »daemons » au démarrage sont situés dans @@/etc/init.d/@@.
///root@bornem:~# ls /etc/init.d/ | grep nginx
nginx
root@bornem:~# sh /etc/init.d/nginx start
redirecting to systemctl
Failed to issue method call: Unit name .d/nginx.service is not valid.
root@bornem:~# ///
On obtient une erreur, car il manque un fichier __nginx.service__.
Depuis la version __12.1__, openSUSE intègre désormais __systemd__, comme service »d’init ».
Nous allons rechercher ce fichier.
///root@bornem:~# find /etc/systemd/ -type f -name ‘nginx.service’ -print
root@bornem:~#
root@bornem:~# find /lib/systemd/ -type f -name ‘nginx.service’ -print
root@bornem:~# ///
Parmis les dossiers susceptibles de posséder un tel fichier, la recherche n’abouti pas, il faut donc le créer $$Je vous conseille de lire la [documentation|http://0pointer.de/public/systemd-man/systemd.service.html] concernant les fichiers @@.service@@.$$.
»Dans les pièces jointes vous trouverez le fichier @@nginx.service@@. »
Vous constaterez que j’ai commenté la ligne @@#WantedBy=multi-user.target@@, car j’ai fait les tests avec une interface graphique, d’où la cible __graphical.target__.
Nous allons placé ce fichier, dans le dossier @@/etc/systemd/system/default.target.wants@@ (si il n’existe pas il faut le créer).
Désormais, nous pouvons lancer le serveur de cette façon :
///root@bornem:~# systemctl start nginx.service///
!!!!Mercurial
Dans cette partie nous allons nous intéressé à __Mercurial__.
L’affichage du « dépôt » se fera par l’intermédiaire du module __hgweb__. Pour cela j’ai écris un script Python ( »Cf. » @@hgweb.wsgi@@, en pièces jointes).
Ce script est très simple, si aucun fichier de configuration lui est donné, il va rechercher tous les utilisateurs présents sur la machine (@@/etc/passwd@@), puis il va examiner le @@$HOME@@ de chaque utilisateur à la recherche de fichiers @@.cfg@@ contenant le mot clé __[paths]__ $$ »Cf. » la [documentation|http://mercurial.selenic.com/wiki/PublishingRepositories#Configuration_of_hgweb]$$ (pour l’instant, le script fonctionne uniquement pour un seul utilisateur). Le tout sera renvoyé à un serveur [WSGI|http://www.wsgi.org/en/latest/servers.html]. Par défaut j’utilise [waitress|http://pypi.python.org/pypi/waitress] $$Je mets à disposition le fichier @@python-waitress.spec@@ permettant de construire un RPM (testé uniquement sous openSUSE).$$, mais si il est absent, le script utilisera le module [wsgiref|http://docs.python.org/library/wsgiref.html] (en standard dans Python).
Le serveur WSGI passe par un »socket » réseau TCP/IP ( »network socket »), c’est à dire qu’il transmet les données à une adresse IP locale (l’adresse de bouclage »localhost »).
Il nous faut maintenant trouver un moyen pour rediriger l’adresse IP locale (inaccessible depuis « l’extérieur ») vers le serveur (accessible grâce à une adresse IP connue). On parle alors de »[proxy|http://fr.wikipedia.org/wiki/Proxy] ».
Nginx possède une particularité intéressante, il est capable d’être utilisé comme »reverse proxy », c’est à dire on passe par lui pour accéder aux ressources internes.
!!!Configurer Nginx pour prendre en compte Mercurial
Nous allons donc utiliser le module [proxy|http://wiki.nginx.org/HttpProxyModule], de plus l’accès au dépôt se fera par l’intermédiaire d’un sous-domaine ( »vhost »).
Vous devez avoir au préalable correctement configuré votre serveur ??DNS|Domain Name System??. Pour des tests en local, on peut passer par le fichier @@/etc/hosts@@.
On rajoute ces lignes :
///[…]
http {
}
[…]
# Subdomain settings
#
# Mercurial
#
server {
listen 80;
server_name hg.errements.net;
access_log /var/log/nginx/access-hg.log;
location / {
root /srv/www/htdocs/vhosts/hg;
autoindex on;
}
}
[…]
}///
On peut (re)lancer le serveur pour voir que tout fonctionne :
///root@bornem:~# systemctl start nginx.service///
Maintenant, on place notre script @@hgweb.wsgi@@ dans le répertoire @@/srv/www/htdocs/vhosts/hg@@.
On exécute le script et on stoppe le serveur.
///root@bornem:~# systemctl stop nginx.service
root@bornem:~# python /srv/www/htdocs/vhosts/hg/hgweb.wsgi &
serving on http://127.0.0.1:8500///
ll s’agit de l’adresse IP mentionnée (vous pouvez changer le port) dans le script pour qu’il s’exécute. Il faut donc rajouter cette information au fichier de configuration de Nginx.
///[…]
# Subdomain settings
#
# Mercurial
#
server {
listen 80;
server_name hg.errements.net;
access_log /var/log/nginx/access-hg.log;
location / {
root /srv/www/htdocs/vhosts/hg;
autoindex off;
proxy_path http://127.0.0.1:8500;
proxy_set_header Host $host;
}
}
[…]///
On relance le serveur, et on ouvre notre navigateur favori à l’adresse indiquée par __server_name__.
!!!Automatiser le lancement du programme
Il va falloir créer un fichier @@.service@@, je me suis inspiré de ce [billet|http://forums.opensuse.org/blogs/jdmcdaniel3/systemd-using-after-local-script-opensuse-12-1-71/|en], pour écrire le mien.
Dans le fichier @@/etc/init.d/after-local@@ j’ai rajouté les mêmes lignes que celles tapées en console :
///PYTHON_BIN=/usr/bin/python
NGINX_DIR= »/srv/www/htdocs »
NGINX_VHOSTS= »$NGINX_DIR/vhosts »
## Mercurial settings (subdomain, hg.)
if [ -e « $NGINX_VHOSTS/hg/hgweb.wsgi » ]; then
$PYTHON_BIN $NGINX_VHOSTS/hg/hgweb.wsgi
fi///
Si l’on veut lancer plusieurs scripts, il faut placer les commandes en « arrière-plan » (on rajoute le caractère __&__ à la fin de la ligne).
Le fichier @@.service@@ qui va exécuter ce programme »d’init » s’appelle @@after-local.service@@ ( »Cf. » les pièces jointes).
///root@bornem:~# systemctl start after-local.service///
On peut rajouter ce fichier au répertoire @@/etc/systemd/system/default.target.wants@@ pour qu’il soit lancé automatiquement au démarrage.
!!!!Cas pratique avec [Gunicorn|http://gunicorn.org/||Green Unicorn]
((/news-custom/public/gunicorn-45.png|Logo du serveur WSGI, Gunicorn|C|Gunicorn, serveur WSGI))
Dans cette dernière partie, nous allons nous intéressé à un serveur WSGI particulier, Gunicorn.
Nous allons supprimer notre premier script (@@hgweb.wsgi@@), pour le remplacer par @@web.py@@ ( »Cf. » pièces jointes). Ce serveur à un mode de fonctionnement [particulier|http://gunicorn.org/run.html#gunicorn].
Ce programme, nous allons l’utilisé comme un « simple module Python » (je ne suis pas sûr à 100%, qu’il soit en mémoire dans @@sys.path@@), de ce fait, nous allons créer en plus un fichier @@__init__.py@@ vide.
///root@bornem:~# touch /srv/www/htdocs/vhosts/hg/__init__.py
root@bornem:~# chown -R nginx:nginx /srv/www/htdocs/
///
On peut lancer le programme et relancer le serveur :
///root@bornem:~# cd /srv/www/htdocs/vhosts ; /usr/bin/gunicorn -p /var/run/gunicorn.pid -g nginx -u nginx -b 127.0.0.1:8500 hg.web:app &
root@bornem:~# systemctl start nginx.service///
Nous sommes obligé de « descendre » dans le répertoire @@vhosts/@@ car les autres dossiers ne sont pas considérés comme des modules par Python.
On peut passer d’autres paramètres, notamment le nombre de »workers » (nombre de cœurs de la machine), ou passer par un fichier de configuration.
!!!Gunicorn et systemd ?
La difficulté ici, c’est de pouvoir se déplacer dans le dossier souhaité. La lecture de la documentation, [systemd.exec| http://0pointer.de/public/systemd-man/systemd.exec.html] nous apprend, qu’il existe une directive __WorkingDirectory__, elle ne peut fonctionner que si le type est __simple__, alors que l’on pourrait s’attendre à mettre __forking__.
> WorkingDirectory=
>
> Takes an absolute directory path. Sets the working directory for executed processes.
En annexe vous trouverez le fichier @@gunicorn-hg.service@@, qui reprend la même commande lancée dans une console avec les spécificités de systemd.
Avec Gunicorn, au lieu de passé par un »socket » TCP/IP, on peut utiliser un »socket » unix (ou »Berkeley socket »), le fichier @@gunicorn-hg.service@@ devient alors :
///[Unit]
Description=Gunicorn for mercurial
Requires=local-fs.target syslog.target
After=syslog.target local-fs.target
Before=nginx.service
[Service]
Type=simple
PIDFile=/var/run/gunicorn.pid
WorkingDirectory=/srv/www/htdocs/vhosts
ExecStartPre=/bin/echo ‘Starting gunicorn (mercurial)’
ExecStart=/usr/bin/gunicorn -p /var/run/gunicorn.pid -g nginx -u nginx -b unix:/tmp/gunicorn.socket hg.web:app
StandardOutput=syslog
StandardError=syslog
KillMode=process
[Install]
#WantedBy=multi-user.target
WantedBy=graphical.target///
Et la partie du serveur :
///[…]
# Subdomain settings
#
# Mercurial
#
server {
listen 80;
server_name hg.errements.net;
access_log /var/log/nginx/access-hg.log;
location / {
root /srv/www/htdocs/vhosts/hg;
autoindex off;
proxy_path http://unix:/tmp/gunicorn.socket;
proxy_set_header Host $host;
}
}
[…]///
J’avoue que dans ce cas, je préfère de loin cette configuration, à celle de passer par le script @@/etc/init.d/after-local@@. Je trouve que c’est plus « propre », et on exploite davantage systemd.