Introduction
Aujourd’hui, nous allons voir comment effectuer des sauvegardes de sa base de données MySQL sur un site web sous Ruby on Rails. La gestion des tâches récurrentes peut être fastidieuse et source d’erreurs. Pour éviter ces problèmes, de nombreuses solutions existent, comme l’utilisation de cronjobs.
Cependant, ces derniers peuvent être difficiles à gérer et à maintenir, en particulier lorsqu’il s’agit de tâches complexes. C’est là que la gemme Whenever intervient.
Cette bibliothèque Ruby permet de configurer des tâches récurrentes de manière claire et simple, en utilisant une syntaxe facile à comprendre. Dans cet article, nous allons examiner de plus près la gemme Whenever, découvrir comment elle fonctionne et explorer ses fonctionnalités pour créer une tâche de backup récurrente.
Automatiser avec les tâches Cron
Présentation de la gemme Whenever
La gemme Whenever est un outil populaire de planification de tâches pour les applications Ruby on Rails. Elle permet de définir des tâches périodiques qui s’exécutent automatiquement à des intervalles réguliers, ce qui peut être très utile pour automatiser des tâches récurrentes.
La syntaxe de la gemme Whenever est simple et intuitive. Elle utilise un langage DSL (Domain-Specific Language) qui ressemble à du code Ruby mais qui est plus facile à comprendre et à écrire. Les tâches peuvent être planifiées pour s’exécuter à des intervalles réguliers (toutes les heures, tous les jours, toutes les semaines, etc.) ou à des moments précis de la journée ou de la semaine.
La gemme Whenever utilise le système de planification de tâches cron sous-jacent de Linux, qui est très fiable et bien testé. Elle génère automatiquement un fichier cron à partir de votre code.
Installer la gemme
Nous allons installer la gemme nécessaire, à savoir whenever pour permettre de créer des tâches cron sur votre serveur.
Procédons à l’installation de la gemme. Ajoutez cette ligne dans votre Gemfile
:
gem 'whenever', require: false
Maintenant que la gemme est installée, générez le fichier de configuration avec cette commande :
wheneverize .
Remarque
Si la commande ne fonctionne pas sur votre environnement de développement essayez en ajoutant bundle exec
devant la commande.
En utilisant cette commande, un fichier nommé schedule.rb
a été créé dans le dossier config
de votre application. Vous pouvez ouvrir ce fichier avec votre éditeur de code.
Voici le code à insérer :
every :sunday, at: '3am' do
rake "backup:db RAKE_ENV=production"
end
Ce code va executer un cronjob tous les dimanches à 3 heures du matin. Concernant le rake
, nous y viendrons tout de suite !
Création d’une tâche pour Rails
Maintenant que le cronjob est créé (mais pas encore activé !) nous allons ajouter une tâche avec Rails comme celles utilisées pour créer les bases de données, compiler les assets, etc.
Pour ce faire on va lancer la commande suivante :
rails g task backup db
Une fois la commande exécutée, un fichier backup.rake
a été créé dans le dossier lib/tasks
à la racine de votre application.
Nous allons ouvrir ce fichier et y insérer le code suivant :
Attention
Le code ci-dessous est fait pour les bases de données MySQL uniquement. À vous de l’adapter si ce n’est pas votre type de base de donnée.
namespace :backup do
desc "rails backup database"
task db: :environment do
settings = Rails.configuration.database_configuration[Rails.env]
output_name = "#{settings['database']}-#{Time.now.strftime('%Y%m%d-%H:%M')}"
output_file = Rails.root.join('backups/db', "#{output_name}.sql")
#mac sql save
if Rails.env == "development"
system("/usr/local/mysql/bin/mysqldump -h localhost -u #{settings['username']} -p#{settings['password']} #{settings['database']} > #{output_file}")
else
#linux (for production) save
system("/usr/bin/env mysqldump -h localhost -u #{settings['username']} -p#{settings['password']} #{settings['database']} > #{output_file}")
end
#File SQL saved ! Compress with gzip
system("gzip #{output_file}")
end
end
Expliquons un peu le code et ce qu’il va faire.
Tout d’abord on va sélectionner en fonction de l’environnement, la base de donnée à utiliser lors de la sauvegarde. Ensuite, on va créer le nom du futur fichier à savoir nom_de_la_base-date_heure_sauvegarde
. Et nous lui disons que ce fichier devra être enregistré à la racine de l’application dans un nouveau dossier nommé backups/db
.
Pour la suite, j’ai laissé ma configuration afin qu’il utilise mysqldump
sous /usr/local
sous Mac (l’OS avec lequel je développe) et sinon il utilisera la config /usr/bin/env
pour mon serveur sous Linux CentOS.
Pour finir, il va compresser la base de donnée créé afin de minimiser la taille.
Test du code et génération du cronjob
Nous allons commencer par tester le code.
Maintenant créez manuellement le dossier backups et db à la racine de votre application (ça évitera des problèmes si le système n’a pas les droits pour le créer de lui-même).
Et enfin, lancer la commande de sauvegarde manuellement comme ceci :
bundle exec rake backup:db RAILS_ENV=development
Normalement si tout s’est déroulé comme prévu, un fichier est disponible dans le dossier backups/db
.
Configurons maintenant le cronjob
Pour ce faire, il vous suffit de taper la ligne de commande suivante :whenever –update-crontab
Et voilà, c’est terminé ! vous avez mis en place votre système de backup pour votre base de donnée en quelques minutes seulement !
Envoyer la sauvegarde par e-mail
Et si on envoyait notre sauvegarde par e-mail ?
Création du template pour l’e-mail
Nous allons utiliser le Mailer de Rails par défaut pour créer ce mail. Pour ce faire, créer un fichier nommé user_mailer.rb
dans le dossier app/mailers
.
Maintenant vous allez ajouter le code suivant :
class UserMailer < ApplicationMailer
default from: "votre-mail[at]site.com"
def send_backup_message(user, file, name)
@user = User.find(user) #passing to view
attachments["#{name}.sql.gz"] = File.read(file)
mail(:to => @user.email, :subject => "Copie de la sauvegarde de la base de donnée !")
end
end
Passons en revu le code ci-dessus.
Nous allons commencer par sélectionner l’utilisateur en fonction de l’id passé dans la définition. Ensuite, nous allons ajouter le fichier compressé de la sauvegarde à l’email. Pour finir, nous envoyons le mail à l’utilisateur.
Passons à la création de la vue
Créez un fichier nommé send_backup_message.html.erb
dans le dossier app/views/user_mailer
. Ajoutez le code suivant (ou celui que vous voulez c’est juste un peu de blabla pour expliquer qu’on a fait la sauvegarde).
<h2 style="font-size: 20px;">Bonjour <%= @user.first_name %>,</h2>
<br>
<p>Veuillez trouver ci-joint, le backup de la base de donnée principale de votre site internet.</p><br>
<p>Cette archive est également disponible à la racine de votre site dans le dossier <strong>backups</strong> ainsi que toutes les futurs sauvegardes.</p><br>
<p>Cet e-mail a été généré automatiquement. Vous l'avez reçu car a priori tout a fonctionné correctement.</p>
Ajoutons l’envoi du mail à la tâche cron
Vous devez à nouveau éditer le fichier backup.rake
disponible dans lib/tasks
et ajouter après system("gzip #{output_file}")
le code suivant :
#on vérifie que le fichier est ok
result = system("gzip -t #{output_file}.gz")
if result
UserMailer.send_backup_message(1, "#{output_file}.gz", output_name).deliver
end
Attention : ici je passe l’id 1 à send_backup_message
car je souhaite envoyer l’e-mail à l’utilisateur ayant cet id. Il vous faudra le modifier en fonction de votre ID.
Améliorations
Voilà tout est prêt et fonctionnel. Mais, maintenant il nous reste une possibilité d’optimisation, car en l’état le code va créer tous les dimanches un backup de la base de données.
Supprimer les anciennes sauvegardes
Nous allons ajouter une option qui va supprimer les sauvegardes qui ont plus de 6 mois. Pour ce faire, il vous faut éditer le fichier backup.rake et ajouter ce code :
#on récupère les dossiers
folder_path = Rails.root.join('backups/db')
# Récupérez tous les fichiers du dossier
files = Dir.entries(folder_path)
# Obtenez la date actuelle
current_time = Time.now
# Parcourez chaque fichier et supprimez ceux qui ont l'extension .gz et qui sont vieux de plus de 6 mois
files.each do |file|
if file.end_with?('.gz')
file_path = File.join(folder_path, file)
# Vérifiez la date de modification du fichier
file_time = File.mtime(file_path)
# Si la date de modification du fichier est antérieure à 6 mois à la date actuelle, supprimez le fichier
if (current_time - file_time) > 1
FileUtils.rm(file_path)
end
end
end
Suggestions d’améliorations
Une autre modification intéressante serait d’envoyer le mail à tous les administrateurs. Ceci n’est pas du tout compliqué à mettre en place, dans le mailer il suffirait de supprimer l’id et de remplacer par un where admin: true
.
Une dernière pour la route, serait de créer une table et y insérer les backups. Ainsi, ils pourraient être directement gérés depuis votre administration et vous permettraient de les supprimer, télécharger … et pourquoi pas de les restaurer !
Conclusion
Voilà ! On arrive au bout, c’est l’heure de se quitter mais si vous avez des problèmes lors de la réalisation de ce tutoriel, n’hésitez pas à en parler dans les commentaires ci-dessous !