Autour du web

MIGRATION DE DONNÉES LOCALES VERS AMAZON S3 : TUTORIEL RUBY ON RAILS

Logo AWS S3

Voici un tutoriel pour effectuer une migration de données depuis un stockage local vers Amazon S3 dans une application Ruby on Rails utilisant Active Storage avec PostgreSQL comme base de données.

J'ai préalablement créé une application web, il s'agit d'un blog où différents utilisateurs peuvent créer et publier des articles qui peuvent contenir plusieurs documents.

Pour des soucis de performances et de sécurité, je souhaite migrer les documents vers une solution de stockage cloud et en conservant les relations existantes.

class Article < ApplicationRecord
 belongs_to :user
 has_many_attached :files
end

Dans un premier temps, faire en sorte que le stockage soit possible sur AWS Amazon S3, puis nous migrerons nos documents sur ce lieu de stockage.

Pour information, le guide Rails en lien de texte pour la configuration.

1. Créer un compte Amazon S3 et configurer le compartiment (bucket) :

Aller sur le site web d'Amazon AWS et créer un compte sont les pré-requis, ainsi que d'y enregistrer un moyen de paiement.

Depuis la console, cliquer sur "créer un nouveau compartiment".

Vous devrez alors nommer le nouveau compartiment, utiliser une propriété d'objets avec les listes ACL activées.
Concernant, les paramètres de blocage de l'accès public pour ce compartiment doivent être libérés pour l'ensemble en décochant l'actuelle case cochée.

Vous devrez ensuite spécifiquement sélectionner :
  • Bloquer l'accès public aux compartiments et aux objets, accordé via de nouvelles stratégies de compartiment ou de point d'accès public
  • Bloquer l'accès public et entre comptes aux compartiments et objets via n'importe quelles stratégies de compartiment ou de point d'accès public

Une fois ces deux cases cochées vous pouvez valider votre compartiment.


Ensuite, vous allez configurer une politique d'accès appropriée pour votre bucket via la création d'autorisations avec les instructions listées ci-après pour pouvoir avoir certaines actions sur les objets qui seront stockés (lecture, enregistrement, téléchargement, suppression). 
Créer nouvelle Politique:
  • S3
  • s3:ListBucket
  • s3:PutObject
  • s3:GetObject
  • s3:DeleteObject
  • Accès public, nécessite aussi s3:PutObjectAcl

Ajouter 2 ARN pour le nom du bucket : "VOTRENOMDEBUCKET" et "VOTRENOMDEBUCKET/*" afin de pouvoir lire les dossiers et éventuels sous-dossiers.

Créer un utilisateur IAM avec la nouvelle autorisation créée pour accéder au bucket.

Une fois l'utilisateur créé nous allons créer une clé d'accès qui nous servira à connecter S3 à notre application web.
 
Ensuite, nous créons la clé d'accès, attention il s'agit de la seule fois où la clé d'accès secrète peut être consultée ou téléchargée. Vous ne pouvez pas la récupérer ultérieurement.

Je vous conseille de directement d'entrer les informations dans votre fichier .env et de définir les variables d'environnement nécessaires :

AWS_BUCKET_NAME= 
AWS_REGION=
AWS_ACCESS_KEY_ID= -GENERE PAR LA CLE-
AWS_ACCESS_KEY_SECRET")= -GENERE PAR LA CLE-


2. Configurer Active Storage pour utiliser Amazon S3 :

Ajouter les informations de votre compte Amazon S3 dans le fichier storage.yml de votre application Rails. Voici un exemple de configuration avec dans votre fichier .env les informations nécessaires. Aussi veuillez faire attention à la région qui dépend de votre configuration de compte Amazon S3.

amazon:
  service: S3
  access_key_id: <%= ENV['AWS_ACCESS_KEY_ID'] %>
  secret_access_key: <%= ENV['AWS_ACCESS_KEY_SECRET")'] %>
  region: <%= ENV['AWS_REGION'] %>
  bucket: <%= ENV['AWS_BUCKET_NAME'] %>
  public: true


3. Cross-Origin Resource Sharing (CORS) Configuration

Implémenter la configuration de CORS dans les autorisations du bucket créé.

Ajoutez votre url de destination finale, ici nous avons également ajouter http://127.0.0.1:3000 qui correspond à notre serveur local.

[
  {
    "AllowedHeaders": [
      "Content-Type",
      "Content-MD5",
      "Content-Disposition"
    ],
    "AllowedMethods": [
      "PUT"
    ],
    "AllowedOrigins": [
      "https://www.example.com",
      "http://127.0.0.1:3000"
    ],
    "MaxAgeSeconds": 3600
  }
]


4. Ajouter la gemme aws-sdk-s3 à votre Gemfile :

Ajouter la gemme dans votre Gemfile et l'installer: 
gem 'aws-sdk-s3', require: false

bundle install 

En plus de cette gemme, suivant votre utilisation vous pourrez avoir besoin d'installer d'autres gemmes pour autoriser la lecture d'image ou de pdf, veuillez suivre le guide Rails, ci-dessous un résumé de ce dont vous pourrez avoir besoin:
  • libvips v8.6+ ou ImageMagick pour l'analyse et les transformations d'images
  • ffmpeg v3.4+ pour les prévisualisations vidéo et ffprobe pour l'analyse vidéo/sonore
  • poppler or muPDF pour les prévisualisations PDF

L'analyse et les transformations d'images nécessitent également le gemme image_processing. Décommentez-le dans votre Gemfile, ou ajoutez-le si nécessaire


5. Configurer les environnements de la base de données Rails :

Assurez-vous que les configurations d'Active Storage dans le fichier config/environments/development.rb et pointe vers Amazon S3.

config.active_storage.service = :amazon


6. Tester la configuration

Essayer d'ajouter un fichier, ou une image dans votre application.
Vérifier ensuite le service de stockage de celui-ci dans votre console ou l'url si vous l'ouvrer dans un nouvel onglet.

Normalement vous ne devriez pas voir d'erreurs et vous devriez pouvoir modifier le stockage également pour la production.


7. Modifier l'environnement de la production

Assurez-vous que les configurations d'Active Storage dans le fichier config/environments/production.rb et pointe vers Amazon S3.

config.active_storage.service = :amazon


8. Créer une tache pour migrer l'ensemble des documents de l'application

Créer un fichier Rake dans le répertoire lib/tasks, vous pouvez utiliser la commande suivante dans votre terminal :

rails generate task storage migrate 

Cette commande générera automatiquement un fichier Rake nommé migrate.rake dans le répertoire lib/tasks de votre application Rails, avec le contenu suivant :

Importer la gemme aws-sdk-s3 qui permet d'interagir avec Amazon S3 dans Ruby.

Cette partie définit un espace de noms storage pour regrouper les tâches Rake liées au stockage. La description de la tâche "migrate" est fournie, indiquant qu'elle migre les "blobs" (c'est-à-dire les fichiers) du stockage local vers Amazon S3. 
La tâche "migrate" dépend de l'environnement Rails, assurant que l'application est correctement chargée avant son exécution.

require 'aws-sdk-s3'

namespace :storage do
  desc 'Migrate blobs from local storage to Amazon S3'
  task migrate: :environment do

  end
end


Créer le compartiment qui sera la destination de vos documents.
Les lignes créent un "client" Amazon S3 en utilisant les informations d'identification stockées dans les variables d'environnement., qui correspondent au compartiment préalablement créé. 

s3_client = Aws::S3::Client.new(
  access_key_id: ENV.fetch("AWS_ACCESS_KEY_ID"),
  secret_access_key: ENV.fetch("AWS_ACCESS_KEY_SECRET"),
  region: ENV.fetch("AWS_REGION")
)
bucket_name = ENV.fetch("AWS_BUCKET_NAME")


Récupération du blob du fichier :
Un blob est une abstraction utilisée par Active Storage pour représenter les données binaires telles que des fichiers.
A l'intérieur de cette tache, on va travailler à partir de toutes les instances de la classe ActiveStorage::Blob, comme cela chaque blob existant sera pris en compte. On choisit d'itérer sur les blobs gérer en local comme lnotre but est de migrer depuis local vers AWS S3.

remaining_blobs = ActiveStorage::Blob.where(service_name: "local")

p "Remaining blobs to be transferred: #{remaining_blobs.count}"

Téléchargement du fichier localement dans un répertoire temporaire :
Cette ligne ouvre le fichier en utilisant la méthode open du blob et télécharge son contenu dans un fichier temporaire. Dir.tmpdir est utilisé pour spécifier le répertoire temporaire où le fichier sera téléchargé.

Génération de la clé S3 :
Une clé S3 unique est générée en utilisant des informations telles que l'ID de l'article, l'ID du fichier et le nom du fichier d'origine.
Cette clé sera utilisée pour stocker le fichier dans le compartiment Amazon S3.

Téléversement du fichier vers Amazon S3 :
Cette ligne téléverse le contenu du fichier temporaire vers Amazon S3 en utilisant la clé générée précédemment.
L'option acl: 'public-read' permet de rendre le fichier accessible au public.

Mise à jour de la clé du blob et de l'URL de stockage :
Ces lignes mettent à jour les métadonnées du blob dans la base de données pour refléter la nouvelle clé de stockage S3 et le service de stockage utilisé (Amazon S3).

Affichage d'un message de succès :
puts "Fichier #{blob.filename} migré avec succès vers #{blob.service_name}"

Cette ligne affiche un message indiquant que le fichier a été migré avec succès vers Amazon S3.
Parce que cela fait plaisir et cela permet de vérifier - vous pouvez ajouter un compteur aussi si vous souhaitez.

Gestion des erreurs :
La gestion d'erreur rescue ActiveStorage::FileNotFoundError traite les cas où une erreur survient lors de l'accès au fichier dans le stockage local.
La classe ActiveStorage::FileNotFoundError est utilisée pour signaler que le fichier n'a pas été trouvé dans le stockage local lorsqu'il est tenté d'être ouvert.
Donc, si une telle erreur se produit, le code continue simplement avec le fichier suivant sans interrompre le processus de migration. Cela garantit que la migration se poursuivra même si certains fichiers ne peuvent pas être trouvés dans le stockage local, mais ils seront simplement ignorés.

    remaining_blobs.find_each do |blob|
      begin
        blob.open(tmpdir: Dir.tmpdir) do |tempfile|
          object_key = "PROD_cities_previews_20240508/EI-#{blob.id}_#{blob.checksum}_#{blob.filename}"

          s3_client.put_object(
            bucket: bucket_name, 
            key: object_key, 
            body: tempfile,
            content_type: blob.content_type, 
            acl: 'public-read'
          )

          blob.update(service_name: 'amazon', key: object_key)

          puts "Blob #{blob.filename} transferred successfully to #{blob.service_name}"
        end
      rescue ActiveStorage::FileNotFoundError
        next
      end
    end


9. Executer la tâche

Pour exécuter la tâche Rake que vous avez définie, vous devez exécuter la commande suivante dans votre terminal :

Assurez-vous d'être dans le répertoire racine de votre application Ruby on Rails lorsque vous exécutez cette commande.
Le préfixe bundle exec garantit que la commande est exécutée avec les gemmes spécifiées dans votre fichier Gemfile.

La partie storage:migrate fait référence au nom de la tâche Rake que vous avez définie dans votre script. Dans ce cas, la tâche s'appelle "migrate" et est située dans l'espace de noms "storage".

bundle exec rake storage:migrate 

Après avoir exécuté cette commande, le processus de migration des fichiers devrait commencer, et vous verrez des messages de progression et de succès s'afficher dans votre terminal.

Félicitations vous avez configuré S3 en lieu de stockage !

Article publié le 29/04/2024