Files
BaC/content/2023-01-02-Garage-:-le-petit-serveur-S3-quil-est-bien-pour-sen-servir.md
2025-02-27 12:52:48 +01:00

243 lines
16 KiB
Markdown
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

+++
title = "Garage: le petit serveur S3 quil est bien pour sen servir"
date = 2023-01-02
[taxonomies]
tags = [ "système", "s3", "garage", "stockage" ]
+++
> Effectivement, ça sent un peu le cambouis
*Michel, utilisateur de garage*
<!-- more -->
# Cest quoi exactement S3?
Commençons par le commencement, S3 (pour *Simple Storage Service*) est un protocole de stockage objet, appuyé sur HTTP. Pensez NAS mais avec autre chose que du NFS/CIFS et plus orienté usage sur WAN plutôt que LAN (et oui, je sais quon *peut* faire du NFS/CIFS sur du WAN mais juste parce que tu peux ne veut pas dire que tu dois…).
Pourquoi est-ce que tu devrais ty intéresser? Parce quil y a tout un tas de logiciels qui lutilisent pour faire du stockage de fichiers et que ça peut du coup être bien pratique den avoir à disposition. On pensera, entre autres, à NextCloud, Peertube, Mastodon, Hugo, Restic, etc…
En plus, comme cest orienté Web, il est aussi possible de lutiliser directement pour servir un site Web statique, ou des éléments statiques sur un site Web (et donc techniquement économiser de la ressource sur ledit site Web).
# Ah et du coup, pourquoi `garage`?
Il y a plein de logiciels qui font du S3: [OpenIO](https://www.openio.io/), [Ceph](https://ceph.com/), [MinIO](https://min.io/) et probablement plein dautres. Mais ils ont tous (ou presque) en commun dêtre relativement compliqués à mettre en œuvre, soit en terme technique, soit en terme de ressource.
Cest là quintervient [`garage`](https://garagehq.deuxfleurs.fr/). Son objectif est de faire du S3 avec le moins de ressources possibles (en gros, ça peut tourner sur un Raspberry Pi, littéralement) tout en conservant un minimum de fonctionnalités propre à ce type de service: redondance des données, transfert optimisé, etc… Il est également conçu pour supporter des latences réseaux relativement élevées (de lordre de 200 ms au max), ce qui nest généralement pas le cas de ces concurrents.
Et en plus, cest écrit en Rust ❤️
# Bon OK, comment quon fait du coup?
On va partir sur une architecture volontairement un peu compliquée pour que tu puisses bien te rendre compte des possibilités offertes par `garage`. En fait, on va créer un cluster avec 6 nœuds au total (que jai appelé `garage{1..5}` et `garagegw1`), dans 3 zones différentes dont un nœud passerelle (jexpliquerai le pourquoi de la passerelle plus tard). Il faut savoir que `garage` est conçu pour assurer lui même lintégrité et la redondance des données, il nest donc généralement pas nécessaire davoir des systèmes super résilients sur les nœuds eux-mêmes (oubliez les RAIDs et autres systèmes de redondance, `garage` fait tout pour vous).
Jai donc monté 6 containers LXC pour ce faire, histoire de me simplifier la vie. Première chose, il faut aller télécharger lexécutable `garage` sur lensemble des 6 nœuds (dans `/usr/local/bin/garage` par exemple avec les bonnes permissions). Tu peux le compiler toi-même si tu as la patience, sinon tu peux le télécharger directement [ici](https://garagehq.deuxfleurs.fr/download/) (il sagit dun exécutable statique compilé avec `musl`, il est donc énorme, mais a lavantage de tourner nimporte où sans autre prérequis).
Petite précision: `garage` étant un exécutable sans aucune dépendance, jai choisi de le faire tourner directement sur les containers, mais on pouvait tout aussi bien le faire tourner dans un container Docker (si besoin, limage officielle est `dxflrs/garage`).
Bref, maintenant quon a nos exécutables sur chaque machine, on va pouvoir créer un service `systemd` pour les démarrer. `garage` supporte par défaut la fonction `DynamicUser` de `systemd` qui permet de démarrer un service avec un numéro dUID aléatoire à chaque fois (cest `systemd` qui recolle les morceaux), ce qui est bien pratique pour ne pas se prendre la tête avec les détails genre créer un utilisateur, une *home directory*, etc…
On peut donc créer, sur chaque nœud, le fichier `/etc/systemd/system/garage.service`:
```ini
[Unit]
Description=Garage Data Store
After=network-online.target
Wants=network-online.target
[Service]
Environment='RUST_LOG=garage=info' 'RUST_BACKTRACE=1'
ExecStart=/usr/local/bin/garage server
StateDirectory=garage
DynamicUser=true
ProtectHome=true
NoNewPrivileges=true
[Install]
WantedBy=multi-user.target
```
Les données seront alors stockées `/var/lib/private/garage/{meta,data}` de manière permanente avec un lien symbolique dans `/var/lib/garage` quand le service est démarré. Mais avant de faire cela, il va falloir créer un fichier de configuration spécifique à `garage`, `/etc/garage.toml`:
```toml
metadata_dir = "/var/lib/garage/meta" # obligatoire sinon rien ne va fonctionner
data_dir = "/var/lib/garage/data" # pareil
db_engine = "lmdb" # cest la DB recommandé par défaut pour stocker les métadonnées
replication_mode = "3" # le mode de réplication. Nous voulons ici une redondance max, ce sera donc 3.
# on peut mettre 2 ou même 1 en fonction de la redondance que lon souhaite.
compression_level = 2 # le degré de compression zstd, de 1 à 19, plus cest élevé, plus cest compressé
# plus ça consomme de ressources
rpc_bind_addr = "[::]:3901" # ladresse par défaut pour le protocole RPC de garage
rpc_public_addr = "garage1.lxc.arpa:3901" # son nom public
# ça, cest le secret RPC qui doit être partagé entre tous les nœuds,
# on peut le générer avec la commande `openssl rand -hex 32`
rpc_secret = "814297d51a3878c66ac4db89e20539b980dd70f43be35dd884d0c2f68aab6c90"
# ça, cest pour accéder à lAPI S3
[s3_api]
s3_region = "garage"
# concernant les nœuds de stockage, on peut éventuellement écouter sur [::1] si on a une passerelle
api_bind_addr = "[::]:3900"
root_domain = ".lxc.arpa"
```
Petite explication complémentaire: `garage` utilise un protocole chiffré qui lui est propre pour communiquer entre les nœuds (ça correspond aux indications `rpc_*`), parfaitement utilisable sur un réseau non-sûr comme Internet. Par contre, par défaut, le S3 est en HTTP et non en HTTPS. Donc si tu dois exposer lAPI S3, il faudra probablement rajouter un reverse proxy devant (ou utiliser une autre astuce que nous verrons un peu plus loin).
Bref, maintenant tu peux démarrer le service `garage` sur chaque nœud et faire un:
```bash
# garage status
==== HEALTHY NODES ====
ID Hostname Address Tags Zone Capacity
f1a78b6b674c054d garage1 127.0.1.1:3901 NO ROLE ASSIGNED
```
Comme tu peux le constater, pour le moment, il ne fait pas grand-chose…
# Fuuuuuuuuuuuuu-sion!
On va donc se dépêcher tinterconnecter tout ce beau monde. On va donc se connecter sur chaque nœud et récupérer son identifiant avec la commande suivante:
```bash
# garage node id -q
93506740b859618e6b5953092485b7717d186b3b15b61d9c59e20f685ee3122f@garage2:3901
```
Cet identifiant peut ensuite être utilisé sur le tout premier nœud pour faire linterconnection:
```bash
# garage node connect 93506740b859618e6b5953092485b7717d186b3b15b61d9c59e20f685ee3122f@garage2:3901
Success.
```
Et ainsi de suite pour lensemble des nœuds du cluster. À la fin, le statut devrait être le suivant:
```bash
# garage status
==== HEALTHY NODES ====
ID Hostname Address Tags Zone Capacity
32e482de67868f30 garage5 100.64.4.34:3901 NO ROLE ASSIGNED
93506740b859618e garage2 100.64.4.105:3901 NO ROLE ASSIGNED
fe8c6c5dd5730813 garagegw1 100.64.4.238:3901 NO ROLE ASSIGNED
38e9e413613a6afe garage4 100.64.4.217:3901 NO ROLE ASSIGNED
f1a78b6b674c054d garage1 127.0.1.1:3901 NO ROLE ASSIGNED
9bc75561e211a385 garage3 100.64.4.153:3901 NO ROLE ASSIGNED
```
Voilà, tous les nœuds se voient entre eux. Il va maintenant falloir les grouper dans des zones, leur donner des capacités et, optionnellement, désigner une paserelle (ce que nous allons en loccurence faire).
## Les zones
La zone ne donne, en soi, aucune indication particulière: cest simplement un moyen de grouper les serveurs entre eux. Derrière le rideau, `garage` va surtout se servir des zones pour multiplier les redondances de données.
Il faut savoir que si elles sont présentes, les zones serviront de base pour la redondance des données et non les serveurs présents dans chaque zone.
## La passerelle
Les passerelles sont simplement des nœuds `garage` qui ne stockent pas de données, mais permettent daccéder aux données via S3. Imaginons que tu ne souhaites pas exposer directemet lAPI S3 sur Internet mais tu ais besoin dun de tes services y accède. Tu peux donc simplement monter un `garage` sur la même machine que celle hébergeant le service en question, le faire écouter sur `localhost` sur ce même serveur et le grouper avec tous les autres. Comme ça, tu peux effectivement accéder à tes *buckets* S3 et leurs données, mais sans jamais exposer publiquement `garage` et sans copier les données sur le serveur susnommé.
## Les capacités
Alors là, ça se complique un tout petit peu. Les capacités sentendent au sens disque, mais en **relatif** dun nœud à lautre. Donc si tu mets 10/10/10 dans une configuration à 3 nœuds, tu indiques simplement que tous les nœuds peuvent héberger la même quantité de données (tu pourrais aussi bien mettre 1/1/1). Si tu mets 10/20/30, tu indiques que le nœud 30 a 3 fois plus de capacité que le nœud 10 et 50% de plus que le nœud 20. **Ça nindique donc pas vraiment la capacité, mais plus la façon dont il faut répartir les données.**
Au passage, une autre fonctionnalité intéressante de `garage`, la plupart des autres logiciels faisant du S3 que je connais obligent à avoir des nœuds de capacité identique, aussi bien dun point de vue stockage, que mémoire ou puissance de calcul.
## Allez, chauffe Marcel!
Allons mettre tout cela en musique. Nous avons 5 nœuds de stockage, nous allons donc les répartir en 3 zones: `maison` (qui simulera les babasses quil y a chez toi), `mere-grand` (qui sera le serveur que tas mis en loucedé chez ta grand-mère) et `boulot` (pour celui que tu as laissé dans le placard à balai du taf un jour que tétais bourré). Nous admettrons aussi que les serveurs `maison` sont identiques en terme de capa, les autres serveurs des autres zones seront 3 fois plus gros chacun (histoire davoir une copie complète de toutes les données dans chaque zone).
```bash
# garage layout assign -z maison -g fe8c6c5dd5730813
# garage layout assign -z maison -c 10 f1a78b6b674c054d
# garage layout assign -z maison -c 10 93506740b859618e
# garage layout assign -z maison -c 10 9bc75561e211a385
# garage layout assign -z mere-grand -c 30 38e9e413613a6afe
# garage layout assign -z boulot -c 30 32e482de67868f30
```
Voilà, avec ça on va avoir un `layout` relativement cohérent par rapport à lénoncé plus haut. Tu peux le voir avec `garage layout show` et lappliquer avec `garage layout --version 1`. Tout ceci est effectivement versionné en cas de besoin mais fais quand même attention: chaque fois que tu fais un changement significatif sur le design général, cela entraîne des mouvements de données pour répondre à ce besoin. Donc autant sur un cluster vide, comme cest le cas actuellement, les conséquences seront moindres, autant sur un cluster rempli ras la gueule, ça risque dêtre bien plus compliqué.
# Jfais quoi moi maintenant? Tu te casses!
Bon ben maintenant, on va pouvoir créer un *bucket*, une clé et téléverser quelques fichiers pour vérifier que tout va bien (et observer comment que ça marche derrière). Pour le client S3 à utiliser, je recommande personnellement `mc`, [le client MinIO](https://min.io/docs/minio/linux/reference/minio-mc.html).
Commençons par créer un *bucket*:
```bash
# garage bucket create potdechambre
Bucket potdechambre was created.
# garage key new --name caca
Key name: caca
Key ID: GK3fdf189892c2c7665049631d
Secret key: 0fd528f075d06ce05bf6e020e50d42bb37e288e1bb36d9a38076ef06f541958a
Can create buckets: false
Key-specific bucket aliases:
Authorized buckets:
```
Il suffit maintenant dassocier la clé en question au *bucket* précédent (on va se mettre tous les droits pour le moment, mais évidemment, on peut faire plus subtil):
```bash
# garage bucket allow potdechambre --owner --read --write --key GK3fdf189892c2c7665049631d
New permissions for GK3fdf189892c2c7665049631d on potdechambre: read true, write true, owner true.
```
Et voilà, il ne reste plus quà tester avec `mc`:
```bash
> mc alias set garage http://garagegw1.lxc.arpa:3900 <access_key> <secret_key>
> mc ls garage/
[2022-12-29 14:07:20 CET] 0B potdechambre/
```
Et donc maintenant, on peut déposer quelques fichiers et voir comment ça se comporte:
```bash
> mc mirror ./Téléchargements garage/potdechambre/
...ur-card.zip: 74.42 MiB / 74.42 MiB ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 56.43 MiB/s 1s
```
En comptant la compression, on voit bien quon a une répartition à peu près intelligente des données:
```bash
> for i in {1..5}; do echo garage${i}; ssh root@garage${i}.lxc.arpa du -hs /var/lib/private/garage/data; done
garage1
14M /var/lib/private/garage/data
garage2
29M /var/lib/private/garage/data
garage3
23M /var/lib/private/garage/data
garage4
65M /var/lib/private/garage/data
garage5
65M /var/lib/private/garage/data
```
Et bien sûr, on peut récupérer nimporte quel fichier depuis le S3 vers la machine locale:
```bash
> mc cp garage/potdechambre/rnamd /tmp/rnamd
...ambre/rnamd: 794.31 KiB / 794.31 KiB ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 13.51 MiB/s 0s
```
# Est-ce que cest vraiment résistant aux pannes?
Et ben, on va essayer!?! Éteignons le nœud 1 et voyons ce quil se passe:
```bash
> mc ls garage/potdechambre
[affiche toujours des trucs]
> mc mirror garage/potdechambre /tmp/Téléchargements
...ur-card.zip: 74.42 MiB / 74.42 MiB ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 151.55 MiB/s 0s
```
Aucun souci. En fait, on peut même éteindre tous les nœuds dune même zone et continuer daccéder aux données. Par contre, sil y a deux pannes dans deux zones différentes (même dun seul serveur pour le cas de `maison`), `garage` ne peut atteindre un quorum et les données ne sont plus accessibles (elles ne sont pas perdues pour autant, en tout cas pour le moment).
Donc en gros, si tu arrives à avoir 3 copies de tes données dans 3 zones différentes, tant que deux de ces zones sont accessibles, tu nauras pas de souci pour accéder aux données (cest ce que signifie le `replication_mode="3"`, 3 répliques sur lensemble du cluster en essayant de les répartir entre toutes les zones).
# Et ben, cétait pas mal tout ça…
Voilà, cétait un exemple un peu compliqué mais qui permet de bien voir toutes les possibilités offertes par `garage`. Dans le cadre dun auto-hébergement, on pourra bien sûr faire bien plus simple avec un seul nœud par exemple ou deux nœuds dans deux zones différentes, chez toi et chez un pote genre.
Et maintenant tu vois à quoi ça ressemble, et tu peux éventuellement commencer à imaginer ce que tu pourrais en faire. Dans un prochain article, on regardera comment on peut lutiliser comme moteur de stockage pour Nextcloud.