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.