L’auteur a choisi Wikimedia Foundation pour recevoir un don dans le cadre du programme Write for Donations.
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.
Avant de commencer ce guide, vous aurez besoin des éléments suivants :
Python , pip
, et le package pyOpenSSL
installé sur votre machine locale. Pour savoir comment installer Python3, pip et les packages Python, reportez-vous à Comment installer Python 3 et mettre en place un environnement de programmation local sur Ubuntu 18.04 .
Trois serveurs Ubuntu 18.04 sur le même réseau local, avec au moins 2 Go de RAM et un accès SSH root. Vous devez également configurer les serveurs pour qu’ils aient les noms d’hôtes etcd1 , etcd2 , et etcd3 . Les étapes décrites dans cet article fonctionneraient sur n’importe quel serveur générique, pas nécessairement DigitalOcean Droplets. Cependant, si vous souhaitez héberger vos serveurs sur DigitalOcean, vous pouvez suivre le guide Comment créer un droplet du panneau de contrôle de DigitalOcean pour remplir cette condition. Notez que vous devez activer l’option Private Networking lors de la création de votre droplet. Pour activer la mise en réseau privée sur les droplets existants, reportez-vous à la section Comment activer la mise en réseau privée sur les droplets .
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.
Une paire de clés SSH permettant à votre machine locale d’accéder aux serveurs etcd1 , etcd2 , et etcd3. Si vous ne savez pas ce que sont les sciences humaines, ou si vous ne disposez pas d’une paire de clés, vous pouvez vous renseigner en lisant Les Fondamentaux SSH : Travailler avec les serveurs, les clients et les clés SSH.
Ansible installé sur votre machine locale. Par exemple, si vous utilisez Ubuntu 18.04, vous pouvez installer Ansible en suivant l’étape 1 de l’article Comment installer et configurer Ansible sur Ubuntu 18.04. Les commandes ansible
et ansible-playbook
seront ainsi disponibles sur votre machine. Vous pouvez également conserver ce mode d’emploi Comment utiliser Ansible : Un guide de référence à portée de main. Les commandes de ce tutoriel devraient fonctionner avec Ansible v2.x ; nous l’avons testé sur Ansible v2.9.7 sous Python v3.8.2.
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 :
- mkdir -p $HOME/playground/etcd-ansible
Ensuite, entrez dans le répertoire que vous venez de créer :
- 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 :
- 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 :
[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
:
- 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 :
Outputetcd2 | 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.
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 :
- nano $HOME/playground/etcd-ansible/playbook.yaml
À l’intérieur de playbook.yaml
, ajoutez les lignes suivantes :
- 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
:
- ansible-playbook -i hosts playbook.yaml
Vous trouverez la sortie suivante, ce qui signifie que votre playbook fonctionne correctement :
OutputPLAY [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.
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 :
- mkdir -p /opt/etcd/bin
- cd /opt/etcd/bin
- wget -qO- https://storage.googleapis.com/etcd/v3.3.13/etcd-v3.3.13-linux-amd64.tar.gz | tar --extract --gzip --strip-components=1
- echo 'export PATH="$PATH:/opt/etcd/bin"' >> ~/.profile
- 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 :
- nano $HOME/playground/etcd-ansible/playbook.yaml
Remplacez la totalité du fichier playbook.yaml
par le contenu suivant :
- 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
:
- 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
:
- 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 :
- 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 :
Outputetcd 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
:
- etcdctl version
Vous recevrez un résultat similaire à celui qui suit :
Outputetcdctl 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.
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/
:
- mkdir files
Ensuite, à l’aide de votre éditeur, créez un nouveau fichier nommé etcd.service
dans ce répertoire :
- nano files/etcd.service
Ensuite, copiez le bloc de code suivant dans le fichier 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 :
- nano $HOME/playground/etcd-ansible/playbook.yaml
Ajoutez la tâche surlignée suivante à la fin de nos tâches existantes :
- 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 :
- 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 :
- ssh root@etcd1_public_ip
Ensuite, lancez systemctl status etcd
pour interroger systemd sur l’état du service etcd
:
- 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 :
- 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.
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 :
- nano $HOME/playground/etcd-ansible/playbook.yaml
Ajoutez la tâche suivante à la fin de la liste des tâches :
- 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 dat
a-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/
:
- mkdir templates
Ensuite, à l’aide de votre éditeur, créez un nouveau fichier nommé etcd.conf.yaml.j2
dans le répertoire :
- nano templates/etcd.conf.yaml.j2
Ensuite, copiez la ligne suivante et collez-la dans le fichier :
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 :
- 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 :
- 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 :
- nano files/etcd.service
Mettez à jour le fichier files/etcd.service
en ajoutant l’indicateur --config-file
mis en évidence dans ce qui suit :
[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.
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
:
- nano $HOME/playground/etcd-ansible/playbook.yaml
Ajoutez les tâches suivantes à la fin de la liste des tâches :
- 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 :
- 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 :
- ssh root@etcd1_public_ip
Ensuite, lancez systemctl status etcd
pour vérifier l’état du service etcd
:
- 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).
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 :
- 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é :
OutputOK
Nous pouvons ensuite récupérer cette entrée en utilisant la sous-commande get
, qui a la syntaxe etcdctl get key
:
- 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 :
Outputfoo
bar
Nous pouvons supprimer l’entrée en utilisant la sous-commande del
, qui a la syntaxe etcdctl del key
:
- etcdctl del foo
Vous trouverez la sortie suivante, qui indique le nombre d’entrées supprimées :
Output1
Maintenant, lançons à nouveau la sous-commande get
pour tenter de récupérer une paire clé-valeur supprimée :
- 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 :
- 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.
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 :
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
:
- nano templates/etcd.conf.yaml.j2
Ajoutez les lignes en surbrillance suivantes :
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 :
127.0.0.1
178.128.169.51
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 :
- 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
:
- 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 :
- 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
:
- ansible-playbook -i hosts playbook.yaml
Vous pouvez vérifier cela en entrant en langage SSH dans n’importe quel nœud membre etcd :
- ssh root@etcd1_public_ip
Ensuite, lancez l’application etcdctl endpoint health --cluster
:
- etcdctl endpoint health --cluster
Le statut de chaque membre du groupe sera ainsi répertorié :
Outputhttp://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
:
- etcdctl put foo "bar"
Ensuite, utilisez un nouveau terminal pour SSH dans un autre nœud membre :
- ssh root@etcd2_public_ip
Ensuite, essayez de retrouver la même entrée en utilisant la clé :
- etcdctl get foo
Vous pourrez récupérer l’entrée, qui prouve que le cluster fonctionne :
Outputfoo
bar
Enfin, sortez de chacun des nœuds gérés et revenez à votre machine locale :
- exit
- 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.
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 :
- ssh root@etcd1_public_ip
Exécutez ensuite la commande suivante :
- ip -f inet addr show eth1
Vous trouverez des résultats similaires aux lignes suivantes :
Output3: 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.
- 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 :
Output10.131.255.176
Une fois que vous êtes convaincu que la commande précédente fonctionne, quittez le nœud géré :
- exit
Pour incorporer les commandes précédentes dans notre playbook, ouvrez d’abord le fichier playbook.yaml
:
- nano $HOME/playground/etcd-ansible/playbook.yaml
Ensuite, ajoutez une nouvelle pièce avec une seule tâche avant notre play existante :
...
- 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).
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 :
- nano $HOME/playground/etcd-ansible/playbook.yaml
Dans celui-ci, utilisez le module file
pour créer le répertoire artefacts/
:
...
- 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 :
...
- 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
:
...
- 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.
- 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 :
- ls artifacts
Vous trouverez les clés privées et les RSE de chacun des membres d’etcd :
Outputetcd1.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).
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 :
- 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 :
- 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 :
- 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 :
- 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 :
- ansible-playbook -i hosts playbook.yaml
Vous pouvez également lancer ls
pour vérifier le contenu du répertoire artefacts/
:
- ls artifacts/
Vous trouverez maintenant le certificat CA nouvellement généré ( ca.crt
) :
Outputca.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.
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 :
- 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é) :
- 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 :
- ansible-playbook -i hosts playbook.yaml
Maintenant, énumérez le contenu du répertoire artefacts
:
- ls artifacts/
Vous y trouverez la clé privée, le CSR et le certificat de chaque membre de l’etcd et de l’AC :
Outputca.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.
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 :
- 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/
:
- 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 :
- 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 :
- 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.
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 :
- 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 :
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 :
- ansible-playbook -i hosts playbook.yaml
Puis, SSH dans l’un des nœuds gérés :
- 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 :
- 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 :
Outputhttps://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 :
- etcdctl --cacert /etc/etcd/ssl/ca.crt put foo "bar"
Ensuite, utilisez un nouveau terminal pour SSH dans un autre nœud membre :
- ssh root@etcd2_public_ip
Récupérez maintenant la même entrée en utilisant la clé foo
:
- etcdctl --cacert /etc/etcd/ssl/ca.crt get foo
Ceci renverra l’entrée, montrant la sortie ci-dessous :
Outputfoo
bar
Vous pouvez faire la même chose sur le troisième nœud pour vous assurer que les trois membres sont opérationnels.
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.
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!