





Cet article tente de décrire l’installation et la maintenance de base d’un serveur web. Pour ce faire nous allons utiliser VirtualBox pour virtualiser la machine et Ubuntu Server comme distribution. Le serveur sera composé des éléments les plus courants (Linux, Apache, MySQL, PHP) qui ont donné naissance à l’appelation LAMP (WAMP sous Windows, MAMP sous OS X, etc). Ubuntu a été choisie pour sa simplicité d’administration. Les configurations d’origine des packages ont été retouchées par les équipes de Canonical et apportent un bon rapport efficacité / complexité
Installation du système
http://www.ubuntu.com/server/get-ubuntu/download
Vous pouvez prendre la version 64 bits si vous avez un processeur compatible et que votre système d’exploitation hôte est lui même une version 64 bits. Dans le cas contraire le mode 64 bits ne sera au mieux qu’émulé et c’est extrêmement lent. Dans la mesure ou nous aurons besoin de communications entre la machine virtuelle et l’hôte ainsi qu’un accès à Internet, il faudra mettre en place un pont réseau (cette opération, dépendante de votre système d’exploitation hôte, sort du cadre de cet article). Il sera nécessaire à partir de l’étape Installation des packages.
L’installation du système n’a rien de compliqué. On vous demandera votre langue (français); l’agencement du clavier (france-autre); ensuite le nom de la machine. Je vous conseille de saisir un nom complet (FQDN) tel que mamachine.mondomain.local. J’utilise souvent des noms en .virtualbox.local histoire d’être tranquille. Le partitionnement du disque se fera tout seul en choisissant l’option Assisté – utiliser un disque entier.
Le compte root est désactivé par défaut sur Ubuntu par mesure de sécurité. Il faudra donc créer un compte utilisateur, qui sera sudoer. Sur une machine virtuelle de test on utilisera un mot de passe simple et pas de chiffrement du dossier personel. Si vous ne savez pas ce qu’est un serveur mandataire (aussi appelé proxy) c’est que vous n’avez pas besoin de remplir ce champ. Je ne préfère pas laisser le système se mettre à jour tout seul. Je teste généralement au préalable les mises à jour lorsqu’elles sortent sur une machine virtuelle afin de m’assurer que le système et les applications sont bien compatibles avec elles.
Dans la mesure où l’on va installer les packages requis lors de la prochaine étape, on ne choisira ici que le paquet OpenSSH server. Dans le cadre de notre machine virtuelle nous n’avons qu’un système d’exploitation sur le disque, donc on installe grub sur le secteur d’amorçage. Pensez bien à retirer le cdrom virtuel de son lecteur avant de redémarrer.
Une fois l’installation terminée, on ouvre une session sur le serveur et on met à jour la liste des paquets, puis le système :
1 2 |
$ sudo apt-get update $ sudo apt-get upgrade --yes |
Note: sudo vous permet d’exécuter une commande en tant que root. Vous êtes autorisé à le faire car le compte créé lors de l’installation est sudoer. Vous pouvez gagner un peu de temps en devenant vraiment root grâce à la commande sudo su, auquel cas vous n’aurez plus à taper sudo devant toutes vos commandes. Lorsque vous aurez terminé, vous redeviendrez vous-même en tapant exit.
A partir de ce point vous pouvez faire une copie de sauvegarde de votre image disque ou prendre un instantané depuis VirtualBox (je préfère la première solution). Cela vous permettra de revenir en arrière en cas d’erreur, ou pour reprendre la procédure.
Installation des packages
A partir de ce point on estime que vous avez une connexion par pont fonctionnelle si vous souhaitez dérouler la procédure en SSH et accéder à vos services depuis une autre machine. Vous pourrez toutefois rester en NAT si vous le faites depuis le terminal de VirtualBox.
Installation d’Apache :
1 |
$ sudo apt-get install --yes apache2 |
Sous Ubuntu Server, Apache a une configuration que j’apprécie beaucoup. La base de la configuration d’Apache est dans le fichier /etc/apache2/apache2.conf. Un fichier /etc/apache2/httpd.conf est présent, vide, mais inclu par le premier afin d’assurer la compatibilité avec d’autres architectures. Le dossier /etc/apache2/conf.d/ permet de séparer toute la configuration d’apache par petits fichiers en rapport avec tel ou tel élément à configurer. La gestion des modules est répartie dans deux dossiers /etc/apache2/mods-available/ et /etc/apache2/mods-enabled/. Le premier contient tous les modules installés et le second ceux qui sont chargés. Il suffit de créer un lien symbolique des modules du dossier available vers le dossier enabled et de recharger apache pour qu’ils soient utilisables. La désactivation revient à supprimer le lien symbolique et recharger Apache. Le même principe existe pour les sites web avec les dossiers /etc/apache2/sites-available/ et /etc/apache2/sites-enabled/.
C’est une gestion que je trouve propre et pratique. Elle est applicable à n’importe quelle distribution, mais le fait qu’elle soit par défaut dans Ubuntu fait partie des détails qui me font apprécier cette distribution.
Par défaut les sites web vont dans /var/www/ et les fichiers logs dans /var/log/apache2/. Mais j’aime stocker tous les éléments d’une application web dans un dossier dédié et nous verrons à la prochaine étape comment j’organise cela.
Installation de PHP :
1 |
$ sudo apt-get install --yes php5 |
Le meta-paquet php5 installera PHP-CGI, qui est le module PHP pour Apache. Dans certain cas on peut aussi avoir besoin de PHP-CLI qui est l’interpréteur en ligne de commande de PHP. Il permet notamment de parser des scripts php pour vérifier leur validité syntaxique, ou de créer des scripts systèmes en PHP, ce qui peut être pratique quand on ne maîtrise pas trop Bash ou qu’on a besoin de fonctions propres à PHP pour gérer le système.
On peut optionnellement installer PHP-CLI avec la commande :
1 |
$ sudo apt-get install --yes php5-cli |
On a maintenant accès à php en ligne de commande, exemples :
1 2 |
$ php -v $ php -r 'echo("Hello world !\n");' |
Installation de MySQL :
1 |
$ sudo apt-get install --yes mysql-server php5-mysql |
Le meta-paquet mysql-server-5.0 installera le serveur MySQL et le client MySQL. Le client est principalement utilisé lorsqu’on lance le terminal MySQL, par exemple lors de déploiements, sauvegardes ou restaurations.
Par souci de sécurité, dès l’installation on vous demandera de choisir un mot de passe pour le compte root de MySQL.
On peut optionnellement installer phpMydmin pour gérer les bases de données (déconseillé en production) :
1 |
$ sudo apt-get install --yes phpmyadmin |
Si vous avez choisi de l’installer, l’installeur de phpmyadmin vous demandera quel serveur web utiliser. Bien sur on choisira Apache 2. Ensuite il vous proposera de configurer la base de données de phpmyadmin, ce qu’il est conseillé de faire si l’on veut avoir accès à toutes ses fonctionnalités. Le cas échéant vous devrez re-préciser le mot de passe choisi à l’installation du serveur.
Création de sites web
Par défaut les sites web sont situés dans /var/www/ sous Apache 2. Mon expérience m’a fait prendre quelques manies quand à l’organisation d’un site web. Elles sont issues des éléments que j’ai l’habitude d’utiliser mais n’en sont pas dépendantes. Voici ce que je préconise :
Je place mes sites webs soit dans /opt/nom_du_projet/; soit dans /home/www/nom_du_projet/. Le dossier /opt/ est reconnu comme le dossier des applications ne faisant pas parties de la distribution système, c’est un bon emplacement dans la mesure ou le serveur a pour but de servir ces applications. Mais selon l’espace disque affecté lors du partitionnement si on n’a pas la main dessus, on a souvent le plus d’espace disque dans le dossier /home/.
Dans chaque dossier d’application, je crée les sous-dossiers suivants :
- logs : fichier logs de tous les éléments de l’application (serveur web, application, déploiement, maintenance, etc).
- scripts : scripts de déploiement, de sauvegarde, de restauration, configuration des éléments externes (Apache notament), schéma de base de donnée au format SQL.
- htdocs ou www ou public : racine web du projet. Contient les fichiers exposés par Apache, limités au strict minimum (fichiers statiques essentiellement).
- doc : documentation du projet (utile uniquement en développement et / ou intégration).
On se retrouve donc avec la configuration et les logs Apache dans l’application (et donc déployés avec elle, sauvegardés avec elle, etc). Soit ces fichiers sont stockés avec l’application et copiés lors du déploiement dans leurs dossiers respectifs (la configuration Apache dans /etc/apache2/sites-enabled/, les logs dans /var/log/apache2.) mais je ne suis pas fan ; soit on crée des liens symboliques pointant vers ces fichiers, ce que je pratique dans le cas des configurations Apache. Pour les logs, la configuration Apache lui indique l’endroit où les stocker.
Parmi vos codes sources, tentez de ne rien stocker qui ait une extension exotique. Un fichier contenant du code PHP doit etre nommé .php et non .inc ou .dat. Bien sur, si le serveur est correctement configuré il interprètera le fichier au lieu de le servir, mais si vous commencez à prendre ce genre de mauvaises habitudes, le jour où une erreur sera faite le serveur commencera à servir le code source de vos scripts au lieu de les interpréter et cela peut vite devenir dramatique. Ce problème ne se pose pas avec les applications de type Zend où le code source est hors racine web, mais ce n’est pas une raison pour se passer de fixer et respecter des conventions 🙂
Il y a trois principaux modes d’hébergement de sites web avec Apache. Je vais donner quelques exemples d’utilisations courantes mais seule l’imagination de l’administrateur peut en définir les bornes :
- Le virtualhost
- Le named virtualhost
- L’alias
Le virtualhost sert un site web en fonction du couple adresse IP / numéro de port demandé. Il permet d’avoir différents noms de domaine sur un même site (fr.monsite.com, uk.monsite.com, etc). Dans ce cas on différencie la version du site à servir dans le code, en analysant la requête du client.
Le named virtualhost sert un site web en fonction du nom de serveur demandé. Il permet d’avoir plusieurs sites sur le même serveur, utilisant tous le même port. C’est typiquement le fonctionnement d’un serveur mutualisé. Ce mode de fonctionnement nécessite un appel de chaque site par nom. Par conséquent vous devrez soit disposer en local d’un serveur DNS; soit avoir modifié votre fichier hosts pour refléter ces sites web (/etc/hosts sous Linux ou OS X; c:\windows\system32\drivers\etc sous Windows).
L’alias sert un site web en fonction d’un nom de dossier virtuel derrière le nom de domaine. Il est beaucoup utilisé pour servir des applications aux clients d’hébergeurs (http://monsite.com/phpmyadmin par exemple).
J’ai volontairement oublié les fichiers .htaccess ici. Ces fichiers n’ont pas a êtres utilisés sur serveurs dédiés. Le fichier .htaccess a été créé comme palliatif aux restrictions imposées par les hébergeurs mutualisés et est une pure catastrophe pour votre site web. A titre de comparaison la configuration Apache est chargée en mémoire au démarrage d’Apache, ou si vous lui demander explicitement de le faire. Un fichier .htaccess, lui, est rechargé depuis le système de fichier a chaque appel d’un fichier présent dans le même dossier que lui ou l’un de ces descendants. Donc si vous en placez un à la racine de votre site, chaque page, chaque image, chaque feuille de style, chaque script va forcer Apache à recharger ce fichier. Je vous laisse imaginer l’impact sur les perfs du serveur.
Prenons par exemple la confguration Apache par défaut de phpMyAdmin lorsqu’il est installé en tant que package Ubutu :
1 2 |
# phpMyAdmin default Apache configuration Alias /phpmyadmin /usr/share/phpmyadmin |
On voit clairement que phpMyAdmin s’installe par défaut en mode alias. Pour y accéder on utilise donc l’adresse IP de la machine (ou n’importe quel nom qui puisse être résolu vers celle-ci), suivi de l’alias /phpmyadmin. Le contenu du site étant situé dans /usr/share/phpmyadmin.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
<Directory /usr/share/phpmyadmin> Options FollowSymLinks DirectoryIndex index.php <IfModule mod_php5.c> AddType application/x-httpd-php .php php_flag magic_quotes_gpc Off php_flag track_vars On php_flag register_globals Off php_value include_path . </IfModule> </Directory> |
Ici on déclare les options Apache pour le dosssier local /usr/share/phpmyadmin. Dans ce dossier Apache suivra les liens symboliques s’il en trouve, et cherchera à afficher par défaut un fichier index.php si la requête du client est incomplète. Si le module mod_php5 est chargé par Apache, on définit quelques options supplémentaires. On définit un nouveau type MIME pour les fichiers dont l’extension est .php et on définit quelques variables PHP, qui auront donc une valeur spécifique pour ce site web.
1 2 3 4 5 6 7 8 9 |
# Authorize for setup <Directory /usr/share/phpmyadmin/setup> <IfModule mod_authn_file.c> AuthType Basic AuthName "phpMyAdmin Setup" AuthUserFile /etc/phpmyadmin/htpasswd.setup </IfModule> Require valid-user </Directory> |
Le dossier setup du site, sera protégé par une authentification Apache. Si le module mof_authn_file est chargé par Apache, on précisera un fichier contenant les mots de passe valides à faire vérifier par Apache.
1 2 3 4 5 6 7 8 9 |
# Disallow web access to directories that don't need it <Directory /usr/share/phpmyadmin/libraries> Order Deny,Allow Deny from All </Directory> <Directory /usr/share/phpmyadmin/setup/lib> Order Deny,Allow Deny from All </Directory> |
Les dossiers /usr/share/phpmyadmin/libraries et /usr/share/phpmyadmin/setup/lib n’ont pas a êtres navigables par les clients. Leur but est uniquement de contenir des fichiers qui seront inclus par d’autres. On demande donc à Apache d’en refuser l’accès. On précise que par défaut apache refuse l’accès, puis on refuse l’accès à tout le monde.
Naturellement ces directives ne sont pas spécifiques aux alias. Elles fonctionnent très bien aussi avec les autres modes.
Comme second exemple, nous allons voir la configuration Apache que j’utilise courament pour des applications Zend en environnement de développement :
1 |
Listen *:8081 |
Je commence par demander à Apache d’écouter sur un autre port que le 80 (par défaut). Car j’ai souvent plusieurs sites sur la même machine.
1 |
<VirtualHost *:8081> |
Puis je crée un VirtualHost. J’ai d’ailleurs tendance a appeler les les configurations Apache de site des VirtualHost, ce qui est une déformation trompeuse.
1 2 3 4 5 |
ServerAdmin webmaster@localhost DocumentRoot /home/www/mon_projet/htdocs ServerName dev.mon_projet.local |
Dans le cas d’un VirtualHost, le serveur a des paramètres locaux (qui apparaitront dans les logs, les messages d’erreur, etc) comme un administrateur par défaut, un nom de serveur et bien sur le chemin vers la racine du site. En développement je stocke souvent mes sites dans /home/www/ (à tort ou à raison).
1 2 3 4 5 |
<Directory /home/www/mon_projet/htdocs/> Options Indexes FollowSymLinks MultiViews AllowOverride None Order allow,deny allow from all |
Contrairement aux dossiers protégés de phpMyADmin, ici on autorise l’accès par défaut. De toute manière, comme nous allons le voir plus loin, les applications Zend sont généralement faites pour n’exposer que le strict minimum de code PHP, le reste étant situé dans une zone en dehors du site web et incluse par l’index (en l’occurence /home/www/mon_projet/application/ et /home/www/mon_projet/library/).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
# Let's make sure that we have setup the timezone for this application, # In some php.ini files, this value is not set. This will ensure it exists # for every reqeust of this application. php_value date.timezone "UTC" # Let's also make sure that we can use the php short tag, "<?". The # reason this is enabled is so that we can use these short tags in our # php based view scripts. This allows for a shorter and cleaner view # script, After all PHP IS A TEMPLATING LANGUAGE :) php_value short_open_tag "1" # Set the error_reporting to E_ALL|E_STRICT # Since .htaccess only take an integer (and cannot render the PHP # contstants, we need to find out what the integer actuall is) # # > php -r "echo E_ALL|E_STRICT;" # 8191 php_value error_reporting "8191" # The following display_*error directives instruct PHP how to display # errors that might come up. In a production environment, it might # be good to set these values inside the actual VHOST definiation. # NOTE: these display error ini's should most likely be OFF # in your production environment php_value display_startup_errors "1" php_value display_errors "1" # NOTE: by setting the above php ini values, we are ensuring that # regardless of the servers php.ini values, we can be assured that # our application will have these set values set on every request. |
Comme dans le cas de phpMyAdmin, on définit quelques variables de configuration PHP propres au site.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
# The rules below basically say that if the file exists in the tree, just # serve it; otherwise, go to index.php. This is more future-proof for your # site, because if you start adding more and more content types, you don't # need to alter the .htaccess to accomodate them. # This is an important concept for the Front Controller Pattern which the # ZF MVC makes use of. RewriteEngine On RewriteCond %{REQUEST_FILENAME} -s [OR] RewriteCond %{REQUEST_FILENAME} -l [OR] RewriteCond %{REQUEST_FILENAME} -d RewriteBase / RewriteRule ^.*$ - [NC,L] RewriteRule ^.*$ index.php [NC,L] |
La partie intéressante des applications Zend est la ré-écriture d’URL Apache, qu’on commence par activer (mod_rewrite). Le principe est que toute les requêtes seront envoyées au même script, index.php sauf si la requête demandée correspond à un fichier non-vide (-s), à un lien symbolique (-l) ou à un dossier (-d). Dans ces trois cas on traite la requête sans la rediriger. Autrement dit tout appel de fichier présent sur le disque sera traité tel quel, et tout appel de fichier inexistant sera envoyé tout de même à l’application, sans générer d’erreur Apache. Sachant qu’une application Zend prévoit un moyen de détecter et d’afficher des messages d’erreur proprement, et en spécifiant bien les erreur HTTP qu’Apache doit transmettre, ce qui est très important.
C’est le genre de paramètre qui permet à des sites d’utiliser des adresses du type http://monsite.com/articles/nom-de-larticle au lieu des anciennes http://monsite.com/articles/index.php?id=123456.
Voir aussi: Documentation Apache 2 mod_rewrite
1 2 3 4 |
SetEnv REGISTER_GLOBALS 0 SetEnv ZEND_OPTIMIZER 0 SetEnv MAGIC_QUOTES 0 SetEnv PHP_VER 5 |
Dans une configuration Apache on peut aussi définir des variables d’environnement locales au site web servi.
1 2 3 4 5 6 7 8 9 10 11 |
</Directory> ErrorLog /home/www/mon_projet/logs/error.log # Possible values include: debug, info, notice, warn, error, crit, # alert, emerg. LogLevel warn CustomLog /home/www/mon_projet/logs/access.log combined </VirtualHost> |
Autre chose qui me tient à cœur, le fait de ranger les logs Apache dans le dossier du projet, comme je précisais plus haut.
Naturellement, pour que ce type de configuration fonctionne, on veillera à ce que mod_rewrite soit présent dans le dossier des modules à charger de la configuration Apache.
MySQL supporte deux modes de fonctionnement :
- Le mode socket est rapide, mais local exclusivement.
- Le mode IP est un peu plus lent mais permet les accès externes.
Il propose aussi plusieurs moteurs de stockage. Les deux plus courant sont myISAM et InnoDB. InnoDB offre l’avantage de gérer les transactions, les relations et les triggers, mais le gros inconvénient de fonctionner totalement en mémoire. myISAM est un moteur basique, robuste et rapide. Il convient le plus souvent. Je n’évoquerais pas NDB, qui fera certainement l’objet d’un article sur mySQL cluster si je ne l’ai pas définitivement abandonné pour PostGres d’ici là 😉
Pour la configuration de tout cela et la façon de coder de manière a obtenir toute une chaîne de compatibilité en UTF-8, je vous conseille cet excellent article :
Voir: UTF-8 PHP MYSQL (histoire d’encodage) par Thierry Sottani.
La seule remarque que j’aurai à y apporter est que je préfère l’utf8_unicode_ci à l’utf8_bin.
Pour conclure avec MySQL, sachez que dans les distributions sources de MySQL on peut trouver SQL Bench, un petit outil remarquable qui vous permettra de mesurer l’efficacité de vos réglages sur un SGBD. Il est compatible avec un grand nombre de serveurs. MysqlDiff et MysqlTuner sont des outils bien pratiques pour aider à gérer les évolutions de base et le réglage fin du moteur.
Maintenance
Déploiement
Le déploiement d’une application consiste à installer et configurer l’application. S’il s’agit d’une nouvelle version, soit elle est désinstallée au préalable, soit on effectue une migration. Il faut prévoir le déploiement très tôt, dès l’étude de l’application. Il est capital de bien séparer dès le début l’application (identique sur tous les environnements), sa configuration (différente sur chaque environnement) et ses données (qui doivent persister entre deux déploiements).
Sur les serveurs mutualisés le déploiement est généralement fait par FTP. FTP est rapide car il n’est pas chiffré, mais il faut prendre en compte qu’on ne peut pas transférer une archive par FTP, que l’on va décompresser sur place. Il est limité au transfert de fichiers.
Sur les plateformes d’exploitation professionnelles on utilise SSH qui permet de déployer un code depuis de nombreuses sources (tarballs, subversion, etc), d’adapter les fichiers de configuration à l’environnement cible à la volée, et d’effectuer des opération aussi complètes que nécessaires.
Une procédure bien standardisée doit pouvoir assurer les rôles de :
- Déploiement
- Migration
- Transfert
- Restauration
Un script de déploiement effectue généralement les tâches suivantes :
- Arrêt ou verrouillage des services, voire mise hors ligne de l’application lorsque cela est possible
- Sauvegarde de la version actuelle, si elle existe (voir Sauvegarde)
- Suppression de la version actuelle de l’application
- Installation de la nouvelle version de l’application
- Configuration de la nouvelle version de l’application si elle n’a pas déjà été préparée avant livraison
- Configuration du système (droits des dossiers, liens symboliques, etc)
- Migration des données lorsque nécessaire (altérations de base de données, par exemple)
- Tests unitaires
- En cas de succès, mise en ligne de l’application, déverrouillage ou redémarrage des services
En cas d’échec, un script de retour arrière (Rollback), devra nettoyer puis redéployer la sauvegarde.
Sauvegarde
La sauvegarde doit contenir, idéalement en une seule archive, l’intégralité d’une application au moment où elle a été déclenchée. On peut éventuellement séparer les constituants (application, données, bases), en sachant qu’un risque est induit d’égarer ou de détruire l’un deux (transfert échoué, support détruit ou volé, etc). Une sauvegarde doit être stockée en deux endroits géographiquement distincts.
Un script de sauvegarde effectue généralement les opérations suivantes :
- Arrêt ou verrouillage des services, voire mise hors ligne de l’application lorsque cela est possible
- Sauvegarde de l’application et de sa configuration
- Sauvegarde des données
- Sauvegarde des bases de données
- Re-mise en ligne de l’application, déverrouillage ou redémarrage des services
- Test de l’archive
- Stockage en deux emplacements
Restauration
La restauration consiste à re-déployer la dernière sauvegarde de l’application sur l’environnement concerné. La procédure étant très proche de celle de déploiement, autant la faire effectuer par le même script.
Nettoyage
Selon la conception de l’application, il peut être nécessaire de nettoyer régulièrement les sessions, les fichiers temporaires et les fichiers logs. On utilisera généralement un script de rotation des logs pour ces derniers, puis un archivage des anciens (avec les sauvegardes par exemple). Les logs ont un niveau différent selon l’environement. En développement on loggera au niveau debug alors qu’en production on loggera au niveau error. Prévoir un système de log à niveaux afin de ne pas trop charger les logs d’exploitation.
Mise à jour
Avant de mettre à jour les composants du serveur, pensez à tester ces mises à jour sur une machine virtuelle identique, comme vous le faites avec le code de votre application sur des environnements d’intégration ou de pré production.
Conclusions
Il est simple et rapide aujourd’hui de mettre en place un environnement de travail complet, et donc de bénéficier des avantages techniques et sécuritaires de grosses usines de développement.
Le cout est virtuellement nul dans la mesure où la virtualisation permet de réutiliser le matériel existant, et tous les logiciels employés peuvent êtres puisés dans le domaine open source.
Il ne reste plus qu’un peu de temps et de formation à investir dans ces bonnes pratiques.





