Module R57
Dalibo SCOP
24.09
29 août 2024
Formation | Module R57 |
Titre | etcd : Architecture et fonctionnement |
Révision | 24.09 |
https://dali.bo/r57_pdf | |
EPUB | https://dali.bo/r57_epub |
HTML | https://dali.bo/r57_html |
Slides | https://dali.bo/r57_slides |
TP | https://dali.bo/r57_tp |
TP (solutions) | https://dali.bo/r57_solutions |
Cette formation est sous licence CC-BY-NC-SA. Vous êtes libre de la redistribuer et/ou modifier aux conditions suivantes :
PostgreSQL® Postgres® et le logo Slonik sont des marques déposées par PostgreSQL Community Association of Canada.
Ce document ne couvre que les versions supportées de PostgreSQL au moment de sa rédaction, soit les versions 12 à 16.
Lors d’une élection:
quorum = (nombre de nœuds / 2) + 1
nombre de nœuds - quorum
Nombre de nœuds | Majorité | Tolérance de panne |
---|---|---|
2 | 2 | 0 |
⇒ 3 | 2 | 1 |
4 | 3 | 1 |
⇒ 5 | 3 | 2 |
6 | 4 | 2 |
⇒ 7 | 4 | 3 |
8 | 5 | 3 |
Démo interactive :
etcdctl
(ETCDCTL_API
)fio
dnf install etcd
apt-get install etcd
(Debian 11)apt-get install etcd-server etcd-client
(Debian
12)Trois méthodes de configuration différentes:
etcd
etcd
--config-file
Configurations employées par les principaux services etcd:
etcd.service
/etc/etcd/etcd.conf
(paquet PGDG Red Hat &
dérivées)/etc/default/etcd
(Debian et dérivées)systemctl start etcd
etcdctl
data-dir
get
, put
,
del
(ou l’API)etcdctl txn
)etcdctl auth enable
user
: pour l’authentification
etcdctl user [add|del|get|list]
etcdctl user [grant-role|revoke-role]
role
: conteneur pour les droits, assigné aux
utilisateurs
etcdctl role [add|del|get|list]
etcdctl role [grant-permission|revoke-permission]
--cert-file
--key-file
--client-cert-auth
--trusted-ca-file
--peer-cert-file
--peer-key-file
--peer-client-cert-auth
--peer-trusted-ca-file
etcdctl … snapshot save …
$ETCD_DATA_DIRECTORY/member/snap/db
)etcdctl snapshot restore …
Pour remplacer un membre sans risquer de perdre le quorum :
/debug
/metrics
/health
Nous allons utiliser le simulateur Raftscope.
Les 5 nœuds portent tous le dernier numéro de mandat qu’ils ont connu. Le leader est cerclé de noir.
Le timeout de chaque follower se décrémente en permanence et est réinitialisé quand un message de heart beat du leader est reçu.
Le journal de chaque nœud est visible sur la droite pour suivre la propagation des informations de chaque request.
Il est possible de mettre en pause, d’accélérer ou ralentir.
Les actions se font par clic droit sur chaque nœud :
e1
,
e2
et e3
.e1
, e2
et e3
.e1
,
e2
et e3
.e1
, e2
et e3
.Ces exercices utilisent l’API v3 d’etcd.
But : manipuler la base de données distribuée d’Etcd.
etcdctl put
pour écrire une clef foo
à la valeur bar
.baz
.food
contenant les
clés/valeurs poisson: bar
et vin: blanc
.food
en exigeant une réponse d’un quorum.But : constater le comportement d’Etcd conforme à l’algorithme Raft.
virsh suspend <nom machine>
.Les nœuds portent au départ le numéro de mandat 1, il n’y a pas de leader :
Il faut attendre que l’un des nœuds ait atteint son timeout pour qu’il propose une élection. Ici, c’est le nœud S2 qui déclenche l’élection :
Les petites pastilles sur ce nœud S2 représentent le nombre de votes lui étant favorable. Un vote sur cinq est pour le moment validé : le sien. Les autres nœuds de l’agrégat reçoivent donc la candidature de S2 et y répondent tous favorablement :
Nous voyons le nombre de vote augmenter au fur et à mesure que S2 les reçoit :
Finalement, S2 remporte l’élection, crée le mandat n°2 et envoie son premier message de keep alive :
Les autres se raccrochent à lui et entretiennent chacun leur time out.
Le même phénomène se produit et l’on arrive au mandat 3. Ici, le timeout du nœud S1 expire en premier :
S1 déclenche une élection :
Les autres nœuds accordent leur vote à S1, sauf S2 qui est éteint :
S1 devient leader :
Elles apparaissent sur la droite dans le journal au fur et à mesure que le leader les diffuse. La diffusion se fait en plusieurs étapes. Écrite et émission depuis S1 :
Bonne réception des nœuds S3, S4 et S5 :
S0 ayant reçu une majorité d’acquittement, il valide la valeur auprès des autres nœuds :
Tous les nœuds ont validé la valeur :
Celui-ci redémarre à 2 (en tant que follower) :
Il bascule sur le mandat 3 au premier heart beat reçu, et commence à remplir son journal :
Il rattrape ensuite son journal avec autant d’échange avec le leader que nécessaire :
Le quorum demeure au sein du cluster, il est donc toujours possible de déclencher une élection. Comme précédemment, les trois nœuds restants s’accordent sur un leader. Dans notre exemple, tous les nœuds déclenchent leur propre élection en même temps :
Chaque nœud ayant voté pour lui-même, il n’y a pas de consensus sur le nœud à élire, les nœuds redeviennent followers. L’un d’entre eux voit son timeout atteint, ce qui donne lieu à une seconde élection :
Elle conduit ici à l’élection du nœud S2 :
La tolérance de panne est maintenant nulle.
Le cluster continue de fonctionner : on peut lui soumettre des écritures (requests) :
Elles sont bien répliquées mais ne sont pas exécutées (ou commitées) par le leader :
Pour cela, il faut que les écritures aient été répliquées vers la majorité des nœuds du cluster. Cela signifie que le client reste en attente de confirmation du traitement de sa demande.
L’un des deux nœuds survivants lance une élection :
L’autre le suit :
Mais comme le quorum de 3 nœuds (moitié de 5 plus 1) n’est pas obtenu, l’élection échoue. Les time out continuent d’expirer et de nouvelles élections sont lancées :
Faute de leader, il devient impossible de soumettre des écritures. Tout client s’appuyant dessus reçoit une erreur. Il faut attendre le retour en ligne d’un nœud et l’élection d’un nouveau leader.
e1
,
e2
et e3
.Les paquets etcd sont disponibles dans les dépôts officiels de Debian
12. L’installation consiste simplement à installer les deux paquets
etcd-server
et etcd-client
:
# apt-get install -y etcd-server etcd-client
Afin de respecter la politique des paquets Debian, l’installation du
paquet etcd-server
crée automatiquement une instance etcd
locale. Nous devons la détruire avant de pouvoir construire notre
cluster etcd à trois nœuds.
Sur chaque serveur etcd, exécuter les commandes suivantes :
# systemctl stop etcd.service
# rm -Rf /var/lib/etcd/default
e1
, e2
et e3
.La configuration sous Debian se situe dans le fichier
/etc/default/etcd
où doivent être renseignés les paramètres
etcd sous leur forme de variables d’environnements.
Voici le fichier de configuration commenté du nœud e1
,
veillez à adapter toutes les adresses IP
10.x.y.z
:
# nom du nœud au sein du cluster
ETCD_NAME=e1
# emplacement du répertoire de données
ETCD_DATA_DIR=/var/lib/etcd/acme
# interface et port d'écoute des autres nœuds
ETCD_LISTEN_PEER_URLS=http://10.0.0.11:2380
# interfaces et port d'écoute des clients
ETCD_LISTEN_CLIENT_URLS=http://10.0.0.11:2379,http://127.0.0.1:2379,http://[::1]:2379
# interface et port d'écoute à communiquer aux clients
ETCD_ADVERTISE_CLIENT_URLS=http://10.0.0.11:2379
#######################################
## Initialisation du cluster et du nœud
# création du nœud au sein d'un nouveau cluster
ETCD_INITIAL_CLUSTER_STATE=new
# interface et port à communiquer aux autres nœuds
ETCD_INITIAL_ADVERTISE_PEER_URLS=http://10.0.0.11:2380
# liste des nœuds composant le cluster
ETCD_INITIAL_CLUSTER=e1=http://10.0.0.11:2380,e2=http://10.0.0.12:2380,e3=http://10.0.0.13:2380
Une fois les trois fichiers de configuration établis, nous pouvons démarrer le cluster en exécutant la commande suivante sur tous les serveurs etcd :
# systemctl start etcd
La commande suivante doit désormais fonctionner sur n’importe quel nœud :
$ etcdctl -wjson endpoint status | jq
[
{
"Endpoint": "127.0.0.1:2379",
"Status": {
"header": {
"cluster_id": 11628162814576028000,
"member_id": 13786016400334385000,
"revision": 95,
"raft_term": 24
},
"version": "3.4.23",
"dbSize": 61440,
"leader": 2266733444126347800,
"raftIndex": 112,
"raftTerm": 24,
"raftAppliedIndex": 112,
"dbSizeInUse": 53248
}
}
]
L’état du cluster est visible grace à la commande suivante :
+-----------------------+------------------+---------+---------+-----------+[..]+--------+
| ENDPOINT | ID | VERSION | DB SIZE | IS LEADER |[..]| ERRORS |
+-----------------------+------------------+---------+---------+-----------+[..]+--------+
| http://10.0.0.11:2379 | 9e80988e833ccb43 | 3.5.13 | 20 kB | false |[..]| |
| http://10.0.0.13:2379 | a10d8f7920cc71c7 | 3.5.13 | 29 kB | true |[..]| |
| http://10.0.0.12:2379 | abdc532bc0516b2d | 3.5.13 | 20 kB | false |[..]| |
+-----------------------+------------------+---------+---------+-----------+[..]+--------+
e1
,
e2
et e3
.Les paquets etcd sont disponibles depuis les dépôts PGDG pour les distributions Red Hat 9 & dérivées (comme ici Rocky Linux 9). Il nous faut donc au préalable configurer ce dépôt :
# dnf install -y https://download.postgresql.org/pub/repos/yum/reporpms/EL-9-x86_64/\
pgdg-redhat-repo-latest.noarch.rpm
Il est désormais possible d’installer le paquet etcd
en
activant le dépôt pgdg-rhel9-extras
:
# dnf --enablerepo=pgdg-rhel9-extras install -y etcd
e1
, e2
et e3
.La configuration sur cette distribution se situe dans le fichier
/etc/etcd/etcd.conf
où sont renseignés les paramètres etcd
sous leur forme de variables d’environnements.
Ci-après les différents paramètres à modifier pour le nœud
e1
, veillez à adapter toutes les adresses
IP 10.x.y.z
:
ETCD_NAME=e1
ETCD_DATA_DIR=/var/lib/etcd/acme
ETCD_LISTEN_PEER_URLS=http://10.0.0.11:2380
ETCD_LISTEN_CLIENT_URLS=http://10.0.0.11:2379,http://127.0.0.1:2379,http://[::1]:2379
ETCD_ADVERTISE_CLIENT_URLS=http://10.0.0.11:2379
ETCD_INITIAL_CLUSTER_STATE=new
ETCD_INITIAL_ADVERTISE_PEER_URLS=http://10.0.0.11:2380
ETCD_INITIAL_CLUSTER=e1=http://10.0.0.11:2380,e2=http://10.0.0.12:2380,e3=http://10.0.0.13:2380
Une fois les trois fichiers de configuration établis, nous pouvons démarrer le cluster en exécutant la commande suivante sur tous les serveurs etcd :
# systemctl start etcd
Afin qu’etcd démarre automatiquement avec le serveur, il est nécessaire d’activer le service :
# systemctl enable etcd
Une fois les trois fichiers de configuration établis, la commande suivante doit fonctionner sur n’importe quel nœud :
$ etcdctl -wjson endpoint status | jq
[
{
"Endpoint": "127.0.0.1:2379",
"Status": {
"header": {
"cluster_id": 11628162814576028000,
"member_id": 13786016400334385000,
"revision": 95,
"raft_term": 24
},
"version": "3.4.23",
"dbSize": 61440,
"leader": 2266733444126347800,
"raftIndex": 112,
"raftTerm": 24,
"raftAppliedIndex": 112,
"dbSizeInUse": 53248
}
}
]
L’état du cluster est visible grace à la commande suivante :
+-----------------------+------------------+---------+---------+-----------+[..]+--------+
| ENDPOINT | ID | VERSION | DB SIZE | IS LEADER |[..]| ERRORS |
+-----------------------+------------------+---------+---------+-----------+[..]+--------+
| http://10.0.0.11:2379 | 9e80988e833ccb43 | 3.5.13 | 20 kB | false |[..]| |
| http://10.0.0.13:2379 | a10d8f7920cc71c7 | 3.5.13 | 29 kB | true |[..]| |
| http://10.0.0.12:2379 | abdc532bc0516b2d | 3.5.13 | 20 kB | false |[..]| |
+-----------------------+------------------+---------+---------+-----------+[..]+--------+
Ces exercices utilisent l’API v3 d’etcd.
But : manipuler la base de données distribuée d’Etcd.
etcdctl put
pour écrire une clef foo
à la valeur bar
.$ etcdctl put foo bar
OK
$ etcdctl get foo
foo
bar
baz
.$ etcdctl put foo baz
OK
food
contenant les
clés/valeurs poisson: bar
et vin: blanc
.$ etcdctl put food/poisson bar
OK
$ etcdctl put food/vin blanc
OK
$ etcdctl get --keys-only --prefix food/
food/poisson
food/vin
food
en exigeant une réponse d’un quorum.$ etcdctl get --consistency="l" --keys-only --prefix food/
/food/poisson
/food/vin
But : constater le comportement d’Etcd conforme à l’algorithme Raft.
virsh suspend <nom machine>
.Le leader peut être identifié avec la commande suivante :
# etcdctl -wtable --endpoints http://10.0.0.11:2379,http://10.0.0.12:2379,http://10.0.0.13:2379 endpoint status
Dans notre correctif, e1
est actuellement
leader. Dans une fenêtre sur e2
et
e3
, laisser défiler le journal :
# journalctl -fu etcd
Depuis une session dans le serveur hôte, interroger e2
ou e3
continuellement à propos de la santé de l’agrégat
avec par exemple :
$ watch -n1 "curl -s -XGET http://10.0.0.12:2379/health | jq"
Depuis le serveur hôte, cette commande interrompt brutalement la
machine virtuelle e1
(mais sans la détruire) :
# virsh destroy e1
Domain 'e1' destroyed
Il est aussi possible de suspendre la machine avec la commande suivante :
# virsh suspend e1
Domain 'e1' suspended
La commande inverse est alors virsh resume e1
.
Notez que la machine est suspendue, donc encore présente,
mais plus du tout exécutée par l’hyperviseur. Cette présence inactive
peut parfois créer des situations déstabilisantes pour les autres
machines virtuelles ayant toujours des connexions TCP en suspend. Aussi,
après son réveil, sauf présence de chrony
ou
ntpsec
, l’horloge de cette machine virtuelle nécessite une
intervention manuelle afin de la resynchroniser.
Dans les traces de e3, nous trouvons :
INFO: 49fc71338f77c1c4 is starting a new election at term 3
INFO: 49fc71338f77c1c4 became candidate at term 4
INFO: 49fc71338f77c1c4 received MsgVoteResp from 49fc71338f77c1c4 at term 4
INFO: 49fc71338f77c1c4 [logterm: 3, index: 9] sent MsgVote request to 97e570ef03022438 at term 4
INFO: 49fc71338f77c1c4 [logterm: 3, index: 9] sent MsgVote request to be3b2f45686f7694 at term 4
INFO: raft.node: 49fc71338f77c1c4 lost leader be3b2f45686f7694 at term 4
L’identifiant 49fc71338f77c1c4
est celui de
e3
lui-même. Ces traces nous indiquent :
e3
pour lui-même ;e1
et e2
.e1
(identifiant
be3b2f45686f7694
) durant le mandat n°4Suivent alors les messages suivants:
INFO: 49fc71338f77c1c4 received MsgVoteResp from 97e570ef03022438 at term 4
INFO: 49fc71338f77c1c4 has received 2 MsgVoteResp votes and 0 vote rejections
INFO: 49fc71338f77c1c4 became leader at term 4
INFO: raft.node: 49fc71338f77c1c4 elected leader 49fc71338f77c1c4 at term 4
Seul e2
(ici 97e570ef03022438
) répond au
vote, mais cette réponse suffit à atteindre le quorum (de 2 sur 3) et
valider l’élection de e3
comme nouveau leader.
En interrogeant l’état de e3, nous confirmons bien qu’il est leader :
$ etcdctl -wtable endpoint status
+----------------+------------------+---------+---------+-----------+-[…]
| ENDPOINT | ID | VERSION | DB SIZE | IS LEADER | […]
+----------------+------------------+---------+---------+-----------+-[…]
| 127.0.0.1:2379 | 49fc71338f77c1c4 | 3.4.23 | 20 kB | true | […]
+----------------+------------------+---------+---------+-----------+-[…]
L’état de l’agrégat tombe en erreur suite à cette perte du quorum :
Dans les traces de e2
, nous constatons qu’il tente en
boucle une élection, envoie des messages mais faute de réponse,
n’obtient jamais d’accord pour l’élection :
15:36:31 INFO: 97e570ef03022438 is starting a new election at term 4
15:36:31 INFO: 97e570ef03022438 became candidate at term 5
15:36:31 INFO: 97e570ef03022438 received MsgVoteResp from 97e570ef03022438 at term 5
15:36:31 INFO: 97e570ef03022438 [logterm: 4, index: 1192] sent MsgVote request to 49fc71338f77c1c4 at term 5
15:36:31 INFO: 97e570ef03022438 [logterm: 4, index: 1192] sent MsgVote request to be3b2f45686f7694 at term 5
15:36:31 INFO: raft.node: 97e570ef03022438 lost leader 49fc71338f77c1c4 at term 5
15:36:33 INFO: 97e570ef03022438 is starting a new election at term 5
15:36:33 INFO: 97e570ef03022438 became candidate at term 6
15:36:33 INFO: 97e570ef03022438 received MsgVoteResp from 97e570ef03022438 at term 6
15:36:33 INFO: 97e570ef03022438 [logterm: 4, index: 1192] sent MsgVote request to 49fc71338f77c1c4 at term 6
15:36:33 INFO: 97e570ef03022438 [logterm: 4, index: 1192] sent MsgVote request to be3b2f45686f7694 at term 6
15:36:34 INFO: 97e570ef03022438 is starting a new election at term 6
15:36:34 INFO: 97e570ef03022438 became candidate at term 7
15:36:34 INFO: 97e570ef03022438 received MsgVoteResp from 97e570ef03022438 at term 7
15:36:34 INFO: 97e570ef03022438 [logterm: 4, index: 1192] sent MsgVote request to 49fc71338f77c1c4 at term 7
15:36:34 INFO: 97e570ef03022438 [logterm: 4, index: 1192] sent MsgVote request to be3b2f45686f7694 at term 7
La situation revient à la normale dès qu’un des deux autres revient
en ligne avec par exemple la commande virsh start e3
.