Déployer un service hautement disponible grâce à Redis Replication

Magazine
Marque
Linux Pratique
Numéro
140
Mois de parution
novembre 2023
Spécialité(s)


Résumé

Redis est un système de stockage de données en mémoire de type NoSQL orienté hautes performances. Il est souvent utilisé comme base de données, système de cache ou de messages. Nous verrons dans cet article comment déployer un service hautement disponible grâce à Redis Replication.


Body

1. Notre premier serveur Redis

Pour démarrer, commençons avec un serveur Redis. L’installation sera présentée pour Ubuntu 22.04 et devrait fonctionner sans trop d’adaptations pour Debian ou RHEL-like.

1.1 Installation

Redis est disponible dans les dépôts classiques de la distribution. Une configuration minimale peut être faite en installant simplement les paquets et en activant le service systemd.

Sudo apt -y install redis-server
sudo systemctl enable --now redis-server

Vérifions maintenant que nous parvenons à nous connecter :

redis-cli
127.0.0.1:6379> ping
PONG

La CLI permet également de récupérer avec la commande INFO un certain nombre d’éléments sur le fonctionnement du serveur. Celle-ci s’appelle seule ou avec un argument pour obtenir les informations relatives à une catégorie particulière.

127.0.0.1:6379> INFO Server
# Server
redis_version:6.0.16
redis_git_sha1:00000000
redis_git_dirty:0
redis_build_id:a3fdef44459b3ad6
redis_mode:standalone
os:Linux 5.15.0-73-generic x86_64
arch_bits:64
multiplexing_api:epoll
atomicvar_api:atomic-builtin
gcc_version:11.2.0
process_id:596
run_id:7ea0cf9f46b211a64874d7a1c0a115be78c42e98
tcp_port:6379
uptime_in_seconds:61483
uptime_in_days:0
hz:10
configured_hz:10
lru_clock:8771018
executable:/usr/bin/redis-server
config_file:/etc/redis/redis.conf
io_threads_active:0

Redis gère plusieurs types de données. Il est possible d’utiliser des chaînes de caractères, des ensembles, des hashs, des listes ainsi que d’autres types de données. Pour procéder à une première vérification de bon fonctionnement, nous allons ainsi écrire une donnée de type string, récupérer la valeur en indiquant la clé puis effacer cette donnée.

127.0.0.1:6379> set foo bar
OK
127.0.0.1:6379> get foo
"bar"
127.0.0.1:6379> del foo
(integer) 1
127.0.0.1:6379> get foo
(nil)

À ce stade, notre serveur est fonctionnel, toutefois sa configuration est très basique. Redis n’écoute que sur l’interface localhost, ce qui est incompatible avec la notion de réplication que nous mettrons en place et il est préférable que systemd prenne en charge le service de manière explicite. Nous allons entreprendre donc nos premières modifications du fichier de configuration /etc/redis/redis.conf et remplacer les paramètres bind et supervised par les valeurs suivantes :

bind 0.0.0.0
supervised systemd
protected-mode

Redémarrons le service redis et vérifions :

sudo systemctl restart redis-server
sudo netstat -lataupen |grep ":6379"
tcp        0      0 0.0.0.0:6379    0.0.0.0:*    LISTEN   115   39165    3267/redis-server 0

En pratique, la plupart des options de Redis peuvent être définies avec la commande CONFIG SET. C’est peut-être moins classique ou confortable qu’un fichier de configuration, mais c’est ce qui permet à Redis d’être entièrement reparamétré sans qu’il ne soit nécessaire de relancer le service et donc sans aucune interruption de service.

1.2 Gérer la persistance

Redis est une base de données en mémoire. Par conséquent si le service est arrêté, quelle qu’en soit la raison, les données sont perdues. Pour assurer la persistance des données, Redis permet d’utiliser deux mécanismes. Dans le premier, appelé RDB, Redis va générer des snapshots à intervalles réguliers de la base de données. Cela se configure avec save suivi de deux indicateurs, le premier consiste en une sauvegarde après n secondes si au moins un certain nombre d’écritures ont eu lieu. Ce paramètre est en outre multivalué. La politique par défaut est celle-ci :

save 900 1
save 300 10
save 60 10000

Généralement, on souhaite avoir au moins un snapshot récent même s’il y a peu d’écritures, d’où celui réalisé après 15 minutes à partir du moment où il y a eu une écriture. À l’inverse, en cas de forte charge, on souhaite également avoir des snapshots fréquents pour minimiser la perte de données, d’où le snapshot toutes les minutes dès 10000 clés modifiées. Ces seuils sont naturellement à adapter en fonction de l’activité de la base et afin de minimiser la perte de données admissible.

Dans le second mécanisme, AOF pour Append Only File, la persistance est gérée via l’utilisation d’un fichier de logs qui va historiser chaque écriture. Pour un maximum de fiabilité, les deux modes peuvent être utilisés conjointement. Naturellement, cela a un impact sur la consommation mémoire et les I/O disques, car pour chaque écriture en base, une seconde est faite pour le journal AOB.

Pour utiliser l’AOF, ajoutons ces deux lignes dans le fichier redis.conf :

appendonly yes
appendfilename "appendonly.aof"
appendfsync everysec

1.3 Un minimum de sécurité

Par défaut, Redis n’est pas joignable via le réseau. Pour que la mise en œuvre de la réplication à suivre soit possible, le service écoute désormais sur l’ensemble des interfaces réseau. Bien qu’il soit possible de restreindre à une ou plusieurs interfaces, l’accès au service à des fins de test s’est réalisé sans authentification. En définissant un mot de passe sur le paramètre requirepass, toute opération sur Redis nécessitera au préalable de s’authentifier avec la commande AUTH suivie du mot de passe. Dans la configuration redis.conf, nous ajoutons donc :

requirepass SECRET

Et nous pouvons vérifier :

# redis-cli
127.0.0.1:6379> AUTH SECRET
OK

Pour assurer une meilleure sécurité, du TLS devrait être mis en œuvre et les ACL Redis 6 devraient être utilisées.

2. Assurer la haute disponibilité

2.1 Les mécanismes

Redis Replication est un système maître/esclave permettant de répliquer de manière asynchrone les données du master sur les slaves. En cas de perte de connectivité avec le master, les slaves vont tenter de resynchroniser l’écart de données dès que la connexion redevient disponible. Si cela n’est pas possible, une resynchronisation complète est réalisée. Un slave peut servir de source de réplication à un autre slave et les réplicas peuvent servir de serveur en lecture pour répartir la charge et donc augmenter la performance.

En cas de perte du master, une procédure de failover doit être mise en œuvre pour promouvoir un slave comme master. Tant que cela n’est pas fait, le fonctionnement est en mode dégradé si ce n’est totalement interrompu. Redis Sentinel utilisé en complément permet de superviser le fonctionnement de Redis et en cas de panne du master, de promouvoir un nouveau master et de reconfigurer les replicas pour qu’ils se répliquent depuis ce nouveau master. Dans ce type d’architecture, il est recommandé de disposer d’au moins 3 serveurs pour gérer le quorum.    

Avec Redis Replication, il est fortement recommandé d’activer la persistance. En effet, sans persistance et en cas de redémarrage du master, il se retrouvera vide de toute donnée. Dans un second temps, les slaves vont s’empresser de répliquer cela les vidant de leurs données.

Pour assurer la montée en charge horizontale, Redis Cluster offre des possibilités complémentaires. Dans ce mode, il s’agit toujours d’avoir une architecture master/slave, mais avec gestion et failover automatique. L’espace de clés est divisé de sorte que chaque groupe de serveur ne gère qu’une partition de la base de données, cela s’appelle du sharding. La taille de la base de données peut ainsi être plus importante et la charge peut être répartie sur plus de nœuds. Un minimum de 6 nœuds est toutefois requis pour Redis Cluster ce qui prédispose ce mode à des infrastructures relativement conséquentes.

De fait, dans cet article, j’aborderai uniquement Redis Replication.

2.2 Configuration

La configuration mise en œuvre sur le premier serveur que nous appellerons db01 devra être reprise à l’identique sur nos deux slaves. Le serveur DB01 sera le master par défaut. Pour que la suite soit aisément compréhensible, voici les serveurs et adresses IP que je vais utiliser :

  • db01 : 192.168.69.81
  • db02 : 192.168.69.82
  • db03 : 192.168.69.83

Sur chacun de nos serveurs, nous allons définir la valeur de masterauth. En effet, nous avons exigé précédemment un mot de passe via requirepass, il faut donc s’authentifier pour avoir les droits pour répliquer :

masterauth SECRET

Et enfin, sur chaque slave, on indique depuis quel serveur notre slave doit se répliquer :

replicaof db01.morot.local 6379

Il suffit de redémarrer nos serveurs, il n’y a rien de plus à faire.

2.3 Vérification de la réplication

Dans un premier temps, envoyons la commande INFO Replication sur notre master. Si la configuration est correcte, alors on doit pouvoir avoir un rôle master, visualiser les deux slaves comme connectés ainsi que leurs adresses IP et enfin en fonctionnement nominal un statut online :

127.0.0.1:6379> AUTH SECRET
OK
127.0.0.1:6379> INFO Replication
# Replication
role:master
connected_slaves:2
slave0:ip=192.168.69.83,port=6379,state=online,offset=1106,lag=0
slave1:ip=192.168.69.82,port=6379,state=online,offset=1106,lag=0
master_replid:75b55df27cafe6bfd7aec3114bfd63e77d45a8cb
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:1106
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:1106

À l’inverse, sur un slave, le rôle doit correspondre et le serveur doit être connecté au slave (master_link_status). Si master_sync_in_progress est à 0 alors la réplication est terminée. La section master_replid indique le dataset de ReplicationID qui doit être identique avec le master lorsque les masters et slaves sont synchronisés. Si une synchronisation était en cours, nous aurions des champs master_sync supplémentaires pour suivre la volumétrie à répliquer et les performances de réplication.

127.0.0.1:6379> INFO Replication
# Replication
role:slave
master_host:db01.morot.local
master_port:6379
master_link_status:up
master_last_io_seconds_ago:3
master_sync_in_progress:0
slave_repl_offset:1288
slave_priority:100
slave_read_only:1
connected_slaves:0
master_replid:75b55df27cafe6bfd7aec3114bfd63e77d45a8cb
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:1288
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:1288

2.4 Failover

Voyons désormais ce qu’il se passe lors d’une panne du master. J’ai simulé un crash du master en coupant violemment la VM qui porte le service. Sur chacun de mes slaves, la réplication indique que le lien avec le master est « down » avec le délai depuis lequel le lien est rompu.

127.0.0.1:6379> INFO Replication
# Replication
role:slave
master_host:db01.morot.local
master_port:6379
master_link_status:down
master_last_io_seconds_ago:-1
master_sync_in_progress:0
slave_repl_offset:3472
master_link_down_since_seconds:42

S’il s’agit d’un simple redémarrage du serveur master, la réplication devrait remonter dès que le serveur sera up. En cas de panne majeure, le service rendu est interrompu. Il faut manuellement reconfigurer les slaves pour rétablir le service.

Sur db03, nous allons donc indiquer que sa source de réplication est db02 :

127.0.0.1:6379> replicaof db02.morot.local 6379
OK

Pour autant, cela ne fait pas de db02 un master par magie. D’autant que Redis supporte la réplication d’un slave depuis un autre slave. Il faut donc indiquer que db02 est un master, ce qui se configure en indiquant qu’il est le réplica d’aucun serveur :

127.0.0.1:6379> replicaof no one
OK
127.0.0.1:6379> info replication
# Replication
role:master
connected_slaves:1
slave0:ip=192.168.69.83,port=6379,state=online,offset=3472,lag=0

2.5 Le retour du master

Se pose ensuite la question de la remise en service du serveur db01 lorsqu’il sera réparé. Avec le failover précédent, il va se retrouver master sans slave et il sera aussi désynchronisé des autres serveurs Redis. Il est donc indispensable de le remettre en synchronisation avec le master qui a été promu. Cela se fait de la même façon que lors du failover précédent :

127.0.0.1:6379> replicaof db02.morot.local 6379
OK

Et c’est tout. Si toutefois vous tenez à remettre le serveur redis master « nominal » en tant que master, il faut alors :

  • le resynchroniser sur le master et s’assurer que la réplication est terminée ;
  • définir les slaves en état nominal (db02 et db03) comme replicaof db01 ;
  • indiquer que db01 n’est plus le réplica d’aucun serveur.

Cela reste toutefois une opération à réaliser en période de maintenance, car une courte rupture du service lors de la bascule est à prévoir.

3. Failover automatique avec Sentinel

3.1 Configuration

Nous venons de le voir, la réplication est simple à mettre en œuvre ou à reconfigurer. Elle a toutefois un énorme inconvénient en cas de perte du master, elle est manuelle. Nous allons donc mettre en œuvre un système complémentaire appelé Redis Sentinel qui viendra gérer automatiquement la promotion d’un nouveau master et la reconfiguration des slaves.

Commençons par installer sentinel :

sudo apt -y install redis-sentinel

Et configurer le service pour qu’il communique sur le réseau et non plus uniquement sur localhost. Au niveau firewall, le port 26379 devra autoriser les flux. Cela se fait via le fichier dédié /etc/redis/sentinel.conf :

bind 0.0.0.0
port 26379

Ajoutons un mot de passe de connexion, il s’agit du mot de passe d’authentification sur Sentinel :

requirepass SECRET

Enfin, démarrons la configuration des règles de bascule. Nous monitorons l’adresse IP du master, 192.168.69. Un quorum de 2 serveurs est exigé, c’est-à-dire que si au moins deux slaves s’accordent sur le fait que le master est injoignable alors une bascule est enclenchée.

sentinel monitor mymaster 192.168.69.81 6379 2
sentinel auth-pass mymaster SECRET

Le master est considéré comme injoignable après une minute :

sentinel down-after-milliseconds mymaster 60000
sentinel failover-timeout mymaster 180000
sentinel parallel-syncs mymaster 1

Une redémarrage du service sentinel plus tard, vérifions l’état de notre service en nous connectant via redis-cli sur le port de Sentinel cette fois :

redis-cli -p 26379
127.0.0.1:26379> AUTH SECRET
OK
127.0.0.1:26379> info sentinel
# Sentinel
sentinel_masters:1
sentinel_tilt:0
sentinel_running_scripts:0
sentinel_scripts_queue_length:0
sentinel_simulate_failure_flags:0
master0:name=mymaster,status=ok,address=192.168.69.81:6379,slaves=2,sentinels=4

Nous retrouvons bien le bon nombre de slaves. Si on souhaite des informations détaillées sur les slaves, il est possible d’envoyer la commande sentinel slaves mymaster et pour les instances sentinel : sentinel sentinels mymaster. La sortie étant peu lisible, il s’agit de la configuration en mode clé valeur dans la base Redis, je ne vais pas l’inclure ici.

3.2 Test de bascule

Sur notre master, simulons une panne en arrêtant le service avec systemctl stop redis-server. En parallèle, regardons les logs de Sentinel dans /var/log/redis/redis-sentinel.log. Par lisibilité, je vais filtrer quelque peu les logs.

En premier lieu, nous avons un event sdown, cela signifie que sentinel a détecté que le master n’était plus joignable :

37873:X 18 Jun 2023 21:33:04.343 # +sdown master mymaster 192.168.69.81 6379

Ensuite, nous avons un odown, c’est-à-dire que le serveur est vu comme injoignable par au moins le nombre de serveurs du quorum, comme nous avons indiqué 2, il faut au moins les deux serveurs pour qu’une promotion d’un nouveau soit réalisée :

37873:X 18 Jun 2023 21:33:05.420 # +odown master mymaster 192.168.69.81 6379 #quorum 3/2

db03 a été élu comme nouveau master :

37873:X 18 Jun 2023 21:34:17.255 # +selected-slave slave 192.168.69.83:6379 192.168.69.83 6379 @ mymaster 192.168.69.81 6379
37873:X 18 Jun 2023 21:34:17.437 # +promoted-slave slave 192.168.69.83:6379 192.168.69.83 6379 @ mymaster 192.168.69.81 6379

db02 est reconfiguré comme slave de db03 :

37873:X 18 Jun 2023 21:34:17.453 * +slave-reconf-sent slave 192.168.69.82:6379 192.168.69.82 6379 @ mymaster 192.168.69.81 6379
37873:X 18 Jun 2023 21:34:17.510 * +slave-reconf-inprog slave 192.168.69.82:6379 192.168.69.82 6379 @ mymaster 192.168.69.81 6379
37873:X 18 Jun 2023 21:34:18.623 * +slave slave 192.168.69.82:6379 192.168.69.82 6379 @ mymaster 192.168.69.83 6379

Enfin, on peut le confirmer via la CLI redis sur db03 :

127.0.0.1:6379> INFO REPLICATION
# Replication
role:master
connected_slaves:1
slave0:ip=192.168.69.82,port=6379,state=online,offset=481996,lag=1

Félicitations, Redis s’est automatiquement reconfiguré sans intervention.

Que se passe-t-il désormais pour notre ancien master ? Au redémarrage de redis, il est devenu un slave. Cela se voit d’ailleurs dans les logs sentinel des autres serveurs. Il est possible de forcer via la CLI sentinel un failover vers un autre serveur :

127.0.0.1:26379> SENTINEL failover mymaster
OK
127.0.0.1:26379> INFO sentinel
# Sentinel
sentinel_masters:1
sentinel_tilt:0
sentinel_running_scripts:0
sentinel_scripts_queue_length:0
sentinel_simulate_failure_flags:0
master0:name=mymaster,status=ok,address=192.168.69.82:6379,slaves=2,sentinels=4

Il n’est toutefois pas possible d’indiquer expressément quel serveur sera promu. Une manière de le contrôler est d’utiliser la directive slave-priority pour favoriser un serveur avec une priorité plus faible.

4. HAProxy

Dans un monde idéal, votre client Redis aura un support de Sentinel. Il saura alors contacter sentinel pour connaître le master et s’y connecter. En récupérant la liste des serveurs, il sera en mesure de distribuer les requêtes en lecture sur les slaves pour répartir la charge. En cas d’anomalie du master, il saura enfin gérer le failover.

En pratique, on ne peut pas toujours connaître le niveau de support des clients notamment lorsque l’on fournit la plateforme à des développeurs tiers. Dans ce cas précis, je recommande d’utiliser le classique HAProxy qui sait tout faire en matière de répartition de charge, que je vous laisse installer via les packages de la distribution.

Le cas particulier qu’il faut néanmoins gérer c’est la détection du master. De manière simple, si le port de destination répond, on considère souvent que le service est fourni par le backend. Dans notre cas, il va falloir identifier le serveur dont le rôle est le master, souvenez-vous nous avions l’information avec INFO Replication avec la CLI Redis. Il faut donc définir un TCP Check qui réalisera une authentification, enverra cette même commande est aura comme backend active celui dont le rôle est master :

frontend ft_redis
  bind 0.0.0.0:6379 name redis
  default_backend bk_redis
 
backend bk_redis
  option tcp-check
  tcp-check send AUTH\ SECRET\r\n
  tcp-check send PING\r\n
  tcp-check expect string +PONG
  tcp-check send INFO\ Replication\r\n
  tcp-check expect string role:master
  tcp-check send QUIT\r\n
  tcp-check expect string +OK
  server r1 db01:6379 check inter 10s
  server r2 db02:6379 check inter 10s
  server r3 db03:6379 check inter 10s

Conclusion

C’est terminé pour ce tour d’horizon de la haute disponibilité d’un service Redis Replication avec Sentinel. J’invite le lecteur curieux à compléter ce parcours pour utiliser les ACL et ajouter ce niveau de sécurité. J’ai volontairement écarté ce point pour légèrement simplifier la configuration et rester ciblé sur le sujet.



Article rédigé par

Par le(s) même(s) auteur(s)

Galera, la solution pour des bases de données hautement disponibles

Magazine
Marque
Linux Pratique
Numéro
137
Mois de parution
mai 2023
Spécialité(s)
Résumé

Lorsqu’une application ne peut tolérer d’indisponibilités ou quand il devient nécessaire de fournir de la redondance au service de bases de données, il convient de mettre en place une architecture limitant les risques d’interruption de service. De même, le besoin de performance peut se faire sentir avec la croissance du nombre d’utilisateurs. Galera est une extension pour MariaDB aidant à résoudre ces deux situations.

Gérez votre stockage avec Stratis

Magazine
Marque
Linux Pratique
Numéro
128
Mois de parution
novembre 2021
Spécialité(s)
Résumé

Quand on parle de gestion du stockage local à un administrateur système Linux, il est généralement question de LVM et de filesystem XFS ou ext4. À un niveau plus évolué, on trouvera ZFS qui n'est pas libre ou bien Btrfs qui tarde à tenir ses promesses. Stratis a pour vocation de proposer quelque chose de neuf avec l’existant !

Déployer des environnements de développement avec Vagrant

Magazine
Marque
Linux Pratique
Numéro
127
Mois de parution
septembre 2021
Spécialité(s)
Résumé

Vagrant est un logiciel libre permettant de déployer rapidement des machines virtuelles. C'est un logiciel développé par la société Hashicorp, déjà connue pour d'autres logiciels comme Terraform, Packer ou Vault. Historiquement lié à VirtualBox, Vagrant s'est désormais largement ouvert à d'autres solutions de virtualisation comme Libvirt ou de conteneurisation comme Docker.

Les derniers articles Premiums

Les derniers articles Premium

La place de l’Intelligence Artificielle dans les entreprises

Magazine
Marque
Contenu Premium
Spécialité(s)
Résumé

L’intelligence artificielle est en train de redéfinir le paysage professionnel. De l’automatisation des tâches répétitives à la cybersécurité, en passant par l’analyse des données, l’IA s’immisce dans tous les aspects de l’entreprise moderne. Toutefois, cette révolution technologique soulève des questions éthiques et sociétales, notamment sur l’avenir des emplois. Cet article se penche sur l’évolution de l’IA, ses applications variées, et les enjeux qu’elle engendre dans le monde du travail.

Petit guide d’outils open source pour le télétravail

Magazine
Marque
Contenu Premium
Spécialité(s)
Résumé

Ah le Covid ! Si en cette période de nombreux cas resurgissent, ce n’est rien comparé aux vagues que nous avons connues en 2020 et 2021. Ce fléau a contraint une large partie de la population à faire ce que tout le monde connaît sous le nom de télétravail. Nous avons dû changer nos habitudes et avons dû apprendre à utiliser de nombreux outils collaboratifs, de visioconférence, etc., dont tout le monde n’était pas habitué. Dans cet article, nous passons en revue quelques outils open source utiles pour le travail à la maison. En effet, pour les adeptes du costume en haut et du pyjama en bas, la communauté open source s’est démenée pour proposer des alternatives aux outils propriétaires et payants.

Sécurisez vos applications web : comment Symfony vous protège des menaces courantes

Magazine
Marque
Contenu Premium
Spécialité(s)
Résumé

Les frameworks tels que Symfony ont bouleversé le développement web en apportant une structure solide et des outils performants. Malgré ces qualités, nous pouvons découvrir d’innombrables vulnérabilités. Cet article met le doigt sur les failles de sécurité les plus fréquentes qui affectent même les environnements les plus robustes. De l’injection de requêtes à distance à l’exécution de scripts malveillants, découvrez comment ces failles peuvent mettre en péril vos applications et, surtout, comment vous en prémunir.

Bash des temps modernes

Magazine
Marque
Contenu Premium
Spécialité(s)
Résumé

Les scripts Shell, et Bash spécifiquement, demeurent un standard, de facto, de notre industrie. Ils forment un composant primordial de toute distribution Linux, mais c’est aussi un outil de prédilection pour implémenter de nombreuses tâches d’automatisation, en particulier dans le « Cloud », par eux-mêmes ou conjointement à des solutions telles que Ansible. Pour toutes ces raisons et bien d’autres encore, savoir les concevoir de manière robuste et idempotente est crucial.

Les listes de lecture

8 article(s) - ajoutée le 01/07/2020
Découvrez notre sélection d'articles pour faire vos premiers pas avec les conteneurs, apprendre à les configurer et les utiliser au quotidien.
11 article(s) - ajoutée le 02/07/2020
Si vous recherchez quels sont les outils du DevOps et comment les utiliser, cette liste est faite pour vous.
8 article(s) - ajoutée le 02/07/2020
Il est essentiel d'effectuer des sauvegardes régulières de son travail pour éviter de perdre toutes ses données bêtement. De nombreux outils sont disponibles pour nous assister dans cette tâche.
Voir les 60 listes de lecture

Abonnez-vous maintenant

et profitez de tous les contenus en illimité

Je découvre les offres

Déjà abonné ? Connectez-vous