Utiliser IPFS pour la réplication des données

cestoliv, il y a 2 ans - ven. 15 juil. 2022

1. Qu'est-ce qu'IPFS ?

IPFS signifie InterPlanetary File System. Comme le dit sa documentation :

IPFS est un système distribué permettant de stocker et d'accéder à des fichiers, des sites web, des applications et des données.

Décentralisé

En effet, c'est un système qui permet de stocker des fichiers en peer-to-peer, comme les torrents. Au lieu de demander un fichier à un serveur central, vous le demandez à tous les pairs qui le possèdent également. Il n'y a pas de serveur central, donc le contenu est toujours accessible (tant qu'au moins un pair le possède).

Adressage par contenu

Dans la plupart des cas, le contenu est adressé par un chemin : les fichiers sur votre ordinateur /home/cestoliv/Documents/fichier.txt ou sur le web cestoliv.com/fichier.txt.

Avec IPFS, le contenu n'est pas adressé par son chemin, mais par son contenu. Ainsi, l'adresse d'un fichier sur IPFS est le hachage de son contenu, par exemple : /ipfs/QmXoypizjW3WknFiJnKowHCnL72vedxjQkDDP1lXWo6uco

2. Créer un réseau privé IPFS avec IPFS Cluster

Réseaux privés IPFS

Si IPFS permet, comme les torrents, à tout le monde d'accéder au contenu, il est aussi possible de créer un réseau privé, où seuls les pairs que vous autorisez peuvent accéder à certains contenus. C'est ce que nous allons faire.

IPFS Cluster

En outre, nous voulons répliquer les données, c'est-à-dire que plusieurs pairs doivent les télécharger. Lorsque nous accédons à un fichier sur IPFS, il reste disponible plusieurs jours sur notre machine avant d'être supprimé pour libérer de l'espace. Or, dans notre contexte de réplication, nous voulons que tous nos pairs téléchargent et conservent tous nos fichiers.

IPFS répond à ce problème grâce au concept de PIN. Si vous "pointez" un fichier, il sera téléchargé et jamais supprimé.

Nous allons donc utiliser IPFS-Cluster, un outil qui permet de contrôler tous les pairs pour leur dire de pointez un fichier. Ainsi, il suffira de pointez un fichier dans notre cluster, et tous les pairs le feront automatiquement.

3. Créer le nœud principal

Allez sur votre nœud principal (celui qui recevra les données à dupliquer en premier) et créez un dossier de travail.

mkdir -p ipfs-cluster && cd ipfs-cluster

Ensuite, créez un fichier docker-compose.yml avec le contenu suivant :

Avant de lancer les conteneurs, nous devons générer la clé swarm pour créer un réseau privé IPFS et un CLUSTER_SECRET pour authentifier les autres nœuds de notre réseau privé.

Générer la clé swarm

mkdir -p ./data/ipfs/data
echo -e "/key/swarm/psk/1.0.0/\n/base16/\n`tr -dc 'a-f0-9' < /dev/urandom | head -c64`" > ./data/ipfs/data/swarm.key

Créer le CLUSTER_SECRET

echo -e "CLUSTER_SECRET=`od -vN 32 -An -tx1 /dev/urandom | tr -d ' \n'`" >> .env

Enfin ! Vous pouvez démarrer les conteneurs !

docker compose up -d

Vérifiez que tout s'est bien passé dans les journaux : docker compose logs (Vous pouvez voir des erreurs fausses car le cluster démarre plus rapidement que le nœud IPFS, donc le cluster ne pourra pas se connecter au nœud au début)

Comme il s'agit d'un réseau privé, nous allons supprimer toutes les connexions avec les nœuds publics :

docker compose exec ipfs ipfs bootstrap rm --all

(Optionnel) Installer ipfs-cluster-ctl sur l'hôte

ipfs-cluster-ctl est l'outil qui nous permet d'interagir avec notre cluster (notamment pour indiquer quels fichiers doivent être épinglés). Cela se fait via le port 9094 que nous avons ouvert sur le conteneur.

wget https://dist.ipfs.io/ipfs-cluster-ctl/v1.0.2/ipfs-cluster-ctl_v1.0.2_linux-amd64.tar.gz
tar -xvf ipfs-cluster-ctl_v1.0.2_linux-amd64.tar.gz
sudo cp ipfs-cluster-ctl/ipfs-cluster-ctl /usr/local/bin
# Vérifier que ipfs-cluster-ctl a bien été installé
ipfs-cluster-ctl --version

Il n'est pas obligatoire d'installer cet outil sur l'hôte, car il est déjà installé dans le conteneur. Si vous ne souhaitez pas l'installer, remplacez simplement la commande ipfs-cluster-ctl par docker compose exec cluster ipfs-cluster-ctl.

Tester que le nœud principal fonctionne

Pour tester que notre réseau est privé, nous allons ajouter un fichier au cluster et vérifier que nous ne pouvons pas l'ouvrir depuis un nœud public.

Pour cela, nous devons ajouter un fichier unique. Si nous ajoutons simplement un fichier contenant "Hello world!" et que quelqu'un l'a déjà ajouté sur un nœud public, le hachage sera le même, donc notre fichier sera indirectement accessible via les nœuds publics.

echo "Hello $USER (`od -vN 6 -An -tx1 /dev/urandom | tr -d ' \n'`)!" > hello.txt
ipfs-cluster-ctl add hello.txt
# output: added <votre hachage de fichier> hello.txt

Testons maintenant que notre réseau IPFS est privé. Essayez d'afficher le contenu du fichier depuis le conteneur (cela devrait fonctionner) :

docker compose exec ipfs ipfs cat <votre hachage de fichier>
# output: Hello <vous> (<un numéro aléatoire>)!

Mais si vous essayez d'afficher le contenu du fichier depuis un nœud qui ne fait pas partie de votre réseau privé, cela ne devrait pas fonctionner :

# Essayez d'ouvrir `https://ipfs.io/ipfs/<votre hachage de fichier>` dans votre navigateur.

4. Ajout de nœud(s) de réplication

Cette partie doit être répétée pour chaque nœud de réplication que vous souhaitez ajouter.

Allez sur votre nœud de réplication et créez un dossier de travail.

mkdir -p ipfs-cluster && cd ipfs-cluster

Créez un fichier docker-compose.yml avec le contenu suivant :

Avant de lancer les conteneurs, nous devons créer le fichier .env et y mettre le même CLUSTER_SECRET que dans .env du nœud principal.

CLUSTER_SECRET=<votre cluster secret>
MAIN_NODE=/ip4/<ip-du-nœud-principal>/tcp/9096/ipfs/<id-du-nœud-principal>
# ex. MAIN_NODE=/ip4/192.168.1.1/tcp/9096/ipfs/13D3KooWFN75ytQMC94a6gVLDQ999zxADpFpi7qAir9ajGrNHn8d

L'ID du nœud principal peut être trouvé en exécutant la commande suivante sur le nœud principal :

cat ./data/cluster/identity.json

Nous devons également copier la swarm.key générée sur le nœud principal vers notre nœud de réplication (./data/ipfs/data/swarm.key sur le nœud principal)

# Sur le nœud principal
# Copier le résultat de :
cat ./data/ipfs/data/swarm.key
# Sur le nœud de réplication
# Coller la swarm key
mkdir -p ./data/ipfs/data
echo "/key/swarm/psk/1.0.0/
/base16/
<votre swarm key>" > ./data/ipfs/data/swarm.key

Vous pouvez maintenant démarrer les conteneurs !

docker compose up -d

Vérifiez que tout s'est bien passé dans les journaux : docker compose logs (Vous pouvez voir des erreurs fausses car le cluster démarre plus rapidement que le nœud IPFS, donc le cluster ne pourra pas se connecter au nœud au début)

Comme il s'agit d'un réseau privé, nous allons supprimer toutes les connexions avec les nœuds publics :

docker compose exec ipfs ipfs bootstrap rm --all

Se connecter au nœud principal

Nous devons maintenant ajouter nos nœuds en tant que pairs pour qu'ils puissent communiquer. Le nœud principal doit connaître les nœuds de réplication, et les nœuds de réplication doivent connaître le nœud principal.

Sur le nœud principal :

docker compose exec ipfs ipfs bootstrap add /ip4/<ip_du_noeud_de_réplication>/tcp/4001/p2p/<id_du_noeud_de_réplication>
# Exemple: /ip4/192.168.1.2/tcp/4001/p2p/12D3KopWK6rfR6SKpmxDwKCjtnJWoK1VRYc7BDMZfJxnopljv68u

L'ID du nœud de réplication peut être trouvé en exécutant la commande suivante sur le nœud de réplication :

docker compose exec ipfs ipfs config show | grep "PeerID"

Sur le nœud de réplication :

docker compose exec ipfs ipfs bootstrap add /ip4/<ip_du_nœud_principal>/tcp/4001/p2p/<id_du_nœud_principal>
# Exemple: /ip4/192.168.1.1/tcp/4001/p2p/92D3KopWK6rfR6SKpmxDwKCjtnJWoK1VRYc7BDMZfJxnopljv69l

L'ID du nœud principal peut être trouvé en exécutant la commande suivante sur le nœud principal :

docker compose exec ipfs ipfs config show | grep "PeerID"

Tester notre installation

echo "Hello $USER (`od -vN 6 -An -tx1 /dev/urandom | tr -d ' \n'`)!" > hello.txt
ipfs-cluster-ctl add hello.txt
# sortie : added <votre hash de fichier> hello.txt

Vous pouvez maintenant observer la réplication en action avec la commande ipfs-cluster-ctl status <votre hash de fichier>.

résultat de la commande avec un cluster de trois nœuds

La mention PINNED signifie que le pair a épinglé le fichier, donc il l'a téléchargé et le conservera.

5. Aller plus loin

Nous allons maintenant voir un cas d'utilisation plus spécifique de notre cluster : répliquer un dossier sur tous nos nœuds.

En ajoutant l'option -r, vous pouvez synchroniser un dossier et tout son contenu.

ipfs-cluster-ctl add -r folder/

Comme vous pouvez le constater, IPFS stocke à la fois le hash du dossier lui-même et le hash de chaque fichier.

résultat de la commande qui épingle récursivement sur un cluster

Mais que se passe-t-il si le contenu du dossier change ? Comment répliquer ces modifications ?

Pour répliquer les modifications d'un dossier mis à jour, il faut l'ajouter à nouveau. À ce moment-là, le hash du dossier changera, et le hash des fichiers modifiés aussi. Si un fichier n'a pas changé, son hash restera identique.

Voici la procédure à suivre :

  1. Désépingler la version actuelle
  2. Épingler la nouvelle version
  3. Supprimer les fichiers qui n'existent plus ou leurs anciennes versions

Voici la version en script Bash :

#!/bin/bash

# Désépingler tous les fichiers
if [[ -n $(ipfs-cluster-ctl pin ls) ]]; then
    ipfs-cluster-ctl pin ls | cut -d' ' -f1 | xargs -n1 ipfs-cluster-ctl pin rm
fi

# Épingler la nouvelle version du dossier
ipfs-cluster-ctl add -r folder/

# Exécuter le garbage collector pour supprimer les fichiers qui ne sont plus épinglés
# (supprime les fichiers ou leurs anciennes versions)
ipfs-cluster-ctl ipfs gc

Merci d'avoir lu !

Illustration issue de ipfs.io, sous licence CC-BY 3.0.