Aller au contenu

1 - Guides d'authentification

Guide d’authentification et de contrôle d’accès basé sur les rôles pour etcd

1.1 - Authentification

Guide d’authentification d’un cluster etcd

auth,user,role pour l’authentification :

export ETCDCTL_API=3
ENDPOINTS=localhost:2379

etcdctl --endpoints=${ENDPOINTS} role add root
etcdctl --endpoints=${ENDPOINTS} role get root

etcdctl --endpoints=${ENDPOINTS} user add root
etcdctl --endpoints=${ENDPOINTS} user grant-role root root
etcdctl --endpoints=${ENDPOINTS} user get root

etcdctl --endpoints=${ENDPOINTS} role add role0
etcdctl --endpoints=${ENDPOINTS} role grant-permission role0 readwrite foo
etcdctl --endpoints=${ENDPOINTS} user add user0
etcdctl --endpoints=${ENDPOINTS} user grant-role user0 role0

etcdctl --endpoints=${ENDPOINTS} auth enable
# now all client requests go through auth

etcdctl --endpoints=${ENDPOINTS} --user=user0:123 put foo bar
etcdctl --endpoints=${ENDPOINTS} get foo
# permission denied, user name is empty because the request does not issue an authentication request
etcdctl --endpoints=${ENDPOINTS} --user=user0:123 get foo
# user0 can read the key foo
etcdctl --endpoints=${ENDPOINTS} --user=user0:123 get foo1

Note :

Il s’agit simplement d’un squelette qui doit être complété et mis à jour avec des informations supplémentaires sur l’authentification. Le texte ci-dessus n’est qu’un exemple de code.

1.2 - Contrôle d'accès basé sur les rôles

Guide d’authentification basique et de contrôle d’accès basé sur les rôles

Aperçu

L’authentification a été ajoutée à etcd 2.1. L’API v3 d’etcd a légèrement modifié l’API et l’interface utilisateur de la fonctionnalité d’authentification afin de mieux s’adapter au nouveau modèle de données. Ce guide vise à aider les utilisateurs à configurer une authentification basique et un contrôle d’accès basé sur les rôles dans etcd v3.

Utilisateurs et rôles spéciaux

Il existe un utilisateur spécial, root, et un rôle spécial, root.

Utilisateur root

L’utilisateur root, qui dispose d’un accès complet à etcd, doit être créé avant d’activer l’authentification. L’idée derrière l’utilisateur root est d’assurer des opérations administratives : gestion des rôles et des utilisateurs ordinaires. L’utilisateur root doit posséder le rôle root et est autorisé à modifier tout élément à l’intérieur d’etcd.

Rôle root

Le rôle root peut être attribué à tout utilisateur, en plus de l’utilisateur racine. Un utilisateur disposant du rôle root dispose à la fois d’un accès en lecture-écriture global et des autorisations pour mettre à jour la configuration d’authentification du cluster. En outre, le rôle root accorde les privilèges nécessaires à la maintenance générale du cluster, notamment la modification de la composition du cluster, la défragmentation du magasin et la prise d’instantanés.

Travail avec les utilisateurs

Le sous-commande user pour etcdctl gère toutes les opérations relatives aux comptes utilisateurs.

Une liste des utilisateurs peut être obtenue avec :

$ etcdctl user list

Créer un utilisateur est aussi simple que

$ etcdctl user add myusername

La création d’un nouvel utilisateur demande de saisir un nouveau mot de passe. Le mot de passe peut être fourni depuis l’entrée standard lorsque l’option --interactive=false est utilisée. --new-user-password peut également être utilisé pour fournir le mot de passe.

La création d’un utilisateur qui ne peut pas être authentifié avec un mot de passe est également possible, comme indiqué ci-dessous :

$ etcdctl user add myusername --no-password

Un tel utilisateur ne peut être authentifié que par TLS Common Name .

Note

etcd ne prend pas en charge l’authentification avec un mot de passe vide via --user username:. Par exemple, un utilisateur créé avec un mot de passe vide, tel que etcdctl user add anonymous:'', ne peut pas s’authentifier par des requêtes username/password et les requêtes telles que etcdctl --user anonymous: get foo échouent avec user name is empty.

Les rôles peuvent être attribués ou retirés à un utilisateur avec :

$ etcdctl user grant-role myusername foo
$ etcdctl user revoke-role myusername bar

Les paramètres de l’utilisateur peuvent être inspectés à l’aide de :

$ etcdctl user get myusername

Et le mot de passe d’un utilisateur peut être modifié avec

$ etcdctl user passwd myusername

Changer le mot de passe provoquera une nouvelle demande de mot de passe. Le mot de passe peut être fourni depuis l’entrée standard lorsque l’option --interactive=false est utilisée.

Supprimez un compte avec :

$ etcdctl user delete myusername

Travail avec les rôles

Le sous-commande role pour etcdctl gère toutes les opérations relatives aux contrôles d’accès pour des rôles spécifiques, tels qu’ils ont été attribués à des utilisateurs individuels.

Lister les rôles avec :

$ etcdctl role list

Créez un nouveau rôle avec :

$ etcdctl role add myrolename

Un rôle n’a pas de mot de passe ; il définit simplement un nouvel ensemble de droits d’accès.

Les rôles ont accès à une clé unique ou à une plage de clés.

La plage peut être spécifiée sous la forme d’un intervalle [clé_de_depart, clé_de_fin) où la clé_de_depart doit être strictement inférieure à la clé_de_fin selon un ordre alphabétique.

L’accès peut être accordé en lecture, écriture ou les deux, comme dans les exemples suivants :

# Give read access to a key /foo
$ etcdctl role grant-permission myrolename read /foo

# Give read access to keys with a prefix /foo/. The prefix is equal to the range [/foo/, /foo0)
$ etcdctl role grant-permission myrolename --prefix=true read /foo/

# Give write-only access to the key at /foo/bar
$ etcdctl role grant-permission myrolename write /foo/bar

# Give full access to keys in a range of [key1, key5)
$ etcdctl role grant-permission myrolename readwrite key1 key5

# Give full access to keys with a prefix /pub/
$ etcdctl role grant-permission myrolename --prefix=true readwrite /pub/

Pour voir ce qui est accordé, nous pouvons consulter le rôle à tout moment :

$ etcdctl role get myrolename

La révocation des autorisations s’effectue de la même manière logique :

$ etcdctl role revoke-permission myrolename /foo/bar

Comme pour supprimer un rôle entièrement :

$ etcdctl role delete myrolename

Activer l’authentification

Les étapes minimales pour activer l’authentification sont les suivantes. L’administrateur peut configurer les utilisateurs et les rôles avant ou après l’activation de l’authentification, selon son choix.

Assurez-vous que l’utilisateur racine est créé :

$ etcdctl user add root
Password of root:

Activer l’authentification :

$ etcdctl auth enable

Après cela, etcd fonctionne avec l’authentification activée. Pour la désactiver pour une raison quelconque, utilisez la commande inverse :

$ etcdctl --user root:rootpw auth disable

Portée de sécurité de l’authentification

Lorsque l’authentification est activée avec etcdctl auth enable, elle protège les opérations de l’API gRPC V3 (get, put, delete, surveillance, etc.).

Les points de terminaison HTTP /metrics et /health fonctionnent sur un gestionnaire distinct et ne sont pas protégés par l’authentification RBAC V3. Ce design permet à Prometheus et aux équilibreurs de charge de récupérer les métriques sans nécessiter d’authentification gRPC, tout en maintenant la protection des données clé-valeur.

Pour sécuriser ces points de visualisation :

  • Activez le mTLS avec --cert-file, --key-file et --client-cert-auth
  • Ou liez les métriques à une interface privée en utilisant --listen-metrics-urls
  • Ou utilisez des règles de réseau policies/firewall pour restreindre l’accès

Utilisation de etcdctl pour l’authentification

etcdctl prend en charge un indicateur similaire à curl pour l’authentification.

$ etcdctl --user user:password get foo

Le mot de passe peut être fourni à partir d’une invite :

$ etcdctl --user user get foo

Le mot de passe peut également être fourni via une option de ligne de commande --password :

$ etcdctl --user user --password password get foo

Sinon, toutes les commandes etcdctl restent identiques. Les utilisateurs et rôles peuvent toujours être créés et modifiés, mais nécessitent une authentification par un utilisateur disposant du rôle root.

Utilisation du nom commun TLS

À compter de la version v3.2, si un serveur etcd est lancé avec l’option --client-cert-auth=true, le champ Common Name (CN) du certificat TLS du client sera utilisé comme utilisateur etcd. Dans ce cas, le nom commun sert à authentifier l’utilisateur, et le client n’a pas besoin de mot de passe. Notez que si les deux conditions suivantes sont remplies : 1. --client-cert-auth=true est fourni et le CN est fourni par le client, et 2. le nom d’utilisateur et le mot de passe sont fournis par le client, l’authentification basée sur le nom d’utilisateur et le mot de passe est prioritaire. Notez que cette fonctionnalité ne peut pas être utilisée avec gRPC-proxy ni avec gRPC-gateway. Cela est dû au fait que gRPC-proxy termine la connexion TLS provenant de son client, si bien que tous les clients partagent un certificat du proxy. gRPC-gateway utilise une connexion TLS interne pour transformer une requête HTTP en requête gRPC, ce qui entraîne la même limitation. Par conséquent, les clients ne peuvent pas transmettre correctement leur CN au serveur. gRPC-proxy provoquera une erreur et s’arrêtera si le certificat fourni a un CN non vide. gRPC-proxy renvoie une erreur indiquant que le client possède un CN non vide dans son certificat.

Remarques sur la force du mot de passe

Les API etcdctl et etcd n’imposent aucune longueur de mot de passe particulière lors de la création d’un utilisateur ou de la mise à jour de son mot de passe. Il incombe à l’administrateur d’appliquer ces exigences. Pour réduire les risques liés aux mots de passe faibles, utilisez l’authentification fondée sur le nom commun TLS ainsi que des utilisateurs créés avec l’option --no-password.

2 - Options de configuration

etcd fichiers de configuration, indicateurs et variables d’environnement

Vous pouvez configurer etcd à l’aide des éléments suivants :

  • Options en ligne de commande
  • Variables d’environnement : chaque option a une variable d’environnement correspondante dont le nom est identique, mais préfixé par ETCD_ et écrit en majuscules et [en notation snake case][]. Par exemple, --some-flag sera ETCD_SOME_FLAG.
  • Fichier de configuration
Avertissement

Avertissement : Si vous mélangez des options de configuration, les règles suivantes s’appliquent.

  • Les indicateurs en ligne de commande ont la priorité sur les variables d’environnement.
  • Si vous fournissez un fichier de configuration, tous les indicateurs en ligne de commande et les variables d’environnement sont ignorés.

Drapeaux de ligne de commande

Les indicateurs sont présentés ci-dessous selon le format --flag-name DEFAULT_VALUE.

La liste des indicateurs fournie ci-dessous peut ne pas être à jour en raison des modifications en cours de développement. Pour obtenir la liste des indicateurs disponibles, exécutez etcd --help ou consultez l’aide de [etcd][].

Note

Remarque : Pour plus de détails concernant les indicateurs nouveaux, mis à jour ou obsolètes de la version 3.7, consultez [CHANGELOG-3.7.md][changelog].

[journal des modifications] : https://github.com/etcd-io/etcd/blob/main/CHANGELOG/CHANGELOG-3.7.md

membre

--name 'default'
  Human-readable name for this member.
--data-dir '${name}.etcd'
  Path to the data directory.
--wal-dir ''
  Path to the dedicated wal directory.
--snapshot-count '10000'
  Number of committed transactions to trigger a snapshot to disk.
--heartbeat-interval '100'
  Time (in milliseconds) of a heartbeat interval.
--election-timeout '1000'
  Time (in milliseconds) for an election to timeout. See tuning documentation for details.
--initial-election-tick-advance 'true'
  Whether to fast-forward initial election ticks on boot for faster election.
--listen-peer-urls 'http://localhost:2380'
  List of URLs to listen on for peer traffic.
--listen-client-urls 'http://localhost:2379'
  List of URLs to listen on for client grpc traffic and http as long as --listen-client-http-urls is not specified.
--listen-client-http-urls ''
  List of URLs to listen on for http only client traffic. Enabling this flag removes http services from --listen-client-urls.
--max-snapshots '5'
  Maximum number of snapshot files to retain (0 is unlimited).
--max-wals '5'
  Maximum number of wal files to retain (0 is unlimited).
--memory-mlock
  Enable to enforce etcd pages (in particular bbolt) to stay in RAM.
--quota-backend-bytes '0'
  Raise alarms when backend size exceeds the given quota (0 defaults to low space quota).
--backend-bbolt-freelist-type 'map'
  BackendFreelistType specifies the type of freelist that boltdb backend uses(array and map are supported types).
--backend-batch-interval ''
  BackendBatchInterval is the maximum time before commit the backend transaction.
--backend-batch-limit '0'
  BackendBatchLimit is the maximum operations before commit the backend transaction.
--max-txn-ops '128'
  Maximum number of operations permitted in a transaction.
--max-request-bytes '1572864'
  Maximum client request size in bytes the server will accept.
--grpc-keepalive-min-time '5s'
  Minimum duration interval that a client should wait before pinging server.
--grpc-keepalive-interval '2h'
  Frequency duration of server-to-client ping to check if a connection is alive (0 to disable).
--grpc-keepalive-timeout '20s'
  Additional duration of wait before closing a non-responsive connection (0 to disable).
--socket-reuse-port 'false'
  Enable to set socket option SO_REUSEPORT on listeners allowing rebinding of a port already in use.
--socket-reuse-address 'false'
  Enable to set socket option SO_REUSEADDR on listeners allowing binding to an address in TIME_WAIT state.

Clusterisation

--initial-advertise-peer-urls 'http://localhost:2380'
  List of this member's peer URLs to advertise to the rest of the cluster.
--initial-cluster 'default=http://localhost:2380'
  Initial cluster configuration for bootstrapping.
--initial-cluster-state 'new'
  Initial cluster state ('new' or 'existing').
--initial-cluster-token 'etcd-cluster'
  Initial cluster token for the etcd cluster during bootstrap.
  Specifying this can protect you from unintended cross-cluster interaction when running multiple clusters.
--advertise-client-urls 'http://localhost:2379'
  List of this member's client URLs to advertise to the public.
  The client URLs advertised should be accessible to machines that talk to etcd cluster. etcd client libraries parse these URLs to connect to the cluster.
--discovery ''
  Discovery URL used to bootstrap the cluster.
--discovery-fallback 'proxy'
  Expected behavior ('exit' or 'proxy') when discovery services fails.
  "proxy" supports v2 API only.
--discovery-proxy ''
  HTTP proxy to use for traffic to discovery service.
--discovery-srv ''
  DNS srv domain used to bootstrap the cluster.
--discovery-srv-name ''
  Suffix to the dns srv name queried when bootstrapping.
--strict-reconfig-check 'true'
  Reject reconfiguration requests that would cause quorum loss.
--pre-vote 'true'
  Enable the raft Pre-Vote algorithm to prevent disruption when a node that has been partitioned away rejoins the cluster.
--auto-compaction-retention '0'
  Auto compaction retention length. 0 means disable auto compaction.
--auto-compaction-mode 'periodic'
  Interpret 'auto-compaction-retention' one of: periodic|revision. 'periodic' for duration based retention, defaulting to hours if no time unit is provided (e.g. '5m'). 'revision' for revision number based retention.
--enable-v2 'false'
  Accept etcd V2 client requests. Deprecated and to be decommissioned in v3.6.
--v2-deprecation 'not-yet'
  Phase of v2store deprecation. Allows to opt-in for higher compatibility mode.
  Supported values:
    'not-yet'                // Issues a warning if v2store have meaningful content (default in v3.5)
    'write-only'             // Custom v2 state is not allowed (default in v3.6 and v3.7)
    'write-only-skip-check'  // Custom v2 state is not supported and, if present, will be ignored (available in v3.5.32+, v3.6.13+, and v3.7.0+). Use this option at your own risk.
    'write-only-drop-data'   // Custom v2 state will get DELETED ! (planned default in v3.8)
    'gone'                   // v2store is not maintained any longer.

Sécurité

--cert-file ''
  Path to the client server TLS cert file.
--key-file ''
  Path to the client server TLS key file.
--client-cert-auth 'false'
  Enable client cert authentication.
  It's recommended to enable client cert authentication to prevent attacks from unauthenticated clients (e.g. CVE-2023-44487), especially when running etcd as a public service.
--client-crl-file ''
  Path to the client certificate revocation list file.
--client-cert-allowed-hostname ''
  Comma-separated list of SAN hostnames for client cert authentication.
--trusted-ca-file ''
  Path to the client server TLS trusted CA cert file.
  Note setting this parameter will also automatically enable client cert authentication no matter what value is set for `--client-cert-auth`.
--auto-tls 'false'
  Client TLS using generated certificates.
--peer-cert-file ''
  Path to the peer server TLS cert file.
--peer-key-file ''
  Path to the peer server TLS key file.
--peer-client-cert-auth 'false'
  Enable peer client cert authentication.
  It's recommended to enable peer client cert authentication to prevent attacks from unauthenticated forged peers (e.g. CVE-2023-44487).
--peer-trusted-ca-file ''
  Path to the peer server TLS trusted CA file.
--peer-cert-allowed-cn ''
  Comma-separated list of allowed CNs for inter-peer TLS authentication.
--peer-cert-allowed-hostname ''
  Comma-separated list of allowed SAN hostnames for inter-peer TLS authentication.
--peer-auto-tls 'false'
  Peer TLS using self-generated certificates if --peer-key-file and --peer-cert-file are not provided.
--self-signed-cert-validity '1'
  The validity period of the client and peer certificates that are automatically generated by etcd when you specify ClientAutoTLS and PeerAutoTLS, the unit is year, and the default is 1.
--peer-crl-file ''
  Path to the peer certificate revocation list file.
--cipher-suites ''
  Comma-separated list of supported TLS cipher suites between client/server and peers (empty will be auto-populated by Go).
--cors '*'
  Comma-separated whitelist of origins for CORS, or cross-origin resource sharing, (empty or * means allow all).
--host-whitelist '*'
  Acceptable hostnames from HTTP client requests, if server is not secure (empty or * means allow all).
--tls-min-version 'TLS1.2'
  Minimum TLS version supported by etcd.
--tls-max-version ''
  Maximum TLS version supported by etcd (empty will be auto-populated by Go).

Auth

--auth-token 'simple'
  Specify a v3 authentication token type and its options ('simple' or 'jwt').
--bcrypt-cost 10
  Specify the cost / strength of the bcrypt algorithm for hashing auth passwords. Valid values are between 4 and 31.
--auth-token-ttl 300
  Time (in seconds) of the auth-token-ttl.

Analyse de performances et surveillance

--enable-pprof 'false'
  Enable runtime profiling data via HTTP server. Address is at client URL + "/debug/pprof/"
--metrics 'basic'
  Set level of detail for exported metrics, specify 'extensive' to include server side grpc histogram metrics.
--listen-metrics-urls ''
  List of URLs to listen on for the metrics and health endpoints.

Journalisation

--logger 'zap'
  Currently only supports 'zap' for structured logging.
--log-outputs 'default'
  Specify 'stdout' or 'stderr' to skip journald logging even when running under systemd, or list of comma separated output targets.
--log-level 'info'
  Configures log level. Only supports debug, info, warn, error, panic, or fatal.
--log-format 'json'
  Configures log format. Only supports json, console.
--enable-log-rotation 'false'
  Enable log rotation of a single log-outputs file target.
--log-rotation-config-json '{"maxsize": 100, "maxage": 0, "maxbackups": 0, "localtime": false, "compress": false}'
  Configures log rotation if enabled with a JSON logger config. MaxSize(MB), MaxAge(days,0=no limit), MaxBackups(0=no limit), LocalTime(use computers local time), Compress(gzip)".
--warning-unary-request-duration '300ms'
  Set time duration after which a warning is logged if a unary request takes more than this duration.
Note

Remarque : Plusieurs indicateurs --experimental-* ont été promus ou renommés dans la version 3.7. Veillez à remplacer les indicateurs obsolètes par leurs équivalents stables indiqués ci-dessous.

Traçage distribué

--enable-distributed-tracing 'false'
  Enable distributed tracing.
--distributed-tracing-address 'localhost:4317'
  Distributed tracing collector address.
--distributed-tracing-service-name 'etcd'
  Distributed tracing service name, must be the same across all etcd instances.
--distributed-tracing-instance-id ''
  Distributed tracing instance ID, must be unique for each etcd instance.
--distributed-tracing-sampling-rate '0'
  Number of samples to collect per million spans for distributed tracing.

v2 Proxy

Avertissement

Remarque : les indicateurs seront obsolètes à partir de la version v3.6.

--proxy 'off'
  Proxy mode setting ('off', 'readonly' or 'on').
--proxy-failure-wait 5000
  Time (in milliseconds) an endpoint will be held in a failed state.
--proxy-refresh-interval 30000
  Time (in milliseconds) of the endpoints refresh interval.
--proxy-dial-timeout 1000
  Time (in milliseconds) for a dial to timeout.
--proxy-write-timeout 5000
  Time (in milliseconds) for a write to timeout.
--proxy-read-timeout 0
  Time (in milliseconds) for a read to timeout.

Fonctionnalités

--corrupt-check-time '0s'
  Duration of time between cluster corruption check passes.
--compact-hash-check-time '1m'
  Duration of time between leader checks followers compaction hashes.
--compaction-batch-limit 1000
  CompactionBatchLimit sets the maximum revisions deleted in each compaction batch.
--peer-skip-client-san-verification 'false'
  Skip verification of SAN field in client certificate for peer connections.
--watch-progress-notify-interval '10m'
  Duration of periodical watch progress notification.
--warning-apply-duration '100ms'
  Warning is generated if requests take more than this duration.
--bootstrap-defrag-threshold-megabytes
  Enable the defrag during etcd server bootstrap on condition that it will free at least the provided threshold of disk space. Needs to be set to non-zero value to take effect.
--max-learners '1'
  Set the max number of learner members allowed in the cluster membership.
--compaction-sleep-interval
  Sets the sleep interval between each compaction batch.
--downgrade-check-time
  Duration of time between two downgrade status checks.
--snapshot-catchup-entries
  Number of entries for a slow follower to catch up after compacting the raft storage entries.

Portes fonctionnelles

--feature-gates=AllAlpha=true|false
  Enables or disables all alpha features. Default is false.
--feature-gates=AllBeta=true|false
  Enables or disables all beta features. Default is false.
--feature-gates=CompactHashCheck=true
  Enables leader to periodically check follower compaction hashes.
  Replaces: --experimental-compact-hash-check-enabled
--feature-gates=InitialCorruptCheck=true
  Enables corruption check before serving client/peer traffic.
  Replaces: --experimental-initial-corrupt-check
--feature-gates=LeaseCheckpoint=true
  ExperimentalEnableLeaseCheckpoint enables primary lessor to persist lease remainingTTL to prevent indefinite auto-renewal of long lived leases.
  Replaces: --experimental-enable-lease-checkpoint
--feature-gates=LeaseCheckpointPersist=true
  Enable persisting remainingTTL to prevent indefinite auto-renewal of long lived leases. Always enabled in v3.6. Should be used to ensure smooth upgrade from v3.5 clusters with this feature enabled.
  Replaces: --experimental-enable-lease-checkpoint-persist
--feature-gates=SetMemberLocalAddr=true
  Allows setting a member’s local address.
--feature-gates=StopGRPCServiceOnDefrag=true
  Enable etcd gRPC service to stop serving client requests on defragmentation.
  Replaces: --experimental-stop-grpc-service-on-defrag
--feature-gates=TxnModeWriteWithSharedBuffer=true
  Enable the write transaction to use a shared buffer in its readonly check operations.
  Replaces: --experimental-txn-mode-write-with-shared-buffer

Fonctionnalités non sécurisées

Avertissement

Avertissement : l’utilisation de fonctionnalités non sécurisées peut compromettre les garanties offertes par le protocole de consensus !

--force-new-cluster 'false'
  Force to create a new one-member cluster.
--unsafe-no-fsync 'false'
  Disables fsync, unsafe, will cause data loss.

Fichier de configuration

Un fichier de configuration etcd est constitué d’une carte YAML dont les clés sont les noms de drapeaux en ligne de commande et les valeurs sont les valeurs des drapeaux. Pour utiliser ce fichier, indiquez le chemin du fichier comme valeur du drapeau --config-file ou de la variable d’environnement ETCD_CONFIG_FILE.

Pour un exemple, voir l’exemple [etcd.conf.yml ][].

Note

Les champs de durée tels que --grpc-keepalive-min-time, --grpc-keepalive-interval, --grpc-keepalive-timeout, --backend-batch-interval, --corrupt-check-time, --compact-hash-check-time, --compaction-sleep-interval, --watch-progress-notify-interval, --warning-apply-duration, --warning-unary-request-duration et --downgrade-check-time acceptent des chaînes lisibles (par exemple 10m, 5s) comme options de ligne de commande, mais dans un fichier de configuration, ils n’acceptent que des valeurs entières représentant des nanosecondes. Il s’agit d’une limitation connue de la bibliothèque standard Go , où time.Duration est désérialisé comme un entier simple.

Par exemple, pour définir un intervalle de notification de progression de 10 minutes dans un fichier de configuration :

# Correct : 10 minutes en nanosecondes
watch-progress-notify-interval: 600000000000

# Incorrect : provoque une erreur de désérialisation
watch-progress-notify-interval: '10m'

3 - Modèle de sécurité du transport

Sécurisation des données en transit

etcd prend en charge le chiffrement TLS automatique ainsi que l’authentification par certificats clients pour les communications clients vers serveur, ainsi que pour les communications entre pairs (serveur vers serveur / cluster). Notez qu’etcd n’active pas par défaut l’authentification basée sur RBAC ni la fonctionnalité d’authentification au niveau du transport afin de réduire les obstacles pour les utilisateurs débutants avec la base de données. En outre, modifier cette valeur par défaut constituerait une modification incompatible pour le projet, établie depuis 2013. Un cluster etcd qui n’active pas les fonctionnalités de sécurité peut exposer ses données à tout client.

Pour démarrer, disposez tout d’abord d’un certificat CA et d’une paire de clés signées pour un membre. Il est recommandé de créer et de signer une nouvelle paire de clés pour chaque membre d’un cluster.

Pour plus de commodité, l’outil cfssl propose une interface simplifiée pour la génération de certificats, et nous fournissons un exemple utilisant cet outil ici . En alternative, consultez ce guide pour générer des paires de clés auto-signées .

La liste des indicateurs fournie ci-dessous peut ne pas être à jour en raison des modifications en cours de développement. Pour obtenir la liste des indicateurs disponibles, exécutez etcd --help ou consultez l’aide de [etcd][].

Configuration de base

etcd prend plusieurs options de configuration liées aux certificats, soit par des drapeaux en ligne de commande, soit par des variables d’environnement :

Communication client-serveur :

--cert-file=<path> : Certificat utilisé pour les connexions SSL/TLS vers etcd. Lorsque cette option est définie, advertise-client-urls peut utiliser le schéma HTTPS.

--key-file=<path> : Clé du certificat. Doit être non chiffrée.

--client-cert-auth : Lorsque cette option est définie, etcd vérifie que toutes les requêtes HTTPS entrantes incluent un certificat client signé par l’autorité de certification fiable. Les requêtes ne fournissant pas de certificat client valide échoueront. Si authentification est activée, le certificat fournit les identifiants pour le nom d’utilisateur indiqué dans le champ Common Name.

--trusted-ca-file=<path>: Autorité de certification de confiance.

--auto-tls : Utilisez des certificats auto-signés générés automatiquement pour les connexions TLS avec les clients.

Communication entre pairs (serveur vers serveur / cluster) :

Les options de pair fonctionnent de la même manière que les options client-serveur :

--peer-cert-file=<path> : Certificat utilisé pour les connexions SSL/TLS entre pairs. Ce certificat sera utilisé à la fois pour écouter sur l’adresse de pair et pour envoyer des requêtes aux autres pairs.

--peer-key-file=<path> : Clé du certificat. Doit être non chiffrée.

--peer-client-cert-auth : Lorsqu’il est défini, etcd vérifie que toutes les requêtes entrantes de pair provenant du cluster sont accompagnées de certificats clients valides signés par l’autorité de certification fournie.

--peer-trusted-ca-file=<path>: Autorité de certification de confiance.

--peer-auto-tls : Utilisez des certificats auto-signés générés automatiquement pour les connexions TLS entre pairs.

Si un certificat client-serveur ou un certificat pair est fourni, la clé doit également être définie. Toutes ces options de configuration sont également disponibles via les variables d’environnement, ETCD_CA_FILE, ETCD_PEER_CA_FILE et ainsi de suite.

Options communes :

--cipher-suites : Liste séparée par des virgules des suites de chiffrement TLS prises en charge entre le serveur/client et les pairs (vide, automatiquement rempli par Go).

--tls-min-version=<version> Définit la version minimale TLS prise en charge par etcd.

--tls-max-version=<version> Définit la version TLS maximale prise en charge par etcd. Si ce paramètre n’est pas défini, la version maximale prise en charge par Go sera utilisée.

Usage de clé et extendedKeyUsage du certificat TLS

Lors de la génération de certificats X.509 pour sécuriser le transport etcd, les certificats doivent inclure les champs keyUsage et extendedKeyUsage appropriés selon leur rôle. etcd s’appuie sur les bibliothèques crypto/tls et crypto/x509 de Go pour la vérification des certificats, qui imposent ces utilisations lors de la négociation TLS.

Le tableau suivant résume les utilisations recommandées pour les rôles de certificat courants :

Rôle du certificatkeyUsageextendedKeyUsage
Serveur (client vers serveur)digitalSignature, keyEnciphermentserverAuth
ClientdigitalSignature, keyEnciphermentclientAuth
Pair (serveur vers serveur)digitalSignature, keyEnciphermentserverAuth, clientAuth

Notes :

  • Lorsque --peer-client-cert-auth est activé, les certificats de pair sont utilisés pour établir une TLS mutuelle entre les membres etcd, ce qui impose l’utilisation de serverAuth et de clientAuth.
  • Les certificats clients utilisés avec --client-cert-auth doivent inclure clientAuth.

Exemple 1 : Sécurité du transport client-serveur avec HTTPS

Pour cela, préparez un certificat d’autorité de certification (ca.crt) et une paire de clés signées (server.crt, server.key).

Configurons etcd pour fournir une sécurité de transport HTTPS simple étape par étape :

$ etcd --name infra0 --data-dir infra0 \
  --cert-file=/path/to/server.crt --key-file=/path/to/server.key \
  --advertise-client-urls=https://127.0.0.1:2379 --listen-client-urls=https://127.0.0.1:2379

Cela devrait démarrer correctement, et il sera possible de tester la configuration en utilisant HTTPS avec etcd :

$ curl --cacert /path/to/ca.crt https://127.0.0.1:2379/v2/keys/foo -XPUT -d value=bar -v

La commande doit indiquer que la négociation a réussi. Étant donné que nous utilisons des certificats auto-signés avec notre propre autorité de certification, il est nécessaire de transmettre l’autorité de certification à curl en utilisant l’option --cacert. Une autre possibilité consisterait à ajouter le certificat de l’autorité de certification au répertoire des certificats fiables du système (généralement situé dans /etc/pki/tls/certs ou /etc/ssl/certs).

Utilisateurs OSX 10.9+ : curl 7.30.0 sous OSX 10.9+ ne comprend pas les certificats passés en ligne de commande. Au lieu de cela, importez directement le certificat dummy ca.crt dans la clé, ou ajoutez le drapeau -k à curl pour ignorer les erreurs. Pour tester sans le drapeau -k, exécutez open ./tests/fixtures/ca/ca.crt et suivez les invites. Veuillez supprimer ce certificat après le test ! Si une solution de contournement existe, faites-le nous savoir.

Exemple 2 : Authentification client-serveur avec des certificats clients HTTPS

Pour l’instant, nous avons donné au client etcd la capacité de vérifier l’identité du serveur et de garantir la sécurité du transport. Nous pouvons toutefois également utiliser des certificats clients pour empêcher l’accès non autorisé à etcd.

Les clients fourniront leurs certificats au serveur, qui vérifiera que le certificat est signé par l’autorité de certification fournie et décidera s’il convient de traiter la requête.

Les mêmes fichiers mentionnés dans le premier exemple sont nécessaires pour cela, ainsi qu’une paire de clés pour le client (client.crt, client.key) signée par la même autorité de certification.

$ etcd --name infra0 --data-dir infra0 \
  --client-cert-auth --trusted-ca-file=/path/to/ca.crt --cert-file=/path/to/server.crt --key-file=/path/to/server.key \
  --advertise-client-urls https://127.0.0.1:2379 --listen-client-urls https://127.0.0.1:2379

Essayez maintenant la même requête quʼau-dessus sur ce serveur :

$ curl --cacert /path/to/ca.crt https://127.0.0.1:2379/v2/keys/foo -XPUT -d value=bar -v

La requête doit être rejetée par le serveur :

...
routines:SSL3_READ_BYTES:sslv3 alert bad certificate
...

Pour y parvenir, nous devons fournir au serveur un certificat client signé par l’autorité de certification :

$ curl --cacert /path/to/ca.crt --cert /path/to/client.crt --key /path/to/client.key \
  -L https://127.0.0.1:2379/v2/keys/foo -XPUT -d value=bar -v

La sortie doit inclure :

...
SSLv3, TLS handshake, CERT verify (15):
...
TLS handshake, Finished (20)

Et également la réponse du serveur :

{
    "action": "set",
    "node": {
        "createdIndex": 12,
        "key": "/foo",
        "modifiedIndex": 12,
        "value": "bar"
    }
}

Spécifiez les suites de chiffrement à bloquer suites de chiffrement TLS faibles .

L’établissement de la mainshaking TLS échouerait lorsque le client Hello est demandé avec des suites de chiffrement non valides.

Par exemple :

$ etcd \
  --cert-file ./server.crt \
  --key-file ./server.key \
  --trusted-ca-file ./ca.crt \
  --cipher-suites TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384

Ensuite, les requêtes clientes doivent préciser l’un des suites de chiffrement spécifiées sur le serveur :

# valid cipher suite
$ curl \
  --cacert /path/to/ca.crt \
  --cert /path/to/client.crt \
  --key /path/to/client.key \
  -L [CLIENT-URL]/metrics \
  --ciphers ECDHE-RSA-AES128-GCM-SHA256

# request succeeds
etcd_server_version{server_version="3.2.22"} 1
...
# invalid cipher suite
$ curl \
  --cacert /path/to/ca.crt \
  --cert /path/to/client.crt \
  --key /path/to/client.key \
  -L [CLIENT-URL]/metrics \
  --ciphers ECDHE-RSA-DES-CBC3-SHA

# request fails with
(35) error:14094410:SSL routines:ssl3_read_bytes:sslv3 alert handshake failure

Exemple 3 : Sécurité du transport et certificats clients dans un cluster

etcd prend en charge le même modèle que ci-dessus pour la communication entre pairs, ce qui signifie la communication entre les membres d’un cluster etcd.

En supposant que nous disposons de notre ca.crt et de deux membres équipés de leurs propres paires de clés (member1.crt & member1.key, member2.crt & member2.key) signées par cette autorité de certification, nous lançons etcd comme suit :

DISCOVERY_URL=... # from https://discovery.etcd.io/new

# member1
$ etcd --name infra1 --data-dir infra1 \
  --peer-client-cert-auth --peer-trusted-ca-file=/path/to/ca.crt --peer-cert-file=/path/to/member1.crt --peer-key-file=/path/to/member1.key \
  --initial-advertise-peer-urls=https://10.0.1.10:2380 --listen-peer-urls=https://10.0.1.10:2380 \
  --discovery ${DISCOVERY_URL}

# member2
$ etcd --name infra2 --data-dir infra2 \
  --peer-client-cert-auth --peer-trusted-ca-file=/path/to/ca.crt --peer-cert-file=/path/to/member2.crt --peer-key-file=/path/to/member2.key \
  --initial-advertise-peer-urls=https://10.0.1.11:2380 --listen-peer-urls=https://10.0.1.11:2380 \
  --discovery ${DISCOVERY_URL}

Les membres etcd formeront un cluster et toutes les communications entre les membres du cluster seront chiffrées et authentifiées à l’aide des certificats clients. La sortie d’etcd indiquera que les adresses auxquelles il se connecte utilisent HTTPS.

Exemple 4 : Sécurité de transport auto-signée automatique

Avertissement

Lorsque vous spécifiez ClientAutoTLS et PeerAutoTLS, la période de validité du certificat client et du certificat pair automatiquement générés par etcd est limitée à 1 an. Vous pouvez utiliser le drapeau –self-signed-cert-validity pour définir la période de validité du certificat en années.

Dans les cas où une encryption de la communication est requise, mais pas une authentification, etcd prend en charge le chiffrement de ses messages à l’aide de certificats auto-signés générés automatiquement. Cela simplifie le déploiement, car il n’est pas nécessaire de gérer séparément les certificats et les clés en dehors d’etcd. Configurez etcd pour utiliser des certificats auto-signés pour les connexions clientes et entre pairs à l’aide des indicateurs --auto-tls et --peer-auto-tls :

DISCOVERY_URL=... # from https://discovery.etcd.io/new

# member1
$ etcd --name infra1 --data-dir infra1 \
  --auto-tls --peer-auto-tls \
  --initial-advertise-peer-urls=https://10.0.1.10:2380 --listen-peer-urls=https://10.0.1.10:2380 \
  --discovery ${DISCOVERY_URL}

# member2
$ etcd --name infra2 --data-dir infra2 \
  --auto-tls --peer-auto-tls \
  --initial-advertise-peer-urls=https://10.0.1.11:2380 --listen-peer-urls=https://10.0.1.11:2380 \
  --discovery ${DISCOVERY_URL}

Les certificats auto-signés ne vérifient pas l’identité, donc curl renverra une erreur :

curl: (60) SSL certificate problem: Invalid certificate chain

Pour désactiver la vérification de la chaîne de certificats, exécutez curl avec le drapeau -k :

$ curl -k https://127.0.0.1:2379/v2/keys/foo -Xput -d value=bar -v

Notes relatives au DNS SRV

Depuis la version 3.1.0 (sauf 3.2.9), le démarrage par découverte SRV authentifie ServerName à l’aide d’un nom de domaine racine provenant du drapeau --discovery-srv. Ceci vise à prévenir les attaques de type « homme du milieu » basées sur les certificats, en exigeant que le certificat possède un nom de domaine racine correspondant dans son champ Nom alternatif du sujet (SAN). Par exemple, etcd --discovery-srv=etcd.local n’authentifiera les pairs ou clients que si les certificats fournis incluent etcd.local comme entrée dans le champ Nom alternatif du sujet (SAN).

Notes sur le proxy etcd

Le proxy etcd termine le TLS provenant de son client si la connexion est sécurisée, puis utilise sa propre clé/certificat spécifiés dans --peer-key-file et --peer-cert-file pour communiquer avec les membres etcd.

Le proxy communique avec les membres etcd à l’aide des --advertise-client-urls et --advertise-peer-urls d’un membre donné. Il achemine les requêtes clientes vers les URL de client annoncées des membres etcd, et synchronise la configuration initiale du cluster à l’aide des URL de pair annoncées des membres etcd.

Lorsqu’une authentification client est activée pour un membre etcd, l’administrateur doit s’assurer que le certificat pair spécifié dans l’option --peer-cert-file du proxy est valide pour cette authentification. Le certificat pair du proxy doit également être valide pour l’authentification pair si l’authentification pair est activée.

Notes sur l’authentification TLS

Depuis v3.2.0 , les certificats TLS sont rechargés à chaque connexion client . Cela est utile pour remplacer des certificats expirés sans arrêter les serveurs etcd ; cela peut être réalisé en écrasant les anciens certificats par de nouveaux. Le rechargement des certificats à chaque connexion ne devrait pas entraîner un surcroît de charge important, mais pourrait être amélioré à l’avenir grâce à une couche de mise en mémoire tampon. Des exemples de tests sont disponibles ici .

Depuis v3.2.0 , le serveur refuse les certificats pairs entrants comportant une IP incorrecte SAN . Par exemple, si le certificat pair contient des adresses IP dans le champ Nom alternatif du sujet (SAN), le serveur authentifie un pair uniquement lorsque l’adresse IP distante correspond à l’une de ces adresses. Ceci vise à empêcher les points de terminaison non autorisés de rejoindre le cluster. Par exemple, la demande de signature de certificat (CSR) du pair B (avec cfssl) est :

{
  "CN": "etcd peer",
  "hosts": [
    "*.example.default.svc",
    "*.example.default.svc.cluster.local",
    "10.138.0.27"
  ],
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [
    {
      "C": "US",
      "L": "CA",
      "ST": "San Francisco"
    }
  ]
}

Lorsque l’adresse IP réelle du pair B est 10.138.0.2, et non 10.138.0.27. Lorsque le pair B tente de rejoindre le cluster, le pair A rejette B avec l’erreur x509: certificate is valid for 10.138.0.27, not 10.138.0.2, car l’adresse IP distante de B ne correspond pas à celle figurant dans le champ Nom alternatif (SAN).

Depuis v3.2.0 , server résout le TLS DNSNames lors de la vérification SAN . Par exemple, si le certificat pair ne contient que des noms DNS (aucune adresse IP) dans le champ Nom alternatif du sujet (SAN), le serveur authentifie un pair uniquement lorsque les résolutions inverses (dig b.com) de ces noms DNS correspondent à l’adresse IP distante. Par exemple, la demande de signature de certificat (CSR) du pair B (avec cfssl) est :

{
  "CN": "etcd peer",
  "hosts": [
    "b.com"
  ],

Lorsque l’adresse IP distante du pair B est 10.138.0.2. Lorsque le pair B tente de rejoindre le cluster, le pair A recherche l’hôte entrant b.com afin d’obtenir la liste des adresses IP (par exemple dig b.com). Il rejette B si la liste ne contient pas l’adresse IP 10.138.0.2, avec l’erreur tls: 10.138.0.2 does not match any of DNSNames ["b.com"].

Depuis v3.2.2 , le serveur accepte les connexions si l’IP correspond, sans vérifier les entrées DNS . Par exemple, si le certificat du pair contient des adresses IP et des noms DNS dans le champ Nom alternatif du sujet (SAN), et que l’adresse IP distante correspond à l’une de ces adresses IP, le serveur accepte simplement la connexion sans vérifier davantage les noms DNS. Par exemple, la demande de signature de certificat (CSR) du pair B (avec cfssl) est :

{
  "CN": "etcd peer",
  "hosts": [
    "invalid.domain",
    "10.138.0.2"
  ],

Lorsque l’adresse IP distante du pair B est 10.138.0.2 et que invalid.domain est un hôte non valide. Lorsque le pair B tente de rejoindre le cluster, le pair A authentifie B avec succès, car le champ Nom alternatif du sujet (SAN) contient une adresse IP correspondante valide. Pour plus de détails, consultez issue#8206 .

Depuis v3.2.5 , server prend en charge la recherche inverse sur les noms DNS avec caractères génériques SAN . Par exemple, si le certificat pair ne contient que des noms DNS (aucune adresse IP) dans le champ Subject Alternative Name (SAN), le serveur effectue d’abord une recherche inverse de l’adresse IP distante pour obtenir la liste des noms associés à cette adresse (par exemple, nslookup IPADDR). Ensuite, il accepte la connexion si ces noms correspondent à un nom du certificat pair (par correspondance exacte ou avec caractère générique). Si aucune correspondance n’est trouvée, le serveur effectue une recherche directe pour chaque entrée DNS du certificat pair (par exemple, recherche de example.default.svc lorsque l’entrée est *.example.default.svc), et accepte la connexion uniquement si les adresses résolues par l’hôte correspondent à l’adresse IP distante du certificat pair. Par exemple, la demande de signature de certificat (CSR) du pair B (avec cfssl) est :

{
  "CN": "etcd peer",
  "hosts": [
    "*.example.default.svc",
    "*.example.default.svc.cluster.local"
  ],

Lorsque l’adresse IP distante du pair B est 10.138.0.2. Lorsque le pair B tente de rejoindre le cluster, le pair A effectue une recherche inverse de l’IP 10.138.0.2 afin d’obtenir la liste des noms d’hôte. Il effectue ensuite une correspondance exacte ou avec caractère générique entre les noms d’hôte et les noms DNS du certificat du pair B dans le champ Nom alternatif du sujet (SAN). Si aucune recherche inverse ni forward n’a abouti, une erreur "tls: "10.138.0.2" does not match any of DNSNames ["*.example.default.svc","*.example.default.svc.cluster.local"] est renvoyée. Pour plus de détails, voir issue#8268 .

v3.3.0 ajoute le drapeau etcd --peer-cert-allowed-cn pour prendre en charge l’authentification CN (Common Name)-based pour les connexions inter-membres . Le démarrage TLS de Kubernetes consiste à générer des certificats dynamiques pour les membres et autres composants du système (par exemple, serveur API, kubelet, etc.). Maintenir des autorités de certification (CA) différentes pour chaque composant permet un contrôle d’accès plus strict sur le cluster etcd, mais peut s’avérer fastidieux. Lorsque le drapeau –peer-cert-allowed-cn est spécifié, un nœud ne peut se joindre qu’avec un nom commun correspondant, même avec des CA partagées. La correspondance est une comparaison exacte de chaîne par rapport au champ Common Name (CN) du certificat — aucune prise en charge des caractères génériques ou des correspondances par préfixe. Pour le filtrage basé sur le nom d’hôte utilisant –peer-cert-allowed-hostname ou –client-cert-allowed-hostname, la correspondance utilise x509.Certificate.VerifyHostname() de Go, qui prend en charge à la fois les noms d’hôte exacts et les entrées génériques (par exemple, *.example.com). Par exemple, chaque membre d’un cluster à 3 nœuds est configuré avec des demandes de signature de certificat (CSRs) (avec cfssl) comme suit :

{
  "CN": "etcd.local",
  "hosts": [
    "m1.etcd.local",
    "127.0.0.1",
    "localhost"
  ],
{
  "CN": "etcd.local",
  "hosts": [
    "m2.etcd.local",
    "127.0.0.1",
    "localhost"
  ],
{
  "CN": "etcd.local",
  "hosts": [
    "m3.etcd.local",
    "127.0.0.1",
    "localhost"
  ],

Ensuite, seuls les pairs possédant un nom commun identique seront authentifiés si --peer-cert-allowed-cn etcd.local est fourni. Les nœuds présentant des CN différents dans les demandes de signature de certificat (CSR) ou un --peer-cert-allowed-cn différent seront rejetés :

$ etcd --peer-cert-allowed-cn m1.etcd.local

I | embed: rejected connection from "127.0.0.1:48044" (error "CommonName authentication failed", ServerName "m1.etcd.local")
I | embed: rejected connection from "127.0.0.1:55702" (error "remote error: tls: bad certificate", ServerName "m3.etcd.local")

Chaque processus doit être lancé avec :

etcd --peer-cert-allowed-cn etcd.local

I | pkg/netutil: resolving m3.etcd.local:32380 to 127.0.0.1:32380
I | pkg/netutil: resolving m2.etcd.local:22380 to 127.0.0.1:22380
I | pkg/netutil: resolving m1.etcd.local:2380 to 127.0.0.1:2380
I | etcdserver: published {Name:m3 ClientURLs:[https://m3.etcd.local:32379]} to cluster 9db03f09b20de32b
I | embed: ready to serve client requests
I | etcdserver: published {Name:m1 ClientURLs:[https://m1.etcd.local:2379]} to cluster 9db03f09b20de32b
I | embed: ready to serve client requests
I | etcdserver: published {Name:m2 ClientURLs:[https://m2.etcd.local:22379]} to cluster 9db03f09b20de32b
I | embed: ready to serve client requests
I | embed: serving client requests on 127.0.0.1:32379
I | embed: serving client requests on 127.0.0.1:22379
I | embed: serving client requests on 127.0.0.1:2379

v3.2.19 et v3.3.4 corrigent le rechargement TLS lorsque le champ SAN du certificat ne contient que des adresses IP, sans nom de domaine . Par exemple, un membre est configuré avec les CSR suivantes (avec cfssl) :

{
  "CN": "etcd.local",
  "hosts": [
    "127.0.0.1"
  ],

En Go, le serveur appelle (*tls.Config).GetCertificate pour recharger le TLS uniquement si le champ (*tls.Config).Certificates du serveur n’est pas vide, ou si (*tls.ClientHelloInfo).ServerName n’est pas vide et que le client fournit un SNI valide. Auparavant, etcd remplissait toujours (*tls.Config).Certificates lors de la première négociation TLS client, en le rendant non vide. Le client était donc toujours censé fournir un SNI correspondant afin de réussir la vérification TLS et de déclencher le rechargement des ressources TLS via (*tls.Config).GetCertificate.

Toutefois, un certificat dont le champ SAN ne contient aucun nom de domaine, mais uniquement des adresses IP demanderait *tls.ClientHelloInfo avec un champ ServerName vide, ce qui empêcherait le rechargement TLS lors de la première négociation TLS ; cela pose problème lorsque des certificats expirés doivent être remplacés en ligne.

Maintenant, (*tls.Config).Certificates est créé vide lors de la première poignée de main TLS client, d’abord pour déclencher (*tls.Config).GetCertificate, puis pour peupler le reste des certificats à chaque nouvelle connexion TLS, même lorsque le SNI client est vide (par exemple, lorsque le certificat ne contient que des adresses IP).

Notes pour la liste blanche d’hôtes

L’indicateur etcd --host-whitelist spécifie les noms d’hôte acceptables provenant des requêtes HTTP clients. La politique d’origine des clients protège contre les attaques de type « rebinding DNS » ciblant des serveurs etcd non sécurisés. En effet, tout site web peut simplement créer un nom DNS autorisé et rediriger ce nom vers "localhost" (ou toute autre adresse). Ainsi, tous les points de terminaison HTTP du serveur etcd écoutant sur "localhost" deviennent accessibles, exposant le serveur aux attaques de rebinding DNS. Pour plus de détails, consultez CVE-2018-5702 .

Politique d’origine du client fonctionne comme suit :

  1. Si la connexion cliente est sécurisée via HTTPS, autoriser n’importe quel nom d’hôte.
  2. Si la connexion cliente n’est pas sécurisée et que "HostWhitelist" n’est pas vide, autoriser uniquement les requêtes HTTP dont le champ Host figure dans la liste blanche.

Notez que la politique d’origine du client est appliquée, qu’une authentification soit activée ou non, pour des contrôles plus stricts.

Par défaut, etcd --host-whitelist et embed.Config.HostWhitelist sont définis sur vide afin d’autoriser tous les noms d’hôte. Notez qu’en spécifiant des noms d’hôte, les adresses de boucle locale ne sont pas ajoutées automatiquement. Pour autoriser les interfaces de boucle locale, ajoutez-les manuellement à la liste blanche (par exemple "localhost", "127.0.0.1", etc.).

Questions fréquemment posées

Je constate une erreur d’alerte SSLv3 handshake lors de l’utilisation de l’authentification client TLS ?

Le paquet crypto/tls de golang vérifie l’utilisation autorisée de la clé publique du certificat avant de l’utiliser. Pour utiliser la clé publique du certificat à des fins d’authentification client, il faut ajouter clientAuth à Extended Key Usage lors de la création de la clé publique du certificat.

Voici comment procéder :

Ajoutez la section suivante à OpenSSL.cnf :

[ ssl_client ]
...
  extendedKeyUsage = clientAuth
...

Lors de la création du certificat, veillez à le référencer dans le drapeau -extensions :

$ openssl ca -config openssl.cnf -policy policy_anything -extensions ssl_client -out certs/machine.crt -infiles machine.csr

Avec l’authentification par certificat pair, j’obtiens « le certificat est valide pour 127.0.0.1, pas pour $MY_IP »

Assurez-vous de signer les certificats avec un nom sujet correspondant à l’adresse IP publique du membre. L’outil etcd-ca, par exemple, propose une option --ip= pour sa commande new-cert.

Le certificat doit être signé pour le nom DNS complet (FQDN) du membre dans son champ « Sujet », utilisez les noms alternatifs du sujet (SAN courts, IP) pour ajouter l’adresse IP. L’outil etcd-ca propose l’option --domain= pour sa commande new-cert, et OpenSSL peut également générer it .

etcd chiffre-t-il les données stockées sur les disques ?

No. etcd ne chiffre pas les données clé/valeur stockées sur les disques. Si un utilisateur doit chiffrer les données stockées dans etcd, plusieurs options sont disponibles :

  • Faire chiffrer et déchiffrer les données par les applications clientes
  • Utiliser une fonctionnalité du système de stockage sous-jacent pour chiffrer les données stockées, comme dm-crypt

Lorsque etcd crée certains répertoires nouveaux, il définit les permissions des fichiers à 700 afin de limiter au maximum l’accès non autorisé. Toutefois, si l’utilisateur a déjà créé un répertoire selon ses préférences, etcd utilise ce répertoire existant et affiche un message d’avertissement si les permissions diffèrent de 700.

4 - Guide de clustering

Initialisation d’un cluster etcd : méthode statique, découverte etcd et découverte DNS

Aperçu

Lancer un cluster etcd de manière statique exige que chaque membre connaisse un autre membre du cluster. Dans certains cas, les adresses IP des membres du cluster peuvent être inconnues à l’avance. Dans ces situations, le cluster etcd peut être initialisé à l’aide d’un service de découverte.

Une fois qu’un cluster etcd est en cours d’exécution, l’ajout ou la suppression de membres s’effectue via la reconfiguration en temps réel runtime reconfiguration . Pour mieux comprendre la conception sous-jacente à la reconfiguration en temps réel, nous recommandons de lire le document de conception de la configuration en temps réel .

Ce guide traite des mécanismes suivants pour amorcer un cluster etcd :

Chaque mécanisme de mise en place initiale sera utilisé pour créer un cluster etcd composé de trois machines avec les détails suivants :

NomAdresseNom d’hôte
infra010.0.1.10infra0.example.com
infra110.0.1.11infra1.example.com
infra210.0.1.12infra2.example.com

Statique

Comme nous connaissons les membres du cluster, leurs adresses et la taille du cluster avant de commencer, nous pouvons utiliser une configuration de bootstrap hors ligne en définissant le drapeau initial-cluster. Chaque machine recevra soit les variables d’environnement suivantes, soit les arguments en ligne de commande :

ETCD_INITIAL_CLUSTER="infra0=http://10.0.1.10:2380,infra1=http://10.0.1.11:2380,infra2=http://10.0.1.12:2380"
ETCD_INITIAL_CLUSTER_STATE=new
--initial-cluster infra0=http://10.0.1.10:2380,infra1=http://10.0.1.11:2380,infra2=http://10.0.1.12:2380 \
--initial-cluster-state new

Notez que les URL spécifiées dans initial-cluster sont les URL de pair annoncées, c’est-à-dire qu’elles doivent correspondre à la valeur de initial-advertise-peer-urls sur les nœuds respectifs.

Si vous lancez plusieurs clusters (ou créez et détruyez un seul cluster) avec la même configuration à des fins de test, il est fortement recommandé d’attribuer à chaque cluster un identifiant initial-cluster-token unique. En procédant ainsi, etcd peut générer des identifiants de cluster et de membre uniques pour chaque cluster, même s’ils ont exactement la même configuration. Cela protège etcd contre les interactions entre clusters, qui pourraient endommager les clusters.

etcd écoute sur listen-client-urls pour accepter le trafic client. Le membre etcd annonce les URL spécifiées dans advertise-client-urls aux autres membres, aux proxies et aux clients. Vérifiez que les advertise-client-urls sont accessibles depuis les clients prévus. Une erreur courante consiste à définir advertise-client-urls sur localhost ou à laisser la valeur par défaut si les clients distants doivent accéder à etcd.

Sur chaque machine, lancez etcd avec ces indicateurs :

$ etcd --name infra0 --initial-advertise-peer-urls http://10.0.1.10:2380 \
  --listen-peer-urls http://10.0.1.10:2380 \
  --listen-client-urls http://10.0.1.10:2379,http://127.0.0.1:2379 \
  --advertise-client-urls http://10.0.1.10:2379 \
  --initial-cluster-token etcd-cluster-1 \
  --initial-cluster infra0=http://10.0.1.10:2380,infra1=http://10.0.1.11:2380,infra2=http://10.0.1.12:2380 \
  --initial-cluster-state new
$ etcd --name infra1 --initial-advertise-peer-urls http://10.0.1.11:2380 \
  --listen-peer-urls http://10.0.1.11:2380 \
  --listen-client-urls http://10.0.1.11:2379,http://127.0.0.1:2379 \
  --advertise-client-urls http://10.0.1.11:2379 \
  --initial-cluster-token etcd-cluster-1 \
  --initial-cluster infra0=http://10.0.1.10:2380,infra1=http://10.0.1.11:2380,infra2=http://10.0.1.12:2380 \
  --initial-cluster-state new
$ etcd --name infra2 --initial-advertise-peer-urls http://10.0.1.12:2380 \
  --listen-peer-urls http://10.0.1.12:2380 \
  --listen-client-urls http://10.0.1.12:2379,http://127.0.0.1:2379 \
  --advertise-client-urls http://10.0.1.12:2379 \
  --initial-cluster-token etcd-cluster-1 \
  --initial-cluster infra0=http://10.0.1.10:2380,infra1=http://10.0.1.11:2380,infra2=http://10.0.1.12:2380 \
  --initial-cluster-state new

Les paramètres de ligne de commande commençant par --initial-cluster seront ignorés lors des exécutions ultérieures de etcd. N’hésitez pas à supprimer les variables d’environnement ou les indicateurs de ligne de commande après le processus d’initialisation. Si des modifications de configuration sont nécessaires ultérieurement (par exemple, l’ajout ou la suppression de membres dans le cluster), consultez le guide configuration en temps réel .

TLS

etcd prend en charge la communication chiffrée via le protocole TLS. Les canaux TLS peuvent être utilisés pour la communication interne chiffrée entre pairs au sein du cluster ainsi que pour le trafic client chiffré. Cette section présente des exemples de configuration d’un cluster avec TLS pour les pairs et les clients. Des informations supplémentaires sur la prise en charge TLS par etcd sont disponibles dans le guide de sécurité .

Certificats auto-signés

Un cluster utilisant des certificats auto-signés chiffrer le trafic et authentifier ses connexions. Pour démarrer un cluster avec des certificats auto-signés, chaque membre du cluster doit disposer d’une paire de clés unique (member.crt, member.key) signée par un certificat CA partagé du cluster (ca.crt) pour les connexions entre pairs et les connexions clients. Les certificats peuvent être générés en suivant l’exemple de configuration TLS d’etcd.

Sur chaque machine, etcd sera lancé avec ces indicateurs :

$ etcd --name infra0 --initial-advertise-peer-urls https://10.0.1.10:2380 \
  --listen-peer-urls https://10.0.1.10:2380 \
  --listen-client-urls https://10.0.1.10:2379,https://127.0.0.1:2379 \
  --advertise-client-urls https://10.0.1.10:2379 \
  --initial-cluster-token etcd-cluster-1 \
  --initial-cluster infra0=https://10.0.1.10:2380,infra1=https://10.0.1.11:2380,infra2=https://10.0.1.12:2380 \
  --initial-cluster-state new \
  --client-cert-auth --trusted-ca-file=/path/to/ca-client.crt \
  --cert-file=/path/to/infra0-client.crt --key-file=/path/to/infra0-client.key \
  --peer-client-cert-auth --peer-trusted-ca-file=ca-peer.crt \
  --peer-cert-file=/path/to/infra0-peer.crt --peer-key-file=/path/to/infra0-peer.key
$ etcd --name infra1 --initial-advertise-peer-urls https://10.0.1.11:2380 \
  --listen-peer-urls https://10.0.1.11:2380 \
  --listen-client-urls https://10.0.1.11:2379,https://127.0.0.1:2379 \
  --advertise-client-urls https://10.0.1.11:2379 \
  --initial-cluster-token etcd-cluster-1 \
  --initial-cluster infra0=https://10.0.1.10:2380,infra1=https://10.0.1.11:2380,infra2=https://10.0.1.12:2380 \
  --initial-cluster-state new \
  --client-cert-auth --trusted-ca-file=/path/to/ca-client.crt \
  --cert-file=/path/to/infra1-client.crt --key-file=/path/to/infra1-client.key \
  --peer-client-cert-auth --peer-trusted-ca-file=ca-peer.crt \
  --peer-cert-file=/path/to/infra1-peer.crt --peer-key-file=/path/to/infra1-peer.key
$ etcd --name infra2 --initial-advertise-peer-urls https://10.0.1.12:2380 \
  --listen-peer-urls https://10.0.1.12:2380 \
  --listen-client-urls https://10.0.1.12:2379,https://127.0.0.1:2379 \
  --advertise-client-urls https://10.0.1.12:2379 \
  --initial-cluster-token etcd-cluster-1 \
  --initial-cluster infra0=https://10.0.1.10:2380,infra1=https://10.0.1.11:2380,infra2=https://10.0.1.12:2380 \
  --initial-cluster-state new \
  --client-cert-auth --trusted-ca-file=/path/to/ca-client.crt \
  --cert-file=/path/to/infra2-client.crt --key-file=/path/to/infra2-client.key \
  --peer-client-cert-auth --peer-trusted-ca-file=ca-peer.crt \
  --peer-cert-file=/path/to/infra2-peer.crt --peer-key-file=/path/to/infra2-peer.key

Certificats automatiques

Si le cluster nécessite une communication chiffrée mais ne requiert pas de connexions authentifiées, etcd peut être configuré pour générer automatiquement ses clés. Lors de l’initialisation, chaque membre crée son propre jeu de clés en fonction de ses adresses IP et hôtes annoncés.

Sur chaque machine, etcd sera lancé avec ces indicateurs :

$ etcd --name infra0 --initial-advertise-peer-urls https://10.0.1.10:2380 \
  --listen-peer-urls https://10.0.1.10:2380 \
  --listen-client-urls https://10.0.1.10:2379,https://127.0.0.1:2379 \
  --advertise-client-urls https://10.0.1.10:2379 \
  --initial-cluster-token etcd-cluster-1 \
  --initial-cluster infra0=https://10.0.1.10:2380,infra1=https://10.0.1.11:2380,infra2=https://10.0.1.12:2380 \
  --initial-cluster-state new \
  --auto-tls \
  --peer-auto-tls
$ etcd --name infra1 --initial-advertise-peer-urls https://10.0.1.11:2380 \
  --listen-peer-urls https://10.0.1.11:2380 \
  --listen-client-urls https://10.0.1.11:2379,https://127.0.0.1:2379 \
  --advertise-client-urls https://10.0.1.11:2379 \
  --initial-cluster-token etcd-cluster-1 \
  --initial-cluster infra0=https://10.0.1.10:2380,infra1=https://10.0.1.11:2380,infra2=https://10.0.1.12:2380 \
  --initial-cluster-state new \
  --auto-tls \
  --peer-auto-tls
$ etcd --name infra2 --initial-advertise-peer-urls https://10.0.1.12:2380 \
  --listen-peer-urls https://10.0.1.12:2380 \
  --listen-client-urls https://10.0.1.12:2379,https://127.0.0.1:2379 \
  --advertise-client-urls https://10.0.1.12:2379 \
  --initial-cluster-token etcd-cluster-1 \
  --initial-cluster infra0=https://10.0.1.10:2380,infra1=https://10.0.1.11:2380,infra2=https://10.0.1.12:2380 \
  --initial-cluster-state new \
  --auto-tls \
  --peer-auto-tls

Cas d’erreur

Dans l’exemple suivant, nous n’avons pas inclus notre nouvel hôte dans la liste des nœuds énumérés. Si c’est un nouveau cluster, le nœud doit être ajouté à la liste des membres initiaux du cluster.

$ etcd --name infra1 --initial-advertise-peer-urls http://10.0.1.11:2380 \
  --listen-peer-urls https://10.0.1.11:2380 \
  --listen-client-urls http://10.0.1.11:2379,http://127.0.0.1:2379 \
  --advertise-client-urls http://10.0.1.11:2379 \
  --initial-cluster infra0=http://10.0.1.10:2380 \
  --initial-cluster-state new
etcd: infra1 not listed in the initial cluster config
exit 1

Dans cet exemple, nous tentons de mapper un nœud (infra0) sur une adresse différente (127.0.0.1:2380) de celle qui est énumérée dans la liste du cluster (10.0.1.10:2380). Si ce nœud doit écouter sur plusieurs adresses, toutes ces adresses doivent être indiquées dans la directive de configuration “initial-cluster”.

$ etcd --name infra0 --initial-advertise-peer-urls http://127.0.0.1:2380 \
  --listen-peer-urls http://10.0.1.10:2380 \
  --listen-client-urls http://10.0.1.10:2379,http://127.0.0.1:2379 \
  --advertise-client-urls http://10.0.1.10:2379 \
  --initial-cluster infra0=http://10.0.1.10:2380,infra1=http://10.0.1.11:2380,infra2=http://10.0.1.12:2380 \
  --initial-cluster-state=new
etcd: error setting up initial cluster: infra0 has different advertised URLs in the cluster and advertised peer URLs list
exit 1

Si un pair est configuré avec un ensemble différent d’arguments de configuration et tente de rejoindre ce cluster, etcd signalera une incompatibilité d’ID de cluster et quittera.

$ etcd --name infra3 --initial-advertise-peer-urls http://10.0.1.13:2380 \
  --listen-peer-urls http://10.0.1.13:2380 \
  --listen-client-urls http://10.0.1.13:2379,http://127.0.0.1:2379 \
  --advertise-client-urls http://10.0.1.13:2379 \
  --initial-cluster infra0=http://10.0.1.10:2380,infra1=http://10.0.1.11:2380,infra3=http://10.0.1.13:2380 \
  --initial-cluster-state=new
etcd: conflicting cluster ID to the target cluster (c6ab534d07e8fcc4 != bc25ea2a74fb18b0). Exiting.
exit 1

Découverte

Dans plusieurs cas, les adresses IP des pairs du cluster ne sont pas connues à l’avance. C’est fréquent lors de l’utilisation de fournisseurs de cloud ou lorsque le réseau utilise DHCP. Dans ces situations, au lieu de spécifier une configuration statique, utilisez un cluster etcd existant pour amorcer un nouveau cluster. Ce processus s’appelle la « découverte ».

Deux méthodes peuvent être utilisées pour la découverte :

  • service de découverte etcd
  • enregistrements DNS SRV

etcd discovery

Pour mieux comprendre la conception du protocole du service de découverte, nous recommandons de lire la documentation du protocole du service de découverte documentation .

Durée de vie d’une URL de découverte

Une URL de découverte identifie un cluster etcd unique. Au lieu de réutiliser une URL de découverte existante, chaque instance etcd partage une nouvelle URL de découverte pour amorcer le nouveau cluster.

En outre, les URL de découverte doivent être utilisées UNIQUEMENT pour le démarrage initial d’un cluster. Pour modifier la composition du cluster une fois qu’il est déjà en cours d’exécution, consultez le guide reconfiguration en temps réel .

Service de découverte etcd personnalisé

La découverte utilise un cluster existant pour amorcer son démarrage. Si vous utilisez un cluster etcd privé, créez une URL comme suit :

$ curl -X PUT https://myetcd.local/v2/keys/discovery/6c007a14875d53d9bf0ef5a6fc0257c817f0fb83/_config/size -d value=3

En définissant la clé size à l’URL, une URL de découverte est créée avec une taille de cluster attendue de 3.

L’URL à utiliser dans ce cas sera https://myetcd.local/v2/keys/discovery/6c007a14875d53d9bf0ef5a6fc0257c817f0fb83 et les membres etcd utiliseront le répertoire https://myetcd.local/v2/keys/discovery/6c007a14875d53d9bf0ef5a6fc0257c817f0fb83 pour s’enregistrer au moment de leur démarrage.

Chaque membre doit avoir un drapeau de nom différent spécifié. Hostname ou machine-id peut être un choix pertinent. Sinon, la découverte échouera en raison d’un nom en double.

Nous lançons maintenant etcd avec les drapeaux pertinents pour chaque membre :

$ etcd --name infra0 --initial-advertise-peer-urls http://10.0.1.10:2380 \
  --listen-peer-urls http://10.0.1.10:2380 \
  --listen-client-urls http://10.0.1.10:2379,http://127.0.0.1:2379 \
  --advertise-client-urls http://10.0.1.10:2379 \
  --discovery https://myetcd.local/v2/keys/discovery/6c007a14875d53d9bf0ef5a6fc0257c817f0fb83
$ etcd --name infra1 --initial-advertise-peer-urls http://10.0.1.11:2380 \
  --listen-peer-urls http://10.0.1.11:2380 \
  --listen-client-urls http://10.0.1.11:2379,http://127.0.0.1:2379 \
  --advertise-client-urls http://10.0.1.11:2379 \
  --discovery https://myetcd.local/v2/keys/discovery/6c007a14875d53d9bf0ef5a6fc0257c817f0fb83
$ etcd --name infra2 --initial-advertise-peer-urls http://10.0.1.12:2380 \
  --listen-peer-urls http://10.0.1.12:2380 \
  --listen-client-urls http://10.0.1.12:2379,http://127.0.0.1:2379 \
  --advertise-client-urls http://10.0.1.12:2379 \
  --discovery https://myetcd.local/v2/keys/discovery/6c007a14875d53d9bf0ef5a6fc0257c817f0fb83

Cela obligera chaque membre à s’enregistrer auprès du service de découverte etcd personnalisé et à démarrer le cluster une fois que toutes les machines auront été enregistrées.

Service de découverte publique etcd

Si aucun cluster existant n’est disponible, utilisez le service de découverte publique hébergé sur discovery.etcd.io. Pour créer une URL de découverte privée à l’aide de l’endpoint « new », utilisez la commande :

$ curl https://discovery.etcd.io/new?size=3
https://discovery.etcd.io/3e86b59982e49066c5d813af1c2e2579cbf573de

Cela créera le cluster avec une taille initiale de 3 membres. Si aucune taille n’est spécifiée, une valeur par défaut de 3 est utilisée.

ETCD_DISCOVERY=https://discovery.etcd.io/3e86b59982e49066c5d813af1c2e2579cbf573de
--discovery https://discovery.etcd.io/3e86b59982e49066c5d813af1c2e2579cbf573de

Chaque membre doit disposer d’un indicateur de nom différent, faute de quoi la découverte échouera en raison de noms en double. Hostname ou machine-id peut être un bon choix.

Nous lançons maintenant etcd avec les drapeaux pertinents pour chaque membre :

$ etcd --name infra0 --initial-advertise-peer-urls http://10.0.1.10:2380 \
  --listen-peer-urls http://10.0.1.10:2380 \
  --listen-client-urls http://10.0.1.10:2379,http://127.0.0.1:2379 \
  --advertise-client-urls http://10.0.1.10:2379 \
  --discovery https://discovery.etcd.io/3e86b59982e49066c5d813af1c2e2579cbf573de
$ etcd --name infra1 --initial-advertise-peer-urls http://10.0.1.11:2380 \
  --listen-peer-urls http://10.0.1.11:2380 \
  --listen-client-urls http://10.0.1.11:2379,http://127.0.0.1:2379 \
  --advertise-client-urls http://10.0.1.11:2379 \
  --discovery https://discovery.etcd.io/3e86b59982e49066c5d813af1c2e2579cbf573de
$ etcd --name infra2 --initial-advertise-peer-urls http://10.0.1.12:2380 \
  --listen-peer-urls http://10.0.1.12:2380 \
  --listen-client-urls http://10.0.1.12:2379,http://127.0.0.1:2379 \
  --advertise-client-urls http://10.0.1.12:2379 \
  --discovery https://discovery.etcd.io/3e86b59982e49066c5d813af1c2e2579cbf573de

Cela obligera chaque membre à s’enregistrer auprès du service de découverte et à démarrer le cluster une fois que tous les membres auront été enregistrés.

Utilisez la variable d’environnement ETCD_DISCOVERY_PROXY pour obliger etcd à utiliser un proxy HTTP afin de se connecter au service de découverte.

Cas d’erreur et d’avertissement

Erreurs du serveur de découverte
$ etcd --name infra0 --initial-advertise-peer-urls http://10.0.1.10:2380 \
  --listen-peer-urls http://10.0.1.10:2380 \
  --listen-client-urls http://10.0.1.10:2379,http://127.0.0.1:2379 \
  --advertise-client-urls http://10.0.1.10:2379 \
  --discovery https://discovery.etcd.io/3e86b59982e49066c5d813af1c2e2579cbf573de
etcd: error: the cluster doesn’t have a size configuration value in https://discovery.etcd.io/3e86b59982e49066c5d813af1c2e2579cbf573de/_config
exit 1
Avertissements

Il s’agit d’un avertissement inoffensif indiquant que l’URL de découverte sera ignorée sur cette machine.

$ etcd --name infra0 --initial-advertise-peer-urls http://10.0.1.10:2380 \
  --listen-peer-urls http://10.0.1.10:2380 \
  --listen-client-urls http://10.0.1.10:2379,http://127.0.0.1:2379 \
  --advertise-client-urls http://10.0.1.10:2379 \
  --discovery https://discovery.etcd.io/3e86b59982e49066c5d813af1c2e2579cbf573de
etcdserver: discovery token ignored since a cluster has already been initialized. Valid log found at /var/lib/etcd

Découverte DNS

Les enregistrements SRV DNS peuvent être utilisés comme mécanisme de découverte. L’option --discovery-srv peut être utilisée pour définir le nom de domaine DNS où les enregistrements SRV de découverte sont situés. La configuration --discovery-srv example.com entraîne la recherche des enregistrements SRV dans l’ordre indiqué :

  • _etcd-server-ssl._tcp.example.com
  • _etcd-server._tcp.example.com

Si _etcd-server-ssl._tcp.example.com est trouvé, etcd tentera le processus d’amorçage via TLS.

Afin d’aider les clients à découvrir le cluster etcd, les enregistrements DNS SRV suivants sont recherchés dans l’ordre indiqué :

  • _etcd-client._tcp.example.com
  • _etcd-client-ssl._tcp.example.com

Si _etcd-client-ssl._tcp.example.com est présent, les clients tenteront de communiquer avec le cluster etcd via SSL/TLS.

Si etcd utilise TLS, l’enregistrement SRV de découverte (par exemple example.com) doit être inclus dans les SAN DNS du certificat SSL en plus de l’hôte, faute de quoi le cluster échouera avec des messages d’erreur similaires aux suivants :

[...] rejected connection from "10.0.1.11:53162" (error "remote error: tls: bad certificate", ServerName "example.com")

Si etcd utilise TLS sans autorité de certification personnalisée, le domaine de découverte (par exemple, example.com) doit correspondre au domaine de l’enregistrement SRV (par exemple, infra1.example.com). Cela permet de limiter les attaques visant à falsifier des enregistrements SRV afin de les faire pointer vers un domaine différent ; le domaine cible aurait alors un certificat valide selon la PKI, mais serait contrôlé par un tiers inconnu.

L’option -discovery-srv-name configure en outre un suffixe dans le nom SRV interrogé lors de la découverte. Utilisez cette option pour distinguer plusieurs clusters etcd situés sous le même domaine. Par exemple, si discovery-srv=example.com et -discovery-srv-name=foo sont définis, les requêtes DNS SRV suivantes sont effectuées :

  • _etcd-server-ssl-foo._tcp.example.com
  • _etcd-server-foo._tcp.example.com

Créer des enregistrements DNS SRV

$ dig +noall +answer SRV _etcd-server._tcp.example.com
_etcd-server._tcp.example.com. 300 IN  SRV  0 0 2380 infra0.example.com.
_etcd-server._tcp.example.com. 300 IN  SRV  0 0 2380 infra1.example.com.
_etcd-server._tcp.example.com. 300 IN  SRV  0 0 2380 infra2.example.com.
$ dig +noall +answer SRV _etcd-client._tcp.example.com
_etcd-client._tcp.example.com. 300 IN SRV 0 0 2379 infra0.example.com.
_etcd-client._tcp.example.com. 300 IN SRV 0 0 2379 infra1.example.com.
_etcd-client._tcp.example.com. 300 IN SRV 0 0 2379 infra2.example.com.
$ dig +noall +answer infra0.example.com infra1.example.com infra2.example.com
infra0.example.com.  300  IN  A  10.0.1.10
infra1.example.com.  300  IN  A  10.0.1.11
infra2.example.com.  300  IN  A  10.0.1.12

Initialiser le cluster etcd à l’aide du DNS

Les membres d’un cluster etcd peuvent annoncer des noms de domaine ou des adresses IP ; le processus d’initialisation résoudra les enregistrements A DNS. À compter de la version 3.2 (la version 3.1 affiche des avertissements), --listen-peer-urls et --listen-client-urls rejettent les noms de domaine pour la liaison sur l’interface réseau.

L’adresse résolue dans --initial-advertise-peer-urls doit correspondre à l’une des adresses résolues figurant dans les cibles SRV. Le membre etcd lit l’adresse résolue afin de déterminer s’il appartient au cluster défini dans les enregistrements SRV.

$ etcd --name infra0 \
--discovery-srv example.com \
--initial-advertise-peer-urls http://infra0.example.com:2380 \
--initial-cluster-token etcd-cluster-1 \
--initial-cluster-state new \
--advertise-client-urls http://infra0.example.com:2379 \
--listen-client-urls http://0.0.0.0:2379 \
--listen-peer-urls http://0.0.0.0:2380
$ etcd --name infra1 \
--discovery-srv example.com \
--initial-advertise-peer-urls http://infra1.example.com:2380 \
--initial-cluster-token etcd-cluster-1 \
--initial-cluster-state new \
--advertise-client-urls http://infra1.example.com:2379 \
--listen-client-urls http://0.0.0.0:2379 \
--listen-peer-urls http://0.0.0.0:2380
$ etcd --name infra2 \
--discovery-srv example.com \
--initial-advertise-peer-urls http://infra2.example.com:2380 \
--initial-cluster-token etcd-cluster-1 \
--initial-cluster-state new \
--advertise-client-urls http://infra2.example.com:2379 \
--listen-client-urls http://0.0.0.0:2379 \
--listen-peer-urls http://0.0.0.0:2380

Le cluster peut également amorcer son démarrage à l’aide d’adresses IP au lieu de noms de domaine :

$ etcd --name infra0 \
--discovery-srv example.com \
--initial-advertise-peer-urls http://10.0.1.10:2380 \
--initial-cluster-token etcd-cluster-1 \
--initial-cluster-state new \
--advertise-client-urls http://10.0.1.10:2379 \
--listen-client-urls http://10.0.1.10:2379 \
--listen-peer-urls http://10.0.1.10:2380
$ etcd --name infra1 \
--discovery-srv example.com \
--initial-advertise-peer-urls http://10.0.1.11:2380 \
--initial-cluster-token etcd-cluster-1 \
--initial-cluster-state new \
--advertise-client-urls http://10.0.1.11:2379 \
--listen-client-urls http://10.0.1.11:2379 \
--listen-peer-urls http://10.0.1.11:2380
$ etcd --name infra2 \
--discovery-srv example.com \
--initial-advertise-peer-urls http://10.0.1.12:2380 \
--initial-cluster-token etcd-cluster-1 \
--initial-cluster-state new \
--advertise-client-urls http://10.0.1.12:2379 \
--listen-client-urls http://10.0.1.12:2379 \
--listen-peer-urls http://10.0.1.12:2380

Depuis la version 3.1.0 (sauf la version 3.2.9), lorsque etcd --discovery-srv=example.com est configuré avec TLS, le serveur n’authentifie les pairs ou clients que si les certificats fournis comportent comme entrée dans le champ Nom alternatif du sujet (SAN) le domaine racine example.com. Voir Notes sur le DNS SRV .

Passerelle

Le passerelle etcd est un proxy TCP simple qui achemine les données réseau vers le cluster etcd. Veuillez consulter le guide gateway pour plus d’informations.

Proxy

Lorsque le drapeau --proxy est défini, etcd s’exécute en mode proxy proxy mode . Ce mode proxy ne prend en charge que l’API etcd v2 ; aucune mise en œuvre de l’API v3 n’est prévue. En revanche, pour la prise en charge de l’API v3, un nouveau proxy doté de fonctionnalités améliorées sera disponible après la sortie d’etcd 3.0.

Pour configurer un cluster etcd avec des proxys de l’API v2, veuillez consulter le document clustering de la version 2.3 d’etcd.

5 - Exécuter des clusters etcd dans des conteneurs

Exécution d’etcd avec Docker en utilisant le bootstrap statique

Le guide suivant explique comment exécuter etcd avec Docker en utilisant le processus de bootstrap statique static bootstrap process .

Docker

Afin d’exposer l’API etcd aux clients situés en dehors de l’hôte Docker, utilisez l’adresse IP hôte du conteneur. Voir docker inspect pour plus de détails sur la manière d’obtenir l’adresse IP. En alternative, spécifiez le drapeau --net=host à la commande docker run afin de passer outre la mise du conteneur dans une pile réseau séparée.

Exécution d’un nœud unique etcd

Utilisez l’adresse IP hôte lors de la configuration d’etcd :

export NODE1=192.168.1.21

Configurez un volume Docker pour stocker les données etcd :

docker volume create --name etcd-data
export DATA_DIR="etcd-data"

Exécutez la dernière version d’etcd (v3.7.0 au moment de la rédaction) :

ETCD_VERSION=v3.7.0
REGISTRY=quay.io/coreos/etcd
# available from v3.2.5
REGISTRY=gcr.io/etcd-development/etcd

docker run \
  -p 2379:2379 \
  -p 2380:2380 \
  --volume=${DATA_DIR}:/etcd-data \
  --name etcd ${REGISTRY}:${ETCD_VERSION} \
  /usr/local/bin/etcd \
  --data-dir=/etcd-data --name node1 \
  --initial-advertise-peer-urls http://${NODE1}:2380 --listen-peer-urls http://0.0.0.0:2380 \
  --advertise-client-urls http://${NODE1}:2379 --listen-client-urls http://0.0.0.0:2379 \
  --initial-cluster node1=http://${NODE1}:2380

Lister le membre du cluster :

etcdctl --endpoints=http://${NODE1}:2379 member list

Exécution d’un cluster etcd à 3 nœuds

REGISTRY=quay.io/coreos/etcd
# available from v3.2.5
REGISTRY=gcr.io/etcd-development/etcd

# For each machine
ETCD_VERSION=v3.7.0
TOKEN=my-etcd-token
CLUSTER_STATE=new
NAME_1=etcd-node-0
NAME_2=etcd-node-1
NAME_3=etcd-node-2
HOST_1=10.20.30.1
HOST_2=10.20.30.2
HOST_3=10.20.30.3
CLUSTER=${NAME_1}=http://${HOST_1}:2380,${NAME_2}=http://${HOST_2}:2380,${NAME_3}=http://${HOST_3}:2380
DATA_DIR=/var/lib/etcd

# For node 1
THIS_NAME=${NAME_1}
THIS_IP=${HOST_1}
docker run \
  -p 2379:2379 \
  -p 2380:2380 \
  --volume=${DATA_DIR}:/etcd-data \
  --name etcd ${REGISTRY}:${ETCD_VERSION} \
  /usr/local/bin/etcd \
  --data-dir=/etcd-data --name ${THIS_NAME} \
  --initial-advertise-peer-urls http://${THIS_IP}:2380 --listen-peer-urls http://0.0.0.0:2380 \
  --advertise-client-urls http://${THIS_IP}:2379 --listen-client-urls http://0.0.0.0:2379 \
  --initial-cluster ${CLUSTER} \
  --initial-cluster-state ${CLUSTER_STATE} --initial-cluster-token ${TOKEN}

# For node 2
THIS_NAME=${NAME_2}
THIS_IP=${HOST_2}
docker run \
  -p 2379:2379 \
  -p 2380:2380 \
  --volume=${DATA_DIR}:/etcd-data \
  --name etcd ${REGISTRY}:${ETCD_VERSION} \
  /usr/local/bin/etcd \
  --data-dir=/etcd-data --name ${THIS_NAME} \
  --initial-advertise-peer-urls http://${THIS_IP}:2380 --listen-peer-urls http://0.0.0.0:2380 \
  --advertise-client-urls http://${THIS_IP}:2379 --listen-client-urls http://0.0.0.0:2379 \
  --initial-cluster ${CLUSTER} \
  --initial-cluster-state ${CLUSTER_STATE} --initial-cluster-token ${TOKEN}

# For node 3
THIS_NAME=${NAME_3}
THIS_IP=${HOST_3}
docker run \
  -p 2379:2379 \
  -p 2380:2380 \
  --volume=${DATA_DIR}:/etcd-data \
  --name etcd ${REGISTRY}:${ETCD_VERSION} \
  /usr/local/bin/etcd \
  --data-dir=/etcd-data --name ${THIS_NAME} \
  --initial-advertise-peer-urls http://${THIS_IP}:2380 --listen-peer-urls http://0.0.0.0:2380 \
  --advertise-client-urls http://${THIS_IP}:2379 --listen-client-urls http://0.0.0.0:2379 \
  --initial-cluster ${CLUSTER} \
  --initial-cluster-state ${CLUSTER_STATE} --initial-cluster-token ${TOKEN}

Pour exécuter etcdctl en utilisant la version 3 de l’API :

docker exec etcd /usr/local/bin/etcdctl put foo bar

Infrastructure physique

Pour provisionner un cluster etcd à 3 nœuds sur du matériel physique, les exemples présents dans le répertoire baremetal peuvent être utiles.

Montage d’un volume de certificat

Le conteneur de version d’étcd ne contient pas de certificats racines par défaut. Pour utiliser HTTPS avec des certificats approuvés par une autorité racine (par exemple, pour la découverte), montez un répertoire de certificats dans le conteneur etcd :

ETCD_VERSION=v3.7.0
REGISTRY=quay.io/coreos/etcd
# available from v3.2.5
REGISTRY=docker://gcr.io/etcd-development/etcd

rkt run \
  --insecure-options=image \
  --volume etcd-ssl-certs-bundle,kind=host,source=/etc/ssl/certs/ca-certificates.crt \
  --mount volume=etcd-ssl-certs-bundle,target=/etc/ssl/certs/ca-certificates.crt \
  ${REGISTRY}:${ETCD_VERSION} -- --name my-name \
  --initial-advertise-peer-urls http://localhost:2380 --listen-peer-urls http://localhost:2380 \
  --advertise-client-urls http://localhost:2379 --listen-client-urls http://localhost:2379 \
  --discovery https://discovery.etcd.io/c11fbcdc16972e45253491a24fcf45e1
ETCD_VERSION=v3.7.0
REGISTRY=quay.io/coreos/etcd
# available from v3.2.5
REGISTRY=gcr.io/etcd-development/etcd

docker run \
  -p 2379:2379 \
  -p 2380:2380 \
  --volume=/etc/ssl/certs/ca-certificates.crt:/etc/ssl/certs/ca-certificates.crt \
  ${REGISTRY}:${ETCD_VERSION} \
  /usr/local/bin/etcd --name my-name \
  --initial-advertise-peer-urls http://localhost:2380 --listen-peer-urls http://localhost:2380 \
  --advertise-client-urls http://localhost:2379 --listen-client-urls http://localhost:2379 \
  --discovery https://discovery.etcd.io/86a9ff6c8cb8b4c4544c1a2f88f8b801

6 - Exécuter des clusters etcd en tant que StatefulSet Kubernetes

Exécution d’etcd en tant que StatefulSet Kubernetes

Ci-dessous montre comment effectuer le processus de bootstrap statique comme un StatefulSet Kubernetes .

Exemple de manifeste

Ce manifeste contient un service et un statefulset pour déployer un cluster etcd statique dans Kubernetes.

Si vous copiez le contenu du manifeste dans un fichier nommé etcd.yaml, vous pouvez l’appliquer à un cluster à l’aide de cette commande.

$ kubectl apply --filename etcd.yaml

Une fois appliqué, attendez que les pods soient prêts.

$ kubectl get pods
NAME     READY   STATUS    RESTARTS   AGE
etcd-0   1/1     Running   0          24m
etcd-1   1/1     Running   0          24m
etcd-2   1/1     Running   0          24m

Le conteneur utilisé dans l’exemple inclut etcdctl et peut être appelé directement à l’intérieur des pods.

$ kubectl exec -it etcd-0 -- etcdctl member list -wtable
+------------------+---------+--------+-------------------------+-------------------------+------------+
|        ID        | STATUS  |  NAME  |       PEER ADDRS        |      CLIENT ADDRS       | IS LEARNER |
+------------------+---------+--------+-------------------------+-------------------------+------------+
| 4f98c3545405a0b0 | started | etcd-2 | http://etcd-2.etcd:2380 | http://etcd-2.etcd:2379 |      false |
| a394e0ee91773643 | started | etcd-0 | http://etcd-0.etcd:2380 | http://etcd-0.etcd:2379 |      false |
| d10297b8d2f01265 | started | etcd-1 | http://etcd-1.etcd:2380 | http://etcd-1.etcd:2379 |      false |
+------------------+---------+--------+-------------------------+-------------------------+------------+

Pour déployer avec un certificat auto-signé, reportez-vous aux en-têtes de configuration commentés commençant par ## TLS afin de trouver les valeurs que vous pouvez décommenter. Des instructions supplémentaires pour générer un certificat avec cert-manager sont fournies dans une section ci-dessous.

# file: etcd.yaml
---
apiVersion: v1
kind: Service
metadata:
  name: etcd
  namespace: default
spec:
  type: ClusterIP
  clusterIP: None
  selector:
    app: etcd
  ##
  ## Ideally we would use SRV records to do peer discovery for initialization.
  ## Unfortunately discovery will not work without logic to wait for these to
  ## populate in the container. This problem is relatively easy to overcome by
  ## making changes to prevent the etcd process from starting until the records
  ## have populated. The documentation on statefulsets briefly talk about it.
  ##   https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/#stable-network-id
  publishNotReadyAddresses: true
  ##
  ## The naming scheme of the client and server ports match the scheme that etcd
  ## uses when doing discovery with SRV records.
  ports:
  - name: etcd-client
    port: 2379
  - name: etcd-server
    port: 2380
  - name: etcd-metrics
    port: 8080
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  namespace: default
  name: etcd
spec:
  ##
  ## The service name is being set to leverage the service headlessly.
  ## https://kubernetes.io/docs/concepts/services-networking/service/#headless-services
  serviceName: etcd
  ##
  ## If you are increasing the replica count of an existing cluster, you should
  ## also update the --initial-cluster-state flag as noted further down in the
  ## container configuration.
  replicas: 3
  ##
  ## For initialization, the etcd pods must be available to eachother before
  ## they are "ready" for traffic. The "Parallel" policy makes this possible.
  podManagementPolicy: Parallel
  ##
  ## To ensure availability of the etcd cluster, the rolling update strategy
  ## is used. For availability, there must be at least 51% of the etcd nodes
  ## online at any given time.
  updateStrategy:
    type: RollingUpdate
  ##
  ## This is label query over pods that should match the replica count.
  ## It must match the pod template's labels. For more information, see the
  ## following documentation:
  ##   https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#label-selectors
  selector:
    matchLabels:
      app: etcd
  ##
  ## Pod configuration template.
  template:
    metadata:
      ##
      ## The labeling here is tied to the "matchLabels" of this StatefulSet and
      ## "affinity" configuration of the pod that will be created.
      ##
      ## This example's labeling scheme is fine for one etcd cluster per
      ## namespace, but should you desire multiple clusters per namespace, you
      ## will need to update the labeling schema to be unique per etcd cluster.
      labels:
        app: etcd
      annotations:
        ##
        ## This gets referenced in the etcd container's configuration as part of
        ## the DNS name. It must match the service name created for the etcd
        ## cluster. The choice to place it in an annotation instead of the env
        ## settings is because there should only be 1 service per etcd cluster.
        serviceName: etcd
    spec:
      ##
      ## Configuring the node affinity is necessary to prevent etcd servers from
      ## ending up on the same hardware together.
      ##
      ## See the scheduling documentation for more information about this:
      ##   https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#node-affinity
      affinity:
        ## The podAntiAffinity is a set of rules for scheduling that describe
        ## when NOT to place a pod from this StatefulSet on a node.
        podAntiAffinity:
          ##
          ## When preparing to place the pod on a node, the scheduler will check
          ## for other pods matching the rules described by the labelSelector
          ## separated by the chosen topology key.
          requiredDuringSchedulingIgnoredDuringExecution:
          ## This label selector is looking for app=etcd
          - labelSelector:
              matchExpressions:
              - key: app
                operator: In
                values:
                - etcd
            ## This topology key denotes a common label used on nodes in the
            ## cluster. The podAntiAffinity configuration essentially states
            ## that if another pod has a label of app=etcd on the node, the
            ## scheduler should not place another pod on the node.
            ##   https://kubernetes.io/docs/reference/labels-annotations-taints/#kubernetesiohostname
            topologyKey: "kubernetes.io/hostname"
      ##
      ## Containers in the pod
      containers:
      ## This example only has this etcd container.
      - name: etcd
        image: quay.io/coreos/etcd:v3.7.0
        imagePullPolicy: IfNotPresent
        ports:
        - name: etcd-client
          containerPort: 2379
        - name: etcd-server
          containerPort: 2380
        - name: etcd-metrics
          containerPort: 8080
        ##
        ## These probes will fail over TLS for self-signed certificates, so etcd
        ## is configured to deliver metrics over port 8080 further down.
        ##
        ## As mentioned in the "Monitoring etcd" page, /readyz and /livez were
        ## added in v3.5.12. Prior to this, monitoring required extra tooling
        ## inside the container to make these probes work.
        ##
        ## The values in this readiness probe should be further validated, it
        ## is only an example configuration.
        readinessProbe:
          httpGet:
            path: /readyz
            port: 8080
          initialDelaySeconds: 10
          periodSeconds: 5
          timeoutSeconds: 5
          successThreshold: 1
          failureThreshold: 30
        ## The values in this liveness probe should be further validated, it
        ## is only an example configuration.
        livenessProbe:
          httpGet:
            path: /livez
            port: 8080
          initialDelaySeconds: 15
          periodSeconds: 10
          timeoutSeconds: 5
          failureThreshold: 3
        env:
        ##
        ## Environment variables defined here can be used by other parts of the
        ## container configuration. They are interpreted by Kubernetes, instead
        ## of in the container environment.
        ##
        ## These env vars pass along information about the pod.
        - name: K8S_NAMESPACE
          valueFrom:
            fieldRef:
             fieldPath: metadata.namespace
        - name: HOSTNAME
          valueFrom:
            fieldRef:
             fieldPath: metadata.name
        - name: SERVICE_NAME
          valueFrom:
            fieldRef:
              fieldPath: metadata.annotations['serviceName']
        ##
        ## Configuring etcdctl inside the container to connect to the etcd node
        ## in the container reduces confusion when debugging.
        - name: ETCDCTL_ENDPOINTS
          value: $(HOSTNAME).$(SERVICE_NAME):2379
        ##
        ## TLS client configuration for etcdctl in the container.
        ## These files paths are part of the "etcd-client-certs" volume mount.
        # - name: ETCDCTL_KEY
        #   value: /etc/etcd/certs/client/tls.key
        # - name: ETCDCTL_CERT
        #   value: /etc/etcd/certs/client/tls.crt
        # - name: ETCDCTL_CACERT
        #   value: /etc/etcd/certs/client/ca.crt
        ##
        ## Use this URI_SCHEME value for non-TLS clusters.
        - name: URI_SCHEME
          value: "http"
        ## TLS: Use this URI_SCHEME for TLS clusters.
        # - name: URI_SCHEME
        # value: "https"
        ##
        ## If you're using a different container, the executable may be in a
        ## different location. This example uses the full path to help remove
        ## ambiguity to you, the reader.
        ## Often you can just use "etcd" instead of "/usr/local/bin/etcd" and it
        ## will work because the $PATH includes a directory containing "etcd".
        command:
        - /usr/local/bin/etcd
        ##
        ## Arguments used with the etcd command inside the container.
        args:
        ##
        ## Configure the name of the etcd server.
        - --name=$(HOSTNAME)
        ##
        ## Configure etcd to use the persistent storage configured below.
        - --data-dir=/data
        ##
        ## In this example we're consolidating the WAL into sharing space with
        ## the data directory. This is not ideal in production environments and
        ## should be placed in it's own volume.
        - --wal-dir=/data/wal
        ##
        ## URL configurations are parameterized here and you shouldn't need to
        ## do anything with these.
        - --listen-peer-urls=$(URI_SCHEME)://0.0.0.0:2380
        - --listen-client-urls=$(URI_SCHEME)://0.0.0.0:2379
        - --advertise-client-urls=$(URI_SCHEME)://$(HOSTNAME).$(SERVICE_NAME):2379
        ##
        ## This must be set to "new" for initial cluster bootstrapping. To scale
        ## the cluster up, this should be changed to "existing" when the replica
        ## count is increased. If set incorrectly, etcd makes an attempt to
        ## start but fail safely.
        - --initial-cluster-state=new
        ##
        ## Token used for cluster initialization. The recommendation for this is
        ## to use a unique token for every cluster. This example parameterized
        ## to be unique to the namespace, but if you are deploying multiple etcd
        ## clusters in the same namespace, you should do something extra to
        ## ensure uniqueness amongst clusters.
        - --initial-cluster-token=etcd-$(K8S_NAMESPACE)
        ##
        ## The initial cluster flag needs to be updated to match the number of
        ## replicas configured. When combined, these are a little hard to read.
        ## Here is what a single parameterized peer looks like:
        ##   etcd-0=$(URI_SCHEME)://etcd-0.$(SERVICE_NAME):2380
        - --initial-cluster=etcd-0=$(URI_SCHEME)://etcd-0.$(SERVICE_NAME):2380,etcd-1=$(URI_SCHEME)://etcd-1.$(SERVICE_NAME):2380,etcd-2=$(URI_SCHEME)://etcd-2.$(SERVICE_NAME):2380
        ##
        ## The peer urls flag should be fine as-is.
        - --initial-advertise-peer-urls=$(URI_SCHEME)://$(HOSTNAME).$(SERVICE_NAME):2380
        ##
        ## This avoids probe failure if you opt to configure TLS.
        - --listen-metrics-urls=http://0.0.0.0:8080
        ##
        ## These are some configurations you may want to consider enabling, but
        ## should look into further to identify what settings are best for you.
        # - --auto-compaction-mode=periodic
        # - --auto-compaction-retention=10m
        ##
        ## TLS client configuration for etcd, reusing the etcdctl env vars.
        # - --client-cert-auth
        # - --trusted-ca-file=$(ETCDCTL_CACERT)
        # - --cert-file=$(ETCDCTL_CERT)
        # - --key-file=$(ETCDCTL_KEY)
        ##
        ## TLS server configuration for etcdctl in the container.
        ## These files paths are part of the "etcd-server-certs" volume mount.
        # - --peer-client-cert-auth
        # - --peer-trusted-ca-file=/etc/etcd/certs/server/ca.crt
        # - --peer-cert-file=/etc/etcd/certs/server/tls.crt
        # - --peer-key-file=/etc/etcd/certs/server/tls.key
        ##
        ## This is the mount configuration.
        volumeMounts:
        - name: etcd-data
          mountPath: /data
        ##
        ## TLS client configuration for etcdctl
        # - name: etcd-client-tls
        #   mountPath: "/etc/etcd/certs/client"
        #   readOnly: true
        ##
        ## TLS server configuration
        # - name: etcd-server-tls
        #   mountPath: "/etc/etcd/certs/server"
        #   readOnly: true
      volumes:
      ##
      ## TLS client configuration
      # - name: etcd-client-tls
      #   secret:
      #     secretName: etcd-client-tls
      #     optional: false
      ##
      ## TLS server configuration
      # - name: etcd-server-tls
      #   secret:
      #     secretName: etcd-server-tls
      #     optional: false
  ##
  ## This StatefulSet will uses the volumeClaimTemplate field to create a PVC in
  ## the cluster for each replica. These PVCs can not be easily resized later.
  volumeClaimTemplates:
  - metadata:
      name: etcd-data
    spec:
      accessModes: ["ReadWriteOnce"]
      ##
      ## In some clusters, it is necessary to explicitly set the storage class.
      ## This example will end up using the default storage class.
      # storageClassName: ""
      resources:
        requests:
          storage: 1Gi

Génération des certificats

Dans cette section, nous utilisons Helm pour installer un opérateur appelé cert-manager .

Avec cert-manager installé dans le cluster, des certificats auto-signés peuvent être générés directement dans le cluster. Ces certificats générés sont placés dans un objet secret pouvant être attaché en tant que fichiers dans des conteneurs.

Voici la commande Helm pour installer cert-manager.

$ helm upgrade --install --create-namespace --namespace cert-manager cert-manager cert-manager --repo https://charts.jetstack.io --set crds.enabled=true

Voici une configuration d’Issuer de cluster exemple pour la génération de certificats auto-signés.

# file: issuer.yaml
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: selfsigned
spec:
  selfSigned: {}

Ce manifeste crée des objets Certificate pour les certificats client et serveur, en faisant référence à l’objet ClusterIssuer « selfsigned ». Les dnsNames doivent constituer une liste exhaustive des noms d’hôte valides pour les certificats créés par cert-manager.

# file: certificates.yaml
---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: etcd-server
  namespace: default
spec:
  secretName: etcd-server-tls
  issuerRef:
    name: selfsigned
    kind: ClusterIssuer
  commonName: etcd
  dnsNames:
  - etcd
  - etcd.default
  - etcd.default.svc.cluster.local
  - etcd-0
  - etcd-0.etcd
  - etcd-0.etcd.default
  - etcd-0.etcd.default.svc
  - etcd-0.etcd.default.svc.cluster.local
  - etcd-1
  - etcd-1.etcd
  - etcd-1.etcd.default
  - etcd-1.etcd.default.svc
  - etcd-1.etcd.default.svc.cluster.local
  - etcd-2
  - etcd-2.etcd
  - etcd-2.etcd.default
  - etcd-2.etcd.default.svc
  - etcd-2.etcd.default.svc.cluster.local
---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: etcd-client
  namespace: default
spec:
  secretName: etcd-client-tls
  issuerRef:
    name: selfsigned
    kind: ClusterIssuer
  commonName: etcd
  dnsNames:
  - etcd
  - etcd.default
  - etcd.default.svc.cluster.local
  - etcd-0
  - etcd-0.etcd
  - etcd-0.etcd.default
  - etcd-0.etcd.default.svc
  - etcd-0.etcd.default.svc.cluster.local
  - etcd-1
  - etcd-1.etcd
  - etcd-1.etcd.default
  - etcd-1.etcd.default.svc
  - etcd-1.etcd.default.svc.cluster.local
  - etcd-2
  - etcd-2.etcd
  - etcd-2.etcd.default
  - etcd-2.etcd.default.svc
  - etcd-2.etcd.default.svc.cluster.local

7 - Modes de défaillance

Types d’échecs et la tolérance d’etcd à leur égard

Les défaillances sont fréquentes dans un déploiement à grande échelle de machines. Une machine défaillante est une machine dont le matériel ou le logiciel présente une anomalie. Plusieurs machines peuvent défaillir simultanément en cas de panne de courant ou de problèmes réseau. Plusieurs types de défaillances peuvent également survenir en même temps ; il est presque impossible d’énumérer toutes les situations de défaillance possibles.

Dans cette section, nous recensons les types d’pannes et discutons de la manière dont etcd est conçu pour y résister. La plupart des utilisateurs, sinon tous, peuvent associer une panne particulière à un type de panne spécifique. Pour se préparer aux rares pannes irréversibles , il est toujours recommandé de sauvegarder le cluster etcd.

Échec mineur des suiveurs

Lorsque moins de la moitié des suiveurs échouent, le cluster etcd peut continuer à accepter des requêtes et à progresser sans interruption majeure. Par exemple, deux échecs de suiveurs n’affectent pas le fonctionnement d’un cluster etcd à cinq membres. Toutefois, les clients perdent la connectivité avec les membres défaillants. Les bibliothèques clientes doivent masquer ces interruptions aux utilisateurs pour les requêtes en lecture en se reconnectant automatiquement à d’autres membres. Les opérateurs doivent s’attendre à une augmentation de la charge système sur les autres membres en raison des reconnexions.

Défaillance du leader

Lorsqu’un leader échoue, le cluster etcd élit automatiquement un nouveau leader. L’élection n’a pas lieu instantanément après l’échec du leader. Elle prend environ un délai d’élection, car le modèle de détection des échecs repose sur un délai d’attente.

Pendant l’élection du leader, le cluster ne peut pas traiter d’écritures. Les requêtes d’écriture envoyées pendant l’élection sont mises en attente jusqu’à l’élection d’un nouveau leader.

Les écritures déjà envoyées au vieux leader mais non encore validées peuvent être perdues. Le nouveau leader peut réécrire n’importe quelle entrée non validée provenant du leader précédent. Du point de vue de l’utilisateur, certaines requêtes d’écriture peuvent expirer après une nouvelle élection de leader. Toutefois, aucune écriture validée n’est jamais perdue.

Le nouveau leader étend automatiquement les délais de tous les bails. Ce mécanisme garantit qu’un bail ne sera pas expiré avant le TTL accordé, même s’il a été accordé par le leader ancien.

Défaillance majoritaire

Lorsque la majorité des membres du cluster échoue, le cluster etcd échoue et ne peut plus accepter d’écritures.

Le cluster etcd ne peut être mis en récupération qu’après la disponibilité de la majorité des membres. Si la majorité des membres ne peut revenir en ligne, l’opérateur doit alors lancer la récupération après sinistre pour restaurer le cluster.

Dès qu’une majorité des membres fonctionne, le cluster etcd élit automatiquement un nouveau leader et redevient sain. Le nouveau leader étend automatiquement les délais de tous les bails. Ce mécanisme garantit qu’aucun bail n’expire en raison d’une indisponibilité du serveur.

Partition réseau

Une partition réseau est similaire à une défaillance mineure d’un suiveur ou à une défaillance du leader. Une partition réseau divise le cluster etcd en deux parties ; l’une dispose d’une majorité de membres, l’autre d’une minorité. Le côté majoritaire devient le cluster disponible, tandis que le côté minoritaire devient indisponible. Il n’y a pas de « split-brain » dans etcd car les membres du cluster sont explicitement added/removed et chaque modification est approuvée par la majorité actuelle des membres.

Si le leader se trouve du côté majoritaire, alors, du point de vue de la majorité, la défaillance correspond à une défaillance d’un suiveur minoritaire. Si le leader se trouve du côté minoritaire, il s’agit d’une défaillance du leader. Le leader du côté minoritaire cède son rôle, et le côté majoritaire élit un nouveau leader.

Une fois que la partition réseau est résolue, le côté minoritaire reconnaît automatiquement le leader provenant du côté majoritaire et restaure son état.

Échec du démarrage

Le démarrage initial d’un cluster réussit uniquement si tous les membres requis démarrent correctement. Si une erreur survient pendant le démarrage initial, supprimez les répertoires de données sur tous les membres, puis redémarrez le cluster avec un nouveau cluster-token ou un nouveau jeton de découverte.

Bien sûr, il est possible de récupérer un cluster initialisé qui a échoué, tout comme on récupère un cluster en cours d’exécution. Toutefois, la récupération de ce cluster prend presque toujours plus de temps et de ressources que le démarrage d’un nouveau cluster, car aucune donnée n’a besoin d’être récupérée.

8 - Récupération après sinistre

etcd Fonctionnalités d’instantané et de restauration v3

etcd est conçu pour résister aux pannes de machines. Un cluster etcd se rétablit automatiquement après des pannes temporaires (par exemple, redémarrages de machine) et tolère jusqu’à (N-1)/2 pannes permanentes pour un cluster composé de N membres. Lorsqu’un membre subit une panne permanente, qu’elle soit due à une défaillance matérielle ou à une corruption du disque, il perd accès au cluster. Si le cluster perd définitivement plus de (N-1)/2 membres, il subit une panne catastrophique, perdant irrévocablement son quorum. Une fois le quorum perdu, le cluster ne peut plus atteindre de consensus et ne peut donc plus accepter de mises à jour.

Pour récupérer après une panne catastrophique, etcd v3 fournit des fonctionnalités d’instantané et de restauration afin de recréer le cluster sans perte de données clés v3. Pour récupérer les clés v2, reportez-vous au guide d’administration v2 .

Instantané de l’espace de clés

La récupération d’un cluster nécessite tout d’abord un instantané de l’espace de clés provenant d’un membre etcd. Un instantané peut être pris à partir d’un membre en cours d’exécution à l’aide de la commande etcdctl snapshot save ou en copiant le fichier member/snap/db depuis un répertoire de données etcd. Par exemple, la commande suivante crée un instantané de l’espace de clés servi par $ENDPOINT dans le fichier snapshot.db :

$ ETCDCTL_API=3 etcdctl --endpoints $ENDPOINT snapshot save snapshot.db

Notez qu’effectuer l’instantané à partir du fichier member/snap/db peut entraîner la perte de données non encore écrites, mais présentes dans le répertoire wal (write-ahead-log).

État d’un instantané

Pour comprendre quelle révision et quel hachage contient un instantané donné, vous pouvez utiliser la commande etcdutl snapshot status :

$ etcdutl snapshot status snapshot.db -w table
+---------+----------+------------+------------+
|  HASH   | REVISION | TOTAL KEYS | TOTAL SIZE |
+---------+----------+------------+------------+
| 7ef846e |   485261 |      11642 |      94 MB |
+---------+----------+------------+------------+

Restauration d’un cluster

Différence de révision

Lorsque vous restaurez un cluster, les clients existants peuvent percevoir une révision qui remonte de plusieurs centaines ou milliers d’unités. Cela est dû au fait qu’un instantané donné ne contient que l’historique des données jusqu’au moment où il a été pris, alors que l’état actuel du cluster peut déjà être plus avancé.

Cela pose particulièrement problème lors de l’exécution de Kubernetes avec etcd, où les contrôleurs et les opérateurs peuvent utiliser ce qu’on appelle informers, qui agissent comme des caches locaux et reçoivent des notifications de mise à jour via des surveillance. Le retour à une révision antérieure peut ne pas actualiser correctement ces caches, entraînant un comportement imprévisible et incohérent dans les contrôleurs.

Lors de la restauration à partir d’un instantané dans le cadre de : consommateurs connus de l’API de surveillance, copies locales mises en cache des données etcd ou de l’utilisation générale de Kubernetes, il est fortement recommandé de procéder à la restauration en utilisant les « augmentations de révision » ci-dessous.

Restauration à partir d’un instantané

Pour restaurer un cluster, il suffit d’un seul fichier d’instantané « db ». La restauration d’un cluster avec etcdutl snapshot restore crée de nouveaux répertoires de données etcd ; tous les membres doivent restaurer à l’aide du même instantané. La restauration écrase certaines métadonnées de l’instantané (en particulier l’ID de membre et l’ID de cluster) ; le membre perd alors son identité antérieure. Cette écrasement des métadonnées empêche le nouveau membre de rejoindre involontairement un cluster existant. Par conséquent, pour démarrer un cluster à partir d’un instantané, la restauration doit démarrer un nouveau cluster logique.

Une restauration simple peut être exécutée comme suit :

$ etcdutl snapshot restore snapshot.db --data-dir output-dir

Vérifications d’intégrité

L’intégrité de l’instantané peut être vérifiée de manière facultative au moment de la restauration. Si l’instantané est pris avec etcdctl snapshot save, il contient un hachage d’intégrité qui est vérifié par etcdutl snapshot restore. Si l’instantané est copié depuis le répertoire de données, aucun hachage d’intégrité n’est présent, et sa restauration ne sera possible qu’en utilisant --skip-hash-check.

Restauration avec augmentation de la révision

Afin de garantir que les révisions ne diminuent jamais après une restauration, vous pouvez utiliser l’option --bump-revision. Cette option prend un entier sur 64 bits, qui indique le nombre de révisions à ajouter à la révision actuelle de l’instantané. Étant donné qu’une écriture dans etcd augmente la révision de un, vous pouvez couvrir un instantané datant d’une semaine en augmentant la révision de 1'000'000'000, à condition que etcd fonctionne avec moins de 1500 écritures par seconde.

Dans le contexte des contrôleurs Kubernetes, il est également important de marquer toutes les révisions, y compris la mise à jour, comme compactées à l’aide de --mark-compacted. Cela garantit que toutes les surveillance sont terminées et qu’etcd ne répond pas aux requêtes concernant les révisions survenues après la prise de l’instantané — ce qui invalide effectivement les caches d’informateurs.

Un appel complet peut avoir l’aspect suivant :

$ etcdutl snapshot restore snapshot.db --bump-revision 1000000000 --mark-compacted --data-dir output-dir

Restauration avec membre mis à jour

Les membres d’un cluster etcd sont stockés dans etcd lui-même et maintenus grâce à l’algorithme de consensus Raft. Lorsque le quorum est entièrement perdu, vous devrez peut-être reconsidérer l’emplacement et la manière dont le nouveau cluster est constitué, par exemple sur un ensemble entièrement nouveau de membres.

Lors de la restauration à partir d’un instantané, vous pouvez fournir directement la nouvelle configuration d’appartenance dans la base de données comme suit :

$ etcdutl snapshot restore snapshot.db \
  --name m1 \
  --data-dir m1.etcd \
  --initial-cluster m1=http://host1:2380,m2=http://host2:2380,m3=http://host3:2380 \
  --initial-cluster-token etcd-cluster-1 \
  --initial-advertise-peer-urls http://host1:2380

Cela garantit que le cluster nouvellement construit ne se connecte qu’aux autres membres restaurés ayant le jeton donné, et non aux membres plus anciens qui pourraient encore être actifs et tenter de se connecter.

En revanche, lors du démarrage d’etcd, vous pouvez fournir --force-new-cluster afin de remplacer l’appartenance au cluster tout en conservant les données d’application existantes. Notez que cette opération est fortement déconseillée, car elle provoquera un arrêt brutal si d’autres membres du cluster précédent sont encore actifs. Veillez à sauvegarder régulièrement des instantanés.

Exemple bout en bout

Prenez un instantané à partir d’un cluster en cours d’exécution à l’aide de :

$ etcdctl snapshot save snapshot.db

En continuant de l’exemple précédent, la commande suivante crée de nouveaux répertoires de données etcd (m1.etcd, m2.etcd, m3.etcd) pour un cluster à trois membres :

$ etcdutl snapshot restore snapshot.db \
  --name m1 \
  --data-dir m1_data_dir.etcd \
  --initial-cluster m1=http://host1:2380,m2=http://host2:2380,m3=http://host3:2380 \
  --initial-cluster-token etcd-cluster-1 \
  --initial-advertise-peer-urls http://host1:2380
$ etcdutl snapshot restore snapshot.db \
  --name m2 \
  --data-dir m2_data_dir.etcd \
  --initial-cluster m1=http://host1:2380,m2=http://host2:2380,m3=http://host3:2380 \
  --initial-cluster-token etcd-cluster-1 \
  --initial-advertise-peer-urls http://host2:2380
$ etcdutl snapshot restore snapshot.db \
  --name m3 \
  --data-dir m3_data_dir.etcd \
  --initial-cluster m1=http://host1:2380,m2=http://host2:2380,m3=http://host3:2380 \
  --initial-cluster-token etcd-cluster-1 \
  --initial-advertise-peer-urls http://host3:2380

Ensuite, démarrez etcd avec les nouveaux répertoires de données :

$ etcd \
  --name m1 \
  --data-dir m1_data_dir.etcd \
  --listen-client-urls http://host1:2379 \
  --advertise-client-urls http://host1:2379 \
  --listen-peer-urls http://host1:2380 &
$ etcd \
  --name m2 \
  --data-dir m2_data_dir.etcd \
  --listen-client-urls http://host2:2379 \
  --advertise-client-urls http://host2:2379 \
  --listen-peer-urls http://host2:2380 &
$ etcd \
  --name m3 \
  --data-dir m3_data_dir.etcd \
  --listen-client-urls http://host3:2379 \
  --advertise-client-urls http://host3:2379 \
  --listen-peer-urls http://host3:2380 &

Le cluster etcd restauré doit maintenant être disponible et servir l’espace de clés depuis l’instantané.

À partir de etcd v3.6, les utilisateurs ne peuvent utiliser que etcdctl pour créer un instantané des données, mais doivent utiliser etcdutl pour restaurer les données à partir d’un instantané. Si --data-dir n’est pas spécifié, la valeur par défaut de --data-dir est <name>.etcd (où <name> correspond à la valeur de --name). Par exemple, si --data-dir n’a pas été fourni et que les membres sont nommés m1, m2 et m3, les répertoires --data-dir seront m1.etcd, m2.etcd et m3.etcd.

9 - etcd gateway

etcd passerelle, quand l’utiliser et comment la configurer

Qu’est-ce que la passerelle etcd

Le passerelle etcd est un proxy TCP simple qui achemine les données réseau vers le cluster etcd. La passerelle est sans état et transparente ; elle n’inspecte ni les requêtes clients ni les réponses du cluster. Elle ne termine pas les connexions TLS, ne réalise pas d’échanges TLS à la place de ses clients, ni ne vérifie si la connexion est sécurisée.

La passerelle prend en charge plusieurs points d’accès serveur etcd et fonctionne selon une politique de rotation simple. Elle ne route que vers les points d’accès disponibles et masque les échecs à ses clients. D’autres politiques de réessai, telles que la rotation pondérée, pourraient être prises en charge à l’avenir.

Quand utiliser la passerelle etcd

Chaque application qui accède à etcd doit d’abord connaître l’adresse d’un point de terminaison client du cluster etcd. Si plusieurs applications sur le même serveur accèdent au même cluster etcd, chaque application doit tout de même connaître les points de terminaison clients annoncés du cluster etcd. Si le cluster etcd est reconfiguré pour utiliser des points de terminaison différents, chaque application peut également devoir mettre à jour sa liste de points de terminaison. Cette reconfiguration à grande échelle est à la fois fastidieuse et sujette aux erreurs.

Le passerelle etcd résout ce problème en agissant comme un point d’accès local stable. Une configuration typique de passerelle etcd fait en sorte que chaque machine exécute une passerelle écoutant sur une adresse locale, et que chaque application etcd se connecte à sa passerelle locale. Le résultat est que seule la passerelle doit mettre à jour ses points de terminaison, et non chaque application individuellement.

En résumé, pour propager automatiquement les modifications des points d’accès du cluster, la passerelle etcd s’exécute sur chaque machine hébergeant plusieurs applications qui accèdent au même cluster etcd.

Quand ne pas utiliser la passerelle etcd

  • Amélioration des performances

La passerelle n’est pas conçue pour améliorer les performances du cluster etcd. Elle ne propose ni mise en cache, ni regroupement ou regroupement par lots des opérations de surveillance. L’équipe etcd développe actuellement un proxy de mise en cache conçu pour améliorer l’évolutivité du cluster.

  • Exécution sur un système de gestion de cluster

Les systèmes de gestion de cluster avancés comme Kubernetes prennent en charge nativement la découverte de services. Les applications peuvent accéder à un cluster etcd à l’aide d’un nom DNS ou d’une adresse IP virtuelle gérée par le système. Par exemple, kube-proxy est équivalent à une passerelle etcd.

Démarrer la passerelle etcd

Considérez un cluster etcd avec les points de terminaison statiques suivants :

NomAdresseNom d’hôtePort
infra010.0.1.10infra0.example.com2379
infra110.0.1.11infra1.example.com2379
infra210.0.1.12infra2.example.com2379

Démarrez la passerelle etcd pour utiliser ces points de terminaison statiques avec la commande :

$ etcd gateway start --endpoints=infra0.example.com:2379,infra1.example.com:2379,infra2.example.com:2379
2016-08-16 11:21:18.867350 I | tcpproxy: ready to proxy client requests to [...]

En revanche, si vous utilisez le DNS pour la découverte de service, envisagez les entrées SRV DNS :

$ dig +noall +answer SRV _etcd-client._tcp.example.com
_etcd-client._tcp.example.com. 300 IN SRV 0 0 2379 infra0.example.com.
_etcd-client._tcp.example.com. 300 IN SRV 0 0 2379 infra1.example.com.
_etcd-client._tcp.example.com. 300 IN SRV 0 0 2379 infra2.example.com.
$ dig +noall +answer infra0.example.com infra1.example.com infra2.example.com
infra0.example.com.  300  IN  A  10.0.1.10
infra1.example.com.  300  IN  A  10.0.1.11
infra2.example.com.  300  IN  A  10.0.1.12

Démarrez la passerelle etcd pour récupérer les points d’accès à partir des entrées DNS SRV avec la commande :

$ etcd gateway start --discovery-srv=example.com
2016-08-16 11:21:18.867350 I | tcpproxy: ready to proxy client requests to [...]

Drapeaux de configuration

etcd cluster

–endpoints

  • Liste séparée par des virgules des cibles serveur etcd vers lesquelles les connexions clients sont acheminées.
  • Valeur par défaut : 127.0.0.1:2379
  • Le port doit être inclus.
  • Exemple incorrect : https://127.0.0.1:2379 (la passerelle ne termine pas le TLS). Notez que la passerelle ne vérifie pas le schéma HTTP ni n’inspecte les requêtes, elle ne fait que les acheminer vers les points de terminaison indiqués.

–discovery-srv

  • Domaine DNS utilisé pour amorcer les points d’accès du cluster via des enregistrements SRV.
  • Par défaut : (non défini)

Réseau

–listen-addr

  • Interface et port d’écoute pour accepter les requêtes clients.
  • Valeur par défaut : 127.0.0.1:23790

–retry-delay

  • Durée du délai avant de réessayer la connexion aux points de terminaison défaillants.
  • Valeur par défaut : 1m0s
  • Exemple non valide : “123” (unité de temps attendue au format)

Sécurité

–insecure-discovery

  • Accepter les enregistrements SRV qui sont non sécurisés ou susceptibles d’attaques d’homme-du-milieu.
  • Valeur par défaut : false

–trusted-ca-file

  • Chemin vers le fichier CA TLS du client pour le cluster etcd, utilisé pour vérifier les points de terminaison retournés par la découverte SRV. Notez qu’il n’est utilisé QUE pour l’authentification des points de terminaison découverts, et non pour établir des connexions de transfert de données. La passerelle ne termine jamais de connexions TLS ni ne crée de connexions TLS en lieu et place de ses clients.
  • Par défaut : (non défini)

10 - Proxie gRPC

Un proxy inversé etcd sans état fonctionnant au niveau gRPC

Le proxy gRPC est un proxy inverse etcd sans état fonctionnant au niveau du protocole gRPC (L7). Le proxy est conçu pour réduire la charge de traitement totale imposée au cluster etcd principal. Pour assurer une évolutivité horizontale, il regroupe les requêtes d’API de surveillance et de bail. Pour protéger le cluster contre les clients abusifs, il met en mémoire tampon les requêtes portant sur des plages de clés.

Le proxy gRPC prend en charge plusieurs points d’entrée de serveur etcd. Au démarrage du proxy, il choisit aléatoirement un point d’entrée de serveur etcd à utiliser. Ce point d’entrée traite toutes les requêtes jusqu’à ce que le proxy détecte une défaillance. Si le proxy gRPC détecte une défaillance d’un point d’entrée, il bascule vers un autre point d’entrée, si disponible, afin de masquer les défaillances à ses clients. D’autres politiques de réessai, telles que le round-robin pondéré, pourraient être prises en charge à l’avenir.

API de surveillance évolutif

Le proxy gRPC regroupe plusieurs observateurs clients (c-watchers) sur la même clé ou plage en un seul observateur (s-watcher) connecté à un serveur etcd. Le proxy diffuse tous les événements provenant du s-watcher à ses c-watchers.

En supposant que N clients effectuent une surveillance sur la même clé, un proxy gRPC peut réduire la charge de surveillance sur le serveur etcd de N à 1. Les utilisateurs peuvent déployer plusieurs proxies gRPC afin de répartir davantage la charge du serveur.

Dans l’exemple suivant, trois clients effectuent une surveillance sur la clé A. Le proxy gRPC regroupe les trois observateurs, créant un seul observateur attaché au serveur etcd.

            +-------------+
            | etcd server |
            +------+------+
                   ^ watch key A (s-watcher)
                   |
           +-------+-----+
           | gRPC proxy  | <-------+
           |             |         |
           ++-----+------+         |watch key A (c-watcher)
watch key A ^     ^ watch key A    |
(c-watcher) |     | (c-watcher)    |
    +-------+-+  ++--------+  +----+----+
    |  client |  |  client |  |  client |
    |         |  |         |  |         |
    +---------+  +---------+  +---------+

Limitations

Pour effectuer une coalescence efficace de plusieurs observateurs clients en un seul observateur, le proxy gRPC effectue une coalescence des nouveaux c-watchers vers un s-watcher existant lorsque cela est possible. Ce s-watcher coalescé peut être hors synchronisation avec le serveur etcd en raison de retards réseau ou d’événements mis en mémoire tampon non livrés. Lorsque la révision de surveillance n’est pas précisée, le proxy gRPC ne garantit pas que le c-watcher commencera à surveiller à partir de la révision la plus récente du magasin. Par exemple, si un client surveille à partir d’un serveur etcd avec la révision 1000, cet observateur commencera à la révision 1000. Si un client surveille à partir du proxy gRPC, il peut commencer à surveiller à partir de la révision 990.

Des limitations similaires s’appliquent à l’annulation. Lorsqu’un observateur est annulé, la révision du serveur etcd peut être supérieure à la révision de la réponse d’annulation.

Ces deux limitations ne devraient pas poser de problème pour la plupart des cas d’utilisation. À l’avenir, des options supplémentaires pourraient être disponibles afin de forcer l’observateur à contourner le proxy gRPC pour des réponses de révision plus précises.

API bail évolutif

Pour maintenir ses bails actifs, un client doit établir au moins une connexion gRPC avec un serveur etcd afin d’envoyer des signaux d’activité périodiques. Si une charge de travail etcd implique une activité de bail importante répartie sur de nombreux clients, ces connexions peuvent entraîner une utilisation excessive du processeur. Pour réduire le nombre total de connexions sur le cluster principal, le proxy prend en charge la fusion des connexions de bail.

En supposant que N clients mettent à jour des bails, un unique proxy gRPC réduit la charge des flux sur le serveur etcd de N à 1. Les déploiements peuvent inclure des proxies gRPC supplémentaires afin de répartir davantage les flux sur plusieurs proxies.

Dans l’exemple suivant, trois clients mettent à jour trois bails indépendants (L1, L2 et L3). Le proxy gRPC fusionne les trois flux de bail client (c-streams) en un seul flux de renouvellement de bail (s-stream) associé à un serveur etcd. Le proxy transfère les battements de cœur de bail côté client provenant des flux c vers le flux s, puis renvoie les réponses aux flux c correspondants.

          +-------------+
          | etcd server |
          +------+------+
                 ^
                 | heartbeat L1, L2, L3
                 | (s-stream)
                 v
         +-------+-----+
         | gRPC proxy  +<-----------+
         +---+------+--+            | heartbeat L3
             ^      ^               | (c-stream)
heartbeat L1 |      | heartbeat L2  |
(c-stream)   v      v (c-stream)    v
      +------+-+  +-+------+  +-----+--+
      | client |  | client |  | client |
      +--------+  +--------+  +--------+

Protection contre les clients abusifs

Le proxy gRPC met en mémoire tampon les réponses aux requêtes lorsqu’il ne compromet pas les exigences de cohérence. Cela peut protéger le serveur etcd contre les clients abusifs exécutant des boucles serrées.

Démarrer le proxy gRPC etcd

Considérez un cluster etcd avec les points de terminaison statiques suivants :

NomAdresseNom d’hôte
infra010.0.1.10infra0.example.com
infra110.0.1.11infra1.example.com
infra210.0.1.12infra2.example.com

Démarrez le proxy gRPC etcd pour utiliser ces points de terminaison statiques avec la commande :

$ etcd grpc-proxy start --endpoints=infra0.example.com,infra1.example.com,infra2.example.com --listen-addr=127.0.0.1:2379

Le proxy gRPC etcd démarre et écoute sur le port 2379. Il achemine les requêtes clientes vers l’un des trois points de terminaison fournis ci-dessus.

Envoi de requêtes via le proxy :

$ ETCDCTL_API=3 etcdctl --endpoints=127.0.0.1:2379 put foo bar
OK
$ ETCDCTL_API=3 etcdctl --endpoints=127.0.0.1:2379 get foo
foo
bar

Synchronisation des points de terminaison client et résolution de noms

Le proxy prend en charge l’enregistrement de ses points d’accès pour la découverte, en écrivant sur un point d’accès défini par l’utilisateur. Cela sert deux objectifs. Premièrement, cela permet aux clients de synchroniser leurs points d’accès avec un ensemble de points d’accès du proxy afin d’assurer une haute disponibilité. Deuxièmement, il agit comme un fournisseur de points d’accès pour etcd gRPC naming .

Inscrivez le ou les proxy en précisant un préfixe défini par l’utilisateur :

$ etcd grpc-proxy start --endpoints=localhost:2379 \
  --listen-addr=127.0.0.1:23790 \
  --advertise-client-url=127.0.0.1:23790 \
  --resolver-prefix="___grpc_proxy_endpoint" \
  --resolver-ttl=60

$ etcd grpc-proxy start --endpoints=localhost:2379 \
  --listen-addr=127.0.0.1:23791 \
  --advertise-client-url=127.0.0.1:23791 \
  --resolver-prefix="___grpc_proxy_endpoint" \
  --resolver-ttl=60

Le proxy répertoriera tous ses membres dans la liste des membres :

ETCDCTL_API=3 etcdctl --endpoints=http://localhost:23790 member list --write-out table

+----+---------+--------------------------------+------------+-----------------+
| ID | STATUS  |              NAME              | PEER ADDRS |  CLIENT ADDRS   |
+----+---------+--------------------------------+------------+-----------------+
|  0 | started | Gyu-Hos-MBP.sfo.coreos.systems |            | 127.0.0.1:23791 |
|  0 | started | Gyu-Hos-MBP.sfo.coreos.systems |            | 127.0.0.1:23790 |
+----+---------+--------------------------------+------------+-----------------+

Cela permet aux clients de découvrir automatiquement les points d’accès du proxy via Sync :

cli, err := clientv3.New(clientv3.Config{
    Endpoints: []string{"http://localhost:23790"},
})
if err != nil {
    log.Fatal(err)
}
defer cli.Close()

// fetch registered grpc-proxy endpoints
if err := cli.Sync(context.Background()); err != nil {
    log.Fatal(err)
}

Notez qu’en cas de configuration d’un proxy sans préfixe de résolveur,

$ etcd grpc-proxy start --endpoints=localhost:2379 \
  --listen-addr=127.0.0.1:23792 \
  --advertise-client-url=127.0.0.1:23792

L’API de liste des membres du grpc-proxy retourne son propre advertise-client-url :

ETCDCTL_API=3 etcdctl --endpoints=http://localhost:23792 member list --write-out table

+----+---------+--------------------------------+------------+-----------------+
| ID | STATUS  |              NAME              | PEER ADDRS |  CLIENT ADDRS   |
+----+---------+--------------------------------+------------+-----------------+
|  0 | started | Gyu-Hos-MBP.sfo.coreos.systems |            | 127.0.0.1:23792 |
+----+---------+--------------------------------+------------+-----------------+

Espace de noms

Supposons qu’une application exige un contrôle total sur l’ensemble de l’espace de clés, mais que le cluster etcd soit partagé avec d’autres applications. Pour permettre à toutes les applications de fonctionner sans se perturber mutuellement, le proxy peut partitionner l’espace de clés etcd de manière à ce que les clients perçoivent un accès à l’espace de clés complet. Lorsque le proxy reçoit le drapeau --namespace, toutes les requêtes clientes entrant dans le proxy sont traduites afin d’ajouter un préfixe défini par l’utilisateur aux clés. Les accès au cluster etcd se font sous ce préfixe, et les réponses du proxy suppriment ce préfixe ; pour le client, il semble qu’aucun préfixe n’existe.

Pour nommer un proxy, lancez-le avec --namespace :

$ etcd grpc-proxy start --endpoints=localhost:2379 \
  --listen-addr=127.0.0.1:23790 \
  --namespace=my-prefix/

Les accès au proxy sont désormais transparentement préfixés sur le cluster etcd :

$ ETCDCTL_API=3 etcdctl --endpoints=localhost:23790 put my-key abc
# OK
$ ETCDCTL_API=3 etcdctl --endpoints=localhost:23790 get my-key
# my-key
# abc
$ ETCDCTL_API=3 etcdctl --endpoints=localhost:2379 get my-prefix/my-key
# my-prefix/my-key
# abc

Terminaison TLS

Met fin au TLS d’un cluster etcd sécurisé en utilisant le proxy gRPC en exposant un point d’accès local non chiffré.

Pour le tester, démarrez un cluster etcd à membre unique avec un client HTTPS :

$ etcd --listen-client-urls https://localhost:2379 --advertise-client-urls https://localhost:2379 --cert-file=peer.crt --key-file=peer.key --trusted-ca-file=ca.crt --client-cert-auth

Vérifiez que le port client est configuré pour servir HTTPS :

# fails
$ ETCDCTL_API=3 etcdctl --endpoints=http://localhost:2379 endpoint status
# works
$ ETCDCTL_API=3 etcdctl --endpoints=https://localhost:2379 --cert=client.crt --key=client.key --cacert=ca.crt endpoint status

Ensuite, démarrez un proxy gRPC sur localhost:12379 en vous connectant au point d’extrémité etcd https://localhost:2379 à l’aide des certificats clients :

$ etcd grpc-proxy start --endpoints=https://localhost:2379 --listen-addr localhost:12379 --cert client.crt --key client.key --cacert=ca.crt --insecure-skip-tls-verify &

Enfin, testez la terminaison TLS en insérant une clé dans le proxy via http :

$ ETCDCTL_API=3 etcdctl --endpoints=http://localhost:12379 put abc def
# OK

Métriques et état de santé

Le proxy gRPC expose les points de terminaison /health et Prometheus /metrics pour les membres etcd définis par --endpoints. Une alternative consiste à définir une URL supplémentaire qui répondra à la fois aux points de terminaison /metrics et /health avec le drapeau --metrics-addr.

$ etcd grpc-proxy start \
  --endpoints https://localhost:2379 \
  --metrics-addr https://0.0.0.0:4443 \
  --listen-addr 127.0.0.1:23790 \
  --key client.key \
  --key-file proxy-server.key \
  --cert client.crt \
  --cert-file proxy-server.crt \
  --cacert ca.pem \
  --trusted-ca-file proxy-ca.pem

Problème connu

L’interface principale du proxy sert à la fois HTTP2 et HTTP/1.1.. Si le proxy est configuré avec TLS comme indiqué dans l’exemple ci-dessus, l’utilisation d’un client tel que cURL contre l’interface d’écoute nécessite de définir explicitement le protocole à HTTP/1.1 dans la requête afin de retourner /metrics ou /health. En utilisant le drapeau --metrics-addr, l’interface secondaire n’aura pas cette exigence.

 $ curl --cacert proxy-ca.pem --key proxy-client.key --cert proxy-client.crt https://127.0.0.1:23790/metrics --http1.1

11 - Recommandations matérielles

Guidelines matériels pour l’administration des clusters etcd

etcd fonctionne généralement correctement avec des ressources limitées à des fins de développement ou de test ; il est courant de développer avec etcd sur un ordinateur portable ou une machine cloud peu coûteuse. Toutefois, lors de l’exécution de clusters etcd en production, certaines recommandations matérielles sont utiles pour une administration appropriée. Ces suggestions ne sont pas des règles strictes ; elles constituent un bon point de départ pour un déploiement productif robuste. Comme toujours, les déploiements doivent être testés avec des charges simulées avant d’être mis en production.

Processeurs

Peu de déploiements etcd nécessitent une grande capacité CPU. Les clusters typiques ont besoin de deux à quatre cœurs pour fonctionner correctement. Les déploiements etcd très chargés, qui servent des milliers de clients ou des dizaines de milliers de requêtes par seconde, sont généralement limités par la CPU, car etcd peut servir les requêtes depuis la mémoire. De tels déploiements nécessitent généralement huit à seize cœurs dédiés.

Mémoire

etcd présente une empreinte mémoire relativement faible, mais ses performances dépendent néanmoins d’une quantité suffisante de mémoire. Un serveur etcd met en cache de manière agressive les données clé-valeur et consacre la majeure partie de sa mémoire restante à la surveillance des observateurs. En général, 8 Go sont suffisants. Pour les déploiements intensifs comportant des milliers d’observateurs et des millions de clés, allouez entre 16 Go et 64 Go de mémoire selon les besoins.

Disques

Les disques rapides constituent le facteur le plus critique pour les performances et la stabilité du déploiement etcd.

Un disque lent augmentera la latence des requêtes etcd et pourrait compromettre la stabilité du cluster. Étant donné que le protocole de consensus d’etcd dépend du stockage persistant des métadonnées dans un journal, une majorité des membres du cluster etcd doit écrire chaque requête sur le disque. En outre, etcd effectue également des points de contrôle incrémentiels de son état sur le disque afin de tronquer ce journal. Si ces écritures prennent trop de temps, les battements de cœur pourraient expirer et déclencher une élection, ce qui affaiblit la stabilité du cluster. En général, pour déterminer si un disque est suffisamment rapide pour etcd, un outil de benchmark tel que fio peut être utilisé. Lisez ici pour un exemple.

etcd est très sensible à la latence d’écriture disque. Une capacité d’au moins 50 IOPS séquentielles (par exemple, un disque dur 7200 RPM) est généralement requise. Pour les clusters fortement sollicités, une capacité de 500 IOPS séquentielles (par exemple, un SSD local typique ou un périphérique de bloc virtuel à haute performance) est recommandée. Notez que la plupart des fournisseurs de cloud publient des IOPS concurrents plutôt que séquentiels ; les IOPS concurrents publiés peuvent être jusqu’à 10 fois supérieurs aux IOPS séquentiels. Pour mesurer les IOPS séquentiels réels, nous recommandons d’utiliser un outil de benchmark disque tel que diskbench ou fio .

etcd nécessite uniquement une bande passante disque modeste, mais une bande passante disque plus élevée permet des temps de récupération plus rapides lorsque membre défaillant doit rattraper le cluster. En général, 10MB/s peut récupérer 100 Mo de données en 15 secondes. Pour les clusters de grande taille, 100MB/s ou supérieur est recommandé pour récupérer 1 Go de données en 15 secondes.

Lorsqu’il est possible, sauvegardez le stockage d’etcd avec un SSD. Un SSD offre généralement des latences d’écriture plus faibles et une variation moindre qu’un disque dur rotatif, ce qui améliore la stabilité et la fiabilité d’etcd. Si vous utilisez un disque dur rotatif, choisissez les disques les plus rapides disponibles (15 000 tr/min). L’utilisation du RAID 0 est également une méthode efficace pour augmenter la vitesse du disque, que ce soit pour les disques rotatifs ou les SSD. Avec au moins trois membres dans le cluster, les variantes de RAID avec miroir et/ou parité sont inutiles ; la réplication cohérente d’etcd assure déjà une haute disponibilité.

Réseau

Les déploiements etcd à plusieurs membres bénéficient d’un réseau rapide et fiable. Afin que etcd soit à la fois cohérent et tolérant aux partitions, un réseau instable présentant des coupures de partition entraînera une disponibilité médiocre. Une faible latence garantit que les membres etcd peuvent communiquer rapidement. Un débit élevé permet de réduire le temps de récupération d’un membre etcd défaillant. Un réseau 1GbE est suffisant pour les déploiements courants de etcd. Pour les grands clusters etcd, un réseau 10GbE réduit le temps moyen de récupération.

Déployez les membres etcd au sein d’un même centre de données lorsque cela est possible, afin d’éviter les surcharges de latence et de réduire la probabilité d’événements de partitionnement. Si un domaine de défaillance dans un autre centre de données est nécessaire, choisissez un centre de données plus proche de celui déjà en place. Veuillez également consulter la documentation tuning pour plus d’informations sur le déploiement à travers des centres de données.

Exemples de configurations matériels

Voici quelques exemples de configurations matériels sur les environnements AWS et GCE. Comme mentionné précédemment, mais doit être souligné malgré tout, les administrateurs doivent tester un déploiement etcd avec une charge de travail simulée avant de le mettre en production.

Notez que ces configurations supposent que ces machines sont entièrement dédiées à etcd. Exécuter d’autres applications en parallèle sur ces machines peut entraîner des conflits de ressources et provoquer une instabilité du cluster.

Petit cluster

Un petit cluster gère moins de 100 clients, moins de 200 requêtes par seconde et stocke au plus 100 Mo de données.

Exemple de charge de travail d’application : un cluster Kubernetes à 50 nœuds

FournisseurTypevCPUsMémoire (Go)IOPS concurrents maxBande passante disque (MB/s)
AWSm4.large28360056,25
GCEn1-standard-2 + 50Go PD SSD27,5150025

Cluster de taille moyenne

Un cluster de taille moyenne prend en charge moins de 500 clients, moins de 1 000 requêtes par seconde et stocke au plus 500 Mo de données.

Exemple de charge de travail d’application : un cluster Kubernetes de 250 nœuds

FournisseurTypevCPUsMémoire (Go)IOPS concurrents maxBande passante disque (MB/s)
AWSm4.xlarge416600093,75
GCEn1-standard-4 + 150Go PD SSD415450075

Grand cluster

Un cluster important sert moins de 1 500 clients, moins de 10 000 requêtes par seconde, et stocke au plus 1 Go de données.

Exemple de charge de travail d’application : un cluster Kubernetes de 1 000 nœuds

FournisseurTypevCPUsMémoire (Go)IOPS simultanés maxBande passante disque (MB/s)
AWSm4.2xlarge8328000125
GCEn1-standard-8 + 250Go PD SSD8307500125

cluster xLarge

Un cluster xLarge prend en charge plus de 1 500 clients, plus de 10 000 requêtes par seconde et stocke plus de 1 Go de données.

Exemple de charge de travail d’application : un cluster Kubernetes de 3 000 nœuds

FournisseurTypevCPUsMémoire (Go)IOPS simultanés maxBande passante disque (MB/s)
AWSm4.4xlarge166416 000250
GCEn1-standard-16 + 500Go PD SSD166015 000250

12 - Maintenance

Guide de maintenance périodique du cluster etcd

Aperçu

Un cluster etcd nécessite une maintenance périodique pour rester fiable. Selon les besoins d’une application etcd, cette maintenance peut généralement être automatisée et effectuée sans interruption de service ni dégradation significative des performances.

Toute maintenance etcd gère les ressources de stockage consommées par l’espace de clés etcd. Une gestion insuffisante de la taille de l’espace de clés est protégée par des quotas d’espace de stockage ; si un membre etcd manque d’espace, un quota déclenchera des alarmes à l’échelle du cluster, mettant le système en mode maintenance à opérations limitées. Pour éviter de manquer d’espace pour les écritures dans l’espace de clés, l’historique de l’espace de clés etcd doit être compacté. L’espace de stockage lui-même peut être récupéré en défragmentant les membres etcd. Enfin, des sauvegardes périodiques d’instantanés de l’état des membres etcd permettent de récupérer toute perte logique de données ou corruption involontaire causée par une erreur opérationnelle.

Rétention du journal Raft

etcd --snapshot-count définit le nombre d’entrées Raft appliquées à conserver en mémoire avant le compactage. Lorsque --snapshot-count est atteint, le serveur persiste d’abord les données d’instantané sur le disque, puis tronque les anciennes entrées. Lorsqu’un suiveur lent demande des journaux antérieurs à un index compacté, le leader envoie un instantané, forçant ainsi le suiveur à écraser son état.

Une valeur plus élevée de --snapshot-count conserve davantage d’entrées Raft en mémoire jusqu’à l’instantané, entraînant ainsi une utilisation mémoire accrue et récurrente . Comme le leader conserve les dernières entrées Raft plus longtemps, un suiveur lent dispose de plus de temps pour se synchroniser avant que le leader ne prenne un instantané. --snapshot-count représente un compromis entre une utilisation mémoire plus élevée et une meilleure disponibilité des suiveurs lents.

Depuis la version 3.2, la valeur par défaut de --snapshot-count a passé de 10 000 à 100 000 .

Sur le plan des performances, un nombre supérieur à 100 000 pour --snapshot-count peut affecter le débit d’écriture. Un plus grand nombre d’objets en mémoire peut ralentir la phase de marquage du ramasse-miettes de runtime.scanobject , et une récupération mémoire peu fréquente rend l’allocation plus lente. Les performances varient selon les charges de travail et les environnements système. Toutefois, en général, un compactage trop fréquent affecte la disponibilité du cluster et le débit d’écriture. Un compactage trop rare est également préjudiciable, car il exerce une pression excessive sur le ramasse-miettes Go. Voir Comprendre les aspects de performance d’etcd et de Raft pour plus de résultats de recherche.

Compactage de l’historique : base de données clé-valeur API v3

Depuis que etcd conserve une historique exacte de son espace de clés, cette historique doit être régulièrement compactée afin d’éviter une dégradation des performances et une épuisement éventuel de l’espace de stockage. La compactation de l’historique de l’espace de clés supprime toutes les informations relatives aux clés remplacées avant une révision donnée de l’espace de clés. L’espace utilisé par ces clés devient alors disponible pour de nouvelles écritures dans l’espace de clés.

L’espace de clés peut être compacté automatiquement selon la politique de rétention des historiques à fenêtre temporelle de etcd, ou manuellement via etcdctl. La méthode etcdctl offre un contrôle fin du processus de compactage, tandis que la compactage automatique convient aux applications qui nécessitent l’historique des clés pendant une durée déterminée.

Un compactage initié par etcdctl fonctionne comme suit :

# compact up to revision 3
$ etcdctl compact 3

Les révisions antérieures à la révision de compactage deviennent inaccessibles :

$ etcdctl get --rev=2 somekey
Error:  rpc error: code = 11 desc = etcdserver: mvcc: required revision has been compacted

Compaction automatique

etcd peut être défini pour effectuer automatiquement le compactage de l’espace de clés à l’aide des options --auto-compaction-mode et --auto-compaction-retention. Deux modes de compactage sont disponibles : periodic (par défaut) et revision.

Compactage périodique

Le compactage périodique conserve une fenêtre temporelle de l’historique de l’espace de clés :

# keep one hour of history
$ etcd --auto-compaction-retention=1h

La valeur de rétention précise la quantité d’historique à conserver. Un enregistrement ne sera pas compacté avant environ cette durée écoulée depuis sa création. Cela garantit que les observateurs lents peuvent encore rattraper leur retard dans la fenêtre de rétention.

Lorsque la période de rétention est supérieure à 1 heure, etcd effectue une compaction toutes les heures tout en maintenant la fenêtre complète de rétention. Lorsque la période de rétention est égale ou inférieure à 1 heure, etcd effectue une compaction à intervalle égal à la période de rétention.

Par exemple, avec --auto-compaction-retention=10h, etcd attend 10 heures pour le premier compactage, puis compacte toutes les heures par la suite :

0hr  (rev = 1)
1hr  (rev = 10)
...
8hr  (rev = 80)
9hr  (rev = 90)
10hr (rev = 100, Compact(1))
11hr (rev = 110, Compact(10))
...

Les valeurs recommandées dépendent du cas d’utilisation :

  • Mises à jour fréquentes des mêmes clés : une période courte, telle que 1h ou 30m
  • Mises à jour peu fréquentes : une période plus longue, telle que 24h, 48h, ou 72h
  • Valeur par défaut générale : 10h

Compactage de révision

Le compactage par révision conserve un nombre fixe de révisions :

# keep 1000 revisions
$ etcd --auto-compaction-mode=revision --auto-compaction-retention=1000

etcd vérifie toutes les 5 minutes et effectue une compaction sur "latest revision" - 1000. Par exemple, lorsque la dernière révision est 30000, elle effectue une compaction à la révision 29000.

défragmentation

Après avoir compacté l’espace de clés, la base de données backend peut présenter une fragmentation interne. Toute fragmentation interne correspond à de l’espace libre pour la base de données backend mais qui continue de consommer de l’espace de stockage. La compactation des anciennes révisions fragmente internement etcd en laissant des espaces vides dans la base de données backend. L’espace fragmenté est disponible pour etcd mais non disponible pour le système de fichiers hôte. Autrement dit, la suppression des données d’application ne libère pas l’espace sur le disque.

Le processus de défragmentation libère cet espace de stockage au système de fichiers. La défragmentation est effectuée au niveau du membre, afin d’éviter des pics de latence affectant l’ensemble du cluster.

Pour défragmenter un membre etcd, utilisez la commande etcdctl defrag :

$ etcdctl defrag
Finished defragmenting etcd member[127.0.0.1:2379]
Avertissement

Notez que la défragmentation d’un membre en cours d’exécution bloque le système en lecture et écriture pendant la reconstruction de ses états

Avertissement

Notez que la demande de défragmentation n’est pas répliquée au sein du cluster. Autrement dit, la demande n’est appliquée qu’au nœud local. Spécifiez tous les membres dans l’option --endpoints ou l’option --cluster pour trouver automatiquement tous les membres du cluster.

Exécutez les opérations de défragmentation pour tous les points d’accès du cluster associé au point d’accès par défaut :

$ etcdctl defrag --cluster
Finished defragmenting etcd member[http://127.0.0.1:2379]
Finished defragmenting etcd member[http://127.0.0.1:22379]
Finished defragmenting etcd member[http://127.0.0.1:32379]

Pour défragmenter directement un répertoire de données etcd lorsque etcd n’est pas en cours d’exécution, utilisez la commande :

etcdutl defrag --data-dir <path-to-etcd-data-dir>

Quota d’espace

La quota d’espace dans etcd garantit un fonctionnement fiable du cluster. Sans quota d’espace, etcd peut connaître des performances médiocres si l’espace de clés devient trop volumineux, ou simplement manquer d’espace de stockage, entraînant un comportement imprévisible du cluster. Si la base de données backend de l’espace de clés de tout membre dépasse la quota d’espace, etcd déclenche une alarme à l’échelle du cluster, qui met le cluster en mode maintenance, acceptant uniquement les lectures et suppressions de clés. Le cluster ne peut reprendre un fonctionnement normal qu’après avoir libéré suffisamment d’espace dans l’espace de clés, défragmenté la base de données backend et effacé l’alarme de quota d’espace.

Par défaut, etcd définit une quota d’espace conservateur adapté à la plupart des applications, mais il peut être configuré en ligne de commande, en octets :

# set a very small 16 MiB quota
$ etcd --quota-backend-bytes=$((16*1024*1024))

La quota d’espace peut être déclenchée par une boucle :

# fill keyspace
$ while [ 1 ]; do dd if=/dev/urandom bs=1024 count=1024  | ETCDCTL_API=3 etcdctl put key  || break; done
...
Error:  rpc error: code = 8 desc = etcdserver: mvcc: database space exceeded
# confirm quota space is exceeded
$ ETCDCTL_API=3 etcdctl --write-out=table endpoint status
+----------------+------------------+-----------+---------+-----------+-----------+------------+
|    ENDPOINT    |        ID        |  VERSION  | DB SIZE | IS LEADER | RAFT TERM | RAFT INDEX |
+----------------+------------------+-----------+---------+-----------+-----------+------------+
| 127.0.0.1:2379 | bf9071f4639c75cc | 2.3.0+git | 18 MB   | true      |         2 |       3332 |
+----------------+------------------+-----------+---------+-----------+-----------+------------+
# confirm alarm is raised
$ ETCDCTL_API=3 etcdctl alarm list
memberID:13803658152347727308 alarm:NOSPACE

Supprimer les données d’espace de clés excessives et défragmenter la base de données du backend ramènera le cluster dans les limites du quota :

# get current revision
$ rev=$(ETCDCTL_API=3 etcdctl --endpoints=:2379 endpoint status --write-out="json" | egrep -o '"revision":[0-9]*' | egrep -o '[0-9].*')
# compact away all old revisions
$ ETCDCTL_API=3 etcdctl compact $rev
compacted revision 1516
# defragment away excessive space
$ ETCDCTL_API=3 etcdctl defrag
Finished defragmenting etcd member[127.0.0.1:2379]
# disarm alarm
$ ETCDCTL_API=3 etcdctl alarm disarm
memberID:13803658152347727308 alarm:NOSPACE
# test puts are allowed again
$ ETCDCTL_API=3 etcdctl put newkey 123
OK

La métrique etcd_mvcc_db_total_size_in_use_in_bytes indique l’utilisation réelle de la base de données après un compactage de l’historique, tandis que etcd_debugging_mvcc_db_total_size_in_bytes affiche la taille de la base de données incluant l’espace libre en attente de défragmentation. Cette dernière n’augmente que lorsque la première est proche d’elle, ce qui signifie qu’une fois que ces deux métriques sont proches du quota, un compactage de l’historique est nécessaire pour éviter de déclencher la limite d’espace.

etcd_debugging_mvcc_db_total_size_in_bytes est renommé en etcd_mvcc_db_total_size_in_bytes à compter de la version 3.4.

Avertissement

Il est possible de recevoir une erreur ErrGRPCNoSpace pour une requête Put/Txn/LeaseGrant, tout en voyant la requête d’écriture réussir en arrière-plan, car etcd vérifie la limite d’espace à la couche API et à la couche Apply, et la couche Apply ne lèvera que l’alarme NOSPACE sans bloquer la transaction.

Sauvegarde d’instantané

Effectuer des instantanés du cluster etcd de manière régulière constitue une sauvegarde durable pour un espace de clés etcd. En prenant des instantanés périodiques de la base de données backend d’un membre etcd, un cluster etcd peut être restauré à un instant donné dans un état connu comme étant valide.

Un instantané est pris avec etcdctl :

$ etcdctl snapshot save backup.db
$ etcdutl --write-out=table snapshot status backup.db
+----------+----------+------------+------------+
|   HASH   | REVISION | TOTAL KEYS | TOTAL SIZE |
+----------+----------+------------+------------+
| fe01cf57 |       10 |          7 | 2.1 MB     |
+----------+----------+------------+------------+

13 - Surveillance d’etcd

Surveillance d’etcd pour l’intégrité du système et le débogage du cluster

Chaque serveur etcd fournit des informations de surveillance locales sur son port client via des points de terminaison HTTP. Les données de surveillance sont utiles à la fois pour le contrôle de santé du système et le débogage du cluster.

Point d’entrée de débogage

Si --log-level=debug est défini, le serveur etcd exporte des informations de débogage sur son port client sous le chemin /debug. Prenez garde à la définition de --log-level=debug, car cela entraînera une dégradation des performances et une journalisation verbose.

Le point de terminaison /debug/pprof est le point de terminaison standard de profilage du runtime Go. Il peut être utilisé pour profiler l’utilisation du processeur, de la mémoire, des verrous et des goroutines. Par exemple, voici comment obtenir les 10 fonctions où etcd consacre le plus de temps :

go tool pprof

$ go tool pprof http://localhost:2379/debug/pprof/profile
Fetching profile from http://localhost:2379/debug/pprof/profile
Please wait... (30s)
Saved profile in /home/etcd/pprof/pprof.etcd.localhost:2379.samples.cpu.001.pb.gz
Entering interactive mode (type "help" for commands)
(pprof) top10
310ms of 480ms total (64.58%)
Showing top 10 nodes out of 157 (cum >= 10ms)
    flat  flat%   sum%        cum   cum%
   130ms 27.08% 27.08%      130ms 27.08%  runtime.futex
    70ms 14.58% 41.67%       70ms 14.58%  syscall.Syscall
    20ms  4.17% 45.83%       20ms  4.17%  github.com/coreos/etcd/vendor/golang.org/x/net/http2/hpack.huffmanDecode
    20ms  4.17% 50.00%       30ms  6.25%  runtime.pcvalue
    20ms  4.17% 54.17%       50ms 10.42%  runtime.schedule
    10ms  2.08% 56.25%       10ms  2.08%  github.com/coreos/etcd/vendor/github.com/coreos/etcd/etcdserver.(*EtcdServer).AuthInfoFromCtx
    10ms  2.08% 58.33%       10ms  2.08%  github.com/coreos/etcd/vendor/github.com/coreos/etcd/etcdserver.(*EtcdServer).Lead
    10ms  2.08% 60.42%       10ms  2.08%  github.com/coreos/etcd/vendor/github.com/coreos/etcd/pkg/wait.(*timeList).Trigger
    10ms  2.08% 62.50%       10ms  2.08%  github.com/coreos/etcd/vendor/github.com/prometheus/client_golang/prometheus.(*MetricVec).hashLabelValues
    10ms  2.08% 64.58%       10ms  2.08%  github.com/coreos/etcd/vendor/golang.org/x/net/http2.(*Framer).WriteHeaders

Le point de terminaison /debug/requests permet d’obtenir des traces gRPC et des statistiques de performance via un navigateur web. Par exemple, voici une requête Range pour la clé abc :

When	Elapsed (s)
2017/08/18 17:34:51.999317 	0.000244 	/etcdserverpb.KV/Range
17:34:51.999382 	 .    65 	... RPC: from 127.0.0.1:47204 deadline:4.999377747s
17:34:51.999395 	 .    13 	... recv: key:"abc"
17:34:51.999499 	 .   104 	... OK
17:34:51.999535 	 .    36 	... sent: header:<cluster_id:14841639068965178418 member_id:10276657743932975437 revision:15 raft_term:17 > kvs:<key:"abc" create_revision:6 mod_revision:14 version:9 value:"asda" > count:1

Point d’extrémité des métriques

Chaque serveur etcd exporte des métriques sous le chemin /metrics sur son port client, et éventuellement sur les emplacements indiqués par --listen-metrics-urls.

Les métriques peuvent être récupérées avec curl :

$ curl -L http://localhost:2379/metrics | grep -v debugging # ignore unstable debugging metrics

# HELP etcd_disk_backend_commit_duration_seconds The latency distributions of commit called by backend.
# TYPE etcd_disk_backend_commit_duration_seconds histogram
etcd_disk_backend_commit_duration_seconds_bucket{le="0.002"} 72756
etcd_disk_backend_commit_duration_seconds_bucket{le="0.004"} 401587
etcd_disk_backend_commit_duration_seconds_bucket{le="0.008"} 405979
etcd_disk_backend_commit_duration_seconds_bucket{le="0.016"} 406464
...

Vérification de santé

Depuis la version 3.3.0, outre la réponse à l’endpoint /metrics, toutes les localisations spécifiées par --listen-metrics-urls répondent également à l’endpoint /health. Cela peut être utile si l’endpoint standard est configuré avec une authentification TLS mutuelle (client), mais qu’un équilibreur de charge ou un service de surveillance doit tout de même accéder à la vérification de santé.

Depuis la version 3.4, deux nouveaux points d’entrée /livez et /readyz ont été ajoutés.

  • le point de terminaison /livez indique si le processus est actif ou s’il nécessite un redémarrage.
  • le point de terminaison /readyz indique si le processus est prêt à servir le trafic.

Les détails de conception des points de terminaison sont documentés dans le KEP .

Chaque point de terminaison inclut plusieurs vérifications de santé individuelles, et vous pouvez utiliser le paramètre verbose pour afficher les détails des vérifications et leur état, par exemple

curl -k http://localhost:2379/readyz?verbose

et vous verriez une réponse similaire à

[+]data_corruption ok
[+]serializable_read ok
[+]linearizable_read ok
ok

L’API HTTP prend également en charge l’exclusion de vérifications spécifiques, par exemple

curl -k http://localhost:2379/readyz?exclude=data_corruption

Prometheus

Exécuter un service de surveillance Prometheus est la méthode la plus simple pour ingérer et enregistrer les métriques d’etcd.

Tout d’abord, installez Prometheus :

PROMETHEUS_VERSION="2.0.0"
wget https://github.com/prometheus/prometheus/releases/download/v$PROMETHEUS_VERSION/prometheus-$PROMETHEUS_VERSION.linux-amd64.tar.gz -O /tmp/prometheus-$PROMETHEUS_VERSION.linux-amd64.tar.gz
tar -xvzf /tmp/prometheus-$PROMETHEUS_VERSION.linux-amd64.tar.gz --directory /tmp/ --strip-components=1
/tmp/prometheus -version

Configurez l’extracteur Prometheus pour cibler les points d’accès du cluster etcd :

cat > /tmp/test-etcd.yaml <<EOF
global:
  scrape_interval: 10s
scrape_configs:
  - job_name: test-etcd
    static_configs:
    - targets: ['10.240.0.32:2379','10.240.0.33:2379','10.240.0.34:2379']
EOF
cat /tmp/test-etcd.yaml

Configurez le gestionnaire Prometheus :

nohup /tmp/prometheus \
    -config.file /tmp/test-etcd.yaml \
    -web.listen-address ":9090" \
    -storage.local.path "test-etcd.data" >> /tmp/test-etcd.log  2>&1 &

Prometheus récupérera désormais les métriques etcd toutes les 10 secondes.

Alerting

Il existe un ensemble d’alertes par défaut pour les clusters etcd v3 destinées à Prometheus .

Note

Notez que les étiquettes job peuvent nécessiter un ajustement pour répondre à un besoin particulier. Les règles ont été rédigées pour s’appliquer à un seul cluster, il est donc recommandé de choisir des étiquettes uniques par cluster.

Grafana

Grafana dispose d’un support intégré pour Prometheus ; ajoutez simplement une source de données Prometheus :

Name:   test-etcd
Type:   Prometheus
Url:    http://localhost:9090
Access: proxy

Ensuite, importez le modèle de tableau de bord par défaut etcd dashboard template et personnalisez-le. Par exemple, si le nom de la source de données Prometheus est my-etcd, les valeurs du champ datasource dans le JSON doivent également être my-etcd.

Tableau de bord d’exemple :

Traçage distribué

À partir de la version 3.5, etcd prend en charge le traçage distribué à l’aide de OpenTelemetry .

Note

Cette fonctionnalité est encore expérimentale et peut être modifiée à tout moment.

Pour activer cette fonctionnalité expérimentale, passez le paramètre --experimental-enable-distributed-tracing=true au serveur etcd, ainsi que le drapeau --experimental-distributed-tracing-sampling-rate=<number> pour choisir le nombre d’échantillons à collecter par million de spans ; le taux d’échantillonnage par défaut est 0.

Configurez le traçage distribué en lançant le serveur etcd avec les indicateurs facultatifs suivants :

  • --experimental-distributed-tracing-address - (Facultatif) - « localhost:4317 » - Adresse du collecteur de traçage.

  • --experimental-distributed-tracing-service-name - (Facultatif) - « etcd » - Nom du service de traçage distribué, doit être identique sur toutes les instances etcd.

  • --experimental-distributed-tracing-instance-id - (Facultatif) - Identifiant d’instance ; bien qu’optionnel, il est fortement recommandé de le définir, et doit être unique par instance etcd.

Avant d’activer le traçage distribué, assurez-vous d’avoir un point de terminaison OpenTelemetry. Si cette adresse diffère de la valeur par défaut, remplacez-la à l’aide du drapeau --experimental-distributed-tracing-address. En raison des différentes manières de faire fonctionner OpenTelemetry, consultez la documentation du collector pour en savoir plus.

Note

Un surcroît de charge ressource existe, comme pour tout signal d’observabilité ; selon nos mesures initiales, cette surcharge pourrait s’élever entre 2 % et 4 % de la charge CPU.

14 - Performances

Comprendre les performances : latence et débit

Comprendre les performances

etcd offre des performances stables et élevées de manière soutenue. Deux facteurs définissent les performances : la latence et le débit. La latence correspond au temps nécessaire pour accomplir une opération. Le débit correspond au nombre total d’opérations effectuées durant une période donnée. En général, la latence moyenne augmente lorsque le débit global augmente, lorsque etcd accepte des requêtes clientes concurrentes. Dans des environnements cloud courants, comme une instance standard n-4 sur Google Compute Engine (GCE) ou un type de machine équivalent sur AWS, un cluster etcd composé de trois membres exécute une requête en moins d’une milliseconde en charge légère, et peut traiter plus de 30 000 requêtes par seconde en charge lourde.

etcd utilise l’algorithme de consensus Raft pour répliquer les requêtes entre les membres et parvenir à un accord. Les performances du consensus, en particulier la latence de validation, sont limitées par deux contraintes physiques : la latence d’E/S réseau et la latence d’E/S disque. Le temps minimal pour finaliser une requête etcd correspond au temps de trajet aller-retour (RTT) réseau entre les membres, plus le temps que fdatasync met à valider les données dans un stockage permanent. Le RTT au sein d’un centre de données peut atteindre plusieurs centaines de microsecondes. Un RTT typique aux États-Unis est d’environ 50 ms, et peut s’élever à 400 ms entre les continents. La latence typique de fdatasync pour un disque rotatif est d’environ 10 ms. Pour les SSD, la latence est souvent inférieure à 1 ms. Pour améliorer le débit, etcd regroupe plusieurs requêtes ensemble et les soumet à Raft. Cette politique de regroupement permet à etcd d’atteindre un haut débit même sous une charge importante.

D’autres sous-systèmes influencent les performances globales d’etcd. Chaque requête etcd sérialisée doit passer par le moteur de stockage MVCC basé sur boltdb, ce qui prend généralement quelques dizaines de microsecondes. de manière périodique, etcd effectue un instantané incrémental de ses requêtes récemment appliquées, qu’il fusionne avec l’instantané précédent sur disque. Ce processus peut entraîner une pointe de latence. Bien que cela ne pose généralement pas de problème sur les SSD, cela peut doubler la latence observée sur les disques durs. De même, les compactages en cours peuvent affecter les performances d’etcd. Heureusement, l’impact est souvent négligeable, car le compactage est progressif, évitant ainsi toute concurrence pour les ressources avec les requêtes régulières. Le système RPC, gRPC, fournit à etcd une API bien définie et extensible, mais introduit également une latence supplémentaire, notamment pour les lectures locales.

Benchmarks

Le benchmark de la performance d’etcd peut être effectué à l’aide de l’outil en ligne de commande benchmark fourni avec etcd.

Pour des mesures de performance de base, nous considérons un cluster etcd composé de trois membres avec la configuration matérielle suivante :

  • Google Cloud Compute Engine
  • 3 machines de 8 vCPU + 16 Go de mémoire + 50 Go de SSD
  • 1 machine (client) de 16 vCPU + 30 Go de mémoire + 50 Go de SSD
  • Ubuntu 17.04
  • etcd 3.2.0, go 1.8.3

Avec cette configuration, etcd peut écrire approximativement :

Nombre de clésTaille de la clé en octetsTaille de la valeur en octetsNombre de connexionsNombre de clientsServeur etcd cibleDébit d’écriture moyen (QPS)Latence moyenne par requêteRSS moyen du serveur
10 000825611leader uniquement5831,6 ms48 Mo
100 00082561001 000leader uniquement44 34122 ms124 Mo
100 00082561001 000tous les membres50 10420 ms126 Mo

Commandes d’exemple :

# write to leader
benchmark --endpoints=${HOST_1} --target-leader --conns=1 --clients=1 \
    put --key-size=8 --sequential-keys --total=10000 --val-size=256
benchmark --endpoints=${HOST_1} --target-leader  --conns=100 --clients=1000 \
    put --key-size=8 --sequential-keys --total=100000 --val-size=256

# write to all members
benchmark --endpoints=${HOST_1},${HOST_2},${HOST_3} --conns=100 --clients=1000 \
    put --key-size=8 --sequential-keys --total=100000 --val-size=256

Les requêtes de lecture linéarisable passent par un quorum de membres du cluster pour atteindre un consensus afin d’obtenir les données les plus récentes. Les requêtes de lecture sérialisable sont moins coûteuses que les lectures linéarisables, car elles sont servies par n’importe quel membre etcd unique, plutôt que par un quorum de membres, au prix d’une possible lecture de données obsolètes. etcd peut effectuer des lectures :

Nombre de requêtesTaille de la clé en octetsTaille de la valeur en octetsNombre de connexionsNombre de clientsCohérenceDébit moyen en lectures (QPS)Latence moyenne par requête
10 000825611Linéarisable1 3530,7 ms
10 000825611Sériealisable2 9090,3 ms
100 00082561001 000Linéarisable141 5785,5 ms
100 00082561001 000Sériealisable185 7582,2 ms

Commandes d’exemple :

# Single connection read requests
benchmark --endpoints=${HOST_1},${HOST_2},${HOST_3} --conns=1 --clients=1 \
    range YOUR_KEY --consistency=l --total=10000
benchmark --endpoints=${HOST_1},${HOST_2},${HOST_3} --conns=1 --clients=1 \
    range YOUR_KEY --consistency=s --total=10000

# Many concurrent read requests
benchmark --endpoints=${HOST_1},${HOST_2},${HOST_3} --conns=100 --clients=1000 \
    range YOUR_KEY --consistency=l --total=100000
benchmark --endpoints=${HOST_1},${HOST_2},${HOST_3} --conns=100 --clients=1000 \
    range YOUR_KEY --consistency=s --total=100000

Nous recommandons d’exécuter le test de charge lors de la mise en place d’un cluster etcd pour la première fois dans un nouvel environnement afin de vérifier que le cluster atteint des performances adéquates ; la latence du cluster et le débit peuvent être sensibles aux légères différences d’environnement.

15 - Conception de la reconfiguration à l'exécution

Conception des commandes de reconfiguration en temps d’exécution d’etcd

La reconfiguration à l’exécution est l’une des fonctionnalités les plus complexes et sujettes aux erreurs dans un système distribué, en particulier dans un système fondé sur le consensus comme etcd.

Lisez la suite pour en savoir plus sur la conception des commandes de reconfiguration en cours d’exécution d’etcd et sur la manière dont nous avons résolu ces problèmes.

Les modifications de configuration en deux phases maintiennent le cluster en sécurité

Dans etcd, toute reconfiguration en cours d’exécution doit suivre deux phases pour des raisons de sécurité. Par exemple, pour ajouter un membre, il faut d’abord informer le cluster de la nouvelle configuration, puis démarrer le nouveau membre.

Phase 1 - Informer le cluster de la nouvelle configuration

Pour ajouter un membre à un cluster etcd, effectuez un appel d’API afin de demander l’ajout d’un nouveau membre au cluster. C’est la seule méthode permettant d’ajouter un nouveau membre à un cluster existant. L’appel d’API se termine lorsque le cluster a accepté le changement de configuration.

Phase 2 - Démarrer un nouveau membre

Pour rejoindre le nouveau membre etcd au cluster existant, précisez le bon initial-cluster et définissez initial-cluster-state sur existing. Lorsque le membre démarre, il contacte d’abord le cluster existant et vérifie que la configuration actuelle du cluster correspond à celle attendue spécifiée dans initial-cluster. Lorsque le nouveau membre démarre correctement, le cluster atteint la configuration attendue.

En divisant le processus en deux phases distinctes, les utilisateurs sont obligés de préciser explicitement les modifications apportées au membre du cluster. Cela accorde en réalité plus de flexibilité aux utilisateurs et simplifie la compréhension du comportement. Par exemple, si une tentative est faite d’ajouter un nouveau membre ayant le même ID qu’un membre existant dans un cluster etcd, l’action échoue immédiatement lors de la première phase, sans affecter le cluster en cours d’exécution. Une protection similaire est mise en place pour empêcher l’ajout accidentel de nouveaux membres. Si un nouveau membre etcd tente de rejoindre le cluster avant que le cluster n’ait accepté le changement de configuration, il ne sera pas accepté par le cluster.

Sans le workflow explicite concernant l’appartenance au cluster, etcd serait vulnérable aux modifications imprévues de l’appartenance au cluster. Par exemple, si etcd est exécuté sous un système d’initialisation tel que systemd, il serait redémarré après avoir été supprimé via l’API d’appartenance, puis tenterait de se réjoindre au cluster au démarrage. Ce cycle se reproduirait chaque fois qu’un membre est supprimé via l’API et que systemd est configuré pour redémarrer etcd après un échec, ce qui est inattendu.

Nous considérons que la reconfiguration à l’exécution doit être une opération rare. Nous avons choisi de la rendre explicite et pilotée par l’utilisateur afin d’assurer la sécurité de la configuration et de maintenir le cluster toujours en fonctionnement sans heurt, sous un contrôle explicite.

Perte permanente du quorum nécessite un nouveau cluster

Si un cluster perd définitivement la majorité de ses membres, un nouveau cluster devra être lancé à partir d’un répertoire de données ancien afin de restaurer l’état précédent.

Il est tout à fait possible de forcer la suppression des membres défaillants du cluster existant afin de procéder à une récupération. Toutefois, nous avons choisi de ne pas prendre en charge cette méthode, car elle contourne la phase normale de validation du consensus, ce qui est dangereux. Si le membre à supprimer n’est pas réellement défaillant ou n’a pas été supprimé de manière forcée par d’autres membres du même cluster, etcd se retrouvera avec un cluster divergent présentant le même clusterID. Cela constitue un risque très important et difficile à debug/fix par la suite.

Avec un déploiement correct, la probabilité de perte définitive de la majorité est très faible. Mais il s’agit d’un problème suffisamment grave pour mériter une attention particulière. Nous recommandons vivement de lire la documentation de récupération après sinistre et de préparer une stratégie de récupération face à une perte définitive de la majorité avant de mettre etcd en production.

N’utilisez pas de service de découverte publique pour la reconfiguration en cours d’exécution

Le service de découverte publique ne doit être utilisé que pour amorcer un cluster. Pour ajouter un membre à un cluster existant, utilisez l’API de reconfiguration en temps d’exécution.

Le service de découverte est conçu pour amorcer un cluster etcd dans un environnement cloud, lorsque les adresses IP de tous les membres ne sont pas connues à l’avance. Une fois le cluster amorcé avec succès, les adresses IP de tous les membres sont connues. Techniquement, le service de découverte ne devrait plus être nécessaire.

Il semble que l’utilisation du service de découverte public soit un moyen pratique de procéder à une reconfiguration en cours d’exécution, puisque le service de découverte possède déjà toutes les informations de configuration du cluster. Toutefois, compter sur le service de découverte public entraîne des difficultés :

  1. introduit des dépendances externes pour l’ensemble du cycle de vie du cluster, et non seulement au moment du démarrage. En cas de problème de réseau entre le cluster et le service de découverte publique, le cluster en sera affecté.

  2. Le service de découverte public doit refléter la configuration d’exécution correcte du cluster tout au long de son cycle de vie. Il doit proposer des mécanismes de sécurité pour éviter les actions non autorisées, ce qui est difficile.

  3. Le service de découverte public doit gérer des dizaines de milliers de configurations de cluster. Le backend de notre service de découverte public n’est pas prêt à supporter cette charge.

Pour disposer d’un service de découverte qui prend en charge la reconfiguration en temps réel, le meilleur choix est de mettre en place le vôtre en interne.

16 - Reconfiguration en cours d'exécution

etcd prise en charge de la reconfiguration en temps réel incrémentielle

etcd dispose d’un support pour la reconfiguration incrémentielle en temps d’exécution, ce qui permet aux utilisateurs de mettre à jour la composition du cluster en cours d’exécution.

Les requêtes de reconfiguration ne peuvent être traitées que lorsque la majorité des membres du cluster sont fonctionnels. Il est fortement recommandé de toujours disposer d’un cluster de taille supérieure à deux en production. Il est dangereux de supprimer un membre d’un cluster à deux membres. La majorité d’un cluster à deux membres est également de deux. En cas d’échec pendant le processus de suppression, le cluster pourrait ne pas être en mesure de progresser et nécessiterait un redémarrage suite à une défaillance de la majorité .

Pour mieux comprendre la conception sous-jacente à la reconfiguration en temps réel, veuillez lire le document de reconfiguration en temps réel .

Cas d’utilisation de la reconfiguration

Cette section explique certaines raisons courantes de reconfiguration d’un cluster. La plupart de ces raisons consistent simplement en des combinaisons d’ajout ou de suppression d’un membre, telles qu’expliquées ci-dessous sous Opérations de reconfiguration du cluster .

Mise à jour ou mise à niveau de plusieurs machines

Si plusieurs membres d’un cluster doivent être déplacés en raison d’une maintenance planifiée (mise à jour matérielle, coupure réseau, etc.), il est recommandé de modifier les membres un par un.

Il est sûr de supprimer le leader, mais un bref temps d’indisponibilité survient pendant le processus d’élection. Si le cluster contient plus de 50 Mo de données v2, il est recommandé de migrer le répertoire de données du membre .

Modifier la taille du cluster

L’augmentation de la taille du cluster peut améliorer la tolérance aux pannes et les performances de lecture. Comme les clients peuvent lire depuis n’importe quel membre, l’augmentation du nombre de membres accroît le débit global des lectures sérialisées.

Réduire la taille du cluster peut améliorer les performances d’écriture du cluster, au prix d’une résilience réduite. Les écritures dans le cluster sont répliquées sur la majorité des membres avant d’être considérées comme validées. Réduire la taille du cluster diminue la taille de la majorité, et chaque écriture est ainsi validée plus rapidement.

Remplacer une machine défaillante

Si une machine tombe en panne à cause d’une défaillance matérielle, d’une corruption du répertoire de données ou d’une autre situation critique, elle doit être remplacée dès que possible. Les machines ayant cessé de fonctionner sans avoir été retirées affectent négativement le quorum et réduisent la tolérance à une défaillance supplémentaire.

Pour remplacer la machine, suivez les instructions pour supprimer le membre du cluster, puis ajouter un nouveau membre à sa place. Si le cluster contient plus de 50 Mo, il est recommandé de migrer le répertoire de données du membre défaillant s’il est toujours accessible.

Redémarrer le cluster après une défaillance majoritaire

Si la majorité du cluster est perdue ou si tous les nœuds ont changé d’adresse IP, une intervention manuelle est nécessaire pour effectuer une récupération en toute sécurité. Les étapes fondamentales du processus de récupération consistent à créer un nouveau cluster à partir des anciennes données , forcer un seul membre à agir en tant que leader, puis utiliser la configuration en temps réel pour ajouter les nouveaux membres à ce nouveau cluster un par un.

Récupérer un cluster suite à une défaillance de la majorité

Si un membre spécifique est perdu, cela revient à remplacer une machine défaillante. Les étapes sont décrites dans Remplacer une machine défaillante .

Opérations de reconfiguration du cluster

Étant donné ces cas d’utilisation, les opérations concernées peuvent être décrites pour chacune.

Avant toute modification, une majorité simple (quorum) des membres etcd doit être disponible. Il s’agit essentiellement de la même exigence que pour toute écriture dans etcd.

Toutes les modifications apportées au cluster doivent être effectuées séquentiellement :

  • Pour mettre à jour les peerURLs d’un seul membre, effectuez une opération de mise à jour
  • Pour remplacer un membre sain, supprimez l’ancien membre puis ajoutez un nouveau membre
  • Pour passer de 3 à 5 membres, effectuez deux opérations d’ajout
  • Pour passer de 5 à 3 membres, effectuez deux opérations de suppression

Tous ces exemples utilisent l’outil en ligne de commande etcdctl fourni avec etcd. Pour modifier l’appartenance sans etcdctl, utilisez l’API membres HTTP v2 ou l’API membres gRPC v3 .

Mettre à jour un membre

Mettre à jour les URL client d’annonce

Pour mettre à jour les URL d’annonce client d’un membre, redémarrez simplement ce membre en spécifiant les URL client mises à jour via le drapeau (--advertise-client-urls) ou la variable d’environnement (ETCD_ADVERTISE_CLIENT_URLS). Le membre redémarré publiera automatiquement les URL mises à jour. Une URL client incorrectement mise à jour n’affecte pas la santé du cluster etcd.

Mettre à jour les URL d’annonce des pairs

Pour mettre à jour les URL d’annonce des pairs d’un membre, mettez à jour explicitement celles-ci à l’aide de la commande member, puis redémarrez le membre. Cette action supplémentaire est nécessaire car la mise à jour des URL de pair modifie la configuration globale du cluster et peut affecter l’intégrité du cluster etcd.

Pour mettre à jour les URL d’annonce du pair, commencez par trouver l’ID du membre cible. Pour lister tous les membres avec etcdctl :

$ etcdctl member list
6e3bd23ae5f1eae0: name=node2 peerURLs=http://localhost:23802 clientURLs=http://127.0.0.1:23792
924e2e83e93f2560: name=node3 peerURLs=http://localhost:23803 clientURLs=http://127.0.0.1:23793
a8266ecf031671f3: name=node1 peerURLs=http://localhost:23801 clientURLs=http://127.0.0.1:23791

Cet exemple va update l’identifiant de membre a8266ecf031671f3 et modifier sa valeur peerURLs en http://10.0.1.10:2380 :

$ etcdctl member update a8266ecf031671f3 --peer-urls=http://10.0.1.10:2380
Updated member with ID a8266ecf031671f3 in cluster

Supprimer un membre

Supposons que l’ID du membre à supprimer soit a8266ecf031671f3. Utilisez la commande remove pour effectuer la suppression :

$ etcdctl member remove a8266ecf031671f3
Removed member a8266ecf031671f3 from cluster

Le membre cible s’arrête lui-même à ce stade et imprime la suppression dans le journal :

etcd: this member has been permanently removed from the cluster. Exiting.

Il est sûr de supprimer le leader, mais le cluster sera inactif pendant la période nécessaire à l’élection d’un nouveau leader. Cette durée correspond normalement au délai d’élection plus la durée du processus de vote.

Ajouter un nouveau membre

Ajouter un membre est un processus en deux étapes :

  • Ajoutez le nouveau membre au cluster au moyen de l’API HTTP des membres , de l’API gRPC des membres ou de la commande etcdctl member add.
  • Démarrez le nouveau membre avec la nouvelle configuration du cluster, y compris la liste actualisée des membres (membres existants + nouveau membre).

etcdctl ajoute un nouveau membre au cluster en spécifiant le nom du membre et les URL de pair annoncés :

$ etcdctl member add infra3 --peer-urls=http://10.0.1.13:2380
added member 9bf1b35fc7761a23 to cluster

ETCD_NAME="infra3"
ETCD_INITIAL_CLUSTER="infra0=http://10.0.1.10:2380,infra1=http://10.0.1.11:2380,infra2=http://10.0.1.12:2380,infra3=http://10.0.1.13:2380"
ETCD_INITIAL_CLUSTER_STATE=existing

etcdctl a informé le cluster du nouveau membre et a affiché les variables d’environnement nécessaires pour le démarrer correctement. Désormais, lancez le processus etcd nouveau avec les drapeaux appropriés pour le nouveau membre :

$ export ETCD_NAME="infra3"
$ export ETCD_INITIAL_CLUSTER="infra0=http://10.0.1.10:2380,infra1=http://10.0.1.11:2380,infra2=http://10.0.1.12:2380,infra3=http://10.0.1.13:2380"
$ export ETCD_INITIAL_CLUSTER_STATE=existing
$ etcd --listen-client-urls http://10.0.1.13:2379 --advertise-client-urls http://10.0.1.13:2379 --listen-peer-urls http://10.0.1.13:2380 --initial-advertise-peer-urls http://10.0.1.13:2380 --data-dir %data_dir%

Le nouveau membre s’exécutera en tant que partie du cluster et commencera immédiatement à se synchroniser avec le reste du cluster.

Lorsque vous ajoutez plusieurs membres, la meilleure pratique consiste à configurer un seul membre à la fois et à vérifier qu’il démarre correctement avant d’ajouter de nouveaux membres. Si vous ajoutez un nouveau membre à un cluster à un seul nœud, le cluster ne peut pas progresser avant que le nouveau membre ne démarre, car il faut deux membres pour atteindre la majorité nécessaire à l’atteinte du consensus. Ce comportement ne se produit que durant la période où etcdctl member add informe le cluster du nouveau membre et où ce dernier établit avec succès une connexion au membre existant.

Ajouter un nouveau membre apprenant

À partir de la version v3.4, etcd prend en charge l’ajout d’un nouveau membre en tant que membre apprenant / membre non votant. La motivation et la conception sont décrites dans le document design doc . Afin de rendre le processus d’ajout d’un nouveau membre plus sûr, et de réduire la durée d’indisponibilité du cluster lors de l’ajout du nouveau membre, il est recommandé de faire rejoindre le nouveau membre au cluster en tant que membre apprenant jusqu’à ce qu’il soit à jour. Ce processus peut être décrit comme une séquence en trois étapes :

  • Ajoutez le nouveau membre comme apprenant au moyen de l’API gRPC des membres ou de la commande etcdctl member add --learner.

  • Démarrez le nouveau membre avec la configuration mise à jour du cluster, incluant la liste des membres mis à jour (membres existants + le nouveau membre). Cette étape est identique à celle précédente.

  • Promouvoir le membre apprenant nouvellement ajouté en membre votant via l’API gRPC members ou la commande etcdctl member promote. Le serveur etcd valide la requête de promotion afin d’assurer sa sécurité opérationnelle. Un membre apprenant ne peut être promu en membre votant qu’après avoir rattrapé le journal Raft du leader. Si un membre apprenant n’a pas encore rattrapé le journal Raft du leader, la requête de promotion échoue (voir la section [cas d’erreur lors de la promotion d’un membre] pour plus de détails). Dans ce cas, l’utilisateur doit attendre puis réessayer ultérieurement.

Dans la version 3.4, le serveur etcd limite le nombre de membres apprenants qu’un cluster peut avoir à un seul. La principale considération est de limiter la charge supplémentaire imposée au leader en raison de la propagation des données du leader vers le membre apprenant.

Utilisez etcdctl member add avec le drapeau --learner pour ajouter un nouveau membre au cluster en tant que membre apprenant.

$ etcdctl member add infra3 --peer-urls=http://10.0.1.13:2380 --learner
Member 9bf1b35fc7761a23 added to cluster a7ef944b95711739

ETCD_NAME="infra3"
ETCD_INITIAL_CLUSTER="infra0=http://10.0.1.10:2380,infra1=http://10.0.1.11:2380,infra2=http://10.0.1.12:2380,infra3=http://10.0.1.13:2380"
ETCD_INITIAL_CLUSTER_STATE=existing

Après avoir lancé le nouveau processus etcd pour le membre apprenant nouvellement ajouté, utilisez etcdctl member promote pour promouvoir le membre apprenant en membre ayant voix délibérative.

$ etcdctl member promote 9bf1b35fc7761a23
Member 9e29bbaa45d74461 promoted in cluster a7ef944b95711739

Cas d’erreur lors de l’ajout de membres

Dans le cas suivant, un nouvel hôte n’est pas inclus dans la liste des nœuds énumérés. Si c’est un nouveau cluster, le nœud doit être ajouté à la liste des membres initiaux du cluster.

$ etcd --name infra3 \
  --initial-cluster infra0=http://10.0.1.10:2380,infra1=http://10.0.1.11:2380,infra2=http://10.0.1.12:2380 \
  --initial-cluster-state existing
etcdserver: assign ids error: the member count is unequal
exit 1

Dans ce cas, indiquez une adresse différente (10.0.1.14:2380) de celle utilisée pour rejoindre le cluster (10.0.1.13:2380) :

$ etcd --name infra4 \
  --initial-cluster infra0=http://10.0.1.10:2380,infra1=http://10.0.1.11:2380,infra2=http://10.0.1.12:2380,infra4=http://10.0.1.14:2380 \
  --initial-cluster-state existing
etcdserver: assign ids error: unmatched member while checking PeerURLs
exit 1

Si etcd démarre en utilisant le répertoire de données d’un membre supprimé, etcd s’arrête automatiquement s’il se connecte à tout membre actif du cluster :

$ etcd
etcd: this member has been permanently removed from the cluster. Exiting.
exit 1

Cas d’erreur lors de l’ajout d’un membre apprenant

Impossible d’ajouter un membre apprenant à un cluster si celui-ci possède déjà 1 membre apprenant (v3.4).

$ etcdctl member add infra4 --peer-urls=http://10.0.1.14:2380 --learner
Error: etcdserver: too many learner members in cluster

Cas d’erreur lors de la promotion d’un membre apprenant

Un membre apprenant ne peut être promu en membre votant que s’il est synchronisé avec le leader.

$ etcdctl member promote 9bf1b35fc7761a23
Error: etcdserver: can only promote a learner member which is in sync with leader

Promouvoir un membre qui n’est pas un membre apprenant échouera.

$ etcdctl member promote 9bf1b35fc7761a23
Error: etcdserver: can only promote a learner member

Promouvoir un membre qui n’existe pas dans le cluster échouera.

$ etcdctl member promote 12345abcde
Error: etcdserver: member not found

Mode de vérification stricte de la configuration (-strict-reconfig-check)

Comme indiqué ci-dessus, la meilleure pratique pour ajouter de nouveaux membres consiste à configurer un seul membre à la fois et à vérifier qu’il démarre correctement avant d’ajouter d’autres nouveaux membres. Cette approche progressive est très importante, car si les nouveaux membres ne sont pas correctement configurés (par exemple, si les URL de pair sont incorrectes), le cluster peut perdre son quorum. La perte de quorum se produit car les nouveaux membres sont pris en compte dans le quorum, même s’ils ne sont pas accessibles depuis les autres membres existants. Une perte de quorum peut également survenir en cas de problème de connectivité ou de problème opérationnel.

Pour éviter ce problème, etcd met à disposition une option -strict-reconfig-check. Si cette option est passée à etcd, celui-ci rejette les demandes de reconfiguration lorsque le nombre de membres démarrés sera inférieur à un quorum du cluster reconfiguré.

Activé par défaut.

17 - Plateformes prises en charge

etcd prise en charge des architectures et systèmes d’exploitation courants

Support tiers

etcd s’exécute sur différentes plates-formes, mais les garanties qu’il fournit dépendent du niveau de prise en charge de la plate-forme :

  • Niveau 1 : entièrement pris en charge par les mainteneurs [etcd][] ; etcd est garanti pour passer tous les tests, y compris les tests fonctionnels et de robustesse.
  • Niveau 2 : etcd est garanti pour passer les tests d’intégration et les tests bout en bout, mais pas nécessairement les tests fonctionnels ou de robustesse.
  • Niveau 3 : etcd est garanti pour être compilé, peut être légèrement testé (ou non), et doit donc être considéré comme instable.

Prise en charge actuelle

Le tableau suivant répertorie les plateformes actuellement prises en charge ainsi que leur niveau de prise en charge correspondant pour etcd :

ArchitectureSystème d’exploitationNiveau de supportResponsables
AMD64Linux1[mainteneurs etcd][]
ARM64Linux1[mainteneurs etcd][]
AMD64Darwin3
ARM64Darwin3
AMD64Windows3
ppc64leLinux3
s390xLinux3

Les plateformes non listées ne sont pas prises en charge.

Prise en charge d’une nouvelle plateforme

Souhaitez-vous contribuer à etcd en tant que « mainteneur officiel » d’une nouvelle plateforme ? En plus d’engager votre soutien à la plateforme, vous devez configurer une intégration continue (CI) d’etcd répondant aux exigences suivantes, selon le niveau de support :

intégration continue etcdNiveau 1Niveau 2Niveau 3
La construction réussit✓✓✓
Les tests unitaires réussissent✓✓
Les tests d’intégration et bout-en-bout réussissent✓✓
Les tests de robustesse réussissent✓

Pour un exemple de configuration du CI de niveau 2 pour ARM64, consultez [PR etcd #12928][].

Plateformes non prises en charge

Pour éviter d’exécuter accidentellement un serveur etcd sur une plateforme non prise en charge, etcd affiche un message d’avertissement et s’arrête immédiatement, sauf si la variable d’environnement ETCD_UNSUPPORTED_ARCH est définie sur l’architecture cible.

Avertissement

Systèmes 32 bits__ etcd présente des problèmes connus sur les systèmes 32 bits en raison d’un bogue dans le runtime Go.

Pour plus d’informations, consultez l’issue Go #599 et la note sur le bogue du paquet

18 - Gestion des versions

Prise en charge de la versionning par etcd

Ce document décrit les versions prises en charge par le projet etcd.

Versioning des services et versions prises en charge

Les versions d’etcd sont exprimées sous la forme x.y.z, où x représente la version majeure, y la version mineure et z la version de correctif, conformément à la terminologie Semantic Versioning . Les nouvelles versions mineures peuvent ajouter des fonctionnalités supplémentaires à l’API.

Le projet etcd maintient des branches de version pour la version actuelle et les versions précédentes. Par exemple, lorsque v3.5 est la version actuelle, v3.4 est prise en charge. Lorsque v3.6 est publiée, v3.4 n’est plus pris en charge.

Les correctifs applicables, y compris les correctifs de sécurité, peuvent être appliqués en retour à ces deux branches de version, selon leur gravité et leur faisabilité. Les versions correctives sont créées à partir de ces branches lorsque nécessaire.

Les responsables du projet Maintainers détiennent cette décision.

Vous pouvez vérifier la version du cluster etcd en cours d’exécution avec etcdctl :

etcdctl --endpoints=127.0.0.1:2379 endpoint status

Versionning de l’API

Les réponses de l’API v3 ne doivent pas changer après la version 3.0.0, mais de nouvelles fonctionnalités seront ajoutées au fil du temps.

19 - Corruption des données

etcd corruption des données et récupération

etcd dispose d’une détection automatique des corruption de données intégrée afin d’éviter que l’état du membre ne diverge.

Activation détection corruption données

Détection de corruption de données possible à l’aide de :

  • Vérification initiale, activée avec le drapeau --experimental-initial-corrupt-check.
  • Vérification périodique de :
    • Hachage de la révision compactée, activée avec le drapeau --experimental-compact-hash-check-enabled.
    • Hachage de la dernière révision, activée avec le drapeau --experimental-corrupt-check-time.

La vérification initiale sera exécutée lors du démarrage du membre etcd. Le membre comparera son état persistant avec celui des autres membres et quittera l’exécution s’il détecte une incohérence.

Les deux vérifications périodiques seront exécutées par le leader du cluster dans un cluster déjà en cours d’exécution. Le leader comparera son état persistant aux autres membres et déclenchera une alarme CORRUPT en cas de désaccord. Les deux vérifications ont le même objectif, mais il est recommandé de les activer toutes les deux afin d’équilibrer performance et temps de détection.

  • Vérification de hachage de la révision compactée – nécessite une compactage régulière, coût minimal sur la performance, gère les suiveurs lents.
  • Vérification de hachage de la dernière révision – coût élevé sur la performance, ne gère pas les suiveurs lents ni les compactages fréquents.

Vérification du hachage de révision compactée

Lorsqu’il est activé à l’aide du drapeau --experimental-compact-hash-check-enabled, la vérification est exécutée toutes les minutes. Cette fréquence peut être ajustée à l’aide du drapeau --experimental-compact-hash-check-time selon le format suivant : 1m - toutes les minutes, 1h - toutes les heures. Cette vérification étend le compactage afin d’effectuer également le calcul d’un checksum pouvant être comparé entre les membres du cluster. Elle ne provoque pas de balayage supplémentaire de la base de données, ce qui la rend très peu coûteuse, mais nécessite un compactage régulier dans le cluster.

Vérification du hachage de la dernière révision

Activé à l’aide du drapeau --experimental-corrupt-check-time, nécessite de préciser une période d’exécution au format : 1m - toutes les minutes, 1h - toutes les heures. La période recommandée est de quelques heures en raison du coût élevé en performance. L’exécution d’un contrôle nécessite le calcul d’un somme de contrôle en analysant l’intégralité du contenu etcd à la révision indiquée.

Restauration d’un membre corrompu

Il existe trois façons de restaurer un membre corrompu :

  • Purger l’état persistant du membre
  • Remplacer le membre
  • Restaurer tout le cluster

Une fois que le membre corrompu est restauré, l’alarme CORRUPT peut être supprimée.

Purger l’état persistant d’un membre

L’état des membres peut être supprimé en procédant comme suit :

  1. Arrêt de l’instance etcd.
  2. Sauvegarde du répertoire de données etcd.
  3. Déplacement du sous-répertoire snap depuis le répertoire de données etcd.
  4. Démarrage de etcd avec --initial-cluster-state=existing et la liste des membres du cluster indiquée dans --initial-cluster.

Le membre etcd est censé télécharger un instantané à jour depuis le leader.

Remplacer le membre

Un membre peut être remplacé par :

  1. Arrêt de l’instance etcd.
  2. Sauvegarde du répertoire de données etcd.
  3. Suppression du répertoire de données.
  4. Suppression du membre du cluster en exécutant etcdctl member remove.
  5. Ajout du membre à nouveau en exécutant etcdctl member add.
  6. Démarrage de etcd avec --initial-cluster-state=existing et la liste des membres du cluster indiquée dans --initial-cluster.

Restaurer l’intégralité du cluster

Un cluster peut être restauré en sauvegardant un instantané depuis le leader actuel et en le restorant sur tous les membres. Exécutez etcdctl snapshot save contre le leader et suivez procédure de restauration d’un cluster .