Tutorial

Comment mettre en place et sécuriser un cluster etcd avec Ansible sur Ubuntu 18.04

Published on August 26, 2020
Default avatar

By Daniel Li

Staff Software Engineer

Français
Comment mettre en place et sécuriser un cluster etcd avec Ansible sur Ubuntu 18.04

L’auteur a choisi Wikimedia Foundation pour recevoir un don dans le cadre du programme Write for Donations.

Introduction

etcd est un magasin de valeurs clés distribué qui repose sur de nombreuses plateformes et outils, dont Kubernetes , Vulcand et Doorman . Dans Kubernetes, etcd est utilisé comme magasin de configuration globale qui stocke l’état du cluster. Savoir administrer etcd est essentiel pour administrer un cluster Kubernetes. Bien qu’il existe de nombreuses offres Kubernetes gérées, également connues sous le nom de Kubernetes-as-a-Service , qui vous déchargent de cette charge administrative, de nombreuses entreprises choisissent encore de gérer des clusters Kubernetes autogérés sur site en raison de la flexibilité qu’ils apportent.

La première moitié de cet article vous guidera dans la mise en place d’un cluster de 3 nœuds etcd sur les serveurs Ubuntu 18.04. La seconde moitié se concentrera sur la sécurisation du cluster en utilisant la Transport Layer Secutiry (Sécurité de la couche transport), ou TLS  Pour exécuter chaque installation de manière automatisée, nous utiliserons Ansible tout au long de ce tutoriel. Ansible est un outil de gestion de configuration similaire à Puppet , Chef , et SaltStack ; il nous permet de définir chaque étape de configuration de manière déclarative, dans des fichiers appelés playbooks .

À la fin de ce tutoriel, vous disposerez d’un cluster sécurisé de 3 nœuds etcd fonctionnant sur vos serveurs.  Vous disposerez également d’un playbook Ansible qui vous permettra de recréer de manière répétée et cohérente la même configuration sur un nouvel ensemble de serveurs.

Conditions préalables

Avant de commencer ce guide, vous aurez besoin des éléments suivants :

Avertissement : Le but de cet article étant de fournir une introduction à la mise en place d’un cluster etcd sur un réseau privé, les trois serveurs Ubuntu 18.04 de cette configuration n’ont pas été testés avec un pare-feu et sont accessibles en tant que root user Dans une installation de production, tout nœud exposé à l’internet public nécessiterait un pare-feu et un sudo user pour adhérer aux meilleures pratiques de sécurité. Pour plus d’informations, consultez le tutoriel Configuration initiale du serveur avec Ubuntu 18.04.

Etape 1 - Configuration d’Ansible pour le nœud de contrôle

Ansible est un outil utilisé pour gérer les serveurs.  Les serveurs qu’Ansible gère sont appelés les nœuds gérés, et la machine qui fait tourner Ansible est appelée le nœud de contrôle. Ansible fonctionne en utilisant les clés SSH sur le nœud de contrôle pour accéder aux nœuds gérés. Une fois qu’une session SSH est établie, Ansible exécute un ensemble de scripts pour fournir et configurer les nœuds gérés. Dans cette étape, nous allons tester que nous sommes capables d’utiliser Ansible pour nous connecter aux nœuds gérés et exécuter la commande hostname .

Une journée typique pour un administrateur système peut impliquer la gestion de différents ensembles de nœuds. Par exemple, vous pouvez utiliser Ansible pour mettre à disposition de nouveaux serveurs, mais l’utiliser ensuite pour reconfigurer un autre ensemble de serveurs. Pour permettre aux administrateurs de mieux organiser l’ensemble des nœuds gérés, Ansible propose le concept d’inventaire sur les hôtes (ou inventaire en abrégé). Vous pouvez définir chaque nœud que vous souhaitez gérer avec Ansible à l’intérieur d’un fichier d’inventaire et les organiser en groupes. Ensuite, lorsque vous exécutez les commandes ansible et ansible-playbook, vous pouvez spécifier à quels hôtes ou groupes la commande s’applique.

Par défaut, Ansible lit le fichier d’inventaire à partir de /etc/ansible/hosts ; cependant, nous pouvons spécifier un fichier d’inventaire différent en utilisant le drapeau --inventory (ou -i en abrégé).

Pour commencer, créez un nouveau répertoire sur votre machine locale (le nœud de contrôle) pour y placer tous les fichiers de ce tutoriel :

  1. mkdir -p $HOME/playground/etcd-ansible

Ensuite, entrez dans le répertoire que vous venez de créer :

  1. cd $HOME/playground/etcd-ansible

À l’intérieur du répertoire, créez et ouvrez un fichier d’inventaire vierge nommé hosts à l’aide de votre éditeur :

  1. nano $HOME/playground/etcd-ansible/hosts

Dans le fichier hosts, listez chacun de vos nœuds gérés selon le format suivant, en remplaçant les adresses IP publiques mises en évidence par les adresses IP publiques réelles de vos serveurs :

~/playground/etcd-ansible/hosts
[etcd]
etcd1 ansible_host=etcd1_public_ip  ansible_user=root
etcd2 ansible_host=etcd2_public_ip  ansible_user=root
etcd3 ansible_host=etcd3_public_ip  ansible_user=root

La ligne [etcd] définit un groupe appelé etcd . Dans la définition du groupe, nous énumérons tous les nœuds que nous gérons. Chaque ligne commence par un alias (par exemple, etcd1 ), ce qui nous permet de nous référer à chaque hôte en utilisant un nom facile à retenir au lieu d’une longue adresse IP. Les variables ansible_host et ansible_user sont des variables Ansible . Dans ce cas, elles sont utilisées pour fournir à Ansible les adresses IP publiques et les noms d’utilisateur SSH à utiliser lors de la connexion via SSH.

Pour s’assurer qu’Ansible est capable de se connecter à nos nœuds gérés, nous pouvons tester la connectivité en utilisant Ansible pour exécuter la commande hostname sur chacun des hôtes du groupe etcd :

  1. ansible etcd -i hosts -m command -a hostname

Décomposons cette commande pour apprendre ce que chaque partie signifie :

  • etcd : spécifie le modèle d’hôte à utiliser pour déterminer quels hôtes de l’inventaire sont gérés avec cette commande.  Ici, nous utilisons le nom du groupe comme modèle d’hôte.
  • -i hosts : spécifie le fichier d’inventaire à utiliser.
  • Commande -m : la fonctionnalité derrière Ansible est fournie par des modules . Le module de commande prend l’argument transmis et l’exécute comme une commande sur chacun des nœuds gérés. Ce tutoriel introduira quelques autres modules Ansible au fur et à mesure de notre progression.
  • -a hostname : l’argument à passer dans le module. Le nombre et les types d’arguments dépendent du module.

Après avoir exécuté la commande, vous trouverez la sortie suivante, ce qui signifie qu’Ansible est configuré correctement :

Output
etcd2 | CHANGED | rc=0 >> etcd2 etcd3 | CHANGED | rc=0 >> etcd3 etcd1 | CHANGED | rc=0 >> etcd1

Chaque commande qu’Ansible exécute est appelée une _tâche _. L’utilisation d’ansible sur la ligne de commande pour exécuter des tâches est appelée exécution de commandes ad hoc. L’avantage des commandes ad hoc est qu’elles sont rapides et ne nécessitent que peu de réglages ; l’inconvénient est qu’elles fonctionnent manuellement et ne peuvent donc pas être confiées à un système de contrôle de version comme Git .

Une légère amélioration serait d’écrire un script shell et d’exécuter nos commandes en utilisant le module de script d’Ansible .  Cela nous permettrait d’enregistrer les étapes de configuration que nous avons suivies pour le contrôle de version. Cependant, les scripts shell sont impératifs, ce qui signifie que nous sommes responsables de la détermination des commandes à exécuter (les « comment ») pour configurer le système dans l’état souhaité. Ansible, d’autre part, préconise une approche déclarative, où nous définissons « quel » état souhaité de notre serveur doit se trouver à l’intérieur des fichiers de configuration, et Ansible est responsable d’amener le serveur à cet état souhaité.

L’approche déclarative est préférable parce que l’intention du fichier de configuration est immédiatement communiquée, ce qui signifie qu’il est plus facile à comprendre et à maintenir. De plus, il incombe à Ansible, et non plus à l’administrateur, de traiter les cas marginaux, ce qui nous épargne beaucoup de travail.

Maintenant que vous avez configuré le nœud de contrôle Ansible pour communiquer avec les nœuds gérés, nous vous présenterons, dans une prochaine étape, les playbooks Ansible, qui vous permettent de spécifier des tâches de manière déclarative.

Étape 2 - Obtenir les noms d’hôte des nœuds gérés à l’aide de playbooks Ansible

Au cours de cette étape, nous reproduirons ce qui a été fait à l’étape 1 en imprimant les noms d’hôte des nœuds gérés, mais au lieu d’exécuter des tâches ad hoc, nous définirons chaque tâche de manière déclarative comme un playbook ansible et nous l’exécuterons. L’objectif de cette étape est de démontrer comment fonctionnent les playbooks Ansible ; nous réaliserons des tâches beaucoup plus substantielles avec des playbooks dans des étapes ultérieures.

Dans le répertoire de votre projet, créez un nouveau fichier nommé playbook.yaml en utilisant votre éditeur :

  1. nano $HOME/playground/etcd-ansible/playbook.yaml

À l’intérieur de playbook.yaml, ajoutez les lignes suivantes :

~/playground/etcd-ansible/playbook.yaml
- hosts: etcd
  tasks:
    - name: "Retrieve hostname"
      command: hostname
      register: output
    - name: "Print hostname"
      debug: var=output.stdout_lines

Fermez et enregistrez le fichier playbook.yaml en appuyant sur CTRL+X, puis sur Y.

Le playbook contient une liste de plays ; chaque play contient une liste de tâches qui doivent être exécutées sur tous les hôtes correspondant au modèle d’hôte spécifié par la clé d’hôtes. Dans ce playbook, nous avons un play qui contient deux tâches. La première tâche exécute la commande hostname en utilisant le module de commande et enregistre la sortie dans une variable nommée output . Dans la deuxième tâche, nous utilisons le module debug pour imprimer la propriété stdout_lines de la variable output.

Nous pouvons maintenant exécuter ce playbook en utilisant la commande ansible-playbook :

  1. ansible-playbook -i hosts playbook.yaml

Vous trouverez la sortie suivante, ce qui signifie que votre playbook fonctionne correctement :

Output
PLAY [etcd] *********************************************************************************************************************** TASK [Gathering Facts] ************************************************************************************************************ ok: [etcd2] ok: [etcd3] ok: [etcd1] TASK [Retrieve hostname] ********************************************************************************************************** changed: [etcd2] changed: [etcd3] changed: [etcd1] TASK [Print hostname] ************************************************************************************************************* ok: [etcd1] => { "output.stdout_lines": [ "etcd1" ] } ok: [etcd2] => { "output.stdout_lines": [ "etcd2" ] } ok: [etcd3] => { "output.stdout_lines": [ "etcd3" ] } PLAY RECAP ************************************************************************************************************************ etcd1 : ok=3 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 etcd2 : ok=3 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 etcd3 : ok=3 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0

Note : ansible-playbook utilise parfois cowsay comme moyen ludique d’imprimer les titres. Si vous trouvez beaucoup de vaches en ASCII imprimées sur votre terminal, vous savez maintenant pourquoi. Pour désactiver cette fonctionnalité, réglez la variable d’environnement ANSIBLE_NOCOWS sur 1 avant de lancer ansible-playbook en exécutant export ANSIBLE_NOCOWS=1 dans votre shell.

Dans cette étape, nous sommes passés de l’exécution de tâches ad hoc impératives à l’exécution de playbooks déclaratifs. Dans la prochaine étape, nous remplacerons ces deux tâches de démonstration par des tâches qui mettront en place notre cluster etcd.

Étape 3 - Installation d’etcd sur les nœuds gérés

Dans cette étape, nous vous montrerons les commandes pour installer etcd manuellement et nous vous montrerons comment traduire ces mêmes commandes en tâches à l’intérieur de notre playbook Ansible.

etcd et son client etcdctl sont disponibles sous forme de binaires, que nous allons télécharger, extraire et déplacer dans un répertoire qui fait partie de la variable d’environnement PATH. Lorsqu’ils sont configurés manuellement, voici les étapes que nous suivons pour chacun des nœuds gérés :

  1. mkdir -p /opt/etcd/bin
  2. cd /opt/etcd/bin
  3. wget -qO- https://storage.googleapis.com/etcd/v3.3.13/etcd-v3.3.13-linux-amd64.tar.gz | tar --extract --gzip --strip-components=1
  4. echo 'export PATH="$PATH:/opt/etcd/bin"' >> ~/.profile
  5. echo 'export ETCDCTL_API=3" >> ~/.profile

Les quatre premières commandes permettent de télécharger et d’extraire les binaires dans le répertoire /opt/etcd/bin/. Par défaut, le client etcdctl utilisera l’API v2 pour communiquer avec le serveur etcd. Puisque nous utilisons etcd v3.x, la dernière commande fixe la variable d’environnement ETCDCTL_API à 3 .

Note : Ici, nous utilisons etcd v3.3.13 construit pour une machine avec des processeurs qui utilisent le jeu d’instructions AMD64. Vous pouvez trouver des binaires pour d’autres systèmes et d’autres versions sur la page officielle des versions de GitHub.

Pour reproduire les mêmes étapes dans un format standardisé, nous pouvons ajouter des tâches à notre playbook. Ouvrez le fichier playbook.yaml dans votre éditeur :

  1. nano $HOME/playground/etcd-ansible/playbook.yaml

Remplacez la totalité du fichier playbook.yaml par le contenu suivant :

~/playground/etcd-ansible/playbook.yaml
- hosts: etcd
  become: True
  tasks:
    - name: "Create directory for etcd binaries"
      file:
        path: /opt/etcd/bin
        state: directory
        owner: root
        group: root
        mode: 0700
    - name: "Download the tarball into the /tmp directory"
      get_url:
        url: https://storage.googleapis.com/etcd/v3.3.13/etcd-v3.3.13-linux-amd64.tar.gz
        dest: /tmp/etcd.tar.gz
        owner: root
        group: root
        mode: 0600
        force: True
    - name: "Extract the contents of the tarball"
      unarchive:
        src: /tmp/etcd.tar.gz
        dest: /opt/etcd/bin/
        owner: root
        group: root
        mode: 0600
        extra_opts:
          - --strip-components=1
        decrypt: True
        remote_src: True
    - name: "Set permissions for etcd"
      file:
        path: /opt/etcd/bin/etcd
        state: file
        owner: root
        group: root
        mode: 0700
    - name: "Set permissions for etcdctl"
      file:
        path: /opt/etcd/bin/etcdctl
        state: file
        owner: root
        group: root
        mode: 0700
    - name: "Add /opt/etcd/bin/ to the $PATH environment variable"
      lineinfile:
        path: /etc/profile
        line: export PATH="$PATH:/opt/etcd/bin"
        state: present
        create: True
        insertafter: EOF
    - name: "Set the ETCDCTL_API environment variable to 3"
      lineinfile:
        path: /etc/profile
        line: export ETCDCTL_API=3
        state: present
        create: True
        insertafter: EOF

Chaque tâche utilise un module ; pour cet ensemble de tâches, nous utilisons les modules suivants :

  • file : pour créer le répertoire /opt/etcd/bin, et pour définir plus tard les permissions des fichiers pour les binaires etcd et etcdctl.
  • get_url : pour télécharger le tarball gzippé sur les nœuds gérés.
  • unarchive : pour extraire et déballer les binaires etcd et etcdctl du tarball gzippé.
  • lineinfile : pour ajouter une entrée dans le fichier .profile.

Pour appliquer ces changements, fermez et enregistrez le fichier playbook.yaml en appuyant sur CTRL+X suivi de Y . Ensuite, sur le terminal, exécutez à nouveau la même commande ansible-playbook :

  1. ansible-playbook -i hosts playbook.yaml

La section PLAY RECAP de la sortie n’affichera que ok et changed :

Output
... PLAY RECAP ************************************************************************************************************************ etcd1 : ok=8 changed=7 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 etcd2 : ok=8 changed=7 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 etcd3 : ok=8 changed=7 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0

Pour confirmer une installation correcte d’etcd, il faut lancer manuellement SSH dans l’un des nœuds gérés et exécuter etcd et etcdctl :

  1. ssh root@etcd1_public_ip

etcd1_public_ip sont les adresses IP publiques du serveur nommé etcd1 . Une fois que vous avez obtenu un accès SSH, lancez etcd --version pour imprimer la version d’etcd installée :

  1. etcd --version

Vous trouverez une sortie similaire à ce qui est montré dans la suite, ce qui signifie que le binaire etcd est installé avec succès :

Output
etcd Version: 3.3.13 Git SHA: 98d3084 Go Version: go1.10.8 Go OS/Arch: linux/amd64

Pour confirmer qu’etcdctl est installé avec succès, lancez la version d'etcdctl :

  1. etcdctl version

Vous recevrez un résultat similaire à celui qui suit :

Output
etcdctl version: 3.3.13 API version: 3.3

Notez que la sortie indique API version: 3.3, ce qui confirme également que notre variable d’environnement ETCDCTL_API a été définie correctement.

Sortez du serveur etcd1 pour revenir à votre environnement local.

Nous avons maintenant installé avec succès etcd et etcdctl sur tous nos nœuds gérés. Dans la prochaine étape, nous ajouterons d’autres tâches à notre play pour faire fonctionner etcd comme un service de fond.

Etape 4 - Création d’un fichier d’unité pour etcd

La façon la plus rapide d’exécuter etcd avec Ansible peut être d’utiliser le module command pour exécuter /opt/etcd/bin/etcd . Cependant, cela ne fonctionnera pas car cela fera fonctionner etcd comme un processus de premier plan. L’utilisation du module command entraînera le blocage d’Ansible en attendant le retour de la commande etcd, ce qu’il ne fera jamais. Dans cette étape, nous allons donc mettre à jour notre playbook afin d’utiliser notre binaire etcd en tant que service de fond à la place.

Ubuntu 18.04 utilise systemd comme système d’initialisation, ce qui signifie que nous pouvons créer de nouveaux services en écrivant des fichiers unitaires et en les plaçant dans le répertoire /etc/systemd/system/.

Tout d’abord, dans le répertoire de notre projet, créez un nouveau répertoire nommé files/ :

  1. mkdir files

Ensuite, à l’aide de votre éditeur, créez un nouveau fichier nommé etcd.service dans ce répertoire :

  1. nano files/etcd.service

Ensuite, copiez le bloc de code suivant dans le fichier files/etcd.service :

~/playground/etcd-ansible/files/etcd.service
[Unit]
Description=etcd distributed reliable key-value store

[Service]
Type=notify
ExecStart=/opt/etcd/bin/etcd
Restart=always

Ce fichier d’unité définit un service qui exécute l’exécutable dans /opt/etcd/bin/etcd , notifie à systemd la fin de l’initialisation, et redémarre toujours s’il se termine.

Note : Si vous souhaitez en savoir plus sur les fichiers systemd et les fichiers d’unité, ou si vous voulez adapter le fichier d’unité à vos besoins, lisez le guide Comprendre les unités et les fichiers d’unité systemd.

Fermez et enregistrez le fichier files/etcd.service en appuyant sur CTRL+X suivi de Y .

Ensuite, nous devons ajouter une tâche à l’intérieur de notre playbook qui copiera le fichier local files/etcd.service dans le répertoire /etc/systemd/system/etcd.service pour chaque noeud géré. Nous pouvons le faire en utilisant le module copy.

Ouvrez votre playbook :

  1. nano $HOME/playground/etcd-ansible/playbook.yaml

Ajoutez la tâche surlignée suivante à la fin de nos tâches existantes :

~/playground/etcd-ansible/playbook.yaml
- hosts: etcd
  become: True
  tasks:
    ...
    - name: "Set the ETCDCTL_API environment variable to 3"
      lineinfile:
        path: /etc/profile
        line: export ETCDCTL_API=3
        state: present
        create: True
        insertafter: EOF
    - name: "Create a etcd service"
      copy:
        src: files/etcd.service
        remote_src: False
        dest: /etc/systemd/system/etcd.service
        owner: root
        group: root
        mode: 0644

En copiant le fichier unitaire dans le fichier /etc/systemd/system/etcd.service , un service est maintenant défini.

Sauvegardez et quittez le playbook.

Exécutez à nouveau la même commande ansible-playbook pour appliquer les nouveaux changements :

  1. ansible-playbook -i hosts playbook.yaml

Pour confirmer que les changements ont bien été appliqués, il faut d’abord faire entrer les SSH dans l’un des nœuds gérés :

  1. ssh root@etcd1_public_ip

Ensuite, lancez systemctl status etcd pour interroger systemd sur l’état du service etcd :

  1. systemctl status etcd

Vous trouverez la sortie suivante, qui indique que le service est chargé :

Output
● etcd.service - etcd distributed reliable key-value store Loaded: loaded (/etc/systemd/system/etcd.service; static; vendor preset: enabled) Active: inactive (dead) ...

Note : La dernière ligne ( Active : inactive (dead)) de la sortie indique que le service est inactif, ce qui signifie qu’il ne sera pas exécuté automatiquement au démarrage du système. Cela est prévu et n’est pas une erreur.

Appuyez sur q pour revenir au shell, puis exécutez exit pour sortir du nœud géré et revenir à votre shell local :

  1. exit

Dans cette étape, nous avons mis à jour notre playbook pour faire fonctionner le binaire etcd comme un service systemd. Dans la prochaine étape, nous continuerons à mettre en place etcd en lui fournissant un espace pour stocker ses données.

Étape 5 - Configurer le répertoire de données

etcd est un magasin de données à valeur clé, ce qui signifie que nous devons lui fournir de l’espace pour stocker ses données. Dans cette étape, nous allons mettre à jour notre playbook afin de définir un répertoire de données dédié à l’usage d’etcd.

Ouvrez votre playbook :

  1. nano $HOME/playground/etcd-ansible/playbook.yaml

Ajoutez la tâche suivante à la fin de la liste des tâches :

~/playground/etcd-ansible/playbook.yaml
- hosts: etcd
  become: True
  tasks:
    ...
    - name: "Create a etcd service"
      copy:
        src: files/etcd.service
        remote_src: False
        dest: /etc/systemd/system/etcd.service
        owner: root
        group: root
        mode: 0644
    - name: "Create a data directory"
      file:
        path: /var/lib/etcd/{{ inventory_hostname }}.etcd
        state: directory
        owner: root
        group: root
        mode: 0755

Ici, nous utilisons /var/lib/etcd/hostname.etcd comme répertoire de données, où hostname est le nom d’hôte du nœud géré actuel. inventory_hostname est une variable qui représente le nom d’hôte du nœud géré actuel ; sa valeur est automatiquement alimentée par Ansible. La syntaxe des accolades (c’est-à-dire {{ inventory_hostname }} ) est utilisée pour la substitution de variables, supportée par le moteur de modèle Jinja2, qui est le moteur de modèle par défaut pour Ansible.

Fermez l’éditeur de texte et enregistrez le fichier.

Ensuite, nous devons demander à etcd d’utiliser ce répertoire de données. Nous le faisons en passant le point terminal data-dir à etcd. Pour définir les points terminaux etcd, nous pouvons utiliser une combinaison de variables d’environnement, de drapeaux de ligne de commande et de fichiers de configuration. Pour ce tutoriel, nous utiliserons un fichier de configuration, car il est beaucoup plus judicieux d’isoler toutes les configurations dans un fichier, plutôt que d’avoir la configuration éparpillée dans notre playbook.

Dans le répertoire de votre projet, créez un nouveau répertoire nommé templates/ :

  1. mkdir templates

Ensuite, à l’aide de votre éditeur, créez un nouveau fichier nommé etcd.conf.yaml.j2 dans le répertoire :

  1. nano templates/etcd.conf.yaml.j2

Ensuite, copiez la ligne suivante et collez-la dans le fichier :

~/playground/etcd-ansible/templates/etcd.conf.yaml.j2
data-dir: /var/lib/etcd/{{ inventory_hostname }}.etcd

Ce fichier utilise la même syntaxe de substitution de variables Jinja2 que notre playbook. Pour substituer les variables et télécharger le résultat sur chaque hôte géré, nous pouvons utiliser le module template. Il fonctionne de la même manière que la copie, sauf qu’il effectue une substitution variable avant le téléchargement.

Sortez d’etcd.conf.yaml.j2 , puis ouvrez votre playbook :

  1. nano $HOME/playground/etcd-ansible/playbook.yaml

Ajoutez les tâches suivantes à la liste des tâches pour créer un répertoire et y télécharger le fichier de configuration modèle :

~/playground/etcd-ansible/playbook.yaml
- hosts: etcd
  become: True
  tasks:
    ...
    - name: "Create a data directory"
      file:
        ...
        mode: 0755
    - name: "Create directory for etcd configuration"
      file:
        path: /etc/etcd
        state: directory
        owner: root
        group: root
        mode: 0755
    - name: "Create configuration file for etcd"
      template:
        src: templates/etcd.conf.yaml.j2
        dest: /etc/etcd/etcd.conf.yaml
        owner: root
        group: root
        mode: 0600

Enregistrez et fermez le fichier.

Comme nous avons effectué ce changement, nous devons mettre à jour le fichier d’unité de notre service pour lui transmettre l’emplacement de notre fichier de configuration (c’est-à-dire /etc/etcd/etcd.conf.yaml ).

Ouvrez le fichier de service etcd sur votre machine locale :

  1. nano files/etcd.service

Mettez à jour le fichier files/etcd.service en ajoutant l’indicateur --config-file mis en évidence dans ce qui suit :

~/playground/etcd-ansible/files/etcd.service
[Unit]
Description=etcd distributed reliable key-value store

[Service]
Type=notify
ExecStart=/opt/etcd/bin/etcd --config-file /etc/etcd/etcd.conf.yaml
Restart=always

Enregistrez et fermez le fichier.

Dans cette étape, nous avons utilisé notre playbook pour fournir un répertoire de données à etcd afin de stocker ses données. Dans l’étape suivante, nous ajouterons quelques tâches supplémentaires pour redémarrer le service etcd et le lancer au démarrage.

Étape 6 - Activation et démarrage du service etcd

Chaque fois que nous apportons des modifications au fichier d’unité d’un service, nous devons redémarrer le service pour qu’elles prennent effet. Nous pouvons le faire en exécutant la commande systemctl restart etcd. En outre, pour que le service etcd démarre automatiquement au démarrage du système, nous devons exécuter systemctl enable etcd . Dans cette étape, nous allons exécuter ces deux commandes en utilisant le playbook.

Pour exécuter les commandes, nous pouvons utiliser le module de commande :

  1. nano $HOME/playground/etcd-ansible/playbook.yaml

Ajoutez les tâches suivantes à la fin de la liste des tâches :

~/playground/etcd-ansible/playbook.yaml
- hosts: etcd
  become: True
  tasks:
    ...
    - name: "Create configuration file for etcd"
      template:
        ...
        mode: 0600
    - name: "Enable the etcd service"
      command: systemctl enable etcd
    - name: "Start the etcd service"
      command: systemctl restart etcd

Enregistrez et fermez le fichier.

Exécutez ansible-playbook -i hosts playbook.yaml une fois de plus :

  1. ansible-playbook -i hosts playbook.yaml

Pour vérifier que le service etcd est maintenant redémarré et activé, SSH dans l’un des nœuds gérés :

  1. ssh root@etcd1_public_ip

Ensuite, lancez systemctl status etcd pour vérifier l’état du service etcd :

  1. systemctl status etcd

Vous trouverez enabled and active (en cours) comme mis en surbrillance ci-dessous ; cela signifie que les modifications que nous avons apportées à notre playbook ont pris effet :

Output
● etcd.service - etcd distributed reliable key-value store Loaded: loaded (/etc/systemd/system/etcd.service; static; vendor preset: enabled) Active: active (running) Main PID: 19085 (etcd) Tasks: 11 (limit: 2362)

Dans cette étape, nous avons utilisé le module command pour exécuter des commandes systemctl qui redémarrent et activent le service etcd sur nos nœuds gérés. Maintenant que nous avons mis en place une installation etcd, nous allons, dans la prochaine étape, tester sa fonctionnalité en effectuant quelques opérations de base de création, lecture, mise à jour et suppression (CRUD).

Étape 7 - Tester etcd

Bien que nous disposions d’une installation etcd en état de marche, elle n’est pas sûre et n’est pas encore prête à être utilisée pour la production. Mais avant de sécuriser notre installation etcd dans les étapes ultérieures, il faut d’abord comprendre ce qu’etcd peut faire en termes de fonctionnalités. Dans cette étape, nous allons envoyer manuellement des demandes à etcd pour ajouter, récupérer, mettre à jour et supprimer des données.

Par défaut, etcd expose une API qui écoute sur le port 2379 pour la communication avec les clients. Cela signifie que nous pouvons envoyer des requêtes API brutes à etcd en utilisant un client HTTP. Cependant, il est plus rapide d’utiliser le client officiel etcd (etcdctl), qui permet de créer/mettre à jour, de récupérer et de supprimer des paires clé-valeur en utilisant respectivement les sous-commandes put , get et del.

Assurez-vous que vous êtes toujours à l’intérieur du nœud géré par etcd1, et exécutez les commandes etcdctl suivantes pour confirmer que votre installation etcd fonctionne.

D’abord, créez une nouvelle entrée en utilisant la sous-commande put.

La sous-commande put a la syntaxe suivante :

etcdctl put key value

Sur etcd1 , lancez la commande suivante :

  1. etcdctl put foo "bar"

La commande que nous venons d’exécuter demande à etcd d’écrire la valeur « bar » dans la clé foo du magasin.

Vous trouverez alors OK imprimé dans la sortie, ce qui indique que les données ont persisté :

Output
OK

Nous pouvons ensuite récupérer cette entrée en utilisant la sous-commande get, qui a la syntaxe etcdctl get key :

  1. etcdctl get foo

Vous trouverez ce résultat, qui indique la clé sur la première ligne et la valeur que vous avez insérée plus tôt sur la deuxième ligne :

Output
foo bar

Nous pouvons supprimer l’entrée en utilisant la sous-commande del, qui a la syntaxe etcdctl del key :

  1. etcdctl del foo

Vous trouverez la sortie suivante, qui indique le nombre d’entrées supprimées :

Output
1

Maintenant, lançons à nouveau la sous-commande get pour tenter de récupérer une paire clé-valeur supprimée :

  1. etcdctl get foo

Vous ne recevrez pas de sortie, ce qui signifie qu’etcdctl n’est pas en mesure de récupérer la paire clé-valeur. Cela confirme qu’une fois l’entrée supprimée, elle ne peut plus être récupérée.

Maintenant que vous avez testé les opérations de base d’etcd et d’etcdctl, sortons de notre nœud géré et retournons à votre environnement local :

  1. exit

Dans cette étape, nous avons utilisé le client etcdctl pour envoyer des demandes à etcd. À ce stade, nous gérons trois instances distinctes d’etcd, chacune agissant indépendamment des autres. Cependant, etcd est conçu comme un magasin de valeurs clés distribué, ce qui signifie que plusieurs instances d’etcd peuvent se regrouper pour former un seul groupe ; chaque instance devient alors un membre du groupe. Après avoir formé un cluster, vous pourrez récupérer une paire clé-valeur insérée à partir d’un autre membre du cluster. Dans la prochaine étape, nous utiliserons notre playbook pour transformer nos trois clusters à un seul nœud en un seul cluster à trois nœuds.

Étape 8 - Formation d’un cluster à l’aide de la découverte statique

Pour créer un cluster à 3 nœuds au lieu de trois clusters à 1 nœud, nous devons configurer ces installations etcd pour qu’elles communiquent entre elles. Cela signifie que chacun doit connaître les adresses IP des autres. Ce processus est appelé découverte. La découverte peut se faire soit par configuration statique, soit par découverte dynamique de services. Dans cette étape, nous discuterons de la différence entre les deux, et nous mettrons à jour notre playbook pour créer un cluster etcd en utilisant la découverte statique.

La découverte par configuration statique est la méthode qui nécessite le moins de configuration ; c’est là que les points terminaux de chaque membre sont passés dans la commande etcd avant qu’elle ne soit exécutée. Pour utiliser la configuration statique, les conditions suivantes doivent être remplies avant l’initialisation du cluster :

  • le nombre de membres est connu
  • les points terminaux de chaque membre sont connus
  • les adresses IP de tous les points terminaux sont statiques

Si ces conditions ne peuvent être remplies, vous pouvez alors utiliser un service de découverte dynamique. Avec le service de découverte dynamique, toutes les instances s’enregistreraient auprès du service de découverte, qui permet à chaque membre de récupérer des informations sur la localisation des autres membres.

Comme nous savons que nous voulons un cluster à 3 nœuds etcd, et que tous nos serveurs ont des adresses IP statiques, nous utiliserons la découverte statique. Pour lancer notre cluster en utilisant la découverte statique, nous devons ajouter plusieurs points terminaux à notre fichier de configuration. Utilisez un éditeur pour ouvrir le fichier modèle templates/etcd.conf.yaml.j2 :

  1. nano templates/etcd.conf.yaml.j2

Ajoutez les lignes en surbrillance suivantes :

~/playground/etcd-ansible/templates/etcd.conf.yaml.j2
data-dir: /var/lib/etcd/{{ inventory_hostname }}.etcd
name: {{ inventory_hostname }}
initial-advertise-peer-urls: http://{{ hostvars[inventory_hostname]['ansible_facts']['eth1']['ipv4']['address'] }}:2380
listen-peer-urls: http://{{ hostvars[inventory_hostname]['ansible_facts']['eth1']['ipv4']['address'] }}:2380,http://127.0.0.1:2380
advertise-client-urls: http://{{ hostvars[inventory_hostname]['ansible_facts']['eth1']['ipv4']['address'] }}:2379
listen-client-urls: http://{{ hostvars[inventory_hostname]['ansible_facts']['eth1']['ipv4']['address'] }}:2379,http://127.0.0.1:2379
initial-cluster-state: new
initial-cluster: {% for host in groups['etcd'] %}{{ hostvars[host]['ansible_facts']['hostname'] }}=http://{{ hostvars[host]['ansible_facts']['eth1']['ipv4']['address'] }}:2380{% if not loop.last %},{% endif %}{% endfor %}

Fermez et enregistrez le fichier templates/etcd.conf.yaml.j2 en appuyant sur CTRL+X suivi de Y .

Voici une brève explication de chaque point terminal :

  • nom - un nom lisible pour le membre. Par défaut, etcd utilise un identifiant unique, généré de manière aléatoire, pour identifier chaque membre ; cependant, un nom lisible par l’utilisateur nous permet de le référencer plus facilement à l’intérieur des fichiers de configuration et sur la ligne de commande. Ici, nous utiliserons les noms d’hôtes comme noms de membres (c’est-à-dire, etcd1 , etcd2 , et etcd3 ).
  • initial-advertise-peer-urls - une liste d’adresses IP/combinaisons de ports que les autres membres peuvent utiliser pour communiquer avec ce membre. En plus du port API (2379), etcd expose également le port 2380 pour la communication entre les membres d’etcd, qui leur permet de s’envoyer des messages et d’échanger des données. Notez que ces URL doivent être accessibles par ses pairs (et ne pas être une adresse IP locale).
  • listen-peer-urls - une liste d’adresses IP/combinaisons de ports où le membre actuel écoutera les communications des autres membres. Cela doit inclure toutes les URL du drapeau -initial-advertise-peer-urls, mais aussi les URL locales comme 127.0.0.1:2380 . L’adresse IP de destination/le port des messages de pairs entrants doit correspondre à l’une des URL énumérées ici.
  • advertise-client-urls - une liste des combinaisons Adresse IP/port que les clients doivent utiliser pour communiquer avec ce membre. Ces URL doivent être accessibles par le client (et ne pas être une adresse locale). Si le client accède au cluster via l’Internet public, il doit s’agir d’une adresse IP publique.
  • listen-client-urls - une liste des combinaisons Adresses IP/ports où le membre actuel écoutera les communications des clients. Cela doit inclure toutes les URL du drapeau --advertise-client-urls, mais aussi les URL locales comme 127.0.0.1:2379 L’adresse IP de destination/le port des messages clients entrants doit correspondre à l’une des URL énumérées ici.
  • groupe initial - une liste de points terminaux pour chaque membre du groupe. Chaque point terminal doit correspondre aux URL initial-advertise-peer-urls du membre correspondant.
  • initial-cluster-state soi new ou existing.

Pour assurer la cohérence, etcd ne peut prendre de décisions que lorsque la majorité des nœuds marche bien. C’est ce qu’on appelle établir le quorum. En d’autres termes, dans un groupe de trois membres, le quorum est atteint si deux ou plusieurs des membres fonctionnent bien.

Si le paramètre initial-cluster-state est réglé sur new,etcd saura qu’il s’agit d’un nouveau cluster en cours de démarrage et permettra aux membres de commencer en parallèle, sans attendre que le quorum soit atteint. Plus concrètement, après l’entrée en fonction du premier membre, le quorum ne sera pas atteint car un tiers (33,33%) est inférieur ou égal à 50%. Normalement, etcd s’arrêtera et refusera d’engager d’autres actions et le cluster ne sera jamais formé. Cependant, comme initial-cluster-state a été fixé comme new, il ignorera le manque initial de quorum.

S’il est fixé comme existing, le membre essaiera de rejoindre un groupe existant, et s’attend à ce que le quorum soit déjà établi.

Note : Vous pouvez trouver plus de détails sur tous les drapeaux de configuration pris en charge dans la section Configuration de la documentation d’etcd.

Dans le fichier modèle mis à jour templates/etcd.conf.yaml.j2, on trouve quelques exemples de hostvars .  Lorsque Ansible s’exécute , il recueillera des variables auprès d’une variété sources. Nous avons déjà utilisé la variable inventory_hostname auparavant, mais il y en a beaucoup d’autres disponibles. Ces variables sont disponibles sous hostvars [inventory_hostname]['ansible_facts'] . Ici, nous extrayons les adresses IP privées de chaque nœud et les utilisons pour construire la valeur de notre point terminal.

Note : Comme nous avons activé l’option Private Networking lors de la création de nos serveurs, chaque serveur serait associé à trois adresses IP :

  • Une adresse IP de_bouclage_ - une adresse qui n’est valable qu’à l’intérieur de la même machine. Elle est utilisée pour que la machine se réfère à elle-même, par exemple, 127.0.0.1
  • Une adresse IP publique - une adresse qui peut être acheminée sur l’Internet public, par exemple, 178.128.169.51
  • Une adresse IP privée - une adresse qui n’est routable qu’au sein du réseau privé ; dans le cas de DigitalOcean Droplets, il y a un réseau privé au sein de chaque centre de données, par exemple, 10.131.82.225

Chacune de ces adresses IP est associée à une interface réseau différente : l’adresse de bouclage est associée à l’interface lo, l’adresse IP publique est associée à l’interface eth0, et l’adresse IP privée à l’interface eth1. Nous utilisons l’interface eth1 pour que tout le trafic reste dans le réseau privé, sans jamais atteindre Internet.

La compréhension des interfaces de réseau n’est pas nécessaire pour cet article, mais si vous souhaitez en savoir plus, Une introduction à la terminologie, aux interfaces et aux protocoles de réseau est un excellent point de départ.

La syntaxe {% %} Jinja2 définit la structure de la boucle for qui itére à travers chaque nœud du groupe etcd pour construire la chaîne du cluster initial dans un format requis par etcd.

Pour former le nouveau cluster de trois membres, vous devez d’abord arrêter le service etcd et effacer le répertoire de données avant de lancer le cluster. Pour ce faire, utilisez un éditeur afin d’ouvrir le fichier playbook.yaml sur votre machine locale :

  1. nano $HOME/playground/etcd-ansible/playbook.yaml

Ensuite, avant la tâche « Create a data directory », ajoutez une tâche pour arrêter le service etcd :

~/playground/etcd-ansible/playbook.yaml
- hosts: etcd
  become: True
  tasks:
    ...
        group: root
        mode: 0644
    - name: "Stop the etcd service"
      command: systemctl stop etcd
    - name: "Create a data directory"
      file:
    ...

Ensuite, mettez à jour la tâche « Create a data directory » pour d’abord supprimer le répertoire de données et le recréer :

~/playground/etcd-ansible/playbook.yaml
- hosts: etcd
  become: True
  tasks:
    ...
    - name: "Stop the etcd service"
      command: systemctl stop etcd
    - name: "Create a data directory"
      file:
        path: /var/lib/etcd/{{ inventory_hostname }}.etcd
        state: "{{ item }}"
        owner: root
        group: root
        mode: 0755
      with_items:
        - absent
        - directory
    - name: "Create directory for etcd configuration"
      file:
    ...

La propriété with_items définit une liste de chaînes de caractères sur lesquelles cette tâche va itérer. Cela équivaut à répéter deux fois la même tâche, mais avec des valeurs différentes pour la propriété state. Dans ce cas, nous répétons la liste avec les éléments absents et le répertoire, ce qui garantit que le répertoire de données est d’abord supprimé et ensuite recréé.

Fermez et enregistrez le fichier playbook.yaml en appuyant sur CTRL+X, puis sur Y. Ensuite, lancez à nouveau ansible-playbook. Ansible va maintenant créer un cluster unique de 3 membres etcd :

  1. ansible-playbook -i hosts playbook.yaml

Vous pouvez vérifier cela en entrant en langage SSH dans n’importe quel nœud membre etcd :

  1. ssh root@etcd1_public_ip

Ensuite, lancez l’application etcdctl endpoint health --cluster :

  1. etcdctl endpoint health --cluster

Le statut de chaque membre du groupe sera ainsi répertorié :

Output
http://etcd2_private_ip:2379 is healthy: successfully committed proposal: took = 2.517267ms http://etcd1_private_ip:2379 is healthy: successfully committed proposal: took = 2.153612ms http://etcd3_private_ip:2379 is healthy: successfully committed proposal: took = 2.639277ms

Nous avons maintenant réussi à créer un cluster de 3 nœuds etcd. Nous pouvons confirmer cela en ajoutant une entrée à etcd sur un nœud membre, et en la récupérant sur un autre nœud membre. Sur l’un des nœuds membres, lancez etcdctl put :

  1. etcdctl put foo "bar"

Ensuite, utilisez un nouveau terminal pour SSH dans un autre nœud membre :

  1. ssh root@etcd2_public_ip

Ensuite, essayez de retrouver la même entrée en utilisant la clé :

  1. etcdctl get foo

Vous pourrez récupérer l’entrée, qui prouve que le cluster fonctionne :

Output
foo bar

Enfin, sortez de chacun des nœuds gérés et revenez à votre machine locale :

  1. exit
  1. exit

Dans cette étape, nous avons mis en place un nouveau cluster de 3 nœuds. À l’heure actuelle, la communication entre les membres d’etcd et leurs pairs et clients se fait par HTTP. Cela signifie que la communication n’est pas cryptée et que toute partie qui peut intercepter le trafic peut lire les messages. Ce n’est pas un gros problème si le cluster etcd et les clients sont tous déployés dans un réseau privé ou un réseau privé virtuel (VPN) que vous contrôlez entièrement. Toutefois, si une partie du trafic doit passer par un réseau partagé (privé ou public), vous devez alors vous assurer que ce trafic est crypté. En outre, un mécanisme doit être mis en place pour qu’un client ou un pair puisse vérifier l’authenticité du serveur.

Dans la prochaine étape, nous examinerons comment sécuriser la communication client-serveur et la communication entre pairs en utilisant le TLS.

Étape 9 - Obtenir les adresses IP privées des nœuds gérés

Pour crypter les messages entre les nœuds membres, etcd utilise Hypertext Transfer Protocol Secure , ou HTTPS , qui est une couche au-dessus du protocole TLS , ou Transport Layer Security . TLS utilise un système de clés privées, de certificats et d’entités de confiance appelées Certificate Authorities (Autorités de certification) (CA) pour s’authentifier et s’envoyer des messages cryptés.

Dans ce tutoriel, chaque nœud membre doit générer un certificat pour s’identifier, et faire signer ce certificat par une AC. Nous allons configurer tous les nœuds membres pour qu’ils fassent confiance à cette AC, et donc aussi à tous les certificats qu’elle signe. Cela permet aux nœuds membres de s’authentifier mutuellement.

Le certificat qu’un nœud membre génère doit permettre aux autres nœuds membres de s’identifier. Tous les certificats comportent le Common Name (Nom Commun) (CN) de l’entité à laquelle il est associé. Cela est souvent utilisé comme identité de l’entité. Toutefois, lors de la vérification d’un certificat, les clients peuvent comparer si les informations qu’ils ont recueillies sur l’entité correspondent à celles qui figurent dans le certificat. Par exemple, lorsqu’un client télécharge le certificat TLS avec l’objet CN=foo.bar.com, mais qu’il se connecte en fait au serveur en utilisant une adresse IP (par exemple 167.71.129.110), il y a alors un décalage et le client peut ne pas faire confiance au certificat. En spécifiant un nom alternatif du sujet (SAN) dans le certificat, il informe le vérificateur que les deux noms appartiennent à la même entité.

Comme nos membres etcd s’échangent des informations en utilisant leurs adresses IP privées, lorsque nous définirons nos certificats, nous devrons fournir ces adresses IP privées comme noms alternatifs du sujet.

Pour connaître l’adresse IP privée d’un nœud géré, il faut y entrer en SSH :

  1. ssh root@etcd1_public_ip

Exécutez ensuite la commande suivante :

  1. ip -f inet addr show eth1

Vous trouverez des résultats similaires aux lignes suivantes :

Output
3: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000 inet 10.131.255.176/16 brd 10.131.255.255 scope global eth1 valid_lft forever preferred_lft forever

Dans notre exemple de sortie, 10.131.255.176 est l’adresse IP privée du nœud géré, et la seule information qui nous intéresse. Pour filtrer tout le reste, à l’exception de la propriété intellectuelle privée, nous pouvons transmettre la sortie de la commande ip à l’utilitaire sed, qui est utilisé pour filtrer et transformer le texte.

  1. ip -f inet addr show eth1 | sed -En -e 's/.*inet ([0-9.]+).*/\1/p'

Maintenant, la seule sortie est l’adresse IP privée elle-même :

Output
10.131.255.176

Une fois que vous êtes convaincu que la commande précédente fonctionne, quittez le nœud géré :

  1. exit

Pour incorporer les commandes précédentes dans notre playbook, ouvrez d’abord le fichier playbook.yaml :

  1. nano $HOME/playground/etcd-ansible/playbook.yaml

Ensuite, ajoutez une nouvelle pièce avec une seule tâche avant notre play existante :

~/playground/etcd-ansible/playbook.yaml
...
- hosts: etcd
  tasks:
    - shell: ip -f inet addr show eth1 | sed -En -e 's/.*inet ([0-9.]+).*/\1/p'
      register: privateIP
- hosts: etcd
  tasks:
...

La tâche utilise le module shell pour exécuter les commandes ip et sed, qui récupère l’adresse IP privée du nœud géré. Elle enregistre ensuite la valeur de retour de la commande shell dans une variable appelée privateIP , que nous utiliserons plus tard.

Dans cette étape, nous avons ajouté une tâche au playbook pour obtenir l’adresse IP privée des nœuds gérés. Dans l’étape suivante, nous allons utiliser ces informations pour générer des certificats pour chaque nœud membre, et faire signer ces certificats par une autorité de certification (CA).

Étape 10 - Générer les clés privées et les RSE des membres d’etcd

Pour qu’un nœud membre puisse recevoir un trafic crypté, l’expéditeur doit utiliser la clé publique du nœud membre pour crypter les données, et le nœud membre doit utiliser sa clé privée pour décrypter le texte chiffré et récupérer les données originales. La clé publique est emballée dans un certificat et signée par une AC pour garantir son authenticité.

Par conséquent, nous devrons générer une clé privée et une demande de signature de certificat (CSR) pour chaque nœud membre etcd. Pour nous faciliter la tâche, nous allons générer toutes les paires de clés et signer tous les certificats localement, sur le nœud de contrôle, puis copier les fichiers pertinents vers les hôtes gérés.

Tout d’abord, créez un répertoire appelé artefacts/, où nous placerons les fichiers (clés et certificats) générés au cours du processus. Ouvrez le fichier playbook.yaml avec un éditeur :

  1. nano $HOME/playground/etcd-ansible/playbook.yaml

Dans celui-ci, utilisez le module file pour créer le répertoire artefacts/ :

~/playground/etcd-ansible/playbook.yaml
...
    - shell: ip -f inet addr show eth1 | sed -En -e 's/.*inet ([0-9.]+).*/\1/p'
      register: privateIP
- hosts: localhost
  gather_facts: False
  become: False
  tasks:
    - name: "Create ./artifacts directory to house keys and certificates"
      file:
        path: ./artifacts
        state: directory
- hosts: etcd
  tasks:
...

Ensuite, ajoutez une autre tâche à la fin du play pour générer la clé privée :

~/playground/etcd-ansible/playbook.yaml
...
- hosts: localhost
  gather_facts: False
  become: False
  tasks:
        ...
    - name: "Generate private key for each member"
      openssl_privatekey:
        path: ./artifacts/{{item}}.key
        type: RSA
        size: 4096
        state: present
        force: True
      with_items: "{{ groups['etcd'] }}"
- hosts: etcd
  tasks:
...

La création de clés privées et de CSR peut se faire en utilisant respectivement les modules openssl_privatekey et openssl_csr.

L’attribut force : True garantit que la clé privée est régénérée à chaque fois, même si elle existe déjà.

De même, ajoutez la nouvelle tâche suivante au même play pour générer les RSE pour chaque membre, en utilisant le module openssl_csr :

~/playground/etcd-ansible/playbook.yaml
...
- hosts: localhost
  gather_facts: False
  become: False
  tasks:
    ...
    - name: "Generate private key for each member"
      openssl_privatekey:
        ...
      with_items: "{{ groups['etcd'] }}"
    - name: "Generate CSR for each member"
      openssl_csr:
        path: ./artifacts/{{item}}.csr
        privatekey_path: ./artifacts/{{item}}.key
        common_name: "{{item}}"
        key_usage:
          - digitalSignature
        extended_key_usage:
          - serverAuth
        subject_alt_name:
          - IP:{{ hostvars[item]['privateIP']['stdout']}}
          - IP:127.0.0.1
        force: True
      with_items: "{{ groups['etcd'] }}"

Nous précisons que ce certificat peut être impliqué dans un mécanisme de signature numérique à des fins d’authentification du serveur. Ce certificat est associé au nom d’hôte (par exemple, etcd1 ), mais le vérificateur doit également traiter les adresses IP privées et de boucle locale de chaque nœud comme des noms alternatifs.  Notez que nous utilisons la variable privateIP que nous avons enregistrée dans le play précédente.

Fermez et enregistrez le fichier playbook.yaml en appuyant sur CTRL+X suivi de Y. Ensuite, lancez à nouveau ansible-playbook.

  1. ansible-playbook -i hosts playbook.yaml

Nous allons maintenant trouver un nouveau répertoire appelé artefacts dans notre répertoire de projets ; utilisez ls pour en énumérer le contenu :

  1. ls artifacts

Vous trouverez les clés privées et les RSE de chacun des membres d’etcd :

Output
etcd1.csr etcd1.key etcd2.csr etcd2.key etcd3.csr etcd3.key

Dans cette étape, nous avons utilisé plusieurs modules Ansible pour générer des clés privées et des certificats de clé publique pour chacun des nœuds membres. Dans la prochaine étape, nous examinerons comment signer une demande de signature de certificat (CSR).

Étape 11 - Génération des certificats CA

Au sein d’un cluster etcd, les nœuds membres cryptent les messages en utilisant la clé publique du destinataire. Pour garantir l’authenticité de la clé publique, le destinataire l’emballe dans une demande de signature de certificat (CSR) et la fait signer par une entité de confiance (c’est-à-dire l’AC). Comme nous contrôlons tous les nœuds membres et les AC auxquelles ils font confiance, nous n’avons pas besoin d’utiliser une AC externe et pouvons agir comme notre propre AC. Dans cette étape, nous allons agir comme notre propre AC, ce qui signifie que nous devrons générer une clé privée et un certificat auto-signé pour fonctionner etant qu’AC.

Ouvrez le fichier playbook.yaml avec votre éditeur :

  1. nano $HOME/playground/etcd-ansible/playbook.yaml

Ensuite, comme pour l’étape précédente, ajoutez une tâche au play localhost pour générer une clé privée pour l’AC :

~/playground/etcd-ansible/playbook.yaml
- hosts: localhost
  ...
  tasks:
    ...
  - name: "Generate CSR for each member"
    ...
    with_items: "{{ groups['etcd'] }}"
    - name: "Generate private key for CA"
      openssl_privatekey:
        path: ./artifacts/ca.key
        type: RSA
        size: 4096
        state: present
        force: True
- hosts: etcd
  become: True
  tasks:
    - name: "Create directory for etcd binaries"
...

Ensuite, utilisez le module openssl_csr pour générer un nouveau CSR. Cette étape est similaire à la précédente, mais dans ce CSR, nous ajoutons la contrainte de base et l’extension d’utilisation des clés pour indiquer que ce certificat peut être utilisé comme un certificat CA :

~/playground/etcd-ansible/playbook.yaml
- hosts: localhost
  ...
  tasks:
    ...
    - name: "Generate private key for CA"
      openssl_privatekey:
        path: ./artifacts/ca.key
        type: RSA
        size: 4096
        state: present
        force: True
    - name: "Generate CSR for CA"
      openssl_csr:
        path: ./artifacts/ca.csr
        privatekey_path: ./artifacts/ca.key
        common_name: ca
        organization_name: "Etcd CA"
        basic_constraints:
          - CA:TRUE
          - pathlen:1
        basic_constraints_critical: True
        key_usage:
          - keyCertSign
          - digitalSignature
        force: True
- hosts: etcd
  become: True
  tasks:
    - name: "Create directory for etcd binaries"
...

Enfin, utilisez le module openssl_certificate pour auto-signer le CSR :

~/playground/etcd-ansible/playbook.yaml
- hosts: localhost
  ...
  tasks:
    ...
    - name: "Generate CSR for CA"
      openssl_csr:
        path: ./artifacts/ca.csr
        privatekey_path: ./artifacts/ca.key
        common_name: ca
        organization_name: "Etcd CA"
        basic_constraints:
          - CA:TRUE
          - pathlen:1
        basic_constraints_critical: True
        key_usage:
          - keyCertSign
          - digitalSignature
        force: True
    - name: "Generate self-signed CA certificate"
      openssl_certificate:
        path: ./artifacts/ca.crt
        privatekey_path: ./artifacts/ca.key
        csr_path: ./artifacts/ca.csr
        provider: selfsigned
        force: True
- hosts: etcd
  become: True
  tasks:
    - name: "Create directory for etcd binaries"
...

Fermez et enregistrez le fichier playbook.yaml en appuyant sur CTRL+X suivi de Y. Ensuite, relancez notre playbook pour appliquer les changements :

  1. ansible-playbook -i hosts playbook.yaml

Vous pouvez également lancer ls pour vérifier le contenu du répertoire artefacts/ :

  1. ls artifacts/

Vous trouverez maintenant le certificat CA nouvellement généré ( ca.crt ) :

Output
ca.crt ca.csr ca.key etcd1.csr etcd1.key etcd2.csr etcd2.key etcd3.csr etcd3.key

Dans cette étape, nous avons généré une clé privée et un certificat auto-signé pour l’AC. Dans la prochaine étape, nous utiliserons le certificat de l’AC pour signer la RSE de chaque membre.

Étape 12 - Signature des RSE des membres d’etcd

Dans cette étape, nous allons signer la RSE de chaque nœud membre. Cela sera similaire à la manière dont nous avons utilisé le module openssl_certificate pour auto-signer le certificat CA, mais au lieu d’utiliser le fournisseur auto-signé, nous utiliserons le fournisseur ownca, qui nous permet de signer en utilisant notre propre certificat CA.

Ouvrez votre playbook :

  1. nano $HOME/playground/etcd-ansible/playbook.yaml

Ajoutez la tâche en surbrillance suivante à la tâche « Generate self-signed CA certificate » (Générer un certificat CA auto-signé) :

~/playground/etcd-ansible/playbook.yaml
- hosts: localhost
  ...
  tasks:
    ...
    - name: "Generate self-signed CA certificate"
      openssl_certificate:
        path: ./artifacts/ca.crt
        privatekey_path: ./artifacts/ca.key
        csr_path: ./artifacts/ca.csr
        provider: selfsigned
        force: True
    - name: "Generate an `etcd` member certificate signed with our own CA certificate"
      openssl_certificate:
        path: ./artifacts/{{item}}.crt
        csr_path: ./artifacts/{{item}}.csr
        ownca_path: ./artifacts/ca.crt
        ownca_privatekey_path: ./artifacts/ca.key
        provider: ownca
        force: True
      with_items: "{{ groups['etcd'] }}"
- hosts: etcd
  become: True
  tasks:
    - name: "Create directory for etcd binaries"
...

Fermez et enregistrez le fichier playbook.yaml en appuyant sur CTRL+X suivi de Y . Ensuite, relancez le playbook pour appliquer les modifications :

  1. ansible-playbook -i hosts playbook.yaml

Maintenant, énumérez le contenu du répertoire artefacts :

  1. ls artifacts/

Vous y trouverez la clé privée, le CSR et le certificat de chaque membre de l’etcd et de l’AC :

Output
ca.crt ca.csr ca.key etcd1.crt etcd1.csr etcd1.key etcd2.crt etcd2.csr etcd2.key etcd3.crt etcd3.csr etcd3.key

Dans cette étape, nous avons signé les RSE de chaque nœud membre en utilisant la clé de l’AC. Dans l’étape suivante, nous allons copier les fichiers pertinents dans chaque nœud géré, de sorte qu’etcd ait accès aux clés et aux certificats pertinents pour établir les connexions TLS.

Étape 13 - Copie de clés privées et de certificats

Chaque nœud doit avoir une copie du certificat auto-signé de l’AC ( ca.crt ). Chaque nœud membre d’etcd doit également avoir sa propre clé privée et son certificat. Dans cette étape, nous allons télécharger ces fichiers et les placer dans un nouveau répertoire /etc/etcd/ssl/.

Pour commencer, ouvrez le fichier playbook.yaml avec votre éditeur :

  1. nano $HOME/playground/etcd-ansible/playbook.yaml

Pour effectuer ces changements sur notre playbook Ansible, il faut d’abord mettre à jour la propriété du chemin de la tâche Create directory for etcd configuration pour créer le répertoire /etc/etcd/ssl/ :

~/playground/etcd-ansible/playbook.yaml
- hosts: etcd
  ...
  tasks:
    ...
      with_items:
        - absent
        - directory
    - name: "Create directory for etcd configuration"
      file:
        path: "{{ item }}"
        state: directory
        owner: root
        group: root
        mode: 0755
      with_items:
        - /etc/etcd
        - /etc/etcd/ssl
    - name: "Create configuration file for etcd"
      template:
...

Ensuite, à la suite de la tâche modifiée, ajoutez trois autres tâches pour copier les fichiers par-dessus :

~/playground/etcd-ansible/playbook.yaml
- hosts: etcd
  ...
  tasks:
    ...
    - name: "Copy over the CA certificate"
      copy:
        src: ./artifacts/ca.crt
        remote_src: False
        dest: /etc/etcd/ssl/ca.crt
        owner: root
        group: root
        mode: 0644
    - name: "Copy over the `etcd` member certificate"
      copy:
        src: ./artifacts/{{inventory_hostname}}.crt
        remote_src: False
        dest: /etc/etcd/ssl/server.crt
        owner: root
        group: root
        mode: 0644
    - name: "Copy over the `etcd` member key"
      copy:
        src: ./artifacts/{{inventory_hostname}}.key
        remote_src: False
        dest: /etc/etcd/ssl/server.key
        owner: root
        group: root
        mode: 0600
    - name: "Create configuration file for etcd"
      template:
...

Fermez et enregistrez le fichier playbook.yaml en appuyant sur CTRL+X, puis sur Y.

Lancez de nouveau ansible-playbook pour apporter ces changements :

  1. ansible-playbook -i hosts playbook.yaml

Dans cette étape, nous avons réussi à télécharger les clés privées et les certificats vers les nœuds gérés. Après avoir copié les fichiers, nous devons maintenant mettre à jour notre fichier de configuration etcd pour pouvoir les utiliser.

Étape 14 - Activer le TLS sur etcd

Dans la dernière étape de ce tutoriel, nous allons mettre à jour certaines configurations Ansible pour activer TLS dans un cluster etcd.

Tout d’abord, ouvrez le fichier modèle templates/etcd.conf.yaml.j2 à l’aide de votre éditeur :

  1. nano $HOME/playground/etcd-ansible/templates/etcd.conf.yaml.j2

Une fois à l’intérieur, modifiez toutes les URL pour utiliser le protocole https au lieu de http.  De plus, ajoutez une section à la fin du modèle pour spécifier l’emplacement du certificat de l’AC, du certificat du serveur et de la clé du serveur :

~/playground/etcd-ansible/templates/etcd.conf.yaml.j2
data-dir: /var/lib/etcd/{{ inventory_hostname }}.etcd
name: {{ inventory_hostname }}
initial-advertise-peer-urls: https://{{ hostvars[inventory_hostname]['ansible_facts']['eth1']['ipv4']['address'] }}:2380
listen-peer-urls: https://{{ hostvars[inventory_hostname]['ansible_facts']['eth1']['ipv4']['address'] }}:2380,https://127.0.0.1:2380
advertise-client-urls: https://{{ hostvars[inventory_hostname]['ansible_facts']['eth1']['ipv4']['address'] }}:2379
listen-client-urls: https://{{ hostvars[inventory_hostname]['ansible_facts']['eth1']['ipv4']['address'] }}:2379,https://127.0.0.1:2379
initial-cluster-state: new
initial-cluster: {% for host in groups['etcd'] %}{{ hostvars[host]['ansible_facts']['hostname'] }}=https://{{ hostvars[host]['ansible_facts']['eth1']['ipv4']['address'] }}:2380{% if not loop.last %},{% endif %}{% endfor %}

client-transport-security:
  cert-file: /etc/etcd/ssl/server.crt
  key-file: /etc/etcd/ssl/server.key
  trusted-ca-file: /etc/etcd/ssl/ca.crt
peer-transport-security:
  cert-file: /etc/etcd/ssl/server.crt
  key-file: /etc/etcd/ssl/server.key
  trusted-ca-file: /etc/etcd/ssl/ca.crt

Fermez et enregistrez le fichier templates/etcd.conf.yaml.j2

Ensuite, lancez votre playbook Ansible :

  1. ansible-playbook -i hosts playbook.yaml

Puis, SSH dans l’un des nœuds gérés :

  1. ssh root@etcd1_public_ip

Une fois à l’intérieur, lancez la commande etcdctl endpoint health pour vérifier si les terminaux utilisent le HTTPS, et si tous les membres sont en bon état :

  1. etcdctl --cacert /etc/etcd/ssl/ca.crt endpoint health --cluster

Comme notre certificat CA n’est pas, par défaut, un certificat CA root de confiance installé dans le répertoire /etc/ssl/certs/, nous devons le passer à etcdctl en utilisant le drapeau --cacert.

Cela donnera le résultat suivant :

Output
https://etcd3_private_ip:2379 is healthy: successfully committed proposal: took = 19.237262ms https://etcd1_private_ip:2379 is healthy: successfully committed proposal: took = 4.769088ms https://etcd2_private_ip:2379 is healthy: successfully committed proposal: took = 5.953599ms

Pour confirmer que le cluster etcd fonctionne réellement, nous pouvons, une fois de plus, créer une entrée sur un nœud membre et la récupérer à partir d’un autre nœud membre :

  1. etcdctl --cacert /etc/etcd/ssl/ca.crt put foo "bar"

Ensuite, utilisez un nouveau terminal pour SSH dans un autre nœud membre :

  1. ssh root@etcd2_public_ip

Récupérez maintenant la même entrée en utilisant la clé foo :

  1. etcdctl --cacert /etc/etcd/ssl/ca.crt get foo

Ceci renverra l’entrée, montrant la sortie ci-dessous :

Output
foo bar

Vous pouvez faire la même chose sur le troisième nœud pour vous assurer que les trois membres sont opérationnels.

Conclusion

Vous avez maintenant approvisionné avec succès un cluster de 3 nœuds etcd, l’avez sécurisé avec TLS et avez confirmé qu’il fonctionne.

etcd est un outil créé à l’origine par CoreOS .  Pour comprendre l’utilisation d’etcd par rapport à CoreOS, vous pouvez lire Comment utiliser Etcdctl et Etcd, le Distributed Key-Value Store de CoreOS .  L’article vous guide également dans la mise en place d’un modèle de découverte dynamique, ce qui a été discuté mais non démontré dans ce tutoriel.

Comme mentionné au début de ce tutoriel, etcd est une partie importante de l’écosystème de Kubernetes. Pour en savoir plus sur Kubernetes et le rôle d’etcd en son sein, vous pouvez lire Une introduction à Kubernetes. Si vous déployez etcd dans le cadre d’un cluster Kubernetes, sachez que d’autres outils sont disponibles, tels que kubespray et kubeadm . Pour plus de détails sur ce dernier point, vous pouvez lire Comment créer un cluster Kubernetes en utilisant Kubeadm sur Ubuntu 18.04.

Enfin, ce tutoriel a fait appel à de nombreux outils, mais n’a pu se plonger dans chacun d’entre eux de manière trop détaillée. Vous trouverez ci-dessous des liens qui vous permettront d’examiner plus en détail chaque outil :

Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.

Learn more about us


About the authors
Default avatar
Daniel Li

author

Staff Software Engineer


Default avatar

Senior Technical Editor

Editor at DigitalOcean, fiction writer and podcaster elsewhere, always searching for the next good nautical pun!


Still looking for an answer?

Ask a questionSearch for more help

Was this helpful?
 
Leave a comment


This textbox defaults to using Markdown to format your answer.

You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!

Try DigitalOcean for free

Click below to sign up and get $200 of credit to try our products over 60 days!

Sign up

Join the Tech Talk
Success! Thank you! Please check your email for further details.

Please complete your information!

Get our biweekly newsletter

Sign up for Infrastructure as a Newsletter.

Hollie's Hub for Good

Working on improving health and education, reducing inequality, and spurring economic growth? We'd like to help.

Become a contributor

Get paid to write technical tutorials and select a tech-focused charity to receive a matching donation.

Welcome to the developer cloud

DigitalOcean makes it simple to launch in the cloud and scale up as you grow — whether you're running one virtual machine or ten thousand.

Learn more
DigitalOcean Cloud Control Panel