apache « Neomarco Insiders

Archive pour le mot-clef ‘apache’

Migration vers Passenger – épilogue

Mercredi 28 janvier 2009

Cet article est le 4ème et dernier dans la série “Migration vers Passenger” qui comprend également :
- le préambule.
- Ruby Entreprise Edition
- VirtualHosts

Et voilà, c’est fini !

Mardi 28 janvier, aux alentours de 22h, la phase finale de cette migration a eu lieu. Voilà ce qu’il a fallu gérer pour que ça se termine bien.

Les VirtualHosts

Par rapport à l’article précédent, j’ai encore fait quelques modifs sur les VirtualHosts.

Les directives de compression des fichiers pouvant l’être est identique d’un site à l’autre, j’ai donc créé un fichier /etc/apache2/deflate.conf qui est inclus dans chaque VHost.

  AddOutputFilterByType DEFLATE application/x-javascript text/html text/plain text/css text/javascript text/xml
  BrowserMatch ^Mozilla/4 gzip-only-text/html
  BrowserMatch ^Mozilla/4.0[678] no-gzip
  BrowserMatch \bMSIE !no-gzip !gzip-only-text/html

Idem pour /etc/apache2/expires.conf qui définit des expirations de cache. Ça peut encore être amélioré, mlais c’est déjà pas mal.

  # turn on the module for this directory
  ExpiresActive on
 
  # cache common graphics for 3 days
  ExpiresByType image/jpg "access plus 3 days"
  ExpiresByType image/gif "access plus 3 days"
  ExpiresByType image/jpeg "access plus 3 days"
  ExpiresByType image/png "access plus 3 days" 
 
  # cache css and javascript for 3 days
  ExpiresByType text/javascript "access plus 3 days"
  ExpiresByType application/x-javascript "access plus 3 days"
  ExpiresByType text/css "access plus 3 days"
 
  ExpiresDefault "access plus 24 hours"

J’ai également rajouté (pour les applications Rails) la redirection vers la page de maintenance au cas où elle existe

# Check for maintenance file and redirect all requests
RewriteCond %{DOCUMENT_ROOT}/system/maintenance.html -f
RewriteCond %{SCRIPT_FILENAME} !maintenance.html
RewriteRule ^.*$ /system/maintenance.html [R=307,L]

À la fin de la migration, je prévois de désactiver tous les VHosts de Nginx, au cas où j’aurai besoin de relancer Nginx pour autre chose.

Les ports

Comme plusieurs serveurs ne peuvent pas écouter le même port en même temps, il fallait faire les changements.

Jusque là Nginx écoutait le port 80 et Apache le port 8080.

La modification du port d’Apache se fait à un seul endroit de sa config générale. Sur Debian, ça se passe dans /etc/apache2/ports.conf. J’y ai ajouté le port 80. Le port 8080 peut rester pour le moment.

Pour Nginx, c’est plus compliqué car il est indiqué dans chaque VirtualHost. Je ne sais pas si c’est une maladresse de ma part, mais dans mon cas c’est fait comme ça. Je n’avais pas très envie de modifier chaque VHost, mais comme Nginx n’avait pas besoin de continuer à fonctionner après la bascule, pas besoin de changer quoi que ce soit.

Les logs

Pour copier les logs de Nginx vers le dossier d’Apache, je pouvais faire une simple copie du contenu du répertoire, mais tout ne m’intéressait pas, notamment parce qu’il y a pas mal de logs de scripts ou sites dont je n’ai plus besoin.

J’ai donc écrit un script en bash pour faciliter ça.

#!/bin/sh
 
if [ $(whoami) != "root" ]; then
	echo "Vous devez etre super-utilisateur pour executer ce script."
	exit
else
	rm /var/log/apache2/*.access*
	rm /var/log/apache2/*.error*
 
	cp /var/log/nginx/default.access* /var/log/apache2/
	cp /var/log/nginx/site1.access* /var/log/apache2/
	# (...)
	cp /var/log/nginx/siteX.access* /var/log/apache2/
 
	invoke-rc.d apache2 restart
 
	invoke-rc.d nginx stop
fi

Comme Apache ne servait à rien d’important jusque là, j’ai commencé par nettoyer tout son dossier de logs. Ensuite je copie uniquement les logs des sites voulus. Le redémarrage d’Apache permettait de reprendre les logs là où ils en étaient et de changer le port d’écoute. L’arrêt de Nginx permettait de libérer le port 80. J’aurais cerainement dû inverser l’arrêt de Nginx et le restart d’Apache, mais ça a bien marché.

Enfin Webalizer utilise /var/log/nginx/siteX.access.log comme source, il suffit de changer le dossier cible avant qu’il fasse son analyse suivante.

Monit et Munin

Je voulais que Monit puisse suivre l’écoute du port 80 par Apache. J’ai donc suivi la recommendation courante et créé un fichier vide dans /var/www/monit/token. Monit va donc tester une connexion HTTP vers http://mon.addresse.ip.publique/monit/token.
Mais comme je veux éviter d’avoir des milliers de logs pour cette URL, j’ai ajouté une directive dans le VHost “default”

SetEnvIf Request_URI "^\/monit\/token$" dontlog
CustomLog /var/log/apache2/access.log combined env=!dontlog

Nginx devant s’arrêter, il fallait le sortir de la liste des ressources à surveiller. Pour cela il suffit de commenter l’inclusion de son fichier de config dans le fichier de config de Monit (/etc/monit/monitrc).
Inversement, j’ai décommenté l’inclusion du fichier pour Apache.

Au niveau de Munin, les plugins sont des scripts qui renvoient du texte dans un format normalisés. Ils sont stockés dans /usr/share/munin/plugins/. Pour les activer, il suffit de créer un lien symbolique dans /etc/munin/plugins/.

J’ai supprimé le lien pour les scripts de Nginx.
J’avais déjà préparé le terrain pour Apache et activé ses scripts en les faisant tester le port 80, ce qui jusqu’à la bascule ne rendait aucune info.

Capistrano et Mongrel

Dans la situation initiale, ce sont des process Mongrel qui faisaient fonctionner les applis Rails. Ils étaient démarrés et relancés par une recette de déploiement Capistrano.

Dans #{RAILS_ROOT}/config/deploy/production.rb il y avait

namespace :deploy do
  desc "Start the application"
  task :start, :roles => :app do
    mongrel.start
  end
 
  desc "Stop the application"
  task :stop, :roles => :app do
    mongrel.stop
  end
 
  namespace :mongrel do
 
    desc "Restart the Mongrel cluster"
    task :restart do
        stop
        start
    end
 
    desc "Start the Mongrel cluster"
    task :start do
        run "/usr/bin/mongrel_rails cluster::start -C #{shared_path}/config/mongrel_cluster.yml --clean"
    end
 
    desc "Stop the Mongrel cluster"
    task :stop do
        run "/usr/bin/mongrel_rails cluster::stop -C #{shared_path}/config/mongrel_cluster.yml --clean"
    end
  end
end

J’ai simplifié comme ça

namespace :deploy do
 
  namespace :passenger do
    desc "Restart the application within Passenger Phusion"
    task :restart do
        run "touch #{release_path}/tmp/restart.txt"
    end
  end
 
  desc "Restart the application"
  task :restart, :roles => :app do
    passenger.restart
  end
 
end

Une fois la modification faite il suffisait de stopper les applis qui tournaient via Mongrel :

$ cap production deploy:stop

Les oublis

Un seul oubli en fait ; mettre certains droits à l’utilisateur www-data (celui qui est attribué à Apache) au niveau de certains fichiers des blogs Wordpress. En conséquence, les fichier .htaccess ne pouvaient plus être utilisés pour les RewriteRules.

Je m’en suis rendu compte car la page d’accueil du site neomarco.com se connecte sur le flux RSS d’un des blogs et là ça ne fonctionnait plus.

Conclusion

Tout s’est passé dans un délai très court ; quelques dizaines de secondes pour l’enchaînement des taches cruciales. Toutes les modifications de config qui pouvaient se faire un amont l’avaient été et celles à faire ensuite n’étaient pas urgente.

Par contre, tout le temps de préparation – recherche de tous les points possibles de changement, établissement d’un enchaînement idéal, tests de portion de la bascule sur d’autres serveurs, … – a pris beaucoup plus de temps. J’ai pas mesuré précisément, mais ça représente une bonne douzaine d’heures.

Il reste certainement des petites modifications pour affiner l’ensemble, mais déjà je trouve que le site réponds beaucoup plus vite.
Le cache semble mieux marcher au niveau du navigateur. Je ne pense pas que ça soit dû à une défaillance de Nginx, mais plutôt à une config mal faite de mon côté.
Par contre il est indéniable que les ressources mobilisés sont bien moindres. J’ai économisé plus de 2go de ram en confiant à Apache+Passenger le soin de lancer les process dont il a besoin.

Je vais maintenant pouvoir mettre en place un bon gros serveur memcached avec la ram libérée. Mais là c’est une autre histoire.

Migration vers Passenger – VirtualHosts

Dimanche 25 janvier 2009

Cet article est le 3ème dans la série “Migration vers Passenger” qui comprend également :
- le préambule.
- Ruby Entreprise Edition

Le temps de tout mettre en place, j’ai installé Apache (2.2) et je l’ai fait écouter toutes les interfaces sur le port 8080. Ça me permet de tester ma config avant de faire le grand saut.

Nginx reprends un certan nombre de principes d’Apache, comme la notion de fichiers de configuration modulaires (via des include), des virtual hosts, … Lors de ma prise n main de Nginx, j’avais même écrit des scripts facilitant la gestion des virtualhosts en m’inspirant très fortement de ceux d’Apache ; a2ensite, a2dissite, …

J’ai donc une série de virtualhosts, chacun dans un fichier (plus facile à gérer individuellement). La syntaxe de configuration est très différente, mais ce qui doit être configuré chez l’un doit aussi l’être chez l’autre il faut donc “simplement” transposer les directives.
J’ai commencé par une copie toute simple des fichiers de virtualhosts de Nginx dans le dossier /etc/apache2/sites-available (le répertoire standard d’Apache pour les virtualhosts disponibles).

Dans Nginx, il n’y a pas de notion de ServerName puis ServerAlias, c’est tout au même niveau, mais là rien de difficile.
J’ai galéré pendant environ 3 heures inutilement car j’avais pas vu que j’avais gardé le “;” de fin de ligne de Nginx dans la définition des ServerName et ServerAlias dans Apache. Les tests de syntaxe de la config étaient OK, l’analyse des Vhosts actifs et leur ordre semblait aller, … mais bon, c’était aussi stupide que ça. Le plus drôle c’est qu’à chaque fois que je modifiais une config Nginx ces derniers mois, j’oubliais de les mettre ces fichus “;” de fin de ligne.

On définit ensuite les règles de re-écriture (RewriteRule). La syntaxe est là aussi différente mais le principe est identique.

Les règles de compression (Gzip) des contenus se gèrent pour Apache dans chaque site (sauf si j’ai mal vu et qu’on peut aussi le régler globalement) et de manière plus détaillée. Dans Nginx on ne se soucie pas du comportement selon les navigateurs. J’avoue ne pas avoir regardé si il traite ça tout seul en interne ou bien si il ne le traite pas.

Il y a quelques règles d’expiration que je n’ai pas encore adaptées, comme par exemple celle qui indiquent une vie de 1 mois pour tous les contenus graphiques, feuilles de style et javascript.

Il me reste à voir la question des logs.
En soi rien de compliqué ; on indique le niveau du log (”warn” par défaut), le fichier pour les logs d’accès et celui des logs d’erreur.
Par contre le dilemne, c’est la continuité. Le format de logs n’est pas un problème, car j’avais fait écrire à Nginx des logs au format d’Apache (combined), donc rien ne change.
Par contre ils étaient stockés dans /var/log/nginx/neomarco-prod.access.log, … Je vais décemment pas continuer à les stocker ici, mais j’ai quand même besoin d’une homogénéité pour logrotate et webalyzer.
Je pense que je vais simplement les déplacer dans /var/log/apache2/ où Apache continuera de mettre les siens, à moins que vous ayez une meilleure idée.

La fin : épilogue

Migration vers Passenger – Ruby Entreprise Edition

Dimanche 25 janvier 2009

Cet article est le second dans la série “Migration vers Passenger” dont vous pouvez commencer par le préambule.

L’utilisation de Passenger ne nécessite rien d’autre qu’Apache, mais il a été développé par une équipe qui a aussi travaillé à un fork de Ruby basé sur la version 1.8.6 officielle. On peut trouver tous les détails sur leur site. En gros c’est une série d’améliorations au niveau du “garbage collector” et de l’allocation mémoire. Ils prétendent qu’une application Ruby on Rails consomme 33% de mémoire en moins avec Passenger et REE qu’avec la machine Ruby officielle (MRI).

Bon, ben vu qu’on va utiliser Passenger et que la version Debian Etch de Ruby est assez ancienne (1.8.5), pourquoi pas utiliser aussi une version optimisée de Ruby.

Étant un utilisateur convaincu de Debian, j’ai pas trop envie d’utiliser des paquets compilés manuellement. Je préfère utiliser Apt pour gérer mes logiciels ou librairies installées, mais là il faut avouer que l’installation de REE est très simple et rassurante.

On télécharge les sources compressées et on lance simplement un script d’install qui se charge de tout avec un miminum de questions. Il installe par défaut le tout dans le dossier /opt/ pour respecter les conventions et il n’a besoin d’aucune dépendance externe.

Il faut ensuite indiquer à Passenger quelle version de Ruby utiliser. Là encore, l’install nous donne un bout de texte à copier/coller dans la config de Passenger.

Pour ce que j’ai pu en tester, ça a l’air de très bien fonctionner jusque là. J’ai même l’impression que les applis vont plus vite. Je ne sais pas encore précisément si c’est vrai ou pas, ni si c’est dû à la version de Ruby (1.8.6 au lieu de 1.8.5), à REE en soi, ou bien au remplacement de Nginx/Mongrel par Apache/Passenger.

Il y a quand même un petit hic, je trouve, dans ce monde de rêve : la gestion des gems.

TOUTES les gems doivent être re-installées via le binaire rubygems fourni par REE. Il faut donc repérer les gems (et leurs versions antérieures éventuelles) nécessaires et les installer avec un très long

$ /opt/ruby-enterprise-1.8.6-20090113/bin/ruby /opt/ruby-enterprise-1.8.6-20090113/bin/gem install XYZ

C’est assez casse pieds d’avoir à taper ces chemins d’accès à chaque fois, et puis à chaque mise à jour de REE (3 fois depuis octobre 2008) le chemin change (à cause de la date dans le chemin). J’ai donc fait un simple lien symbolique “/opt/ruby-ee” qui pointe vers la version courante.

J’ai pas encore réfléchi aux conséquences possibles d’un lien symbolique plus courant pour avoir REE à la place de Ruby globalement dans tout le système. C’est sûr queça serait pratique d’avoir le même Ruby pour l’appli web via Apache que via IRB ou la console de Rails (qui est IRB + le code de Rails chargé d’ailleurs). Mais pour le moment je peux supporter la coexistence de 2 Ruby sur la machine sans trop m’enmêler avec.

la suite : VirtualHosts

Migration vers Passenger – préambule

Dimanche 25 janvier 2009

Il y a quelques semaines, j’écrivais sur mon blog perso un article sur Passenger.

Depuis, la solution a continué de prouver sa stabilité. De plus en plus de grands noms ont basculé leur ancien système vers Apache + Passenger (aka mod_rails).

Ce qui jusque là était considéré comme LA meilleure solution pour héberger des applis Rails – Nginx + Mongrel cluster – est toujours une excellente solution ; rapide, fiable, … mais elle souffre tout de même d’une certaine complexité à laquelle on n’était plus habitué depuis la facilité d’héberger des applis web à base de PHP et consort.

On a démarré neomarco.com sur un système à base de Nginx + Mongrel cluster (entre 5 et 20 process Mongrel selon les moments). Y est adossé un superviseur (Monit) qui s’assure que les process sont bien en vie, les empêche de dépasser un certain seuil de mémoire, les redémarre si besoin, …

Chaque fois qu’on déploie une nouvelle version de l’appli, le cluster redémarre tous les process, Monit hurle que rien ne va plus et nous balance des dizaines de mails (pour de vrai). Il ne fait que son travail, mais là je suis au courant que le cluster redémarre, c’est moi qui ait appuyé sur le bouton ;-)

En plus, je suis obligé d’anticiper le nombre de membres dans le cluster ; trop c’est inutile et la ram est prise, pas assez et c’est la file d’attente comme à la Sécu pour les requêtes web. En plus il n’y a pas vraiment d’optimisation, tous les process Mongrel utilisent en Ram tout le code de Rails et de l’appli.

À l’opposé, Passenger (un module pour Apache, pour faire simple et rapide) permet d’éviter tout ça. Les process sont démarrés à la demande par Apache lui-même, la mémoire qui peut être partagée l’est, …

L’avantage c’est qu’en reprenant Apache comme serveur web principal, il redevient très facile d’héberger des applis non Rails, comme des blogs Wordpress, ou des forums, … alors qu’avec Nginx il fallait utiliser PHP en mode FCGI ou bien faire un proxy vers Apache sur un autre port.

Me voilà donc fortement motivé pour repasser mon système sur Apache + Passenger. Ce qui va suivre est un récit progressif (en plusieurs étapes certainement) de cette migration.

La suite : Ruby Entreprise Edition