Un maño entre gaúchos

Una historia de capistrano, crontabs y pipes

with 2 comments

Al montar el deploy de una nueva máquina con capistrano he querido afinar un poco la carga de crontabs.
No me gusta poner las tareas de crontab en el /etc/crontab. Creo que es una muy mala práctica. En vez de eso prefiero que cada usuario tenga su propia tabla de crontabs y para ello hago uso del comando crontab.
En muchas ocasiones hace falta definir crontabs para más de un usuario. Para no andar añadiendo una linea en el script de deploy por cada fichero de crontab que se tenga doy por hecho que los nombres de todos los archivos con las tablas de crontab siguen el mismo formato, que es “crontab ..
El código pues para capistrano es:

task :app_deploy, :roles => [:app] do
  Dir["./appserver/etc/crontab.*"].each { |crontab|
    sudo "sh -c 'cat #{release_path}/#{crontab} | \
    crontab -u  #{File.extname(File.basename(crontab)).delete('.')} -"
  }
end

En este código hay varias cosas que explicar.
Primero se presupone pues que si quiero, por ejemplo, añadir el crontab para el usuario www-data simplemente lo crearé y lo guardaré en appserver/etc/crontab.www-data
En cuanto al código, por un lado el bloque lo que hace es cargar en un array los ficheros con un nombre que coincida con el patrón comentado anteriormente. Hay que tener en cuenta que el código en ruby se ejecuta en la máquina desde la que se lanza el deploy y lo que se le pasa al comando run o sudo es un comando unix que se va a ejecutar en la máquina en la que se quiera hacer el deploy.

Por otro lado está el hecho de que cuando se ejecutan con sudo dos comandos unidos por una tubería, el sudo se va a aplicar únicamente al primero.
Si se hace:


$ sudo echo '* * * * * date > /tmp/date' | crontab -u root -

el sudo se va a aplicar únicamente al comando echo y no al comando crontab por lo que eso no funcionará ya que no tenemos privilegios suficientes.

Por lo tanto para no tener que repetir el comando sudo a los dos lados de la tubería y también para no complicar el comando en capistrano, lo que se puede hacer es englobar toda la sentencia en una subshell de la siguiente forma:


$ sudo sh -c 'echo "* * * * * date > /tmp/date" | crontab -u root -'

Written by luis

April 15th, 2009 at 4:37 pm

2 Responses to 'Una historia de capistrano, crontabs y pipes'

Subscribe to comments with RSS or TrackBack to 'Una historia de capistrano, crontabs y pipes'.

  1. Yo lo que uso para los crons es el directorio /etc/cron.d , allí puedes crear varios ficheros con formato crontab (por ejemplo uno por aplicación), y en los cuales puedes especificar el usuario que ejecutará cada uno de los comandos.

    Miguel

    17 Apr 09 at 4:31 pm

  2. Es otra solución sí. Más limpia que meterlo todo en el /etc/crontab pero aun así, en mi opinión, no demasiado buena. Lo bueno de usar el comando crontab es que además de tener distribuidas las tablas de cron por cada usuario, te valida las entradas. Si acabas de escribir un fichero con el comando crontab y has cometido un error, te obliga a arreglarlo y en ningún caso guarda esa tabla incorrecta. Si escribes un fichero de cron a mano, ya sea el crontab o dejándolo como comentas tú en el cron.d te puedes encontrar con que no se ejecuta ninguna tarea en el caso de que hayas cometido algun error de sintaxis o similar. Eso en producción por ejemplo, si dependes de tareas de background es muy malo.

    Luis

    17 Apr 09 at 4:41 pm

Leave a Reply