Aller au contenu
medium

SeaweedFS : stockage distribué haute performance pour objets et fichiers

34 min de lecture

SeaweedFS est un système de stockage distribué rapide capable de servir des milliards de fichiers avec un accès disque en O(1). Écrit en Go et distribué sous licence Apache 2.0, il fournit un stockage objet (API S3), un système de fichiers distribué (Filer) et un montage FUSE, le tout via un unique binaire weed. Ce guide utilise SeaweedFS v4.13 et vous accompagne dans le déploiement d'un cluster 3 nœuds avec master HA (Raft), volume servers, filer et S3 gateway, chaque commande a été testée sur un lab KVM réel.

Le parcours va de l'architecture au cluster fonctionnel, chaque commande ayant été rejouée sur un lab KVM de trois nœuds.

  • Architecture : comprendre les rôles master, volume server, filer et S3 gateway
  • Déploiement : installer un cluster 3 nœuds avec haute disponibilité (Raft)
  • API S3 : créer des buckets, uploader et télécharger des fichiers
  • Filer : utiliser l'interface fichiers HTTP avec hiérarchie de répertoires
  • Réplication : configurer la réplication inter-rack avec le modèle XYZ

SeaweedFS est un système de stockage distribué inspiré du papier Haystack de Facebook. Son objectif principal : stocker et servir des milliards de petits fichiers (images, documents, logs) avec un accès disque en O(1), une seule lecture disque par fichier, sans couche de métadonnées intermédiaire.

Ces caractéristiques expliquent le positionnement de l'outil : il vise un très grand nombre de petits fichiers, profil que la plupart des stockages objets pénalisent par le coût de leurs métadonnées.

CaractéristiqueDétail
Accès O(1)Seulement 16 octets de métadonnées par blob, une seule lecture disque
Binaire uniqueUn seul exécutable weed pour tous les composants
Multi-interfaceAPI S3, Filer HTTP/gRPC, FUSE, WebDAV
Réplication flexibleModèle XYZ (datacenter/rack/même rack)
Erasure CodingRéduction d'espace pour données froides (10+4 par défaut)
Cloud tieringMigration transparente vers S3/GCS/Azure pour données tièdes
KubernetesDriver CSI officiel pour stockage persistant
S3 Tables / IcebergCatalogue REST Iceberg natif pour data lakes (v4.09+)

SeaweedFS repose sur 4 composants qui peuvent tourner ensemble (mode all-in-one) ou séparément (production).

Architecture SeaweedFS : master cluster Raft, volume servers, filer et S3 gateway

Le master gère la topologie du cluster : il sait quels volume servers sont disponibles, dans quels datacenters et racks. En cluster de 3, les masters utilisent le protocole Raft pour élire un leader et assurer la haute disponibilité.

Rôles du master :

  • Assigner des identifiants de fichiers (file ID) via /dir/assign
  • Localiser les volumes via les lookups
  • Gérer la réplication et l'équilibrage des volumes
  • Port gRPC : 19333 (port HTTP + 10000)

Le volume server stocke les données réelles. Chaque volume server gère plusieurs volumes (fichiers .dat de 30 Go par défaut). Les blobs sont écrits séquentiellement dans ces volumes, ce qui garantit l'accès O(1).

Caractéristiques :

  • 16 octets de métadonnées par fichier (cookie, needle ID, taille)
  • Pas d'index en mémoire pour les lookups, accès direct via offset
  • Compaction automatique pour récupérer l'espace des fichiers supprimés

Le filer ajoute une couche système de fichiers au-dessus des volume servers. Il transforme les file IDs en chemins lisibles (/documents/rapport.pdf). Les métadonnées du filer sont stockées dans un backend configurable : LevelDB (défaut), MySQL, PostgreSQL, Redis, etcd, etc.

Fonctionnalités :

  • API HTTP : upload, download, listing de répertoires
  • FUSE : montage comme un système de fichiers local
  • WebDAV : accès depuis n'importe quel client WebDAV
  • Notifications : événements en temps réel (création, modification, suppression)

Le S3 gateway expose une API compatible AWS S3. Les buckets S3 correspondent à des répertoires dans le filer (/buckets/nom-du-bucket/).

Depuis la version 4.01 (Direct Access), le S3 gateway communique directement avec les volume servers pour les données. Le filer n'est consulté que pour les métadonnées, ce qui réduit la latence d'écriture de 30 % et la latence de lecture de 5 %.

SeaweedFS utilise un format à 3 chiffres XYZ pour définir la réplication :

ChiffreSignificationPortée
XCopies sur d'autres datacentersInter-DC
YCopies sur d'autres racks (même DC)Inter-rack
ZCopies sur le même rackIntra-rack

Exemples concrets :

ValeurCopies totalesDescription
0001Pas de réplication (développement)
00121 copie sur le même rack
01021 copie sur un rack différent du même DC
10021 copie sur un datacenter différent
20032 copies sur 2 datacenters différents

Chaque composant écoute sur deux ports : le port HTTP annoncé, et un port gRPC valant ce numéro augmenté de 10000, réservé aux échanges internes au cluster. Les deux doivent être ouverts entre les nœuds, faute de quoi Raft ne converge jamais.

ServicePort HTTPPort gRPCUsage
Master933319333Topologie, assignation
Volume808018080Stockage données
Filer888818888Système de fichiers
S3 Gateway833318333API S3 compatible

Le lab construit un cluster complet : trois masters en quorum Raft, trois volume servers répartis dans des racks distincts pour rendre la réplication observable, un filer et une passerelle S3. Les adresses du lab apparaissent en clair dans les commandes, remplacez-les par celles de vos machines. Chaque étape se termine par une vérification dont la sortie attendue est reproduite.

Deux gigaoctets de RAM par nœud suffisent, les composants étant légers ; c'est l'espace disque alloué aux volumes qui dimensionne réellement la machine.

  • 3 machines (physiques ou virtuelles) sous Ubuntu 24.04 avec 2 Go de RAM minimum
  • Accès SSH entre les nœuds
  • AWS CLI v2 installé sur votre poste pour tester l'API S3

Ce lab utilise des VMs KVM/libvirt. Adaptez les IP à votre réseau si vous utilisez un autre hyperviseur.

Créez un fichier cloud-init.yaml pour provisionner les 3 VMs :

#cloud-config
hostname: seaweedfs-node
users:
- name: bob
groups: sudo
shell: /bin/bash
sudo: ALL=(ALL) NOPASSWD:ALL
ssh_authorized_keys:
- ssh-ed25519 VOTRE_CLE_PUBLIQUE
packages:
- chrony
- wget
runcmd:
- cd /tmp && wget -q https://github.com/seaweedfs/seaweedfs/releases/latest/download/linux_amd64.tar.gz && tar -xzf linux_amd64.tar.gz && mv weed /usr/local/bin/weed && chmod +x /usr/local/bin/weed
- mkdir -p /var/lib/seaweedfs/master /var/lib/seaweedfs/volume /var/lib/seaweedfs/filer

Créez les 3 VMs :

Fenêtre de terminal
for i in 1 2 3; do
sudo virt-install \
--name seaweed${i} \
--ram 2048 --vcpus 1 \
--disk path=/var/lib/libvirt/images/seaweed${i}.qcow2,size=15,backing_store=/var/lib/libvirt/images/ubuntu-24.04-cloud.img,format=qcow2 \
--os-variant ubuntu24.04 \
--cloud-init user-data=cloud-init.yaml \
--network bridge=virbr0 \
--graphics none --noautoconsole --import
done

Vérification : attendez 2 minutes puis récupérez les IP :

Fenêtre de terminal
sudo virsh net-dhcp-leases default

Dans notre lab, les IP sont :

NœudIPRôle
seaweed1192.168.122.187Master + Volume + Filer + S3
seaweed2192.168.122.159Master + Volume
seaweed3192.168.122.144Master + Volume

Configurez les hostnames et /etc/hosts sur chaque nœud :

Fenêtre de terminal
# Sur chaque nœud, ajouter les entrées
sudo tee -a /etc/hosts << EOF
192.168.122.187 seaweed1
192.168.122.159 seaweed2
192.168.122.144 seaweed3
EOF

Vérification : confirmez l'installation du binaire sur chaque nœud :

Fenêtre de terminal
ssh bob@192.168.122.187 "weed version"
# version 30GB 4.13 linux amd64

Pour tester rapidement, SeaweedFS peut démarrer tous les composants avec une seule commande :

Fenêtre de terminal
weed server \
-dir=/var/lib/seaweedfs/volume \
-master.port=9333 \
-volume.port=8080 \
-ip=192.168.122.187 \
-filer \
-s3

Cette commande lance simultanément le master (9333), le volume server (8080), le filer (8888) et le S3 gateway (8333).

Vérification :

Fenêtre de terminal
curl -s http://192.168.122.187:9333/cluster/status
# {"IsLeader":true,"Leader":"192.168.122.187:9333.19333"}

Depuis v4.04, la commande weed mini démarre tous les composants avec détection automatique :

Fenêtre de terminal
export AWS_ACCESS_KEY_ID=seaweedadmin
export AWS_SECRET_ACCESS_KEY=seaweedadmin123
weed mini -dir=/var/lib/seaweedfs

Attention : les modes weed server et weed mini sont réservés au développement. En production, déployez chaque composant séparément.

L'ordre de démarrage compte : les masters d'abord puisqu'ils portent la topologie, puis les volume servers qui viennent s'y enregistrer, enfin le filer et la passerelle S3 qui dépendent des deux précédents. Un volume server lancé avant l'élection du leader reste en attente sans message d'erreur explicite.

  1. Démarrer les 3 masters (cluster Raft)

    Lancez un master sur chaque nœud avec la liste des pairs :

    Fenêtre de terminal
    # Sur seaweed1
    weed master \
    -mdir=/var/lib/seaweedfs/master \
    -ip=192.168.122.187 \
    -port=9333 \
    -defaultReplication=001 \
    -peers=192.168.122.187:9333,192.168.122.159:9333,192.168.122.144:9333
    Fenêtre de terminal
    # Sur seaweed2
    weed master \
    -mdir=/var/lib/seaweedfs/master \
    -ip=192.168.122.159 \
    -port=9333 \
    -defaultReplication=001 \
    -peers=192.168.122.187:9333,192.168.122.159:9333,192.168.122.144:9333
    Fenêtre de terminal
    # Sur seaweed3
    weed master \
    -mdir=/var/lib/seaweedfs/master \
    -ip=192.168.122.144 \
    -port=9333 \
    -defaultReplication=001 \
    -peers=192.168.122.187:9333,192.168.122.159:9333,192.168.122.144:9333

    Vérification : après ~10 secondes, un leader est élu via Raft :

    Fenêtre de terminal
    curl -s http://192.168.122.144:9333/cluster/status
    # {
    # "IsLeader": true,
    # "Leader": "192.168.122.144:9333.19333",
    # "Peers": [
    # "192.168.122.159:9333.19333",
    # "192.168.122.187:9333.19333"
    # ]
    # }
  2. Démarrer les volume servers

    Lancez un volume server sur chaque nœud, dans des racks différents pour la réplication :

    Fenêtre de terminal
    # Sur seaweed1
    weed volume \
    -dir=/var/lib/seaweedfs/volume \
    -ip=192.168.122.187 -port=8080 \
    -mserver=192.168.122.187:9333,192.168.122.159:9333,192.168.122.144:9333 \
    -max=10 -dataCenter=dc1 -rack=rack1
    Fenêtre de terminal
    # Sur seaweed2
    weed volume \
    -dir=/var/lib/seaweedfs/volume \
    -ip=192.168.122.159 -port=8080 \
    -mserver=192.168.122.187:9333,192.168.122.159:9333,192.168.122.144:9333 \
    -max=10 -dataCenter=dc1 -rack=rack2
    Fenêtre de terminal
    # Sur seaweed3
    weed volume \
    -dir=/var/lib/seaweedfs/volume \
    -ip=192.168.122.144 -port=8080 \
    -mserver=192.168.122.187:9333,192.168.122.159:9333,192.168.122.144:9333 \
    -max=10 -dataCenter=dc1 -rack=rack3

    Vérification : la topologie montre les 3 volume servers :

    Fenêtre de terminal
    curl -s http://192.168.122.144:9333/vol/status | python3 -m json.tool
    # "DataCenters": {
    # "dc1": {
    # "rack1": { "192.168.122.187:8080": [...] },
    # "rack2": { "192.168.122.159:8080": [...] },
    # "rack3": { "192.168.122.144:8080": [...] }
    # }
    # }
  3. Démarrer le filer

    Le filer s'installe sur un ou plusieurs nœuds (ici seaweed1) :

    Fenêtre de terminal
    # Sur seaweed1
    weed filer \
    -ip=192.168.122.187 \
    -port=8888 \
    -master=192.168.122.187:9333,192.168.122.159:9333,192.168.122.144:9333

    Vérification :

    Fenêtre de terminal
    curl -s -H "Accept: application/json" http://192.168.122.187:8888/ | python3 -m json.tool
    # {"Path":"/","Entries":[...],"EmptyFolder":false}
  4. Démarrer le S3 gateway

    Créez d'abord le fichier de credentials S3 :

    {
    "identities": [
    {
    "name": "admin",
    "credentials": [
    {
    "accessKey": "seaweedadmin",
    "secretKey": "seaweedadmin123"
    }
    ],
    "actions": ["Admin", "Read", "Write", "List", "Tagging", "Lock"]
    },
    {
    "name": "readonly",
    "credentials": [
    {
    "accessKey": "seaweedread",
    "secretKey": "seaweedread123"
    }
    ],
    "actions": ["Read", "List"]
    }
    ]
    }

    Enregistrez-le dans /etc/seaweedfs-s3.json, puis lancez le S3 gateway :

    Fenêtre de terminal
    weed s3 \
    -port=8333 \
    -filer=192.168.122.187:8888 \
    -config=/etc/seaweedfs-s3.json

    Vérification :

    Fenêtre de terminal
    curl -s http://192.168.122.187:8333/ | head -5
    # <?xml version="1.0" encoding="UTF-8"?>

Pour un déploiement persistant, créez des fichiers de service systemd.

[Unit]
Description=SeaweedFS Master
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=root
ExecStart=/usr/local/bin/weed master \
-mdir=/var/lib/seaweedfs/master \
-ip=ADRESSE_IP_DU_NOEUD \
-port=9333 \
-defaultReplication=001 \
-peers=IP1:9333,IP2:9333,IP3:9333
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target

L'API native de SeaweedFS fonctionne en 2 étapes : assigner un identifiant, puis uploader vers le volume server.

Le master ne reçoit jamais les données : il renvoie un file ID et l'adresse du volume server concerné, l'upload partant ensuite directement vers ce serveur.

Fenêtre de terminal
# Étape 1 : demander un file ID au master
curl -s http://192.168.122.144:9333/dir/assign
# {"fid":"4,031c0dfc08","url":"192.168.122.187:8080","publicUrl":"192.168.122.187:8080","count":1}
# Étape 2 : uploader le fichier sur le volume server
echo "Hello SeaweedFS!" > /tmp/test.txt
curl -F file=@/tmp/test.txt http://192.168.122.187:8080/4,031c0dfc08
# {"name":"test.txt","size":17,"eTag":"c76e4f38","mime":"text/plain"}

Le file ID suffit à localiser le fichier, sans repasser par le master. La suppression marque l'emplacement comme libre : l'espace n'est réellement rendu qu'au passage de volume.vacuum.

Fenêtre de terminal
# Télécharger via le volume server
curl http://192.168.122.187:8080/4,031c0dfc08
# Hello SeaweedFS!
# Supprimer
curl -X DELETE http://192.168.122.187:8080/4,031c0dfc08
# {"size":51}

Le paramètre replication agit au moment de l'assignation et non de l'upload : il oriente le choix du volume par le master, donc le placement des copies.

Fenêtre de terminal
# Réplication inter-rack (010)
curl -s "http://192.168.122.144:9333/dir/assign?replication=010"
# Le master choisit un volume répliqué sur un rack différent

Le filer expose une interface fichiers avec chemins hiérarchiques via HTTP.

L'URL se termine par un slash pour désigner un répertoire, que le filer crée si nécessaire. L'en-tête Accept: application/json est le seul moyen d'obtenir un listing exploitable par script, la réponse par défaut étant du HTML.

Fenêtre de terminal
# Uploader un fichier dans /documents/
curl -F file=@/tmp/rapport.txt http://192.168.122.187:8888/documents/
# {"name":"rapport.txt","size":18}
# Lister le contenu (HTML par défaut)
curl http://192.168.122.187:8888/documents/
# Lister en JSON
curl -H "Accept: application/json" http://192.168.122.187:8888/documents/
# {"Path":"/documents","Entries":[{"FullPath":"/documents/rapport.txt",...}]}

La lecture se fait par le chemin logique : le filer résout lui-même le file ID puis le volume server à contacter.

Fenêtre de terminal
curl http://192.168.122.187:8888/documents/rapport.txt
# (contenu du fichier)

Le filer expose également une interface web sur le port 8888 pour parcourir et uploader des fichiers graphiquement.

La passerelle accepte les clients AWS standards, aws s3 comme les SDK, à condition de leur imposer l'option --endpoint-url. Les buckets ne constituent pas une structure séparée : ce sont des répertoires du filer sous /buckets/, ce qui explique les messages d'erreur signalant un répertoire déjà existant.

Les clés proviennent du fichier /etc/seaweedfs-s3.json chargé par la passerelle. La région n'a aucun effet côté serveur, mais reste obligatoire pour que le client calcule la signature.

Fenêtre de terminal
export AWS_ACCESS_KEY_ID=seaweedadmin
export AWS_SECRET_ACCESS_KEY=seaweedadmin123
export AWS_DEFAULT_REGION=us-east-1

Le client bascule seul en multipart au-delà de son seuil, ce qui est le cas du fichier de 10 Mo ; SeaweedFS réassemble les parties côté serveur.

Fenêtre de terminal
# Créer un bucket
aws --endpoint-url http://192.168.122.187:8333 s3 mb s3://mon-bucket
# make_bucket: mon-bucket
# Lister les buckets
aws --endpoint-url http://192.168.122.187:8333 s3 ls
# 2026-03-01 10:57:44 mon-bucket
# Uploader un fichier
echo "Fichier via S3" > /tmp/s3-test.txt
aws --endpoint-url http://192.168.122.187:8333 s3 cp /tmp/s3-test.txt s3://mon-bucket/documents/
# upload: ./s3-test.txt to s3://mon-bucket/documents/s3-test.txt
# Uploader un gros fichier (multipart automatique)
dd if=/dev/urandom of=/tmp/bigfile.bin bs=1M count=10 2>/dev/null
aws --endpoint-url http://192.168.122.187:8333 s3 cp /tmp/bigfile.bin s3://mon-bucket/data/
# upload: ./bigfile.bin to s3://mon-bucket/data/bigfile.bin
# Lister le contenu récursivement
aws --endpoint-url http://192.168.122.187:8333 s3 ls s3://mon-bucket --recursive
# 2026-03-01 10:58:00 10485760 data/bigfile.bin
# 2026-03-01 10:57:59 15 documents/s3-test.txt
# Télécharger
aws --endpoint-url http://192.168.122.187:8333 s3 cp s3://mon-bucket/documents/s3-test.txt /tmp/download.txt
cat /tmp/download.txt
# Fichier via S3
# Générer une URL présignée (valide 1 heure)
aws --endpoint-url http://192.168.122.187:8333 s3 presign s3://mon-bucket/documents/s3-test.txt --expires-in 3600

La suppression d'un bucket exige qu'il soit vide ; l'option --force enchaîne la purge du contenu puis la suppression du bucket lui-même.

Fenêtre de terminal
# Supprimer un objet
aws --endpoint-url http://192.168.122.187:8333 s3 rm s3://mon-bucket/data/bigfile.bin
# Supprimer un bucket (doit être vide)
aws --endpoint-url http://192.168.122.187:8333 s3 rb s3://mon-bucket
# Forcer la suppression (bucket + contenu)
aws --endpoint-url http://192.168.122.187:8333 s3 rb s3://mon-bucket --force

Deux interfaces cohabitent pour l'exploitation : les endpoints HTTP du master, interrogeables avec curl depuis n'importe quelle machine, et le shell weed shell, qui donne accès aux opérations de maintenance. Les tâches lourdes comme l'équilibrage, la compaction ou l'Erasure Coding n'existent que dans le shell.

Ces endpoints répondent sur n'importe quel master, mais seule la réponse du leader porte IsLeader: true, ce qui permet de l'identifier.

Fenêtre de terminal
# Statut du cluster (identifier le leader)
curl -s http://IP_MASTER:9333/cluster/status
# Statut des volumes
curl -s http://IP_MASTER:9333/vol/status
# Version
weed version

SeaweedFS fournit un shell interactif pour l'administration :

Fenêtre de terminal
weed shell -master=192.168.122.144:9333

Commandes utiles dans le shell :

# Lister les volumes
volume.list
# Vérifier la santé du cluster
cluster.check
# Équilibrer les volumes
volume.balance
# Compacter un volume (récupérer l'espace)
volume.vacuum
# Forcer la réplication d'un volume
volume.fix.replication

Pour les données froides, l'Erasure Coding (EC) remplace la réplication par un codage 10+4, réduisant l'espace de 300 % (réplication 3x) à 140 % :

Fenêtre de terminal
# Dans weed shell
ec.encode -collection=archive -fullPercent=95
ec.rebuild -force

Le scrubbing vérifie l'intégrité des volumes et détecte les corruptions silencieuses :

Fenêtre de terminal
# Dans weed shell
volume.scrub -collection=important
ec.scrub -collection=archive

Les incidents sur ce type de cluster se concentrent sur deux zones : la convergence Raft entre les masters au démarrage, et la disponibilité de volumes accessibles en écriture. Le tableau reprend les messages rencontrés pendant le lab, avec leur correction.

Deux entrées méritent une lecture attentive car leur message induit en erreur : Only odd number of masters are supported révèle une incohérence entre -ip et -peers, pas un mauvais nombre de nœuds.

SymptômeCause probableSolution
no leader lors du démarrageRaft n'a pas convergéVérifier que les 3 masters sont démarrés et joignables (port 9333 et 19333)
Only odd number of masters are supportedIncohérence entre -ip et -peersUtiliser exactement la même valeur (IP ou hostname) dans -ip et dans -peers pour chaque nœud
no writable volumesVolumes non assignés ou max atteintAugmenter -max sur les volume servers ou compacter avec volume.vacuum
connection refused sur 8888Filer non démarréLancer weed filer avec les bons -master
Upload S3 échoue avec InvalidAccessKeyIdCredentials S3 non configurésVérifier -config=/etc/seaweedfs-s3.json ou utiliser les flags env AWS_ACCESS_KEY_ID
Bucket "already exists"Répertoire préexistant dans le filerLes buckets S3 correspondent à /buckets/nom/ dans le filer
-ip invalide pour weed s3Le flag correct est -ip.bindUtiliser weed s3 -ip.bind=IP au lieu de -ip=IP

La boucle finale sur les trois masters est le moyen le plus rapide d'identifier le leader courant, information nécessaire avant toute opération d'administration.

Fenêtre de terminal
# Vérifier les processus
ps aux | grep weed
# Vérifier les ports ouverts
ss -tlnp | grep -E '9333|8080|8888|8333'
# Consulter les logs master
journalctl -u seaweedfs-master -f
# Topologie complète avec volumes
curl -s http://IP_MASTER:9333/vol/status | python3 -m json.tool
# Vérifier le leader Raft
for ip in IP1 IP2 IP3; do
echo -n "$ip: "
curl -s http://${ip}:9333/cluster/status
echo ""
done

Trois niveaux se superposent. Les clés statiques du fichier de configuration conviennent à un accès applicatif unique. Le système IAM avec OIDC et STS cloisonne plusieurs équipes dans un même bucket au moyen d'identifiants temporaires. L'isolation réseau reste indispensable dans les deux cas, car les ports master et volume n'imposent aucune authentification.

Créez des identités avec le minimum de droits nécessaires :

{
"identities": [
{
"name": "app-backend",
"credentials": [
{
"accessKey": "app-key-xxxxx",
"secretKey": "secret-xxxxx"
}
],
"actions": ["Read:mon-bucket", "Write:mon-bucket", "List:mon-bucket"]
}
]
}

Les actions peuvent être restreintes à un bucket spécifique avec la syntaxe Action:bucket. Attention à la sémantique : pour scoper une action à un préfixe dans ce modèle simple, il faut un joker (Read:mon-bucket/equipe-a/*). Sans joker, la comparaison se fait au niveau du bucket uniquement. Pour un contrôle par préfixe robuste et une fédération d'identité, préférez le système IAM avancé décrit ci-dessous.

Contrôle d'accès avancé : bucket policies, OIDC et STS

Section intitulée « Contrôle d'accès avancé : bucket policies, OIDC et STS »

Au-delà des clés statiques, SeaweedFS active un système IAM complet via l'option -s3.iam.config : bucket policies (avec conditions s3:prefix), fédération OIDC et jetons temporaires STS AssumeRoleWithWebIdentity. C'est ce qui permet de cloisonner des équipes par préfixe dans un bucket partagé, sans distribuer de clés permanentes.

Le fichier de configuration IAM déclare trois blocs : le fournisseur OIDC (sous sts.providers), des policies (documents IAM avec ressources et conditions) et des rôles (dont la trustPolicy sélectionne le rôle selon un claim du jeton). Voici une configuration qui restreint un rôle au préfixe equipe-a/ :

{
"sts": {
"tokenDuration": "1h", "maxSessionLength": "12h", "issuer": "seaweedfs-sts",
"signingKey": "<cle-de-signature-base64-32-octets>",
"providers": [
{ "name": "keycloak-oidc", "type": "oidc", "enabled": true,
"config": {
"issuer": "https://sso.example.com/realms/stockage",
"clientId": "seaweedfs-s3", "clientSecret": "<secret>",
"jwksUri": "https://sso.example.com/realms/stockage/protocol/openid-connect/certs",
"scopes": ["openid","profile","email","roles"] } }
]
},
"policy": { "defaultEffect": "Deny" },
"policies": [
{ "name": "EquipeAPolicy", "document": { "Version": "2012-10-17", "Statement": [
{ "Effect": "Allow", "Action": ["s3:GetObject","s3:PutObject","s3:DeleteObject"],
"Resource": ["arn:aws:s3:::data/equipe-a/*"] },
{ "Effect": "Allow", "Action": ["s3:ListBucket"], "Resource": ["arn:aws:s3:::data"],
"Condition": { "StringLike": { "s3:prefix": ["equipe-a/*"] } } }
] } }
],
"roles": [
{ "roleName": "EquipeARole", "roleArn": "arn:aws:iam::role/EquipeARole",
"trustPolicy": { "Version": "2012-10-17", "Statement": [
{ "Effect": "Allow", "Principal": { "Federated": "keycloak-oidc" },
"Action": ["sts:AssumeRoleWithWebIdentity"],
"Condition": { "StringEquals": { "oidc:roles": "s3-read-write" } } } ] },
"attachedPolicies": ["EquipeAPolicy"] }
]
}

On démarre le S3 gateway en pointant sur ce fichier :

Fenêtre de terminal
weed server -s3 -s3.port=8333 -dir=/data \
-s3.config=/config/s3.json \
-s3.iam.config=/config/iam.json

Un utilisateur s'authentifie auprès du fournisseur OIDC, récupère un jeton, puis l'échange contre des identifiants temporaires via STS. La trustPolicy du rôle vérifie le claim oidc:roles du jeton pour décider quel rôle accorder.

Fenêtre de terminal
# 1. Jeton OIDC (ici via grant password d'un client de confiance)
TOKEN=$(curl -s https://sso.example.com/realms/stockage/protocol/openid-connect/token \
-d grant_type=password -d client_id=seaweedfs-s3 -d client_secret=<secret> \
-d username=<user> -d password=<pwd> -d scope=openid | jq -r .access_token)
# 2. Échange STS (POST) contre des identifiants temporaires
curl -s -X POST http://localhost:8333/ \
--data-urlencode "Action=AssumeRoleWithWebIdentity" \
--data-urlencode "Version=2011-06-15" \
--data-urlencode "RoleArn=arn:aws:iam::role/EquipeARole" \
--data-urlencode "RoleSessionName=session-1" \
--data-urlencode "WebIdentityToken=$TOKEN"
# -> <Credentials> : AccessKeyId (ASIA...), SecretAccessKey, SessionToken

Les identifiants temporaires (avec le SessionToken) portent la policy par préfixe : leur détenteur lit et écrit sous equipe-a/, mais reçoit un AccessDenied sur tout autre préfixe, et l'accès expire au bout de la durée configurée.

Si vous n'avez pas d'IdP, la même logique de préfixe s'applique via une bucket policy attachée directement, avec la condition s3:prefix :

Fenêtre de terminal
aws --endpoint-url http://localhost:8333 s3api put-bucket-policy \
--bucket data --policy file://policy-equipe-a.json

Les ports 9333 et 8080 exposent la topologie et les données sans le moindre contrôle d'identité : les fermer depuis l'extérieur n'est pas une précaution supplémentaire, c'est la seule protection existante.

  • Isolez le réseau interne : les ports 9333 et 8080 ne doivent pas être exposés sur Internet
  • Exposez uniquement le S3 gateway (8333) derrière un reverse proxy avec TLS
  • TLS natif : SeaweedFS supporte les certificats via -cert.file et -key.file

L'option -encryptVolumeData chiffre les données au repos dans les fichiers de volume. Elle ne protège pas les échanges réseau, qui relèvent des options -cert.file et -key.file.

Fenêtre de terminal
# Volume server avec chiffrement au repos
weed volume -dir=/var/lib/seaweedfs/volume -encryptVolumeData

Ces recommandations découlent directement du fonctionnement interne : le quorum Raft impose un nombre impair de masters, la réplication XYZ n'a d'effet que si la topologie déclarée correspond à la réalité physique, et le nombre maximal de volumes par serveur borne la capacité d'écriture du cluster.

Le nombre de volume servers doit au moins égaler le nombre de copies demandées, sinon le master ne trouve aucun placement valide et refuse les écritures.

  • Masters : 3 instances minimum pour le quorum Raft (nombre impair)
  • Volume servers : au moins autant que le facteur de réplication
  • Filer : 1 ou 2 instances (avec un backend de métadonnées externe pour la HA)
  • RAM : 128 Mo par composant minimum, 512 Mo recommandé

Les valeurs -dataCenter et -rack ne sont pas de simples étiquettes : le master s'en sert pour choisir l'emplacement des copies. Déclarer trois nœuds dans le même rack rend la réplication 010 impossible à satisfaire.

  • Un rack par nœud physique : pour que la réplication 010 protège contre la perte d'un serveur
  • Datacenters : utilisez -dataCenter pour identifier les sites géographiques
  • Collections : groupez les données par type (-collection=images, -collection=logs) pour faciliter la maintenance

Le profil d'accès diffère fortement selon le composant : les masters écrivent en continu de petits journaux Raft en synchrone, les volume servers écrivent de gros blocs séquentiels.

  • SSD pour les masters : les écritures Raft bénéficient de la faible latence
  • HDD pour les volume servers : l'accès séquentiel exploite bien les disques mécaniques
  • -max : définissez le nombre maximum de volumes par serveur selon l'espace disque disponible (1 volume = 30 Go par défaut)
  • Compaction : exécutez volume.vacuum régulièrement pour récupérer l'espace des fichiers supprimés

Ces points résument ce qui distingue SeaweedFS des autres moteurs de stockage objet, ainsi que les décisions à arrêter avant un déploiement en production.

  • SeaweedFS est un stockage distribué haute performance avec accès O(1) par fichier, idéal pour les blobs et petits fichiers
  • Le binaire unique weed exécute tous les composants : master, volume, filer, S3
  • Les masters Raft (3 minimum) assurent la haute disponibilité ; les volume servers stockent les données réelles
  • Le filer ajoute une couche système de fichiers avec hiérarchie, FUSE et WebDAV
  • Depuis v4.01, l'architecture Direct Access permet au S3 gateway de communiquer directement avec les volumes (−30 % latence écriture, −5 % latence lecture)
  • La réplication XYZ (000, 001, 010, 100, 200) contrôle finement la distribution des copies
  • L'API S3 compatible AWS permet d'utiliser aws s3, s3cmd et tout SDK S3 existant
  • Pour la production : déployez chaque composant séparément, utilisez -dataCenter et -rack, et configurez des services systemd
  • L'Erasure Coding (10+4) réduit considérablement l'espace pour les données froides par rapport à la réplication 3x

Le choix d'un moteur de stockage se fait rarement isolément. Garage vise les déploiements géo-distribués légers, MinIO l'API S3 seule, Ceph le stockage unifié bloc, objet et fichier ; le comparatif détaille les écarts d'authentification et de gestion des droits par préfixe.

Le wiki du dépôt reste la source la plus à jour, la documentation n'étant pas publiée séparément du code. Le papier Haystack décrit le modèle de stockage dont dérive l'accès en O(1) revendiqué par le projet.

Ce site vous est utile ?

Sachez que moins de 1% des lecteurs soutiennent ce site.

Je maintiens +700 guides gratuits, sans pub ni tracking. Un soutien, même symbolique, m'aide à couvrir l'hébergement et à garder ces ressources gratuites. Merci pour votre appui.

Le formulaire ne s'affiche pas ? Ouvrir Ko-fi dans un onglet.

Abonnez-vous et suivez mon actualité DevSecOps sur LinkedIn