Carnet Wiki

La mutualisation d’avant SPIP-2

Version 7 — Février 2011 — bealach

Problème avec les images version du plugin avant .6.1

Le problème se pose pour les liens commençant par /IMG, dans vos feuilles de style par exemple.
/ !\ Cette partie n’est plus valable avec la version .6.1 du plugin qui permet de gerer cela avec l’option ’url_img_courtes’ => true.

  • Ajoutez cette simple règle de réécriture dans votre fichier .htaccess
    RewriteCond %{REQUEST_URI} !(sites)
    RewriteCond %{REQUEST_URI} /(IMG)/[^/]+$
    RewriteRule ([^/]+)/([^/]+)$ sites/%{SERVER_NAME}/$1/$2 [QSA,L]

    Cela signifie que :

  • on teste que l’adresse ne contient pas sites
  • on détecte si c’est bien un fichiers dans IMG
  • Alors on réécrit l’url pour que le chemin corresponde aux fichiers du site mutualisés.

Méthode de mutualisation avant SPIP-2

  • Il existe déja une documentation complète (mais complexe) sur la Mutualisation du noyau SPIP. Ce code est en train d’être revu, dans la branche de développement de SPIP 1.9.3, de manière à simplifier le procédé. Vous n’en avez pas besoin pour réussir votre mutualisation.

Voici où nous en sommes, à la révision 9330 de SPIP et 12308 du plugin :

  • Installez les fichiers de SPIP dans un répertoire, comme vous le ferez classiquement ; appelons-le spip/, c’est la racine de votre système (tous les virtualhost pointent dans ce répertoire).
  • Créez un répertoire sites/ à l’intérieur du répertoire racine.

j’ai changé un nom de variable $site en $domaine pour que ce soit plus clair.

Le principe est simple : à chaque site, qu’on identifiera par une variable $domaine, correspond à un répertoire sites/$domaine/ ; ce répertoire contient lui-même les quatre sous-répertoires de données du site :
-  spip/sites/$domaine/config/ contient les données de connexion connect.php et éventuellement le fichier mes_options.php du site ;
-  spip/sites/$domaine/IMG/ contient les images et documents associés aux articles de ce site ;
-  spip/sites/$domaine/tmp/ contient les fichiers cache de tout type ;
-  spip/sites/$domaine/local/ contient les images calculées (vignettes réduites, formules TeX...).

fin de la modification de la variable, il est tard, alors je préfère écrire ce que je modifie ici

Comment un site est identifié par le moteur ?

Prenons l’exemple d’un système mutualisé qui sert les pages pour différents noms de domaines, chose.tld et truc.tld.

Il faut, tout d’abord, que http://chose.tld/ et http://truc.tld/ pointent tous deux vers la racine du système : pour cela il faut :

  1. que le DNS pointe vers le serveur ;
  2. que le serveur soit configuré de manière à servir ces domaines depuis notre répertoire spip/ (directive VirtualHost, commande « Héberger un domaine » d’AlternC, etc.)
    Voir article 2194

Voici en quelques copies d’écran les paramétrages utilisés pour scriibe (ceci est un exemple, il y a d’autres moyens d’y arriver) .

config dns
On choisit d’utiliser les dns de gandi
les dns de gandi
L’adresse des 3 serveurs dns de gandi
Redirection vers le serveur
Ici on indique l’adresse ip du serveur vers lequel les adresses seront redirigées. A noter l’Etoile qui permet de rediriger n’importe quel adresse *.scriibe.net vers le serveur qui héberge la mutualisation

Une fois ceci effectué, nous avons donc deux sites, tous deux servis par la même installation de SPIP. Bien évidemment, si on installe le site maintenant, il répondra de la même manière aux deux noms de domaines, il faut donc agir avant de procéder à l’installation du site.

On commence par créer un fichier central de configuration, dans spip/config/mes_options.php, qui contient :

<?php
    require _DIR_RACINE . 'mutualisation/mutualiser.php';
    demarrer_site($_SERVER['HTTP_HOST']);
?>

Si votre fichier mes_options.php est dans /ecrire, ça fonctionnera aussi très bien

Cette configuration est exécutée à chaque hit sur un fichier de SPIP, et définit les répertoires où le script ira chercher les images, la connexion à la base de données etc, comme indiqué plus haut.

En l’occurrence, nous avons décidé que chaque site serait représenté par la valeur de $_SERVER['HTTP_HOST'], c’est-à-dire truc.tld ou chose.tld. Si le site n’est pas installé (le répertoire spip/sites/truc.tld/ n’existe pas), la commande demarrer_site($site) le signale et vous demande de créer ce répertoire ainsi que les quatre sous-répertoires indispensables.

<blockquote class="spip">

www ou pas ?

Il est possible de décider que l’url http://www.chose.tld/ doit être identifiée avec l’url http://chose.tld/, en d’autres termes qu’il ne s’agit que d’un seul et même site ; il faut dans ce cas être un peu plus subtil :

<?php
   require _DIR_RACINE . 'mutualisation/mutualiser.php';
    $site = str_replace('www.', '', $_SERVER['HTTP_HOST']);
    demarrer_site($site);
?>

Cependant, une autre possibilité est largement préférable : il faut rediriger http://www.chose.tld/ vers http://chose.tld/ en faisant par exemple :

<?php
   require _DIR_RACINE . 'mutualisation/mutualiser.php';
    $site = str_replace('www.', '', $_SERVER['HTTP_HOST']);
    if ($site != $_SERVER['HTTP_HOST']) {
         include_spip('inc/headers');
        redirige_par_entete('http://'.$site.'/');
    }
    demarrer_site($site);
?>

Ou à l’inverse, rediriger de http://chose.tld/ vers http://www.chose.tld/ en faisant par exemple :

<?php
   require _DIR_RACINE . 'mutualisation/mutualiser.php';
   if (substr($site = $_SERVER['HTTP_HOST'],0,4)!='www.') { $site = 'www.'.$site;}
   if ($site != $_SERVER['HTTP_HOST']) {
         include_spip('inc/headers');
        redirige_par_entete('http://'.$site.'/');
    }
    demarrer_site($site);
?>
</blockquote>

et dans ce cas les répertoires créés seront spip/sites/www.chose.tld/...

Pour aller plus loin
Quoiqu’il en soit, le système de nommage du site est ouvert et peut se baser sur ce que l’on veut ; si on héberge par exemple sur ce système mutualisé tous les sites des sections locales de chose.tld, par exemple antananarivo.chose.tld, capetown.chose.tld, beijing.chose.tld, on fera :

notez que ici, c’est un exemple, si vous débutez ne le faites pas

<?php
    require _DIR_RACINE . 'mutualisation/mutualiser.php';
    if (!preg_match(',^(www\.)?(.*)\.chose.tld$,', $_SERVER['HTTP_HOST'], $r))
        die ("Erreur : ce site n'est pas une chose.tld : ".$_SERVER['HTTP_HOST']);
    demarrer_site($r[2]); // $r[2] correspond au (.*) de l'expression rationnelle
?>

et dans ce cas les répertoires créés seront spip/sites/antanarivo/, spip/sites/capetown/, spip/sites/beijing/...

Ou placer mes_options.php ?
pour ce faire j’ai recopié le fichier fourni mes_options.php.txt dans /spip/config/mes_options.php et cela permet de retrouver les dossiers automatiquement si les vhosts sont créés bien sur.

Partager une même base de données ?

Il est bien clair que les sites ne doivent pas mélanger leurs données ; en revanche il est possible (mais pas forcément souhaitable) de n’utiliser qu’une seule et unique base de données pour l’ensemble des sites d’un même système de mutualisation. On séparera alors les différents sites en utilisant des tables dont les noms seront préfixés différemment.

Pour utiliser les préfixes de table, dans notre exemple ci-dessus, on utilisera antanarivo comme préfixe pour les tables du site antanarivo, etc ; ainsi dans la base de données (unique), on aura des tables antanarivo_articles pour les articles du site antanarivo, et beijing_articles pour les articles du site beijing.

Pour faire cela, il suffit d’indiquer l’option suivante, dans l’appel à demarrer_site() :

    demarrer_site($site, array('table_prefix' => true)); 

Cette option « rabote » un peu la variable $site de manière à en faire un préfixe valide pour MySQL (12 caractères, lettres ou chiffres, pas de points ni de tiret, commence par une lettre). Le préfixe est enregistré dans la variable globale de SPIP $GLOBALS['table_prefix'].

Pour tenter de garantir l’unicité (tant que possible), si le nom du site est trop long (saint-gravier-la-bruyarde), le préfixe sera de la forme 'saintg'+6 derniers caractères de la clé md5($site), afin d’éviter un clash avec saint-gravier-le-feuillu. Afin d’éviter de confondre deux sites comme a.b.domaine.tld et ab.domaine.tld, la fonction appose aussi un bout de md5 si le nom du site contient des caractères autres que lettres ou chiffres (un point, par exemple).

Si l’on utilise des bases différentes, on peut aussi changer le préfixe, mais pour le coup ça n’a pas d’importance. C’est lors de la connexion initiale (« installation du site ») qu’on précisera la base à employer, après l’avoir éventuellement créée, depuis SPIP ou dans l’interface AlternC.

Compatibilité AlternC

Une option permet de créer les bases de données dans un compte AlternC ; cela se passe par l’intermédiaire du bureau, que le script va solliciter directement par son interface Web, comme s’il était le propriétaire du compte. Par exemple :

'url_creer_base' =>
'https://bureau.tld/admin/sql_doadd.php?username=USER&password=PASS&dbn='.prefixe_mutualisation($site)

Ce mode nécessite que php soit compilé avec la librairie libcurl (connexion https oblige).
L’appel de cet URL par le script provoquera la création de la base <span class="base64" title="PGNvZGUgY2xhc3M9InNwaXBfY29kZSBzcGlwX2NvZGVfaW5saW5lIiBkaXI9Imx0ciI+JHVzZXJuYW1lXyRkYm48L2NvZGU+"></span>, qui sera ensuite affectée au site créé.

Héberger des sites pour d’autres personnes

Dès lors que l’on veut héberger des sites pour d’autres que soi-même, on est confronté au processus d’installation de SPIP qui exige le login, mot de passe de la base de données, et permet ensuite à celle qui installe de choisir sa base parmi la liste des bases disponibles. S’il faut créer une base par utilisateur, ou si on doit prendre le risque de laisser l’installation partir sur les données d’un autre site (suite à une fausse manipe de sélection de la base ou du préfixe), on perd un des avantages de la mutualisation.

Considérons ici le scénario d’une seule base de données partagée par tous les sites : SPIP permet désormais de préciser dans mes_options.php une ou plusieurs des données de connexion à la base :

define ('_INSTALL_HOST_DB', 'localhost');
define ('_INSTALL_USER_DB', 'truc.tld');
define ('_INSTALL_PASS_DB', '1234FGH78');
define ('_INSTALL_NAME_DB', 'touslestructld');

Ces données étant ainsi figées, l’installation de SPIP ne les demande pas à la personne qui installe le site, mais lui signale que la donnée est fournie par l’hébergeur. Cette astuce est aussi bien pratique si on veut installer beaucoup de sites pour soi-même : on peut par exemple définir _INSTALL_HOST_DB, _INSTALL_USER_DB, _INSTALL_PASS_DB, mais laisser libre le nom de la base, pour pouvoir la créer/choisir à chaque installation (mais alors attention à la sécurité de la chose : mal géré, ça peut devenir catastrophique).

Création automatique du site

Les réglages précédents indiquent comment on gère l’association entre une URL, un site et une base de données. Il reste à voir comment créer automatiquement le site, sans avoir à aller par FTP créer des sous-répertoires etc.

Création automatique des sous-répertoires. C’est très simple, pour que le répertoire spip/sites/xxxx/ et ses sous-répertoires soient créés à la volée lors de la première connexion sur le site (au premier hit, une fois le DNS et le VHost/VirtualServer installés), il suffit de l’indiquer dans les options de la fonction demarrer_site() :

demarrer_site($site, array(
     'creer_site' => true
));

La première visite sur le site créera automatiquement les répertoires nécessaires, et il vous suffira d’aller dans ecrire/ pour lancer l’installation.

Attention ici à la sécurité : il faut absolument éviter d’avoir à la fois un domaine pas encore installé, les mots de passe SQL définis et un accès lire à toutes les bases ou tables de votre base mutualisée ; dans un tel cas en effet n’importe qui peut venir prendre le contrôle de n’importe quel site hébergé, ce qui fait mauvais effet...

Toutefois, le système est verrouillé par un code d’activation qui sera demandé au visiteur qui souhaite créer le site. Par défaut ce code est ecureuil, mais vous pouvez le supprimer en indiquant, dans demarrer_site(), l’option 'code' => '', ou le modifier en indiquant 'code' => 'rhooo' ou tout autre secret. (Et, si le code est activé, il faut que le visiteur accepte les cookies pour pouvoir créer son site.)

Création automatique de la base de donnée

Le système peut aussi créer la base à la demande. A condition que tous les codes d’accès à la base de données soient définis, et que l’utilisateur MySQL ainsi défini ait les droits suffisants pour créer de nouvelles bases, l’option 'creer_base' => true permet de créer la base à la volée avant de déclencher l’installation. Il est alors recommandé de nommer la table de a façon suivante :
define ('_INSTALL_NAME_DB', 'mutu_'.prefixe_mutualisation($site));
ce qui donnera des tables nommées spip_articles dans la base mutu_capetown.

Attention, En l’état du code, _INSTALL_NAME_DB est utilisé pour _INSTALL_USER_DB et peut dépasser la limite de 16 caractères de MySQL pour le nom d’un utilisateur.
Solution dans le mes_options :
define ('_INSTALL_USER_DB', prefixe_mutualisation($site));

Création automatique d’un utilisateur sur la base de donnée créé automatiquement

Le système peut créer un utilisateur mysql à la demande. Pour cela, il faut renseigner l’option 'creer_user_base' => true. Toutefois, comme c’est le script qui s’occupe de créer les constantes _INSTALL_USER_DB (sur la valeur de _INSTALL_NAME_DB) et _INSTALL_PASS_DB (mot de passe aléatoire), il faut utiliser d’autres identifiants de connexion, à savoir un utilisateur mysql pouvant et créer une base et créer des utilisateurs. On définit ainsi _INSTALL_USER_DB_ROOT et _INSTALL_PASS_DB_ROOT, à la place des précédents, ce qui peut donner :

if (!defined("_ECRIRE_INC_VERSION")) return;
require _DIR_RACINE . 'mutualisation/mutualiser.php';


// mutualise http://serveur/spip/le_site/
if ( 
		preg_match(',^/spip/([\.a-zA-Z0-9_-]+)/,', $_SERVER['REQUEST_URI'], $r)
		AND !is_dir(_DIR_RACINE . $r[1])
) {
	// definir les prefixe des tables aussi lorsque ce n'est pas une installation.
	$GLOBALS['table_prefix'] = 'spip'; 
	
	// rep plugin (decocher pour qu'il soit dans /sites/le_site/plugins et non mutualises dans /plugins)
	#define('_DIR_PLUGINS', _DIR_RACINE . 'sites/' . $r[1] . '/plugins/');
	
	// rep Aide (aide commune aux sites ?)
	define('_DIR_AIDE', _DIR_RACINE . _NOM_TEMPORAIRES_INACCESSIBLES . "cache/aide/");


// params installation
	define ('_INSTALL_HOST_DB', 'localhost');
	define ('_INSTALL_USER_DB_ROOT', 'marcimat');
	define ('_INSTALL_PASS_DB_ROOT', 'blablabla');
	define ('_INSTALL_NAME_DB', 'mutu_'.prefixe_mutualisation($r[1]));
	define ('_INSTALL_TABLE_PREFIX', 'spip');
	
	// demarrage mutu
	demarrer_site($r[1], 
		array(
			'cookie_prefix' => true, 
			'table_prefix' => false,
			'creer_site' => true,
			'creer_base' => true,
			'creer_user_base' => true,
			'repertoire' => 'sites',
			'mail' => 'mon@mail.org'
		) 
	);
}

Cas sans doute rare : le nom DNS du serveur mysql différent du nom réel de ce dernier.

Usage : dans le mes_options du code maître :

define ('_INSTALL_HOST_DB', 'nomdns');
define ('_INSTALL_HOST_DB_LOCALNAME', 'nomserveur'); // sera utilisé pour créer les user@nomserveur sur MySQL

Envoi d’un email lorsqu’un site est créé

Envoyer des mails à la création d’un site. L’option 'email' => 'fil@rezo.net' tente d’envoyer un email à cette adresse à chaque création automatique d’une base ou des répertoires d’un site.

TODO 1 : éviter les curieux

Avec ce système les documents ajoutés peuvent être communs à tous les sites : qu’il s’agisse des documents installés dans IMG/ ou d’éventuelles pages ajoutées « à la main », le serveur enverra la même chose pour tous les appels http ne passant pas par SPIP, indépendamment du nom de domaine.

Autrement dit, le document chargé dans le site capetown sous l’URL
http://capetown.chose.tld/sites/capetown/IMG/pdf/document.pdf
est aussi accessible via
http://antananarivo.chose.tld/sites/capetown/IMG/pdf/document.pdf

Il est possible de contourner ce problème en bidouillant le .htaccess des répertoires sites/capetown/ pour bloquer les appels provenant d’un autre nom d’hôte. Cela pourrait être ajouté comme option de mutualiser_creer().

Réflexions sur les mises à jour

Mises à jour centralisées. Au fil du temps, on voudra sans doute procéder à des mises à jour, forcément centralisées, des fichiers de SPIP. Le schéma actuel des mises à jour est le suivant :

  1. mise à jour des fichiers
  2. lors d’un accès à l’espace privé, comparaison de la variable $spip_version avec la valeur mémorisée dans la base de données.
  3. si ces valeurs diffèrent, SPIP propose une mise à niveau de la base (ecrire/base/upgrade.php), laquelle se fait après une vérification FTP.

Clairement, ce processus ne peut pas fonctionner dans le cadre de la mutualisation : on ne va pas demander au responsable de chaque site d’aller créer un fichier ad hoc par FTP.

Remarque : avec dans le mes_options.php global la ligne suivante :

define('_ID_WEBMESTRES', '1');

on va permettre à celui qui crée le site d’être webmestre de celui-ci et de ne pas avoir besoin de l’authentification FTP et donc de réaliser les mises à jour sans accès FTP.

Il faut donc procéder autrement. Une piste (TODO) serait de faire un script spécialisé, qui ferait un « foreach » sur les sites, et les mettrait à jour chacun son tour en appelant la fonction spécifique d’upgrade. Sachant que, si une mise à jour se passe mal, le risque est de planter tous les sites d’un coup.

Offrir plusieurs versions. Autre problématique, la compatibilité des squelettes avec les versions de SPIP. En règle générale ça ne pose pas de problème, mais dans certains cas, on peut avoir envie de vérifier avant de basculer. On suppose alors que chaque site va vouloir upgrader à son rythme, après des vérifications (par exemple en local).

Dans ce cas, il faut prévoir quelque chose comme plusieurs installations mutualisées de SPIP, côte à côte, avec un lien symbolique identifiant les répertoires spip-version1/sites/ et spip-version2/sites/ (dans ce cas, on va créer les répertoire sites/ au même niveau que les répertoires spip-version1/ et spip-version2/).

La définition d’un site, du coup, repose dans la création d’un lien symbolique de la racine VHost du site vers la version souhaitée de SPIP. Pour changer de version, il suffit alors de modifier un lien symbolique, de vérifier si le site tourne, puis d’aller dans l’espace privé procéder à la « mise à niveau » (en général irréversible) de la base de données.

TODO : Récupérer son site

Pour être viable, le système doit proposer au webmestre de chaque site une méthode simple pour récupérer ses données et les réinstaller aillurs (en local pour tests, ou sur un site « dédié »). Pour récupérer ses données, il y a deux méthodes : la sauvegarde SPIP et le dump SQL classique. Il faut voir comment on détermine qui est le webmestre autorisé à récupérer cela (une piste avec autoriser(webmestre), cf. -Autorite-). Pour récupérer les images, un simple script avec wget suffit, mais est-ce suffisamment pratique ?

Mettre en place un service d’hébergement mutualisé

Des notes sur la mise en place d’un hébergement mutualisé de sites spip avec gestion des utilisateurs et activation individuel des espaces : Service d’hébergement mutualisé

Un site était mutualisé mais maintenant il vole de ses propres ailes ailleurs

Soit un site (toto.scriibe.net) par exemple ... il a maintenant déménagé et se trouve à l’url www.toto.com , c’est bien mais google a encore des adresses sur toto.scriibe.net

pour ce faire, une méthode peut être la suivante :
dans le mes_options.php général :

        $site = $_SERVER['HTTP_HOST'];
        if ($site == "toto.scriibe.net") 
        {
                include_spip('inc/headers');
                redirige_par_entete("http://www.toto.com");
        }