Lancer un cron sur un cluster

Sur un de mes projets j’ai besoin d’exécuter une tâche tous les jours. Cet exercice pourtant trivial peut devenir plus complexe si mon service utilise plusieurs serveurs et que je ne veux exécuter la tâche qu’une seule fois. En Java j’utilisais le scheduler Quartz pour régler ce problème. Il est très efficace et en cas de cluster il utilise une base de données pour s’assurer que la tâche n’est lancée que sur un seul serveur. Mais si votre tâche doit être lancée en shell ? J’ai donc cherché une solution pour le faire simplement sur mon projet utilisant des instances EC2.

Après pas mal de recherches, j’ai trouvé une solution simple sur stackoverflow. L’idée est bonne mais le script ne fonctionne pas (rien que l’utilisation de la commande hostname est étrange…).

J’ai finalement fait fonctionner ce script :

#!/usr/bin/env ruby

AWS_AUTO_SCALING_HOME='/opt/aws/apitools/as'
JAVA_HOME='/usr/lib/jvm/jre'
MY_GROUP = '<GroupName>'

@cmd_out = `bash -c 'JAVA_HOME=#{ JAVA_HOME } AWS_AUTO_SCALING_HOME=#{ AWS_AUTO_SCALING_HOME } as-describe-auto-scaling-instances -region <REGION> -I <KEY> -S <SECRET>'`

raise "Output empty, should not happen!" if @cmd_out.empty?
@lines = @cmd_out.split(/\r?\n/)
@last = @lines.select {|l| l.match MY_GROUP }.reverse.
detect { |l| l =~ /^INSTANCE\s+\S+\s+\S+\s+\S+\s+InService\s+HEALTHY/ }
raise "No suitable host in autoscaling group!" unless @last
@last_host = @last.match(/^INSTANCE\s+(\S+)/)[1]
@hostname = `wget -q -O - http://169.254.169.254/latest/meta-data/instance-id`

if @hostname.index(@last_host)
  puts "It's me!"
  exit(0)
else
  puts "Someone else will do it!"
  exit(1)
end

Il est aussi possible de gérer différemment la gestion de l’authentification AWS en passant par un fichier comme expliqué ici.

Ce script doit être exécuté par root. Ensuite il suffit d’ajouter une ligne dans la crontab de vos instances du type :

02 01 * * * ~/lastonly.rb && wget -q -O – http://www.mysite.com/cron/crontasks

Cette solution n’est pas parfaite mais elle a le mérite d’être simple. Une autre solution plus “propre” serait sans doute d’utiliser Amazon Simple Workflow Service