One place for hosting & domains

      Cluster

      Comment configurer un cluster Mesosphere prêt pour la production sur Ubuntu 14.04


      Introduction

      Mesosphere est un système qui combine un certain nombre de composants afin de gérer efficacement la création de clusters des serveurs et des déploiements hautement disponibles au-dessus d’une couche de système d’exploitation existante. Contrairement aux systèmes comme CoreOS, Mesosphere n’est pas un système d’exploitation spécialisé mais plutôt un ensemble de paquets.

      Dans ce guide, nous allons découvrir comment configurer un cluster hautement disponible dans Mesosphere. Cette configuration nous permettra de nous doter d’une fonctionnalité de basculement dans le cas où l’un de nos nœuds maîtres tomberait en panne, ainsi qu’un pool de serveurs esclaves qui gère les tâches planifiées.

      Au cours de ce guide, nous utiliserons des serveurs Ubuntu 14.04.

      Conditions prélables et objectifs

      Avant de suivre ce guide, il est fortement recommandé de revoir notre introduction à Mesosphere. Il s’agit d’un excellent moyen pour vous de vous familiariser avec les composants dont le système est composé et de vous aider à identifier de quoi chaque unité est responsable.

      Au cours de ce tutoriel, nous utiliserons six serveurs Ubuntu. Cela satisfait à la recommandation Apache Mesos qui préconise l’utilisation d’au moins trois maîtres dans un environnement de production. Cela permet également d’avoir un pool de trois serveurs de travail ou esclaves, qui se verront attribués du travail lorsque les tâches seront envoyées au cluster.

      Les six serveurs avec lesquels nous travaillerons utiliserons zookeeper pour suivre le serveur maître leader actuel. La couche Mesos, intégré au-dessus, offrira une synchronisation et une manipulation des ressources distribuées Elle est responsable de la gestion du cluster. Marathon, le système d’init distribué du cluster, est utilisé pour planifier des tâches et travailler manuellement sur les serveurs esclaves.

      Dans le cas de ce guide, nous supposerons que nos machines ont la configuration suivante :

      Nom d’hôte Fonction adresse IP
      maître1 Maître Mesos 192.0.2.1
      maître2 Maître Mesos 192.0.2.2
      maître3 Maître Mesos 192.0.2.3
      esclave1 Esclave Mesos 192.0.2.51
      esclave2 Esclave Mesos 192.0.2.52
      esclave3 Esclave Mesos 192.0.2.53

      Ubuntu 14.04 devrait être installé sur chacune de ces machines. Vous devriez compléter les éléments de configuration de base répertoriés dans notre guide de configuration du serveur initial Ubuntu 14.04.

      Une fois que vous aurez terminé les deux étapes ci-dessus, poursuivez la lecture de ce guide.

      Installer Mesosphere sur les serveurs

      La première étape pour faire fonctionner votre cluser consiste à installer le logiciel. Heureusement, le projet Mesosphere conserve un dépôt Ubuntu avec des packages à jour faciles à installer.

      Ajouter les dépôts Mesosphere à vos hôtes

      Sur tous les hôtes (maîtres et esclaves), suivez les étapes suivantes.

      Tout d’abord, ajoutez le dépôt Mesosphere à votre liste de sources. Pour suivre ce processus, la clé du projet Mesosphere devra être téléchargée à partir du serveur de clés Ubuntu, puis l’URL applicable de notre version Ubuntu développée. Le projet vous offre un moyen pratique de le faire :

      sudo apt-key adv --keyserver keyserver.ubuntu.com --recv E56151BF
      DISTRO=$(lsb_release -is | tr '[:upper:]' '[:lower:]')
      CODENAME=$(lsb_release -cs)
      echo "deb http://repos.mesosphere.io/${DISTRO} ${CODENAME} main" | sudo tee /etc/apt/sources.list.d/mesosphere.list
      

      Installer les composants nécessaires

      Une fois le dépôt Mesosphere ajouté à votre système, vous devez mettre à jour votre cache de package local pour avoir accès à votre nouveau composant :

      sudo apt-get -y update
      

      Ensuite, vous devez installer les packages nécessaires. Les composants dont vous avez besoin dépendront du rôle de l’hôte.

      Pour vos hôtes maîtres, vous avez besoin du package de méta mesosphere. Cela inclut les applications zookeeper, mesos, marathon et chronos :

      sudo apt-get install mesosphere
      

      Pour vos hôtes slave, vous avez uniquement besoin du package mesos, qui extrait également zookeeper comme dépendance :

      sudo apt-get install mesos
      

      Configurer les informations de connexion de Zookeeper pour Mesos

      Dans un premier temps, nous allons configurer nos informations de connexion à zookeeper. Il s’agit de la couche sous-jacente qui permet à tous vos hôtes de se connecter aux bons serveurs maîtres, c’est pourquoi il est logique de commencer ici.

      Nos serveurs maîtres seront les seuls membres de notre cluster zookeeper, mais l’intégralité de nos serveurs devront être configurés pour pouvoir communiquer en utilisant le protocole. Le fichier qui définit cela est le suivant /etc/mesos/zk.

      Sur tous vos hôtes, procédez à l’étape suivante. Ouvrez le fichier avec les privilèges root :

      sudo nano /etc/mesos/zk
      

      À l’intérieur, vous trouverez l’URL de connexion configurée par défaut pour accéder à une instance locale. Cela ressemble à ce qui suit :

      zk://localhost:2181/mesos
      

      Vous devez la modifier afin qu’elle pointe vers nos trois serveurs maîtres. Pour cela, vous devez remplacer localhost par l’adresse IP de notre premier maître Mesos. Nous pouvons ensuite ajouter une virgule après la spécification du port et reproduire le format pour ajouter nos deuxième et troisième maîtres à la liste.

      Dans notre guide, les adresses IP de nos maîtres sont les suivantes : 192.0.2.1, 192.168.2.2 et 192.168.2.3. En utilisant ces valeurs, notre fichier ressemblera à ce qui suit :

      zk://192.0.2.1:2181,192.0.2.2:2181,192.0.2.3:2181/mesos
      

      La ligne doit commencer par zk:// et se terminer par /mesos. Entre deux sont spécifiées les adresses IP de vos serveurs maîtres et les ports zookeeper (2181 par défaut).

      Enregistrez et fermez le fichier lorsque vous avez terminé.

      Utilisez cette même saisie dans chacun de vos maîtres et esclaves. Cela aidera chaque serveur individuel à se connecter aux bons serveurs maîtres et pouvoir ainsi communiquer avec le cluster.

      Paramétrer la configuration de Zookeeper des serveurs maîtres

      Sur vos serveurs master, nous devrons procéder à une configuration supplémentaire de zookeeper.

      La première étape consiste à configurer un numéro d’identification unique, de 1 à 255 pour chacun de vos serveurs maîtres. Il se trouve dans le fichier /etc/zookeeper/conf/myid. Maintenant, ouvrez-le :

      sudo nano /etc/zookeeper/conf/myid
      

      Supprimez toutes les informations qui se trouvent dans ce fichier et remplacez-les par un seul numéro, de 1 à 255. Le numéro attribué à chacun de vos serveurs maîtres doit être unique. Par souci de simplicité, il est plus facile de commencer par 1 et de progresser. Dans notre guide, nous utiliserons 1, 2 et 3.

      Notre premier serveur contiendra uniquement ce qui suit dans le fichier :

      1
      

      Enregistrez et fermez le fichier lorsque vous avez terminé. Procédez ainsi sur chacun de vos serveurs maîtres.

      Ensuite, nous devons modifier notre fichier de configuration zookeeper pour cartographier nos ID zookeeper sur les hôtes en tant que tel. Vous aurez ainsi la garantie que le service est bien en capacité de résoudre correctement chaque hôte à partir du système d’ID qu’il utilise.

      Maintenant, ouvrez le fichier de configuration zookeeper :

      sudo nano /etc/zookeeper/conf/zoo.cfg
      

      Dans ce fichier, vous devrez cartographier chaque ID sur un hôte. La spécification de l’hôte inclura deux ports. Le premier servira à communiquer avec le leader. Le second se chargera des élections au moment où un nouveau leader sera nécessaire. Les serveurs zookeeper sont identifiés par le terme « server » suivi d’un point et de leur numéro d’ID.

      Dans notre guide, nous utiliserons les ports par défaut pour chaque fonction et nos ID vont de 1 à 3. Notre fichier ressemblera à ce qui suit :

      server.1=192.168.2.1:2888:3888
      server.2=192.168.2.2:2888:3888
      server.3=192.168.2.3:2888:3888
      

      Ajoutez ces mêmes cartographies dans chacun des fichiers de configuration de vos serveurs maîtres. Enregistrez et fermez chaque fichier lorsque vous avez terminé.

      Avec cela, notre configuration zookeeper est achevée. Nous pouvons commencer à nous concentrer sur Mesos et Marathon.

      Configurer Mesos sur les serveurs maîtres

      Ensuite, nous allons configurer Mesos sur les trois serveurs maîtres. Suivez ces étapes sur chacun de vos serveurs maîtres.

      Modifier le Quorum pour refléter la taille de votre cluster

      Tout d’abord, nous devons ajuster le quorum nécessaire à la prise de décision. Il déterminera le nombre d’hôtes nécessaires pour que le cluster soit en état de fonctionnement.

      Le quorum doit être configuré de manière à ce que plus de 50 pour cent des membres maîtres soient présents pour prendre des décisions. Cependant, il nous faut également établir une certaine tolérance de défaillance dans le cas où, si tous nos maîtres ne sont pas présents, le cluster puisse toujours fonctionner.

      Nous disposons de trois maîtres. Par conséquent, le seul réglage qui satisfait à ces deux exigences est un quorum de deux. Étant donné que la configuration initiale suppose un réglage de serveur unique, le quorum est actuellement défini à un.

      Ouvrez le fichier de configuration du quorum :

      sudo nano /etc/mesos-master/quorum
      

      Remplacez la valeur par « 2 » :

      2
      

      Enregistrez et fermez le fichier. Procédez ainsi sur chacun de vos serveurs maîtres.

      Configurer le nom d’hôte et l’adresse IP

      Nous allons ensuite spécifier le nom de l’hôte et l’adresse IP de chacun de nos serveurs maîtres. Pour le nom de l’hôte, nous utiliserons l’adresse IP afin que nos instances n’aient pas de difficultés à se résoudre correctement.

      Pour nos serveurs maîtres, l’adresse IP doit être placée dans ces fichiers :

      • /etc/mesos-master/ip
      • /etc/mesos-master/hostname

      Tout d’abord, ajoutez l’adresse IP individuelle de chaque nœud maître dans le fichier /etc/mesos-master/ip. N’oubliez pas de la modifier sur chaque serveur afin qu’elle corresponde à la valeur applicable :

      echo 192.168.2.1 | sudo tee /etc/mesos-master/ip
      

      Maintenant, nous pouvons copier cette valeur sur le fichier du nom de l’hôte :

      sudo cp /etc/mesos-master/ip /etc/mesos-master/hostname
      

      Procédez ainsi sur chacun de vos serveurs maîtres.

      Configurer Marathon sur les serveurs maîtres

      Maintenant que Mesos est configuré, nous pouvons procéder à la configuration de l’implémentation du système init en cluster de Mesosphere.

      Marathon s’exécutera sur chacun de nos hôtes maîtres. Cependant, seul le serveur maître principal pourra planifier des tâches. Les autres instances de Marathon transmettront les requêtes de manière transparente sur le serveur maîtres.

      Tout d’abord, nous devons à nouveau configurer le nom de l’hôte pour l’instance de Marathon de chaque serveur. Encore une fois, nous utiliserons l’adresse IP dont nous disposons déjà dans un fichier. Nous pouvons la copier dans l’emplacement du fichier dont nous avons besoin.

      Cependant, la structure du répertoire de configuration de Marathon dont nous avons besoin n’est pas automatiquement créée. Nous allons devoir créer le répertoire pour pouvoir ensuite y copier le fichier :

      sudo mkdir -p /etc/marathon/conf
      sudo cp /etc/mesos-master/hostname /etc/marathon/conf
      

      Ensuite, nous devons définir la liste des maîtres zookeeper auxquels Marathon se connectera pour obtenir des informations et une planification. Il s’agit de la même chaîne de connexion zookeeper que nous avons utilisée pour Mesos afin que nous puissions tout simplement y copier le fichier. Nous devons le placer dans un fichier appelé master :

      sudo cp /etc/mesos/zk /etc/marathon/conf/master
      

      Notre service Marathon pourra ainsi se connecter au cluster Mesos. Cependant, nous souhaitons également que Marathon stocke ses propres informations dans zookeeper. Pour cela, nous utiliserons l’autre fichier de connexion zookeeper comme base et modifierons tout simplement le point final.

      Tout d’abord, copiez le fichier dans l’emplacement zookeeper de Marathon :

      sudo cp /etc/marathon/conf/master /etc/marathon/conf/zk
      

      Ensuite, ouvrez le fichier dans votre éditeur :

      sudo nano /etc/marathon/conf/zk
      

      Le point final est la seule partie que nous ayons besoin de modifier. Nous allons remplacer /mesos par /marathon :

      zk://192.0.2.1:2181,192.0.2.2:2181,192.0.2.3:2181/marathon
      

      C’est tout ce qu’il faut faire pour configurer Marathon.

      Configurer les règles d’init des services et redémarrer les services

      Ensuite, nous allons redémarrer les services de serveurs maîtres pour utiliser les paramètres qui nous avons configurés.

      Tout d’abord, nous allons nous assurer que nos serveurs maîtres exécutent uniquement le processus maître Mesos et non pas le processus esclave. Nous pouvons arrêter tout processus esclave actuellement en cours d’exécution (cela peut échouer mais ce n’est pas un problème car il s’agit uniquement de vérifier que le processus est arrêté). Nous pouvons également que le serveur ne commence pas avec le processus esclave au démarrage en créant un fichier de remplacement :

      sudo stop mesos-slave
      echo manual | sudo tee /etc/init/mesos-slave.override
      

      Maintenant, il ne reste plus qu’à redémarrer zookeeper, qui configurera nos élections maîtresses. Nous pouvons ensuite démarrer nos processus Mesos maître et Marathon :

      sudo restart zookeeper
      sudo start mesos-master
      sudo start marathon
      

      Pour obtenir un pic dans ce que vous venez juste de configurer, consultez l’un de nos serveurs maîtres notre navigateur web au port 5050 :

      http://192.168.2.1:5050
      

      Vous devriez voir l’interface principale de Mesos. Vous serez éventuellement informé que vous êtes redirigé vers le master actif en fonction de si vous vous êtes connecté au leader ou pas. Dans tous les cas, l’écran ressemblera à ce qui suit :

      Principale interface de Mesos

      Il s’agit d’une vue de votre cluster actuel. Vous n’y voyez pas grand-chose car il n’y a aucun nœud esclave disponible et aucune tâche a été démarrée.

      Nous avons également configuré Marathon, le contrôleur de tâches longue durée de Mesosphere. Il sera disponible sur le port 8080 sur l’un de vos maîtres :

      Interface principale de Marathon

      Nous allons brièvement voir comment utiliser ces interfaces une fois que nous avons configuré nos esclaves.

      Configurer les serveurs esclaves

      Maintenant que nous avons configuré nos serveurs maîtres, nous pouvons commencer à configurer nos serveurs esclaves.

      Nous avons déjà configuré nos esclaves avec les informations de connexion zookeeper de nos serveurs maîtres. Les esclaves eux-mêmes n’exécutent pas leurs propres instances zookeeper.

      Nous pouvons arrêter tout processus zookeeper qui fonctionne actuellement sur nos nœuds esclaves et créer un fichier de remplacement afin qu’il ne démarre pas automatiquement lorsque le serveur redémarre :

      sudo stop zookeeper
      echo manual | sudo tee /etc/init/zookeeper.override
      

      Ensuite, nous voulons créer un autre fichier de remplacement pour s’assurer que le processus maître Mesos ne commence pas sur nos serveurs esclaves. Nous allons également nous assurer qu’il est actuellement arrêté (il se peut que cette commande échoue si le processus est déjà arrêté. Ceci n’est pas un problème) :

      echo manual | sudo tee /etc/init/mesos-master.override
      sudo stop mesos-master
      

      Ensuite, nous devons configurer l’adresse IP et le nom de l’hôte, tout comme nous l’avons fait pour nos serveurs maîtres. Cela implique la mise en place de l’adresse IP de chaque nœud dans un fichier, cette fois sous le répertoire /etc/mesos-slave. Nous utiliserons également le nom de l’hôte pour accéder facilement aux services via l’interface web :

      echo 192.168.2.51 | sudo tee /etc/mesos-slave/ip
      sudo cp /etc/mesos-slave/ip /etc/mesos-slave/hostname
      

      Encore une fois, utilisez l’adresse IP de chaque serveur esclave pour la première commande. Cela garantira qu’elle est bien liée à la bonne interface.

      Maintenant, nous avons tous les éléments pour démarrer nos esclaves Mesos. Il nous suffit juste d’activer le service :

      sudo start mesos-slave
      

      Procédez ainsi sur chacune de vos machines esclaves.

      Pour voir si vos esclaves s’enregistrent avec succès dans votre cluster, revenez à l’un de vos serveurs maîtres au port 5050 :

      http://192.168.2.1:5050
      

      Maintenant, vous devriez voir le nombre d’esclaves actifs afficher « 3 » dans l’interface :

      Trois esclaves Mesos

      Vous pouvez également voir que les ressources disponibles dans l’interface ont été mises à jour pour refléter les ressources pooled dans vos machines esclaves :

      Ressources Mesos

      Pour obtenir de plus amples informations sur chacune de vos machines, vous pouvez cliquer sur le lien « Slaves » en haut de l’interface. Ceci nous donnera un aperçu de la contribution en ressources de chaque machine, ainsi que les liens vers une page de chaque esclave :

      Page des esclaves Mesos

      Démarrer des services sur Mesos et Marathon

      Marathon est l’utilitaire Mesosphere permettant de planifier les tâches de longue durée. Il est facile de penser que Marathon est le système init pour un cluster Mesosphere car il prend en charge les services de démarrage et d’arrêt, tout en planifiant les tâches et en s’assurant que les applications remontent si elles descendent.

      Vous pouvez également ajouter des services et des tâches à Marathon de quelques manières différentes. Nous allons uniquement couvrir les services de base. Les conteneurs Docker seront traités dans un prochain guide.

      Démarrer un service via l’interface Web

      La manière la plus directe de lancer un service rapidement sur le cluster consiste à ajouter une application par le biais de l’interface Web Marathon.

      Tout d’abord, consultez l’interface Web de Marathon sur l’un de vos serveurs maîtres. N’oubliez pas que l’interface de Marathon se trouve sur le port 8080 :

      http://192.168.2.1:8080
      

      À partir de là, vous pouvez cliquer sur le bouton « New App » dans le coin supérieur droit. Cela fera apparaître une superposition dans laquelle vous pouvez ajouter des informaions sur votre nouvelle application :

      Nouvelle application Marathon

      Complétez les champs avec les exigences de votre application. Seules les champs suivants sont obligatoires

      • ID : un ID unique que l’utilisateur choisi pour identifier un processus. Vous pouvez utiliser ce que vous voulez, mais il doit être unique.
      • Command : il s’agit de la commande réelle que Marathon exécutera réellement. Il s’agit du processus qui sera surveillé et redémarré en cas d’échec.

      En utilisant ces informations, vous pouvez configurer un service simple qui imprime simplement « hello » et se met en veille pendant 10 secondes. Nous allons appeler ce « hello » :

      Application simple de Marathon

      Une fois que vous reviendrez à l’interface, le service passera de « Deploying » à « Running » :

      Application Marathon en cours d'exécution

      Environ toutes les 10 secondes, l’élément « Tasks/Instances » passera de « 1/1 » à « 0/1 » au fur et à mesure que la quantité de mise en veille passe et le service s’arrête. Ensuite, Marathon redémarre automatiquement la tâche. Nous pouvons voir ce processus plus clairement dans l’interface Web de Mesos sur le port 5050 :

      http://192.168.2.1:5050
      

      Ici, vous pouvez voir le processus se terminer et être redémarré :

      Tâche de redémarrage de Mesos

      En cliquant sur « Sandbox » et que « stdout » d’une des tâches, vous pouvez voir la sortie « hello » en cours de production :

      Sortie Mesos

      Démarrage d’un service via l’API

      Nous pouvons également soumettre des services via l’API Marathon. Pour cela, il vous faudra y transmettre un objet JSON contenant tous les champs que la superposition contenait.

      Il s’agit d’un processus relativement simple. Encore une fois, les seuls champs requis sont id pour l’identificateur de processus et cmd qui contient la commande réelle à exécuter.

      Donc, nous devons créer un fichier JSON appelé hello.json avec ces informations :

      nano hello.json
      

      À l’intérieur, la spécification minimum devrait ressembler à ce qui suit :

      {
        "id": "hello2",
        "cmd": "echo hello; sleep 10"
      }
      

      Ce service fonctionnera correctement. Cependant, si nous souhaitons réellement émuler le service que nous avons créé dans l’IU web, nous devons ajouter quelques champs supplémentaires. Ces valeurs seront configurées par défaut dans l’IU web et nous pouvons les répliquer ici :

      {
        "id": "hello2",
        "cmd": "echo hello; sleep 10",
        "mem": 16,
        "cpus": 0.1,
        "instances": 1,
        "disk": 0.0,
        "ports": [0]
      }
      

      Sauvegardez et fermez le fichier JSON une fois que vous aurez fini.

      Ensuite, nous pouvons le soumettre en utilisant l’API Marathon. La cible est l’un de nos services maître de Marathon sur le port 8080 et le point de terminaison est /v2/apps. La charge de données de notre fichier, que nous nous pouvons y lire curl en utilisant la balise -d avec la balsise @ pour indiquer un fichier.

      La commande ressemblera à ceci :

      curl -i -H 'Content-Type: application/json' -d@hello2.json 192.168.2.1:8080/v2/apps
      

      Si nous regardons l’interface Marathon, nous pouvons voir que l’ajout à bien été effectué. Il semble avoir exactement les mêmes propriétés que notre premier service :

      Deux services Marathon

      Vous pouvez surveiller et accéder au nouveau service exactement de la même manière que le premier service.

      Conclusion

      À ce stade, vous devriez avoir un cluster Mesosphere qui fonctionne. Pour le moment, nous avons uniquement abordé la configuration de base. Vous devriez cependant être en mesure de voir les possibilités qu’offre le système Mesosphere.

      Dans les futurs guides, nous allons voir comment déployer des conteneurs Docker sur votre cluster et utiliser certains outils plus en profondeur.



      Source link

      Securing Your Kubernetes Cluster From Threats


      How to Join

      This Tech Talk is free and open to everyone. Register below to get a link to join the live event.

      Format Date RSVP
      Presentation and Q&A November 3, 2020, 11:00 a.m.–12:00 p.m. ET

      If you can’t join us live, the video recording will be published here as soon as it’s available.

      About the Talk

      Kubernetes is the most popular container orchestration choice right now, but is it really secure? What are the risks of the high-risk vulnerabilities that have been discovered in Kubernetes and containers? How do we fix these known bugs?

      This Tech Talk will cover key security practices for both individuals and organizations that are designing and implementing Kubernetes.

      What You’ll Learn

      • How vulnerable is your Kubernetes cluster
      • Known vulnerabilities in Kubernetes and their potential risks
      • How to fix these known vulnerabilities
      • Security best practices to follow while designing a Kubernetes cluster

      Prerequisites

      • Basic knowledge of Kubernetes and container technologies. This session will focus on real-world security threats associated with Kubernetes and how we can follow some best practices to make our Kubernetes clusters more secure.

      About the Presenter

      Saurabh Gupta is a tech enthusiast with over a decade of experience in the software industry. Currently a Senior Developer Advocate with DigitalOcean, he can be found speaking at community meetups and conferences, as he is also part of the CNCF Speakers Bureau. His focus areas are around open source, DevOps, cloud, containers, and Kubernetes.

      To join the live Tech Talk, register here.



      Source link

      Cómo configurar y proteger un clúster etcd con Ansible en Ubuntu 18.04


      El autor seleccionó a Wikimedia Foundation para recibir una donación como parte del programa Write for DOnations.

      Introducción

      etcd es un almacén de clave-valor distribuido en el que se basan muchas plataformas y herramientas, como Kubernetes, Vulcand y Doorman. En Kubernetes, etcd se utiliza como una tienda de configuración global que almacena el estado del clúster. Saber administrar etcd es esencial para gestionar clústeres de Kubernetes. Si bien hay muchas ofertas de Kubernetes gestionados, conocidos como Kubernetes como servicio, que eliminan esta carga administrativa, muchas empresas siguen prefiriendo ejecutar clústeres autogestionados de Kubernetes en sus instalaciones debido a la flexibilidad que ofrecen.

      La primera mitad de este artículo lo guiará en el proceso de configuración de un clúster etcd de 3 nodos en servidores de Ubuntu 18.04. La segunda mitad se centrará en la protección del clúster usando Transport Layer Security, o TLS. Para ejecutar cada configuración de forma automatizada, utilizaremos Ansible en todo momento. Ansible es una herramienta de administración de configuración similar a Puppet, Chef y SaltStack que nos permite definir cada paso de configuración de manera declarativa dentro de archivos denominados cuadernos de estrategias.

      Al final de este tutorial, tendrá un clúster etcd de 3 nodos en ejecución en sus servidores. También tendrá un cuaderno de estrategias de Ansible que le permite recrear de manera repetida y consistente la misma configuración en conjuntos de servidores nuevos.

      Requisitos previos

      Para completar esta guía, necesitará lo siguiente:

      Advertencia: Como el propósito de este artículo es proporcionar una introducción a los ajustes de un clúster etcd en una red privada, los tres servidores de Ubuntu 18.04 de esta configuración no se probaron con un firewall y se accedió a ellos con un usuario root. En una configuración de producción, todos los nodos expuestos a una red pública de Internet requerirían un firewall y un usuario sudo para respetar las prácticas recomendadas de seguridad. Para obtener más información, consulte el tutorial Configuración inicial para servidores con Ubuntu 18.04.

      Paso 1: Configurar Ansible para el nodo de control

      Ansible es una herramienta que se utiliza para administrar servidores. Los servidores que administra Ansible se denominan nodos administrados y el equipo que ejecuta Ansible, nodo de control. Ansible utiliza las claves SSH del nodo de control para obtener acceso a los nodos administrados. Una vez que se establezca una sesión SSH, Ansible ejecutará un conjunto de secuencias de comandos para proporcionar y configurar los nodos administrados. En este paso, comprobaremos que podemos usar Ansible para establecer conexión con nodos administrados y ejecutar el comando hostname.

      Un día típico de un administrador de sistemas puede implicar administrar diferentes conjuntos de nodos. Por ejemplo, puede usar Ansible para proporcionar servidores nuevos, pero utilizarlo más adelante para volver a configurar otro conjunto de servidores. Ansible proporciona el concepto de inventario de host (o inventario para abreviar) para permitir a los administradores organizar mejor el conjunto de nodos administrados. Puede definir todos los nodos que desee administrar con Ansible en un archivo de inventario y organizarlos en grupos. Luego, al ejecutar los comandos ansible y ansible-playbook, puede especificar los hosts o los grupos a los que se aplica el comando.

      Ansible lee el archivo de inventario de /etc/ansible/hosts de forma predeterminada; sin embargo, podemos especificar un archivo de inventario diferente utilizando el indicador --inventory (o -i para abreviar).

      Para comenzar, cree un directorio nuevo en su equipo local (el nodo de control) para alojar todos los archivos de este tutorial:

      • mkdir -p $HOME/playground/etcd-ansible

      Luego, ingrese el directorio que acaba de crear:

      • cd $HOME/playground/etcd-ansible

      Dentro del directorio, cree y abra un archivo de inventario en blanco denominado hosts con su editor:

      • nano $HOME/playground/etcd-ansible/hosts

      Dentro del archivo hosts, enumere cada uno de sus nodos administrados con el siguiente formato, sustituyendo las direcciones IP públicas resaltadas por las de sus servidores:

      ~/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 línea [etcd] define un grupo denominado etcd. En la definición del grupo, enumeramos todos nuestros nodos administrados. Cada línea comienza con un alias (por ejemplo, etcd1), lo que nos permite referirnos a cada host usando un nombre fácil de recordar en vez de una dirección IP larga. ansible_host y ansible_user son variables de Ansible. En este caso, se utilizan para proporcionarle a Ansible las direcciones IP públicas y los nombres de usuario de SSH que se deben utilizar para la conexión a través de SSH.

      Para asegurarnos de que Ansible pueda conectarse con nuestros nodos administrados, podemos probar la conectividad al utilizar Ansible para ejecutar el comando hostname en cada uno de los hosts del grupo etcd:

      • ansible etcd -i hosts -m command -a hostname

      Desglosemos este comando para aprender lo que significa cada sección:

      • etcd: especifica el patrón de host que se utiliza para determinar qué hosts del inventario se administran con este comando. Aquí, utilizamos el nombre del grupo como patrón de host.
      • -i hosts: especifica el archivo de inventario que se debe utilizar.
      • -m command: la funcionalidad detrás de Ansible la proporcionan los módulos. El módulo command toma el argumento pasado y lo ejecuta como comando en cada uno de los nodos administrados. Presentaremos algunos módulos más de Ansible más adelante en este tutorial.
      • -a hostname: es el argumento que se pasa al módulo. La cantidad y los tipos de argumentos dependen del módulo.

      Después de ejecutar el comando, obtendrá el siguiente resultado, que indica que Ansible está configurado correctamente:

      Output

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

      Los comandos que ejecuta Ansible se denominan tareas. El uso de ansible en la línea de comandos para ejecutar tareas se denomina comandos ad-hoc. La ventaja de los comandos ad-hoc es que son rápidos y requieren poca configuración; la desventaja es que se ejecutan manualmente y, por lo tanto, no pueden utilizarse con sistemas de control de versiones como Git.

      Una leve mejora sería escribir una secuencia de comandos de shell y ejecutar nuestros comandos usando el módulo script de Ansible. Esto nos permitiría registrar los pasos de configuración que tomamos en un control de versiones. Sin embargo, las secuencias de comandos de shell son imperativas, lo que significa que debemos asumir la tarea de determinar los comandos que se deben ejecutar para configurar el sistema de acuerdo al estado deseado (es decir, la manera de hacerlo). Ansible, por otro lado, promueve un enfoque declarativo en el que nosotros definimos el estado deseado que debería tener nuestro servidor en los archivos de configuración y Ansible se encarga de que lo alcance.

      El enfoque declarativo es preferible porque la intención del archivo de configuración se transmite de forma inmediata, por lo que es más fácil de entender y mantener. También atribuye la manipulación de casos de borde a Ansible en lugar de al administrador, lo que nos ahorra mucho trabajo.

      Ahora que configuró el nodo de control de Ansible para establecer conexión con los nodos administrados, en el siguiente paso, le presentaremos los cuadernos de estrategias de Ansible, que permiten especificar tareas de forma declarativa.

      Paso 2: Obtener nombres de los host de nodos administrados con cuadernos de estrategias de Ansible

      En este paso, vamos a replicar lo que hicimos en el Paso 1, imprimir los nombres de host de los nodos administrados, pero, en vez de ejecutar tareas ad-hoc, definiremos todas las tareas de forma declarativa como cuadernos de estrategias de Ansible y las ejecutaremos. El propósito de este paso es demostrar cómo funcionan los cuadernos de estrategias de Ansible; realizaremos tareas mucho más importantes con ellos en pasos posteriores.

      Cree un archivo nuevo denominado playbook.yaml en el directorio de su proyecto con su editor:

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

      Añada las siguientes líneas en playbook.yaml:

      ~/playground/etcd-ansible/playbook.yaml

      - hosts: etcd
        tasks:
          - name: "Retrieve hostname"
            command: hostname
            register: output
          - name: "Print hostname"
            debug: var=output.stdout_lines
      

      Cierra y guarde el archivo playbook.yaml presionando CTRL+X y, luego, Y.

      El playbook contiene una lista de estrategias; y cada una de ellas contiene una lista de tareas que se deben ejecutar en todos los hosts que coincidan con el patrón de host que especifica la clave hosts. En este cuaderno de estrategias, tenemos una estrategia que contiene dos tareas. La primera tarea ejecuta el comando hostname utilizando el módulo command y registra el resultado en una variable denominada output. En la segunda tarea, usamos el módulo debug para imprimir la propiedad stdout_lines de la variable output.

      Ahora, podemos ejecutar este cuaderno de estrategias utilizando el comando ansible-playbook:

      • ansible-playbook -i hosts playbook.yaml

      Obtendrá el siguiente resultado, que indica que su cuaderno de estrategias está funcionando correctamente:

      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

      Nota: a veces, ansible-playbook utiliza cowsay como manera lúdica de imprimir los encabezamientos. Si encuentra muchos dibujos de una vaca en código ASCII en su terminal, ahora sabe el motivo. Para desactivar esta función, establezca la variable de entorno ANSIBLE_NOCOWS en 1 antes de ejecutar ansible-playbook ejecutando export ANSIBLE_NOCOWS=1 en su shell.

      En este paso, pasamos de ejecutar tareas ad-hoc imperativas a ejecutar cuadernos de estrategias declarativos. En el siguiente paso, sustituiremos estas dos tareas de demostración con tareas que configurarán nuestro clúster etcd.

      Paso 3: Instalar etcd en nodos administrados

      En este paso, le presentaremos los comandos necesarios para instalar etcd de forma manual y le demostraremos cómo convertir esos mismos comandos en tareas de nuestro cuaderno de estrategias de Ansible.

      etcd y su cliente etcdctl están disponibles como binarios, que descargaremos, extraeremos y colocaremos en un directorio que es parte de la variable de entorno PATH. Estos son los pasos que se deben llevar a cabo en cada uno de los nodos administrados al realizar la configuración de forma manual:

      • 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

      Con los primeros cuatro comandos, se descargan los binarios y se extraen en el directorio /opt/etcd/bin/ . El cliente etcdctl usará API v2 para comunicarse con el servidor etcd de manera predeterminada. Como estamos ejecutando etcd v3.x, el último comando establece la variable de entorno ETCDCTL_API en 3.

      Nota: Aquí, utilizamos etcd v3.3.13 en un equipo con procesadores que utilizan el conjunto de instrucciones AMD64. Puede encontrar binarios de otros sistemas y otras versiones en la página oficial de Lanzamientos de GitHub.

      Para replicar los mismos pasos en un formato estandarizado, podemos agregar tareas a nuestro cuaderno de estrategias. Abra el archivo playbook.yaml en su editor:

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

      Sustituya el archivo playbook.yaml en su totalidad por el siguiente contenido:

      ~/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
      

      Cada tarea utiliza un módulo; en este conjunto de tareas, estamos utilizando los siguientes:

      • file: para crear el directorio /opt/etcd/bin y, más adelante, establecer los permisos del archivo para los binarios etcd y etcdctl.
      • get_url: para descargar el paquete tarball comprimido con gzip en los nodos administrados.
      • unarchive: para extraer y desempaquetar los binarios etcd y etcdctl del paquete tarball comprimido con gzip.
      • lineinfile: para agregar una entrada en el archivo .profile.

      Para aplicar estos cambios, cierre y guarde el archivo playbook.yaml presionando CTRL+X y, luego, Y. A continuación, vuelva a ejecutar el mismo comando ansible-playbook en la terminal:

      • ansible-playbook -i hosts playbook.yaml

      La sección PLAY RECAP del resultado solo mostrará ok y 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

      Para confirmar si etcd se instaló de forma correcta, ingrese con SSH de forma manual a uno de los nodos administrados y ejecute etcd y etcdctl:

      etcd1_public_ip es la dirección IP pública del servidor denominado etcd1. Una vez que haya obtenido acceso mediante SSH, ejecute etcd --version para imprimir la versión de etcd instalada:

      Obtendrá un resultado similar al siguiente, que indica que el binario etcd está instalado correctamente:

      Output

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

      Para confirmar que etcdctl se haya instalado correctamente, ejecute la versión etcdctl:

      Verá un resultado similar al siguiente:

      Output

      etcdctl version: 3.3.13 API version: 3.3

      Tenga en cuenta que el resultado dice API version: 3.3, lo que también confirma que nuestra variable de entorno ETCDCTL_API se estableció correctamente.

      Salga del servidor etcd1 para regresar a su entorno local.

      Con esto, instalamos etcd y etcdctl en todos nuestros nodos administrados correctamente. En el siguiente paso, añadiremos más tareas a nuestra estrategia para ejecutar etcd como servicio en segundo plano.

      Paso 4: Crear un archivo de unidad para etcd

      La manera más rápida de ejecutar etcd con Ansible parece ser ejecutar /opt/etcd/bin/etcd con el módulo command. Sin embargo, no funcionará, porque hará que etcd se ejecute como proceso en primer plano. El uso del módulo command hará que Ansible no responda mientras espera que se devuelva el comando etcd, lo que no sucederá en ningún momento. Por lo tanto, en este paso, vamos a actualizar nuestro cuaderno de estrategias para ejecutar nuestro binario etcd como servicio en segundo plano en su lugar.

      Ubuntu 18.04 utiliza systemd como sistema init, lo que significa que podemos crear servicios nuevos al escribir archivos de unidades y colocarlos en el directorio /etc/systemd/system/.

      Primero, cree un directorio nuevo denominado files/ en el directorio de nuestro proyecto:

      A continuación, cree un archivo nuevo denominado etcd.service en ese directorio con su editor:

      Luego, copie el siguiente bloque de código en el archivo 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
      

      Este archivo de unidad define un servicio que ejecuta el ejecutable en /opt/etcd/bin/etcd, notifica a systemd cuando termina de inicializarse y, si sale en algún momento, se reinicia.

      Nota: Si desea obtener más información sobre los archivos de unidades y systemd o adaptar el archivo de unidad a sus necesidades, consulte la guía Información sobre unidades y archivos de unidades de Systemd.

      Cierre y guarde el archivo files/etcd.service presionando CTRL+X y, luego, Y.

      A continuación, debemos agregar una tarea en nuestro cuaderno de estrategias para copiar el archivo local files/etcd.service al directorio /etc/systemd/system/etcd.service de cada nodo administrado. Podemos hacerlo usando el módulo copy.

      Abra su cuaderno de estrategias:

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

      Añada la siguiente tarea resaltada al final de nuestras tareas existentes:

      ~/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
      

      Al copiar el archivo de unidad en /etc/systemd/system/etcd.service, definimos un servicio.

      Guarde y cierre el cuaderno de estrategias.

      Vuelva a ejecutar el mismo comando ansible-playbook para aplicar los cambios nuevos:

      • ansible-playbook -i hosts playbook.yaml

      Para confirmar que los cambios se hayan aplicado, primero, ingrese mediante SSH a uno de los nodos administrados:

      A continuación, ejecute systemctl status etcd para consultar a systemd el estado del servicio etcd:

      Obtendrá el siguiente resultado, que indica que el servicio está cargado:

      Output

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

      Nota: La última línea (Active: inactive (dead)) del resultado indica que el servicio está inactivo, lo que significa que no se ejecutará de forma automática cuando el sistema se inicie. Esto es de esperar, no es un error.

      Presione q para regresar al shell y, luego, ejecute exit para salir del nodo administrado y volver a su shell local:

      En este paso, actualizamos nuestro cuaderno de estrategias para ejecutar el binario etcd como servicio de systemd. En el siguiente paso, continuaremos configurando etcd al proporcionarle espacio para almacenar sus datos.

      Paso 5: Configurar el directorio de datos

      etcd es un almacén de datos de valor clave, lo que significa que debemos proporcionarle espacio para almacenar sus datos. En este paso, vamos a actualizar nuestro cuaderno de estrategias para definir un directorio de datos específico para etcd.

      Abra su cuaderno de estrategias:

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

      Añada la siguiente tarea al final de la lista de tareas:

      ~/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
      

      En este caso, utilizamos /var/lib/etcd/hostname.etcd como directorio de datos, donde hostname es el nombre de host del nodo administrado actual. inventory_hostname es una variable que representa el nombre de host del nodo administrado actual; Ansible completa su valor de forma automática. La sintaxis de llaves (es decir, {{ inventory_hostname }}) se utiliza para sustituir variables con el motor de plantillas Jinja2, que es el motor predeterminado de Ansible.

      Cierre el editor de texto y guarde el archivo.

      A continuación, debemos indicarle a etcd que utilice este directorio de datos. Lo haremos pasando el parámetro data-dir a etcd. Para establecer los parámetros de etcd, podemos utilizar una combinación de variables de entorno, indicadores de la línea de comandos y archivos de configuración. En este tutorial, utilizaremos un archivo de configuración, dado que es mucho más fácil aislar todas las configuraciones en un archivo en lugar de tenerlas esparcidas por todo nuestro cuaderno de estrategias.

      Cree un directorio nuevo denominado templates/ en el directorio de su proyecto:

      A continuación, cree un archivo nuevo denominado etcd.conf.yaml.j2 en ese directorio con su editor:

      • nano templates/etcd.conf.yaml.j2

      Luego, copie la siguiente línea y péguela en el archivo:

      ~/playground/etcd-ansible/templates/etcd.conf.yaml.j2

      data-dir: /var/lib/etcd/{{ inventory_hostname }}.etcd
      

      Este archivo utiliza la misma sintaxis de sustitución de variables de Jinja2 que nuestro cuaderno de estrategias. Podemos usar el módulo template para sustituir las variables y cargar el resultado en cada host administrado. Funciona de manera similar a copy, con la excepción de que realiza la sustitución de variables antes de la carga.

      Salga de etcd.conf.yaml.j2 y abra su cuaderno de estrategias:

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

      Añada las siguientes tareas a la lista de tareas para crear un directorio y cargar el archivo de configuración basado en una plantilla en su interior:

      ~/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
      

      Guarde y cierre este archivo.

      Como realizamos este cambio, debemos actualizar el archivo de unidad de nuestro servicio para que pase la ubicación de nuestro archivo de configuración (es decir, /etc/etcd/etcd.conf.yaml).

      Abra el archivo del servicio etcd en su equipo local:

      Actualice el archivo files/etcd.service añadiendo el indicador --config-file resaltado en lo siguiente:

      ~/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
      

      Guarde y cierre este archivo.

      En este paso, utilizamos nuestro cuaderno de estrategias para proporcionar un directorio de datos para que etcd almacene sus datos. En el siguiente paso, añadiremos algunas tareas adicionales para reiniciar el servicio etcd y hacer que se ejecute en el inicio.

      Paso 6: Habilitar e iniciar el servicio etcd

      Siempre que realizamos cambios en el archivo de unidad de un servicio, debemos reiniciarlo para que surtan efecto. Podemos hacerlo ejecutando el comando systemctl restart etcd. Además, para que el servicio etcd se inicie de forma automática en el inicio del sistema, debemos ejecutar systemctl enabled etcd. En este paso, ejecutaremos esos dos comandos utilizando el cuaderno de estrategias.

      Podemos usar el módulo command para ejecutar comandos:

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

      Añada las siguientes tareas al final de la lista de tareas:

      ~/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
      

      Guarde y cierre el archivo.

      Vuelva a ejecutar ansible-playbook -i hosts playbook.yaml una vez más:

      • ansible-playbook -i hosts playbook.yaml

      Para verificar que el servicio etcd se haya reiniciado y habilitado, ingrese mediante SSH a uno de los nodos administrados:

      A continuación, ejecute systemctl status etcd para consultar el estado del servicio etcd:

      Encontrará enabled y active (running), como indica lo resaltado en el siguiente ejemplo, lo que significa que se implementaron los cambios que hicimos en nuestro cuaderno de estrategias:

      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)

      En este paso, utilizamos el módulo command para ejecutar comandos systemctl para reiniciar y habilitar el servicio etcd en nuestros nodos administrados. Ahora que configuramos una instalación de etcd, en el siguiente paso, vamos a probar su funcionalidad al llevar a cabo algunas operaciones básicas de creación, lectura, actualización y eliminación (CRUD).

      Paso 7: Probar etcd

      Si bien tenemos una instalación de etcd en funcionamiento, no es segura ni está listo para usarse en producción. Pero antes de proteger nuestra configuración de etcd en pasos posteriores, primero, vamos comprender lo que puede hacer etcd en términos de funcionalidad. En este paso, vamos a enviar solicitudes a etcd de forma manual para añadir, recuperar, actualizar y eliminar sus datos.

      Por defecto, etcd expone una API que escucha en el puerto 2379 para la comunicación con el cliente. Esto significa que podemos enviar solicitudes de API sin procesar a etcd utilizando un cliente HTTP. Sin embargo, es más rápido usar el cliente oficial de etcd, etcdctl, que permite crear/actualizar, recuperar y eliminar pares clave-valor usando los subcomandos put, get y del respectivamente.

      Asegúrese de seguir estando en el nodo administrado etcd1 y ejecute los siguientes comandos de etcdctl para confirmar que su instalación de etcd esté funcionando.

      Primero, cree una nueva entrada utilizando el subcomando put.

      El subcomando put tiene la siguiente sintaxis:

      etcdctl put key value
      

      En etcd1, ejecute el siguiente comando:

      El comando que acabamos de ejecutar le indica a etcd que escriba el valor "bar" en la clave foo del almacén.

      A continuación, encontrará OK impreso en el resultado, lo que indica que los datos se mantuvieron:

      Output

      OK

      Luego, podemos recuperar esta entrada utilizando el subcomando get, que tiene la sintaxis etcdctl get key:

      Obtendrá este resultado, que muestra la clave en la primera línea y el valor que insertó anteriormente en la segunda:

      Output

      foo bar

      Podemos eliminar la entrada utilizando el subcomando del, que tiene la sintaxis etcdctl del key:

      Obtendrá el siguiente resultado, que indica la cantidad de entradas eliminadas:

      Output

      1

      Ahora, vamos a ejecutar el subcomando get una vez más para intentar recuperar un par clave-valor eliminado:

      No obtendrá ningún resultado, lo que significa que etcdctl no puede recuperar el par clave-valor. Esto confirma que las entradas eliminadas no pueden recuperarse.

      Ahora que probó las operaciones básicas de etcd y etcdctl, salga del nodo administrado y regrese a su entorno local:

      En este paso, utilizamos el cliente etcdctl para enviar solicitudes a etcd. En este punto, estamos ejecutando tres instancias separadas de etcd que actúan de forma independiente unas de otras. Sin embargo, etcd se diseñó como un almacén de clave-valor distribuido, lo que significa que se pueden agrupar varias instancias de etcd para formar un clúster único; luego, cada instancia se convierte en miembro del clúster. Después de crear un clúster, podrá recuperar pares clave-valor insertados desde distintos miembro del clúster. En el siguiente paso, utilizaremos nuestro playbook para transformar nuestros tres clústeres de un solo nodo en un clúster único de tres nodos.

      Paso 8: Crear un clúster utilizando descubrimiento estático

      Para crear un clúster de tres nodos en lugar de tres de un nodo, debemos configurar estas instalaciones de etcd para que se comuniquen entre sí. Esto significa que cada una de ellas debe conocer las direcciones IP de las demás. Este proceso se denomina descubrimiento. El descubrimiento se puede realizar utilizando una configuración estática o un descubrimiento de servicio dinámico. En este paso, analizaremos la diferencia entre ambas y, también, actualizaremos nuestro cuaderno de estrategias para configurar un clúster de etcd con descubrimiento estático.

      El descubrimiento mediante configuración estática es el método que requiere menos configuración; los puntos de conexión de cada miembro se pasan al comando etcd antes de su ejecución. Para usar la configuración estática, se deben cumplir las siguientes condiciones antes de la inicialización del clúster:

      • se debe conocer la cantidad de miembros
      • se deben conocer los puntos de conexión de cada miembro
      • las direcciones IP de todos los puntos de conexión deben ser estáticas

      Si no se pueden cumplir estas condiciones, puede usar un servicio de descubrimiento dinámico. Con el descubrimiento de servicios dinámicos, todas las instancias se registran en el servicio de descubrimiento, lo que permite a cada miembro recuperar información sobre la ubicación de los demás.

      Como sabemos que queremos tener un clúster de etcd de tres nodos y todos nuestros servidores tienen direcciones IP estáticas, utilizaremos el descubrimiento estático. Para iniciar nuestro clúster utilizando descubrimiento estático, debemos agregar varios parámetros a nuestro archivo de configuración. Utilice un editor para abrir el archivo de plantilla templates/etcd.conf.yaml.j2 :

      • nano templates/etcd.conf.yaml.j2

      A continuación, añada las siguientes líneas resaltadas:

      ~/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 %}
      

      Cierre y guarde el archivo templates/etcd.conf.yaml.j2  presionando CTRL+X y, luego, Y.

      A continuación, presentamos una breve explicación de cada parámetro:

      • name: el nombre del miembro en lenguaje natural. Por defecto, etcd utiliza un identificador único generado al azar para identificar cada miembro; sin embargo, un nombre en lenguaje natural nos permite referirnos a ellos con mayor facilidad tanto en los archivos de configuración como en la línea de comandos. Aquí, utilizaremos los nombres de host como nombres de los miembros (es decir, etcd1, etcd2 y etcd3).
      • initial-advertise-peer-urls: una lista de combinaciones de direcciones IP y puertos que pueden usar otros miembros para comunicarse con este. Además del puerto de la API (2379) de etcd también expone el puerto 2380 para la comunicación de punto a punto entre los miembros de etcd, lo que les permite enviar mensajes e intercambiar datos. Tenga en cuenta que estas URL deben poder ser accesibles para sus pares (y no deben ser una dirección IP local).
      • listen-peer-urls: una lista de combinaciones de direcciones IP y puertos que el miembro actual utilizará para escuchar comunicaciones de otros miembros. Debe incluir todas las URL del indicador --initial-advertise-peer-urls, así como las URL locales, como 127.0.0.1:2380. La dirección IP y el puerto de destino de los mensajes entrantes de pares deben coincidir con una de las URL que se enumeran aquí.
      • advertise-client-urls: una lista de combinaciones de direcciones IP y puertos que deben usar los clientes para comunicarse con este miembro. El cliente debe poder acceder a estas URL (y no deben ser una dirección local). Si el cliente está accediendo al clúster a través de una red pública de Internet, la dirección IP debe ser pública.
      • listen-client-urls: una lista de combinaciones de direcciones IP y puertos que el miembro actual utilizará para escuchar comunicaciones de los clientes. Debe incluir todas las URL del indicador --advertise-client-urls, así como las URL locales, como 127.0.0.1:2379. La dirección IP y el puerto de destino de los mensajes entrantes de clientes deben coincidir con una de las URL que se enumeran aquí.
      • initial-cluster: una lista de los puntos de conexión de cada miembro del clúster. Cada punto de conexión debe coincidir con una de las URL de initial-advertise-peer-urls del miembro correspondiente.
      • initial-cluster-state: puede ser new o existing.

      Para asegurar consistencia, etcd solo puede tomar decisiones cuando la mayoría de los nodos tienen un estado correcto. Esto se conoce como establecimiento de quorum. En otras palabras, en un clúster de tres miembros, hay quorum cuando dos o más de ellos tienen un estado correcto.

      Si el parámetro initial-cluster-state se establece en new, etcd sabrá que se trata de un clúster nuevo que se está iniciando y permitirá que los miembros se inicien en paralelo sin esperar a que se alcance el quorum. Más concretamente, una vez que se inicie el primer miembro, no habrá quorum porque un tercio (33,33 %) es menor o igual que el 50 %. Normalmente, etcd se detendría y se negaría a realizará más acciones, por lo que el clúster nunca se crearía. Sin embargo, con initial-cluster-state establecido en new, ignorará la falta de quorum inicial.

      Si se establece en existing, el miembro intentará unirse a un clúster existente, y esperará que el quorum ya esté establecido.

      Nota: Puede obtener más información sobre todos los indicadores de configuración compatibles en la sección Configuración de la documentación de etcd.

      En el archivo de plantilla templates/etcd.conf.yaml.j2 actualizado, hay algunas instancias de hostvars. Cuando Ansible se ejecute, recopilará variables de diversas fuentes. Ya hemos utilizado la variable inventory_hostname, pero hay mucho más disponible. Estas variables están disponibles en hostvars[inventory_hostname]['ansible_facts']. Aquí, estamos extrayendo las direcciones IP privadas de cada nodo y las estamos utilizando para crear el valor de nuestro parámetro.

      Nota: Como habilitamos la opción Redes privadas cuando creamos nuestros servidores, habrá tres direcciones IP asociadas a cada servidor:

      • Una dirección IP de bucle invertido: una dirección que solo es válida dentro del mismo equipo. Se utiliza para que el equipo haga referencia a sí mismo, por ejemplo, 127.0.0.1
      • Una dirección IP pública: una dirección que se puede enrutar mediante una red pública de Internet, por ejemplo, 178.128.169.51
      • Una dirección IP privada: una dirección que solo se puede enrutar mediante una red privada; en el caso de Droplets de DigitalOcean, hay una red privada en cada centro de datos, por ejemplo, 10.131.82.225

      Cada una de estas direcciones IP está asociada con una interfaz de red distinta: la dirección de bucle invertido se asocia con la interfaz lo, la dirección IP pública se asocia con la interfaz eth0 y la dirección IP privada con la interfaz eth1. Estamos utilizando la interfaz eth1 para que todo el tráfico se mantenga en la red privada, sin llegar a Internet.

      Para los fines de este artículo, no es necesario tener conocimientos sobre las interfaces de red, pero, si desea obtener más información, el artículo Introducción a terminología de redes, interfaces y protocolos es un buen punto de partida.

      La sintaxis {% %} de Jinja2 define la estructura de bucle for que se itera a través de cada nodo del grupo etcd para crear la cadena initial-cluster en un formato requerido por etcd.

      Para crear el clúster nuevo de tres miembros, debe detener el servicio etcd y borrar el directorio de datos antes de iniciar el clúster. Para hacerlo, utilice un editor para abrir el archivo playbook.yaml en su equipo local:

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

      Luego, antes de la tarea "Create a data directory", añada una tarea para detener el servicio 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:
          ...
      

      A continuación, actualice la tarea "Create a data directory" para eliminar el directorio de datos y recrearlo:

      ~/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 propiedad with_items define una lista de cadenas sobre la cual iterará en esta tarea. Es equivalente a repetir la misma tarea dos veces, pero con valores diferentes para la propiedad state. Aquí, estamos iterando sobre la lista con elementos absent y directory, lo que garantiza que el directorio de datos se elimine primero y, luego, se vuelva a crear.

      Cierra y guarde el archivo playbook.yaml presionando CTRL+X y, luego, Y. A continuación, vuelva a ejecutar ansible-playbook. Ahora, Ansible creará un clúster de etcd único, de 3 miembros:

      • ansible-playbook -i hosts playbook.yaml

      Puede verificarlo al ingresar mediante SSH en cualquier nodo miembro de etcd:

      A continuación, ejecute etcdctl endpoint health --cluster:

      • etcdctl endpoint health --cluster

      Esto enumerará el estado de cada miembro del clúster:

      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

      Ahora, hemos creado correctamente un clúster de etcd de tres nodos. Podemos confirmar esto añadiendo una entrada a etcd en un nodo miembro y recuperándola de otro. En uno de los nodos miembro, ejecute etcdctl put:

      A continuación, utilice una terminal nueva para ingresar mediante SSH en un nodo miembro diferente:

      Luego, intente recuperar la misma entrada usando la clave:

      Podrá recuperar la entrada, lo que demuestra que el clúster está funcionando:

      Output

      foo bar

      Por último, salga de todos los nodos administrados y regrese a su equipo local:

      En este paso, proporcionamos un clúster de tres nodos. En este momento, las comunicaciones entre los miembros de etcd y sus pares y clientes se llevan a cabo mediante HTTP. Esto significa que la comunicación no está cifrada y cualquier parte que pueda interceptar el tráfico puede leer los mensajes. Esto no es un problema importante si el clúster y los clientes de etcd están implementados en una red privada o una red privada virtual (VPN) que controle por completo. Sin embargo, si algún tráfico debe transmitirse a través de una red compartida (privada o pública), deberá asegurarse de que ese tráfico esté cifrado. Además, se debe establecer un mecanismo para que un cliente o par compruebe la autenticidad del servidor.

      En el siguiente paso, veremos cómo proteger las comunicaciones de cliente a servidor y las de punto a punto utilizando TLS.

      Paso 9: Obtener direcciones IP privadas de nodos administrados

      Para cifrar mensajes entre nodos miembros, etcd utiliza el Protocolo seguro de transferencia de hipertexto, o HTTPS, que es una capa por encima de la Seguridad de la capa de transporte, o TLS. TLS utiliza un sistema de claves privadas, certificados y entidades de confianza denominadas Entidades de certificación (CA) para autenticarse y enviar mensajes cifrados.

      En este tutorial, cada nodo miembro debe generar un certificado para identificarse y este debe estar firmado por una CA. Configuraremos todos los nodos miembros para que confíen en esta CA y, por lo tanto, también en cualquier certificado firmado por ella. Esto les permite a los nodos miembro autenticarse mutuamente.

      El certificado que genere cada nodo miembro debe permitir que los demás lo identifiquen. Todos los certificados incluyen el Nombre común (CN) de la entidad a la que están asociados. Esto se suele utilizar como la identidad de la entidad. Sin embargo, al verificar un certificado, las implementaciones de los clientes pueden comparar si la información recopilada sobre la entidad concuerda con la proporcionada en el certificado. Por ejemplo, si un cliente descarga un certificado TLS con firmante CN=foo.bar.com, pero, en realidad, se está conectando al servidor usando una dirección IP (por ejemplo, 167.71.129.110), hay una incongruencia y es posible que el cliente no confíe en el certificado. Al especificar un nombre alternativo del firmante (SAN) en el certificado, le informa que ambos nombres pertenecen a la misma entidad.

      Como nuestros miembros de etcd se emparejan utilizando sus direcciones IP privadas, al definir nuestros certificados, tendremos que proporcionar estas direcciones IP privadas como nombres alternativos del firmante.

      Para obtener la dirección IP privada de un nodo administrado, ingrese a él mediante SSH:

      Luego, ejecute el siguiente comando:

      • ip -f inet addr show eth1

      Verá un resultado similar a las siguientes líneas:

      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

      En nuestro resultado ejemplo, 10.131.255.176 es la dirección IP privada del nodo administrado, y la única información que nos interesa. Para filtrar todo lo que no corresponde a la IP privada, podemos canalizar el resultado del comando ip a la utilidad sed, que se utiliza para filtrar y transformar texto.

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

      Ahora, el único resultado es la dirección IP privada:

      Output

      10.131.255.176

      Una vez que esté satisfecho con el funcionamiento del comando anterior, salga del nodo administrado:

      Para incorporar los comandos anteriores a nuestro playbook, abra el archivo playbook.yaml:

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

      A continuación, añada una nueva estrategia con una sola tarea antes de nuestra estrategia existente:

      ~/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 tarea utiliza el módulo shell para ejecutar los comandos ip y sed, con lo cual se obtiene la dirección IP privada del nodo administrado. A continuación, registra el valor que devolvió el comando shell dentro de una variable denominada privateIP, que utilizaremos más adelante.

      En este paso, añadimos una tarea al cuaderno de estrategias para obtener la dirección IP privada de los nodos administrados. En el siguiente paso, vamos a utilizar esta información para generar certificados para cada nodo miembro que firmaremos con una Entidad de certificación (CA).

      Paso 10: Generar claves privadas y CSR para los miembros de etcd

      Para que un nodo miembro pueda recibir tráfico cifrado, el remitente debe utilizar la clave pública del nodo miembro para cifrar los datos y el nodo miembro debe usar su clave privada para descifrar el texto cifrado y obtener los datos originales. La clave pública se empaqueta en un certificado y la firma una CA para garantizar que sea auténtica.

      Por lo tanto, debemos generar una clave privada y una solicitud de firma de certificado (CSR) para cada nodo miembro de etcd. Para facilitar la tarea, generaremos todos los pares de claves y firmaremos todos los certificados de forma local, en el nodo de control y, luego, copiaremos los archivos pertinentes a los hosts administrados.

      Primero, cree un directorio denominado artifacts/, donde colocaremos los archivos (claves y certificados) que se generarán durante el proceso. Abra el archivo playbook.yaml con un editor:

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

      Utilice el módulo file para crear el directorio artifacts/ en su interior:

      ~/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:
      ...
      

      A continuación, añada otra tarea al final de la estrategia para generar la clave privada:

      ~/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 creación de claves privadas y CSR se puede llevar a cabo utilizando los módulos openssl_privatekey y openssl_csr respectivamente.

      El atributo force: True garantiza que siempre se genere una clave privada, incluso si ya existe.

      Del mismo modo, añada la siguiente tarea nueva al misma estrategia para generar las CSR para cada miembro utilizando el módulo 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'] }}"
      

      Estamos especificando que este certificado puede estar involucrado en un mecanismo de firma digital para la autenticación del servidor. Este certificado se asocia con el nombre de host (por ejemplo, etcd1), pero el verificador debe tratar las direcciones IP de bucle inverso privadas y locales de cada nodo como nombres alternativos. Tenga en cuenta que estamos utilizando la variable privateIP que registramos en la estrategia anterior.

      Cierre y guarde el archivo playbook.yaml presionando CTRL+X y, luego, Y. A continuación, vuelva a ejecutar el playbook:

      • ansible-playbook -i hosts playbook.yaml

      Ahora, encontraremos un nuevo directorio denominado artifacts en el directorio de nuestro proyecto; usaremos ls para enumerar su contenido:

      Encontrará las claves privadas y las CSR de cada miembro de etcd:

      Output

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

      En este paso, usamos varios módulos de Ansible para generar claves privadas y certificados de clave pública para cada uno de los nodos miembro. En el siguiente paso, veremos cómo firmar una solicitud de firma de certificado (CSR).

      Paso 11: Generar certificados de CA

      Dentro de un clúster de etcd, los nodos miembro cifran los mensajes usando la clave pública del receptor. Para garantizar que la clave pública sea genuina, el receptor empaqueta la clave pública en una solicitud de firma de certificado (CSR) y hace que una entidad de confianza (es decir, la CA) lo firme. Como controlamos todos los nodos miembro y las CA en las que confían, no necesitamos usar una CA externa y podemos actuar como nuestra propia CA. En este paso, vamos a actuar como nuestra propia CA, lo que significa que debemos generar una clave privada y un certificado autofirmado para que funcione como CA.

      Primero, abra el archivo playbook.yaml con su editor:

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

      A continuación, al igual que en el paso anterior, añada una tarea a la estrategia localhost para generar una clave privada para la CA:

      ~/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"
      ...
      

      A continuación, utilice el módulo openssl_csr para generar una CSR nueva. Esto es similar a lo que hicimos en el paso anterior, con la diferencia de que, en esta CSR, estamos añadiendo la extensión de uso de la clave y la restricción básica para indicar que este certificado puede utilizarse como certificado de 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"
      ...
      

      Por último, utilice el módulo openssl_certificate para autofirmar la 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"
      ...
      

      Cierre y guarde el archivo playbook.yaml presionando CTRL+X y, luego, Y. A continuación, vuelva a ejecutar el playbook para aplicar los cambios:

      • ansible-playbook -i hosts playbook.yaml

      También puede ejecutar ls para verificar el contenido del directorio artifacts/ :

      Ahora, encontrará el certificado de CA generado recientemente (ca.crt):

      Output

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

      En este paso, generamos una clave privada y un certificado autofirmado para la CA. En el siguiente paso, usaremos el certificado de CA para firmar la CSR de cada miembro.

      Paso 12: Firmar las CSR de los miembros de etcd

      En este paso, vamos a firmar la CSR de cada nodo miembro. Lo haremos de forma similar a la manera en que usamos el módulo openssl_certificate para firmar el certificado de CA, pero, en lugar de usar el proveedor self-signed, utilizaremos el proveedor ownca, que nos permite firmar con nuestro propio certificado de CA.

      Abra su cuaderno de estrategias:

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

      Añada la siguiente tarea resaltada a la tarea "Generate self-signed CA certificate":

      ~/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"
      ...
      

      Cierre y guarde el archivo playbook.yaml presionando CTRL+X y, luego, Y. A continuación, vuelva a ejecutar el cuaderno de estrategias para aplicar los cambios:

      • ansible-playbook -i hosts playbook.yaml

      Ahora, enumere el contenido del directorio artifacts/:

      Encontrará la clave privada, la CSR y el certificado de cada miembro de etcd y la CA:

      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

      En este paso, firmamos las CSR de cada nodo miembro usando la clave de la CA. En el siguiente paso, vamos a copiar los archivos pertinentes en cada nodo administrado, para que etcd tenga acceso a las claves y los certificados correspondientes para configurar las conexiones de TLS.

      Paso 13: Copiar claves privadas y certificados

      Cada nodo debe tener una copia del certificado de la CA autofirmado (ca.crt). Cada nodo de miembro de etcd también debe tener su clave privada y certificado propios. En este paso, vamos a cargar estos archivos y los colocaremos en un directorio nuevo /etc/etcd/ssl/.

      Para comenzar, abra el archivo playbook.yaml con su editor:

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

      Para realizar estos cambios en nuestro playbook de Ansible, primero, actualice la propiedad path de la tarea Create directory for etcd configuration para crear el directorio /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:
      ...
      

      A continuación, detrás de la tarea modificada, añada tres tareas más para copiar los archivos:

      ~/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:
      ...
      

      Cierre y guarde el archivo playbook.yaml presionando CTRL+X y, luego, Y.

      Vuelva a ejecutar ansible-playbook para realizar estos cambios:

      • ansible-playbook -i hosts playbook.yaml

      En este paso, cargamos correctamente las claves privadas y los certificados a los nodos administrados. Ahora que copiamos los archivos, debemos actualizar nuestro archivo de configuración para usarlos.

      Paso 14: Habilitar TLS en etcd

      En el último paso de este tutorial, vamos a actualizar algunas configuraciones de Ansible para habilitar TLS en un clúster de etcd.

      Primero, abra el archivo de plantillas templates/etcd.conf.yaml.j2 con su editor:

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

      En su interior, cambie todas las URL para usar https como protocolo en lugar de http. Adicionalmente, añada una sección al final de la plantilla para especificar la ubicación del certificado de CA, el certificado del servidor y la clave del servidor:

      ~/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
      

      Cierre y guarde el archivo templates/etcd.conf.yaml.j2.

      A continuación, ejecute su playbook de Ansible:

      • ansible-playbook -i hosts playbook.yaml

      Luego, ingrese mediante SSH a uno de los nodos administrados:

      Cuando haya ingresado, ejecute el comando etcdctl endpoint health para verificar si los puntos de conexión están usando HTTPS y si el estado de todos los miembros es correcto:

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

      Como nuestro certificado de CA no es, por defecto, un certificado root de CA de confianza instalado en el directorio /etc/ssl/certs/, debemos pasarlo a etcdctl usando el indicador --cacert.

      Esto generará el siguiente resultado:

      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

      Para confirmar que el clúster etcd efectivamente esté funcionando, podemos crear una entrada en un nodo miembro y recuperarla desde otro:

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

      A continuación, utilice una terminal nueva para ingresar mediante SSH en un nodo diferente:

      Ahora, recupere la misma entrada utilizando la clave foo:

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

      Esto devolverá la entrada y verá el siguiente resultado:

      Output

      foo bar

      Puede hacer lo mismo en el tercer nodo para garantizar que los tres miembros estén en funcionamiento.

      Conclusión

      Proporcionó correctamente un clúster de etcd de tres nodos, lo protegió con TLS y confirmó que esté funcionando.

      etcd es una herramienta creada originalmente por CoreOS. Para entender el uso de etcd en relación con CoreOS, puede consultar el artículo Cómo usar Etcdctl y Etcd, el almacén de clave-valor distribuido de CoreOS. El artículo también lo guía a través del proceso de configuración de un modelo de descubrimiento dinámico, algo que mencionamos, pero no demostramos en este tutorial.

      Como se mencionó al principio de este tutorial, etcd es un componente importante del ecosistema de Kubernetes. Para obtener más información sobre Kubernetes y la función que desempeña etcd en él, puede consultar el artículo Introducción a Kubernetes. Si está implementando etcd como parte de un clúster de Kubernetes, tenga en cuenta que puede utilizar otras herramientas, como kubespray y kubeadm. Para obtener más información al respecto, puede consultar el artículo Cómo crear un clúster de Kubernetes usando Kubeadm en Ubuntu 18.04.

      Por último, en este tutorial, se utilizaron muchas herramientas, pero no se pudo entrar en demasiado detalle en cada una de ellas. Encontrará enlaces que proporcionarán un análisis más detallado de cada herramienta aquí:



      Source link