Aller au contenu principal

Administrer des clusters ETCD

· 10 minutes de lecture
Stéphane ROBERT
Consultant DevOps

Suite à l'introduction de Kubernetes où j'ai décrit le fonctionnement d'un cluster Kubernetes, je vais expliquer comment administrer le composant ETCD.

Etcd, qu'est-ce-que-c'est ?

Rappel : ETCD est une base de données distribuée de type clé-valeur, qui a été développée en "Go", le langage de programmation de Google. Dans un cluster Kubernetes, ETCD est chargé de stocker la configuration et les informations nécessaires au fonctionnement du cluster, c'est-à-dire de tous ses composants : les nœuds, les pods, les configs, les secrets, les rôles, les comptes, ...

Création d'un cluster Kubernetes avec Kind

Je vais utiliser des clusters [Kubernetes créés avec l'utilitaire Kind](nc un parfait candidat pour se préparer aux certifications CKA. En effet, kind intègre tous les composants contrairement à d’autres solutions comme minikube ou k3s.

En premier créer un fichier de config pour configurer un cluster avec trois master nodes et un worker. Pourquoi trois nodes et pas deux ? Parce qu'il est fortement conseiller de mettre en place des clusters avec un nombre impair de serveurs.

kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
  - role: control-plane
  - role: control-plane
  - role: control-plane
  - role: worker

Maintenant lançons la création du cluster avec la dernière version de Kubernetes (voir le lien ci-dessus) :

kind create cluster --config=cluster.yaml --name cluster-1 --image kindest/node:latest

 ✓ Ensuring node image (kindest/node:v1.23.1) 🖼
 ✓ Preparing nodes 📦 📦 📦 📦
 ✓ Writing configuration 📜
 ✓ Starting control-plane 🕹️
 ✓ Installing CNI 🔌
 ✓ Installing StorageClass 💾
 ✓ Joining worker nodes 🚜
Set kubectl context to "kind-cluster-1"
You can now use your cluster with:

kubectl cluster-info --context kind-cluster-1

Have a question, bug, or feature request? Let us know! https://kind.sigs.k8s.io/#community 🙂

Contrôlons nos nodes :

NAME                       STATUS   ROLES                  AGE   VERSION   INTERNAL-IP   EXTERNAL-IP   OS-IMAGE       KERNEL-VERSION                CONTAINER-RUNTIME
cluster-1-control-plane    Ready    control-plane,master   83m   v1.23.1   172.18.0.4    <none>        Ubuntu 21.04   4.18.0-348.2.1.el8_5.x86_64   containerd://1.5.2
cluster-1-control-plane2   Ready    control-plane,master   83m   v1.23.1   172.18.0.5    <none>        Ubuntu 21.04   4.18.0-348.2.1.el8_5.x86_64   containerd://1.5.2
cluster-1-control-plane3   Ready    control-plane,master   82m   v1.23.1   172.18.0.6    <none>        Ubuntu 21.04   4.18.0-348.2.1.el8_5.x86_64   containerd://1.5.2
cluster-1-worker           Ready    <none>                 82m   v1.23.1   172.18.0.3    <none>        Ubuntu 21.04   4.18.0-348.2.1.el8_5.x86_64   containerd://1.5.

Contrôlons que nous avons bien un cluster ETCD :

NAMESPACE            NAME                                               READY   STATUS    RESTARTS      AGE   IP           NODE                       NOMINATED NODE   READINESS GATES
kube-system          coredns-64897985d-6wznk                            1/1     Running   0             84m   10.244.0.2   cluster-1-control-plane    <none>           <none>
kube-system          coredns-64897985d-95x8p                            1/1     Running   0             84m   10.244.0.3   cluster-1-control-plane    <none>           <none>
kube-system          etcd-cluster-1-control-plane                       1/1     Running   0             84m   172.18.0.4   cluster-1-control-plane    <none>           <none>
kube-system          etcd-cluster-1-control-plane2                      1/1     Running   0             84m   172.18.0.5   cluster-1-control-plane2   <none>           <none>
kube-system          etcd-cluster-1-control-plane3                      1/1     Running   0             83m   172.18.0.6   cluster-1-control-plane3   <none>           <none>
kube-system          kindnet-fw86t                                      1/1     Running   0             84m   172.18.0.4   cluster-1-control-plane    <none>           <none>
kube-system          kindnet-k9mrt                                      1/1     Running   0             83m   172.18.0.6   cluster-1-control-plane3   <none>           <none>
kube-system          kindnet-m4k22                                      1/1     Running   0             84m   172.18.0.5   cluster-1-control-plane2   <none>           <none>
kube-system          kindnet-wmnp8                                      1/1     Running   0             83m   172.18.0.3   cluster-1-worker           <none>           <none>
kube-system          kube-apiserver-cluster-1-control-plane             1/1     Running   0             84m   172.18.0.4   cluster-1-control-plane    <none>           <none>
kube-system          kube-apiserver-cluster-1-control-plane2            1/1     Running   0             84m   172.18.0.5   cluster-1-control-plane2   <none>           <none>
kube-system          kube-apiserver-cluster-1-control-plane3            1/1     Running   1 (83m ago)   83m   172.18.0.6   cluster-1-control-plane3   <none>           <none>
kube-system          kube-controller-manager-cluster-1-control-plane    1/1     Running   1 (84m ago)   84m   172.18.0.4   cluster-1-control-plane    <none>           <none>
kube-system          kube-controller-manager-cluster-1-control-plane2   1/1     Running   0             84m   172.18.0.5   cluster-1-control-plane2   <none>           <none>
kube-system          kube-controller-manager-cluster-1-control-plane3   1/1     Running   0             82m   172.18.0.6   cluster-1-control-plane3   <none>           <none>
kube-system          kube-proxy-68hlh                                   1/1     Running   0             83m   172.18.0.3   cluster-1-worker           <none>           <none>
kube-system          kube-proxy-9ns2b                                   1/1     Running   0             84m   172.18.0.5   cluster-1-control-plane2   <none>           <none>
kube-system          kube-proxy-gzfwh                                   1/1     Running   0             83m   172.18.0.6   cluster-1-control-plane3   <none>           <none>
kube-system          kube-proxy-hwljt                                   1/1     Running   0             84m   172.18.0.4   cluster-1-control-plane    <none>           <none>
kube-system          kube-scheduler-cluster-1-control-plane             1/1     Running   1 (84m ago)   84m   172.18.0.4   cluster-1-control-plane    <none>           <none>
kube-system          kube-scheduler-cluster-1-control-plane2            1/1     Running   0             84m   172.18.0.5   cluster-1-control-plane2   <none>           <none>
kube-system          kube-scheduler-cluster-1-control-plane3            1/1     Running   0             83m   172.18.0.6   cluster-1-control-plane3   <none>           <none>
local-path-storage   local-path-provisioner-d6d9f7ffc-jznc9             1/1     Running   0             84m   10.244.1.2   cluster-1-control-plane2   <none>           <none>

Etcd est bien installé sur les 3 nodes masters !

Affichons la configuration du cluster etcd :

kubectl describe pod etcd-cluster-1-control-plane -n kube-system
Name:                 etcd-cluster-1-control-plane
Namespace:            kube-system
Priority:             2000001000
Priority Class Name:  system-node-critical
Node:                 cluster-1-control-plane/172.18.0.4
Start Time:           Mon, 03 Jan 2022 09:42:25 +0000
Labels:               component=etcd
                      tier=control-plane
Annotations:          kubeadm.kubernetes.io/etcd.advertise-client-urls: https://172.18.0.4:2379
                      kubernetes.io/config.hash: a6b006f7d9d8f4eb0ad9ccd08c1722ed
                      kubernetes.io/config.mirror: a6b006f7d9d8f4eb0ad9ccd08c1722ed
                      kubernetes.io/config.seen: 2022-01-03T09:42:03.405878931Z
                      kubernetes.io/config.source: file
                      seccomp.security.alpha.kubernetes.io/pod: runtime/default
Status:               Running
IP:                   172.18.0.4
IPs:
  IP:           172.18.0.4
Controlled By:  Node/cluster-1-control-plane
Containers:
  etcd:
    Container ID:  containerd://3b604561e0d1b49f05dc124361d8e4f7ac3403f3d6326ddccc8a6da3701ecfe3
    Image:         k8s.gcr.io/etcd:3.5.1-0
    Image ID:      sha256:25f8c7f3da61c2a810effe5fa779cf80ca171afb0adf94c7cb51eb9a8546629d
    Port:          <none>
    Host Port:     <none>
    Command:
      etcd
  --advertise-client-urls=https://172.18.0.4:2379
  --cert-file=/etc/kubernetes/pki/etcd/server.crt
  --client-cert-auth=true
  --data-dir=/var/lib/etcd
  --initial-advertise-peer-urls=https://172.18.0.4:2380
  --initial-cluster=cluster-1-control-plane=https://172.18.0.4:2380
  --key-file=/etc/kubernetes/pki/etcd/server.key
  --listen-client-urls=https://127.0.0.1:2379,https://172.18.0.4:2379
  --listen-metrics-urls=http://127.0.0.1:2381
  --listen-peer-urls=https://172.18.0.4:2380
  --name=cluster-1-control-plane
  --peer-cert-file=/etc/kubernetes/pki/etcd/peer.crt
  --peer-client-cert-auth=true
  --peer-key-file=/etc/kubernetes/pki/etcd/peer.key
  --peer-trusted-ca-file=/etc/kubernetes/pki/etcd/ca.crt
  --snapshot-count=10000
  --trusted-ca-file=/etc/kubernetes/pki/etcd/ca.crt
    State:          Running
      Started:      Mon, 03 Jan 2022 09:42:07 +0000
    Ready:          True
    Restart Count:  0
    Requests:
      cpu:        100m
      memory:     100Mi
    Liveness:     http-get http://127.0.0.1:2381/health delay=10s timeout=15s period=10s #success=1 #failure=8
    Startup:      http-get http://127.0.0.1:2381/health delay=10s timeout=15s period=10s #success=1 #failure=24
    Environment:  <none>
    Mounts:
      /etc/kubernetes/pki/etcd from etcd-certs (rw)
      /var/lib/etcd from etcd-data (rw)
Conditions:
  Type              Status
  Initialized       True
  Ready             True
  ContainersReady   True
  PodScheduled      True
Volumes:
  etcd-certs:
    Type:          HostPath (bare host directory volume)
    Path:          /etc/kubernetes/pki/etcd
    HostPathType:  DirectoryOrCreate
  etcd-data:
    Type:          HostPath (bare host directory volume)
    Path:          /var/lib/etcd
    HostPathType:  DirectoryOrCreate
QoS Class:         Burstable
Node-Selectors:    <none>
Tolerations:       :NoExecute op=Exists
Events:            <none>

Les informations importantes à retenir dont nous aurons besoin avec la commande etcdctl :

  --cert-file=/etc/kubernetes/pki/etcd/server.crt
  --key-file=/etc/kubernetes/pki/etcd/server.key
  --trusted-ca-file=/etc/kubernetes/pki/etcd/ca.crt

Connexion au serveur du Control-Plane

Ici comme utilisons kind, nous nous connecterons aux nodes non pas en SSH mais avec la commande docker. En effet, les conteneurs etcd sont vraiment léger et ne possède que peu de commandes.

docker exec -it cluster-1-control-plane bash
root@cluster-1-control-plane:/#

Quelques commandes pour démarrer avec ETCD

Installation de la CLI etcdctl

Nous devons installer la CLI etcdctl sur le master node (Toutes les commandes seront lancées depuis ce node) :

docker exec -it cluster-1-control-plane bash
apt update
apt install etcd-client
etcdctl --version
etcdctl version: 3.3.25

Nous utiliserons la version 3 de l'API d'ETCD :

export ETCDCTL_API=3

Contrôle de l'état de santé cluster ETCD

Dans un premier temps listons les membres du cluster ETCD :

etcdctl --endpoints 172.18.0.4:2379,172.18.0.5:2379,172.18.0.6:2379 --cert=/etc/kubernetes/pki/etcd/server.crt --key=/etc/kubernetes/pki/etcd/server.key --cacert=/etc/kubernetes/pki/etcd/ca.crt member list --write-out=table
+------------------+---------+--------------------------+-------------------------+-------------------------+
|        ID        | STATUS  |           NAME           |       PEER ADDRS        |      CLIENT ADDRS       |
+------------------+---------+--------------------------+-------------------------+-------------------------+
| 26aead55408e4ad0 | started | cluster-1-control-plane2 | https://172.18.0.5:2380 | https://172.18.0.5:2379 |
| 5320353a7d98bdee | started |  cluster-1-control-plane | https://172.18.0.4:2380 | https://172.18.0.4:2379 |
| 983acdc6eb33276f | started | cluster-1-control-plane3 | https://172.18.0.6:2380 | https://172.18.0.6:2379 |
+------------------+---------+--------------------------+-------------------------+-------------------------+

Nous retrouvons bien nos trois nœuds qui sont en bonne santé. Pour connaître le leader nous allons plutôt utiliser la commande endpoint status :

etcdctl --endpoints 172.18.0.4:2379,172.18.0.5:2379,172.18.0.6:2379 --cert=/etc/kubernetes/pki/etcd/server.crt --key=/etc/kubernetes/pki/etcd/server.key --cacert=/etc/kubernetes/pki/etcd/ca.crt endpoint status --write-out=table
+-----------------+------------------+---------+---------+-----------+-----------+------------+
|    ENDPOINT     |        ID        | VERSION | DB SIZE | IS LEADER | RAFT TERM | RAFT INDEX |
+-----------------+------------------+---------+---------+-----------+-----------+------------+
| 172.18.0.4:2379 | 5320353a7d98bdee |   3.5.1 |  3.2 MB |      true |         3 |      28711 |
| 172.18.0.5:2379 | 26aead55408e4ad0 |   3.5.1 |  3.2 MB |     false |         3 |      28712 |
| 172.18.0.6:2379 | 983acdc6eb33276f |   3.5.1 |  3.2 MB |     false |         3 |      28712 |
+-----------------+------------------+---------+---------+-----------+-----------+------------+

Le node 1 est le leader !

Si nous arrêtons un noeud le cluster etcd sera toujours fonctionnel. En effet, avec l'algorithme Raft nous avons droit à une défaillance sur les 3 serveurs. Si deux sont inaccessibles, le cluster passe en read-only et du coup le cluster kubernetes devient instable !

Nombre de noeudsTolérance de défaillance
10
20
31
41
52
62

Promouvoir un nœud

Il est possible à tout moment de promouvoir un nœud comme leader dans le cas ou vous devriez intervenir sur le noeud master actuel :

etcdctl --endpoints 172.18.0.4:2379,172.18.0.5:2379,172.18.0.6:2379 --cert=/etc/kubernetes/pki/etcd/server.crt --key=/etc/kubernetes/pki/etcd/server.key --cacert=/etc/kubernetes/pki/etcd/ca.crt move-leader 26aead55408e4ad0
Leadership transferred from 5320353a7d98bdee to 26aead55408e4ad0
etcdctl --endpoints 172.18.0.4:2379,172.18.0.5:2379,172.18.0.6:2379 --cert=/etc/kubernetes/pki/etcd/server.crt --key=/etc/kubernetes/pki/etcd/server.key --cacert=/etc/kubernetes/pki/etcd/ca.crt endpoint status --write-out=table
+-----------------+------------------+---------+---------+-----------+-----------+------------+
|    ENDPOINT     |        ID        | VERSION | DB SIZE | IS LEADER | RAFT TERM | RAFT INDEX |
+-----------------+------------------+---------+---------+-----------+-----------+------------+
| 172.18.0.4:2379 | 5320353a7d98bdee |   3.5.1 |  3.2 MB |     false |         4 |      34709 |
| 172.18.0.5:2379 | 26aead55408e4ad0 |   3.5.1 |  3.2 MB |      true |         4 |      34709 |
| 172.18.0.6:2379 | 983acdc6eb33276f |   3.5.1 |  3.2 MB |     false |         4 |      34709 |
+-----------------+------------------+---------+---------+-----------+-----------+------------+

Faire des backups/restaurations d'etcd

Parfois en développement, nous n'utiliserons pas forcément des manifests pour gérer nos déploiements. Donc pour revenir en arrière nous pouvons utiliser des sauvegardes de la base ETCD.

Il suffit d'utiliser les commandes snapshot save pour le backup et snapshot restore pour la restauration :

etcdctl --endpoints 172.18.0.4:2379,172.18.0.5:2379,172.18.0.6:2379 --cert=/etc/kubernetes/pki/etcd/server.crt --key=/etc/kubernetes/pki/etcd/server.key --cacert=/etc/kubernetes/pki/etcd/ca.crt snapshot save /tmp/snapshot-etcd-1.db

Pour restaurer :

etcdctl --endpoints 172.18.0.4:2379,172.18.0.5:2379,172.18.0.6:2379 --cert=/etc/kubernetes/pki/etcd/server.crt --key=/etc/kubernetes/pki/etcd/server.key --cacert=/etc/kubernetes/pki/etcd/ca.crt snapshot restore /tmp/snapshot-etcd-1.db

Jouer avec les clés/valeurs

Ajout de clé/valeur

La commande pour ajout une clé/valeur est put :

etcdctl --endpoints 172.18.0.4:2379,172.18.0.5:2379,172.18.0.6:2379 --cert=/etc/kubernetes/pki/etcd/server.crt --key=/etc/kubernetes/pki/etcd/server.key --cacert=/etc/kubernetes/pki/etcd/ca.crt put toto test

OK

Afficher une clé/valeur

La commande pour ajout une clé/valeur est get :

etcdctl --endpoints 172.18.0.4:2379,172.18.0.5:2379,172.18.0.6:2379 --cert=/etc/kubernetes/pki/etcd/server.crt --key=/etc/kubernetes/pki/etcd/server.key --cacert=/etc/kubernetes/pki/etcd/ca.crt get toto
toto
test

Par afficher que la valeur on peut ajouter --print-value-only et pour que la clé --keys-only. De même on peut demander l'affichage en json :

etcdctl --endpoints 172.18.0.4:2379,172.18.0.5:2379,172.18.0.6:2379 --cert=/etc/kubernetes/pki/etcd/server.crt --key=/etc/kubernetes/pki/etcd/server.key --cacert=/etc/kubernetes/pki/etcd/ca.crt get toto -w json
{
  "header": {
    "cluster_id": 13122874208970910000,
    "member_id": 2787355801055808000,
    "revision": 25139,
    "raft_term": 3
  },
  "kvs": [
    {
      "key": "dG90bw==",
      "create_revision": 8610,
      "mod_revision": 24658,
      "version": 2,
      "value": "dGVzdA=="
    }
  ],
  "count": 1
}

Les valeurs sont codées en base64.

echo dG90bw== | base64 -d
toto

Pour afficher toutes les clés commençant par le prefix :

etcdctl --endpoints 172.18.0.4:2379,172.18.0.5:2379,172.18.0.6:2379 --cert=/etc/kubernetes/pki/etcd/server.crt --key=/etc/kubernetes/pki/etcd/server.key --cacert=/etc/kubernetes/pki/etcd/ca.crt get --prefix tot
toto
titi

Pour afficher tous les objets Kubernetes de la base :

etcdctl --endpoints 172.18.0.4:2379,172.18.0.5:2379,172.18.0.6:2379 --cert=/etc/kubernetes/pki/etcd/server.crt --key=/etc/kubernetes/pki/etcd/server.key --cacert=/etc/kubernetes/pki/etcd/ca.crt get / --prefix --keys-only

...

/registry/services/specs/default/kubernetes
/registry/services/specs/kube-system/kube-dns
/registry/storageclasses/standard

Créer/Détruire un dossier

etcdctl --endpoints 172.18.0.4:2379,172.18.0.5:2379,172.18.0.6:2379 --cert=/etc/kubernetes/pki/etcd/server.crt --key=/etc/kubernetes/pki/etcd/server.key --cacert=/etc/kubernetes/pki/etcd/ca.crt mkdir /newpath

Lister toutes les clés

Il est possible d'afficher toutes les clés d'une base avec la commande suivante :

etcdctl --endpoints 172.18.0.4:2379,172.18.0.5:2379,172.18.0.6:2379 --cert=/etc/kubernetes/pki/etcd/server.crt --key=/etc/kubernetes/pki/etcd/server.key --cacert=/etc/kubernetes/pki/etcd/ca.crt get / --prefix --keys-only

Effacer une clé/valeur

La commande pour ajout une clé/valeur est del :

etcdctl  del toto
1

Scruter les changements d'une clé

La commande watch permet de monitorer une clé. Sur le premier nœud :

etcdctl --endpoints 172.18.0.4:2379,172.18.0.5:2379,172.18.0.6:2379 --cert=/etc/kubernetes/pki/etcd/server.crt --key=/etc/kubernetes/pki/etcd/server.key --cacert=/etc/kubernetes/pki/etcd/ca.crt watch toto

Dans une seconde session modifier la valeur ou détruisez-la !

etcdctl --endpoints 172.18.0.4:2379,172.18.0.5:2379,172.18.0.6:2379 --cert=/etc/kubernetes/pki/etcd/server.crt --key=/etc/kubernetes/pki/etcd/server.key --cacert=/etc/kubernetes/pki/etcd/ca.crt put toto titi
etcdctl --endpoints 172.18.0.4:2379,172.18.0.5:2379,172.18.0.6:2379 --cert=/etc/kubernetes/pki/etcd/server.crt --key=/etc/kubernetes/pki/etcd/server.key --cacert=/etc/kubernetes/pki/etcd/ca.crt del toto

Vous devriez voir apparaître des messages dans la première session :

etcdctl --endpoints 172.18.0.4:2379,172.18.0.5:2379,172.18.0.6:2379 --cert=/etc/kubernetes/pki/etcd/server.crt --key=/etc/kubernetes/pki/etcd/server.key --cacert=/etc/kubernetes/pki/etcd/ca.crt watch toto
PUT
toto
titi
DELETE
toto

Quelques Conseils

Les performances et la stabilité du cluster sont liés aux performances E/S réseau et disque des serveurs. Toute pénurie de ressources peut entraîner une expiration du délai de pulsation, provoquant une instabilité du cluster. Un etcd instable est un cluster ou aucun master n'est élu. Dans de telles circonstances, un cluster ne peut apporter aucune modification à son état actuel.

Il est conseillé d'installer les clusters etcd sur des machines dédiées ou des environnements isolés pour garantir les besoins en ressources.

La version minimale recommandée d'etcd à exécuter en production est 3.2.10+.

Sources

kubernetes.io Documentation etcdctl