Mutualisation du noyau SPIP

Procédure pour partager le noyau de SPIP entre plusieurs sites.

La mutualisation du noyau de SPIP est possible. Il s’agit de pouvoir gérer plusieurs sites SPIP sur un seul serveur ou hébergement en n’utilisant qu’une seule fois les fichiers du noyau de SPIP.

Cela permet un gain d’espace disque important, ainsi qu’une possibilité de mise à jour de SPIP simple de l’ensemble des sites en ne mettant à jour que le noyau.

Cet article explique la procédure pour SPIP1.9.2, sur des serveurs Apache [1] autorisant la réécriture d’url (url_rewriting). Pour les versions ultérieures de SPIP, voir plutôt l’article Ferme à SPIP.

Il y a plusieurs méthodes pour arriver aux mêmes résultats, selon que l’on souhaite configurer directement la mutualisation depuis un hébergement ou depuis un serveur.

Le concept...

Les dossiers nécessaires au fonctionnement du noyau SPIP (ecrire, plugins-dist, prive, squelettes-dist, ...), et ceux marquant l’activité d’un site (config, IMG, tmp, local) sont clairement identifiables et séparés. C’est cette séparation qui permet d’avoir plusieurs sites SPIP autonomes pour un même noyau de SPIP.

Cette autonomie repose sur l’existence d’un quatuor de répertoires par site, dans lesquels Spip va écrire les données résultant de l’activité du site. Ces répertoires sont au nombre de quatre, car on distingue les données temporaires et permanentes d’une part, et les données accessibles par http et celles qui ne le sont pas d’autre part, d’où quatre types de données. Les deux répertoires inaccessibles par http seront protégées par un .htaccess installé automatiquement par SPIP (les administrateurs du serveur pourront même mettre ces répertoires en dehors de l’arborescence servie par http).

Ces quatre répertoires sont distincts et nommés par les constantes PHP suivantes :

define('_NOM_TEMPORAIRES_INACCESSIBLES', "tmp/");
define('_NOM_TEMPORAIRES_ACCESSIBLES', "local/");
define('_NOM_PERMANENTS_INACCESSIBLES', "config/");
define('_NOM_PERMANENTS_ACCESSIBLES', "IMG/");

Dans une installation de Spip non mutualisée, ces répertoires sont créés à la racine du site, lors de l’application de la fonction spip_initialisation sur les quatre valeurs ci-dessus. Pour obtenir la mutualisation des sources de SPIP,
il suffit de savoir associer une URL spécifique à son quatuor de répertoires spécifiques, en appelant la fonction spip_initialisation sur quatre autres valeurs, déduites de l’URL.

Créer les bons répertoires

Pour démarrer la mutualisation, il faut tout d’abord partir d’un site fonctionnel. Pour les exemples, nous partirons d’un site appelé par l’url http://example.org/ stocké physiquement dans /home/toto/public_html/.

Il faut créer un répertoire (par exemple nommé ’sites’) qui va contenir les répertoires des sites mutualisés, à la racine de la distribution (au même niveau que /ecrire).

-  mutualisation d’un répertoire :
Si l’on souhaite que les adresses http://example.org/premier_site/ et http://example.org/deuxieme_site/ appellent chacun un site spip, il faut créer alors les sous-répertoires /premier_site et /deuxieme_site dans le répertoire /sites, puis dans chacun d’eux créer les répertoires /config, /IMG, /tmp, /local. Ces quatre répertoires doivent être accessibles en écriture. Il sera possible par la suite d’ajouter un répertoire /squelettes aussi, à côté de ces répertoires.

-  mutualisation sous-domaine et domaine (quelques idées) :
Si l’on souhaite que l’adresse http://toto.example.org/, http://exemple.tld/ et http://utilisateur.example.com/ appellent chacun un site spip, on peut envisager de créer dans /sites les répertoires /toto, /exemple.tld, et /example.com/utilisateur

Remarque : Toutes les url doivent pointer à la racine de la distribution SPIP, c’est-à-dire dans /home/toto/public_html/. C’est le rôle que vont remplir soit un fichier .htaccess soit la configuration du serveur Apache expliqués plus loin.

Des redirections bien faites !

Pour que SPIP reconnaisse qu’un site est mutualisé, il faut que le script spip.php (appelé par index.php) soit executé. Celui-ci cherchera, grâce à un code ajouté dans /config/mes_options.php si l’URL appelée correspond à un site mutualisé ou non. Pour cela, il faut qu’une adresse http://example.org/premier_site/ ou http://example.org/deuxieme_site/ soit redirigée vers http://example.org/ pour executer alors index.php...

C’est le rôle attribué au fichier .htaccess (ou directement dans la configuration du serveur Apache)

Il faut copier et renommer le fichier htaccess.txt à la racine de la distribution en .htaccess, puis le modifier :

Pour autoriser la réécriture d’URL (rien ne change normalement) :
RewriteEngine On

Si la distribution SPIP est dans un sous-répertoire, modifier rewritebase. Ici, le site est à la racine donc :
RewriteBase /

Enfin, dans réglages personnalisés, ajouter le code suivant pour que les répertoires /premier_site , /deuxieme_site et /troisieme_site soient traités depuis la racine de la distribution :
#Mutualisation RewriteRule ^(premier_site|deuxieme_site|troisieme_site)$ /$1/ [R,L] RewriteRule ^(premier_site|deuxieme_site|troisieme_site)/(.*) /$2 [QSA,L]

Le premier rewriterule redirige les adresses http://example.org/premier_site vers http://example.org/premier_site{{/. Le deuxième redirige tout ce qu’il y a derrière /premier_site/ à la racine, par exemple : http://example.org/premier_site/article112 est redirigé vers http://example.org/article112. (Cependant, cette redirection est transparente, le nom de l’url ne change pas, il est toujours http://example.org/premier_site/article112, même et surtout pour php !)

Et si SPIP est dans un sous répertoire ?
Dans ce cas là, les fichiers de spip se trouvent dans /home/toto/public_html/spip/ , SPIP est appelé par http://example.org/spip/, les sites mutualisés par http://example.org/spip/premier_site/ ou http://example.org/spip/deuxieme_site/.

Il faut alors mettre dans le .htaccess :

RewriteEngine On
RewriteBase /spip/

#Mutualisation
RewriteRule ^(premier_site|deuxieme_site|troisieme_site)$ /spip/$1/ [R,L]
RewriteRule ^(premier_site|deuxieme_site|troisieme_site)/(.*) /spip/$2 [QSA,L]

Et pour rediriger tous les répertoires comme des sites mutualisés ?

Il vous est possible à la place des rewriterules ci-dessus
d’utiliser un code générique, utilisable quelque-soit le nom du
répertoire du site mutualisé :

-  à la racine

RewriteCond %{REQUEST_URI} !^/(config|squelettes-dist|ecrire|IMG|oo|plugins|sites|squelettes|tmp|local)/(.*)
RewriteRule ^[^/]+/(.*) /$1 [QSA,L]

-  ou dans un dossier /spip :

RewriteCond %{REQUEST_URI} !^/spip/(config|squelettes-dist|ecrire|IMG|oo|plugins|sites|squelettes|tmp|local)/(.*)
RewriteRule ^[^/]+/(.*) /spip/$1 [QSA,L]

Et pour les domaines et sous domaines ?
Par un heureux hasard, il n’y a rien à faire ici puisqu’ils pointent normalement déjà vers la racine de la distribution SPIP...

Mutualiser selon l’URL grâce à mes_options.php

C’est le fichier /config/mes_options.php à la racine de la distribution qui va réaliser la majeure partie du travail : il doit chercher si une URL est à mutualiser ou non, et initialiser SPIP en fonction.

De nombreux cas peuvent se présenter, entre les répertoires, les domaines et sous domaines. PHP peut utiliser deux variables pour tester les URLs qui ont appelé le script :
$_SERVER['REQUEST_URI']; // contient tout ce qu'il y a derrière le nom de domaine : /premier_site/article112 par exemple... $_SERVER['SERVER_NAME']; // contient le nom du domaine et sous domaine : utilisateur.example.org par exemple

Ce sont ces deux variables qui vont être comparées à une expression régulière pour extraire le nom du répertoire qui contient la mutualisation.

Un moyen simple de mutualiser tous les répertoires est de copier le code suivant :

<?php 
if ( preg_match(',/([a-zA-Z0-9_-]+)/?,',$_SERVER['REQUEST_URI'],$r)) {

    if (is_dir($e = _DIR_RACINE . 'sites/' . $r[1]. '/')) {

        $cookie_prefix = $table_prefix = $r[1]; 

        define('_SPIP_PATH',
            $e . ':' .
            _DIR_RACINE .':' .
            _DIR_RACINE .'squelettes-dist/:' .
	    _DIR_RACINE.'prive/:'.
            _DIR_RESTREINT);

        spip_initialisation(
            ($e . _NOM_PERMANENTS_INACCESSIBLES),
            ($e . _NOM_PERMANENTS_ACCESSIBLES),
            ($e . _NOM_TEMPORAIRES_INACCESSIBLES),
            ($e . _NOM_TEMPORAIRES_ACCESSIBLES)
            );

       $GLOBALS['dossier_squelettes'] = $e.'squelettes';

        if (is_readable($f = $e._NOM_PERMANENTS_INACCESSIBLES._NOM_CONFIG.'.php')) include($f);
    }
} 
?>

La ligne preg_match récupère le nom d’un dossier dans l’arborescence de l’URL, par exemple ’premier_site’ dans http://example.org/premier_site/ ... Puis le script tente une mutualisation si le répertoire /sites/premier_site/ existe.

Si SPIP est dans un répertoire /spip, il faut modifier la première ligne par :
if (preg_match(',/spip/([a-zA-Z0-9_-]+)/?,', $_SERVER['REQUEST_URI'], $r)) {

Il faut dire dire à SPIP quel est le préfixe de table de base de données utilisé, ce que fait $cookie_prefix = $table_prefix = $r[1];
Cette ligne va créer des tables MySQL avec des préfixes ayant le nom du répertoire contenant le site : mon_site_article à la place de spip_article par exemple. Cela permet d’héberger tous les sites sur une seule et même base de données. Vous pouvez garder le préfixe par défaut (spip), mais il faut penser à avoir plusieurs bases de données différentes pour chaque site.
Pour cela, mettre à la place :

$cookie_prefix = $r[1]; 
$table_prefix='spip';

La fonction spip_initialisation admet quatre paramètres qui sont l’adresse de chacun des répertoires nécessaires au fonctionnement du site mutualisé.

$GLOBALS['dossier_squelettes'] = $e.'squelettes'; définit l’emplacement du dossier squelettes du site mutualisé.

Enfin les dernières lignes chargent un éventuel sites/premier_site/config/mes_options.php.

Information :

Toute modification du fichier config/mes_options.php du noyau SPIP affectera les options de tous les sites hébergés.

Par exemple, mettre dans ce fichier :
$type_urls = ’propres’ ;

Donnera par défaut à tous les sites ce type d’url... Mais chaque site peut le changer dans son propre /sites/mon_site/config/mes_options.php.

Configurer apache pour les domaines et sous-domaines

La mutualisation côté serveur, pour ce qui concerne la gestion des sous-domaines ou des domaines reste simple, mais nécessite de créer quelques redirections d’URL dans la configuration du serveur Apache pour tenir compte de ces sites.

Voici un exemple de configuration pour un serveur nommé ’exemple.tld’ (ici un SPIP mutualisé) utilisant des sous-domaines (SPIP appelé par http://utilisateur.example.org/spip/).

Le noyau SPIP est dans ’/home/toto/public_html/spip/’.

Il faut alors créer les répertoires /sites/exemple.tld/ , /sites/exemple.tld/utilisteur/.

Le fichier de configuration se situe dans Apache 2/linux dans /etc/apache2/sites_availables/default (vous pouvez aussi créer un nouveau fichier dans le dossier sites_availables et l’activer).

# SERVEUR exemple.tld
# SPIP par sous domaine...
<VirtualHost *>
        ServerName exemple.tld
	ServerAlias *.exemple.tld

        # Redirection vers le SPIP noyau
	DocumentRoot "/home/toto/public_html"
	<Directory "/home/toto/public_html/">
		AllowOverride All
		Order allow,deny
		Allow from all
	</Directory>

	# Seule l'adresse http://utilisateur.exemple.tld/spip/* doit être redirigée

	# (utilisateur.exemple.tld/spip/* -> /home/toto/public_html/*)
	RewriteCond %{SERVER_NAME} (www\.)?([^.]+)\.example\.net$
	RewriteRule ^/spip/(.*) /home/toto/public_html/spip/$1 [QSA,L]

	# (utilisateur.exemple.tld/* -> /home/toto/public_html/sites/exemple.tld/utilisateur/*)
	RewriteCond %{SERVER_NAME} (www\.)?([^.]+)\.example\.net$
	RewriteRule (.*) /home/toto/public_html/sites/exemple.tld/%1/$1 [QSA,L]

</VirtualHost>

Il est par contre nécessaire de tester ces mutualisations possibles dans /config/mes_options.php :

<?php
# pour utilisateur.exemple.tld/spip/ 
if ( preg_match(',(.*)\.exemple\.tld/spip/?,',$_SERVER['SERVER_NAME'].$_SERVER['REQUEST_URI'],$r)) {

    if (is_dir($e = _DIR_RACINE . 'sites/exemple.tld/' . $r[1]. '/')) {

       $cookie_prefix = $table_prefix = $r[1]; 

       define('_SPIP_PATH',
            $e . ':' .
            _DIR_RACINE .':' .
            _DIR_RACINE .'squelettes-dist/:' .
            _DIR_RACINE.'prive/:'.
            _DIR_RESTREINT);

        spip_initialisation(
            ($e . _NOM_PERMANENTS_INACCESSIBLES),
            ($e . _NOM_PERMANENTS_ACCESSIBLES),
            ($e . _NOM_TEMPORAIRES_INACCESSIBLES),
            ($e . _NOM_TEMPORAIRES_ACCESSIBLES)
            );

       $GLOBALS['dossier_squelettes'] = $e.'squelettes';

        if (is_readable($f = $e._NOM_PERMANENTS_INACCESSIBLES._NOM_CONFIG.'.php')) include($f);
    }
} 
?>

Note sur les sauvegardes et les restaurations

Chaque site copie ses fichiers de sauvegardes dans le répertoire
/sites/premier_site/tmp/dump (ou /sites/premier_site/tmp/upload/login pour les sauvegardes d’un administrateur restreint).
Les restaurations se font par le même dossier.

Attention :

A l’heure actuelle, SPIP enregistre dans la base de données, pour ce qui concerne le dossier /IMG des sites mutualisés une adresse sites/premier_site/IMG/* au lieu de IMG/* comme pour un site SPIP seul.

Une restauration ne fonctionnera donc que si elle s’effectue dans le site qui a créé la sauvegarde.

Une astuce pour restaurer le site ailleurs consiste à éditer le fichier dump.xml et à remplacer toutes les occurrences (à l’exception de celles des déclarations dir_img et dir_logo dans l’ouverture de la balise spip) :
-  sites/premier_site/IMG/

  • par (SPIP seul) : IMG/
  • ou (SPIP mutualisé) : sites/mon_nouveau_site/IMG/

Inversement, pour passer un SPIP seul dans un répertoire mutualisé, il faut remplacer toutes les occurrences de :
-  IMG/
-  par : sites/mon_site/IMG/

Notes

[1Pour information : ces procédures ont été testées avec Apache 2.0.55 et PHP 5.1.2 sur serveur Ubuntu Dapper Drake ainsi que sur PHP 5.1.6 sur serveur Ubuntu Edgy Eft

Auteur Committo, Ergo Sum., Matthieu Marcillaud Publié le : Mis à jour : 14/07/23

Traductions : عربي, català, English, Español, français, italiano, Türkçe