One place for hosting & domains

      Cluster

      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

      Como configurar e proteger um cluster etcd com o Ansible no Ubuntu 18.04


      O autor selecionou a Wikimedia Foundation para receber uma doação como parte do programa Write for DOnations.

      Introdução

      O etcd é um armazenamento distribuído de chaves-valores utilizado em muitas plataformas e ferramentas, incluindo o Kubernetes, Vulcand e o Doorman. Dentro do Kubernetes, o etcd é usado como um armazenamento de configuração global que armazena o estado do cluster. Saber administrar o etcd é essencial para administrar um cluster Kubernetes. Embora existam muitas opções gerenciadas do Kubernetes, também conhecidas como Kubernetes-as-a-Service, que removem esse peso administrativo de você, muitas empresas ainda escolhem utilizar clusters Kubernetes locais autogeridos devido à flexibilidade que eles trazem.

      A primeira metade deste artigo irá guiá-lo através da configuração de um cluster etcd de 3 nós em servidores Ubuntu 18.04. A segunda metade irá focar na proteção do cluster usando o Transport Layer Security, ou TLS. Para executar cada configuração de forma automatizada, usaremos o Ansible durante todo o processo. O Ansible é uma ferramenta de gerenciamento de configuração semelhante ao Puppet, Chef e ao SaltStack. Ele nos permite definir cada passo da configuração de maneira declarativa, dentro de arquivos chamados playbooks.

      No final deste tutorial, você terá um cluster etcd de 3 nós protegido em execução nos seus servidores. Você também terá um playbook do Ansible que lhe permite recriar repetidamente e consistentemente a mesma configuração em um conjunto de servidores novos.

      Pré-requisitos

      Antes de iniciar este guia, será necessário o seguinte:

      Aviso: como o objetivo deste artigo é fornecer uma introdução para configurar de um cluster etcd em uma rede privada, os três servidores Ubuntu 18.04 nesta configuração não foram testados com um firewall e são acessados com o usuário root. Em uma configuração de produção, qualquer nó exposto à internet pública exigiria um firewall e um usuário sudo para que ele se adequasse às práticas recomendadas de segurança. Para mais informações, confira o tutorial Configuração inicial de servidor com o Ubuntu 18.04.

      Passo 1 — Configurando o Ansible para o nó de controle

      O Ansible é uma ferramenta usada para gerenciar servidores. Os servidores que o Ansible está gerenciando são chamados de nós gerenciados, e a máquina que está executando o Ansible é chamada de nó de controle. O Ansible funciona usando as chaves SSH no nó de controle para obter acesso aos nós gerenciados. Depois que uma sessão SSH for estabelecida, o Ansible irá executar um conjunto de scripts para provisionar e configurar os nós gerenciados. Neste passo, vamos testar se somos capazes de usar o Ansible para nos conectar aos nós gerenciados e executar o comando hostname.

      Um dia típico para um administrador de sistemas pode envolver o gerenciamento de conjuntos diferentes de nós. Por exemplo, é possível usar o Ansible para provisionar alguns novos servidores, mas mais tarde usá-lo para reconfigurar outro conjunto de servidores. Para permitir que os administradores organizem melhor o conjunto de nós gerenciados, o Ansible oferece o conceito de inventário de host (ou somente inventário). Você pode definir todos os nós que deseja gerenciar com o Ansible dentro de um arquivo de inventário, e organizá-los em grupos. Dessa forma, ao executar os comandos ansible e ansible-playbook, é possível especificar a quais hosts ou grupos o comando se aplica.

      Por padrão, o Ansible lê o arquivo de inventário a partir de /etc/ansible/hosts. No entanto, podemos especificar um arquivo de inventário diferente usando o sinalizador --inventory (ou somente -i).

      Para iniciar, crie um novo diretório em sua máquina local (o nó de controle) para abrigar todos os arquivos para este tutorial:

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

      Em seguida, entre no diretório que acabou de criar:

      • cd $HOME/playground/etcd-ansible

      Dentro do diretório, crie e abra um arquivo de inventário em branco chamado hosts usando seu editor:

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

      Dentro do arquivo hosts, liste todos os seus seus nós gerenciados no seguinte formato, substituindo os endereços IP públicos destacados pelos endereços IP públicos reais dos seus 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
      

      A linha [etcd] define um grupo chamado etcd. Sob a definição de grupo, listamos todos os nossos nós gerenciados. Cada linha começa com um alias (por exemplo, etcd1), que nos permite fazer referência a cada host usando um nome fácil de lembrar, em vez de um endereço IP longo. O ansible_host e o ansible_user são variáveis do Ansible. Neste caso, são usados para fornecer ao Ansible os endereços IP públicos e os nomes de usuários SSH para usar ao conectar-se via SSH.

      Para garantir que o Ansible seja capaz de se conectar com nossos nós gerenciados, podemos testar a conectividade usando o Ansible para executar o comando hostname em cada um dos hosts dentro do grupo etcd:

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

      Vamos dividir esse comando para aprender o que cada parte significa:

      • etcd: especifica o padrão de host a ser usado para determinar quais hosts do inventário estão sendo gerenciados com este comando. Aqui, usamos o nome do grupo como padrão de host.
      • -i hosts: especifica o arquivo de inventário a ser usado.
      • -m command: as funcionalidades por trás do Ansible são fornecidas através de módulos. O módulo command recebe o argumento passado e executa-o como um comando em cada um dos nós gerenciados. Este tutorial irá introduzir alguns outros módulos do Ansible à medida que progredimos.
      • -a hostname: o argumento a ser passado para o módulo. A quantidade e os tipos de argumentos dependem do módulo.

      Depois de executar o comando, você verá o seguinte resultado, o que significa que o Ansible está configurado corretamente:

      Output

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

      Cada comando que o Ansible executa é chamado de tarefa. Usar o ansible na linha de comando para executar tarefas é um processo conhecido como executar comandos ad-hoc. A vantagem dos comandos ad-hoc é que eles são rápidos e exigem pouca configuração. O lado negativo é que eles são executados manualmente, e assim não podem ser consignados a um sistema de controle de versão como o Git.

      Uma ligeira melhoria seria escrever o script do shell e executar nossos comandos usando o módulo script do Ansible. Isso permitiria que gravássemos os passos de configuração que fizemos no controle de versão. No entanto, os scripts do shell são imperativos, ou seja, somos responsáveis por arranjar os comandos a serem executados (os “como”s) para configurar o sistema no estado desejado. Por outro lado, o Ansible defende uma abordagem declarativa, onde definimos “qual” o estado desejado em que o nosso servidor deve estar dentro dos arquivos de configuração, e o Ansible é responsável por levar o servidor até esse estado desejado.

      A abordagem declarativa é mais interessante porque a intenção do arquivo de configuração é imediatamente transmitida, sendo assim mais fácil entendê-lo e mantê-lo. Ela também coloca a responsabilidade de casos extremos nas costas do Ansible ao invés do administrador, poupando-nos muito trabalho.

      Agora que o nó de controle do Ansible foi configurado para se comunicar com os nós gerenciados, no próximo passo, vamos apresentar os playbooks do Ansible, que permitem especificar tarefas de forma declarativa.

      Neste passo, vamos replicar o que foi feito no Passo 1 — impressão dos nomes de host dos nós gerenciados— mas em vez de executar tarefas ad-hoc, iremos definir cada tarefa declarativamente como um playbook do Ansible para então executá-lo. O objetivo deste passo é demonstrar como os playbooks do Ansible funcionam. Vamos realizar tarefas muito mais robustas com os playbooks em etapas posteriores.

      Dentro do seu diretório de projeto, crie um novo arquivo chamado playbook.yaml usando seu editor:

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

      Dentro do playbook.yaml, adicione as seguintes linhas:

      ~/playground/etcd-ansible/playbook.yaml

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

      Feche e salve o arquivo playbook.yaml pressionando CTRL+X e depois Y.

      O playbook contém uma lista de peças; cada peça contém uma lista de tarefas que devem ser executadas em todos os hosts que correspondem ao padrão de host especificado pela chave hosts. Neste playbook, temos uma peça que contém duas tarefas. A primeira tarefa executa o comando hostname usando o módulo command e registra o resultado em uma variável chamada output. Na segunda tarefa, usamos o módulo debug para imprimir a propriedade stdout_lines da variável output.

      Agora, podemos executar este playbook usando o comando ansible-playbook:

      • ansible-playbook -i hosts playbook.yaml

      Você verá o seguinte resultado, o que significa que seu playbook está funcionando corretamente:

      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: o ansible-playbook às vezes usa o cowsay como uma maneira brincalhona de imprimir os cabeçalhos. Caso encontre muitas vacas em arte ASCII impressas em seu terminal, agora sabe porquê. Para desativar esse recurso, defina a variável de ambiente ANSIBLE_NOCOWS como 1 antes de executar o ansible-playbook. Faça isso executando export ANSIBLE_NOCOWS=1 no seu shell

      Neste passo, passamos a executar playbooks declarativos ao invés de tarefas ad-hoc imperativas. No próximo passo, vamos substituir essas duas tarefas demonstrativas por tarefas que irão configurar nosso cluster do etcd.

      Passo 3 — Instalando o etcd nos nós gerenciados

      Neste passo, vamos mostrar os comandos para instalar o etcd manualmente e demonstrar como traduzir esses mesmos comandos para tarefas dentro do nosso playbook do Ansible.

      O etcd e seu cliente etcdctl estão disponíveis como binários, os quais vamos baixar, extrair e mover para um diretório que faz parte da variável de ambiente PATH. Quando configurados manualmente, esses são os passos que vamos seguir em cada um dos nós gerenciados:

      • 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

      Os primeiros quatro comandos baixam e extraem os binários para o diretório /opt/etcd/bin/. Por padrão, o cliente etcdctl usará a API v2 para se comunicar com o servidor do etcd. Como estamos executando etcd v3.x, o último comando define a variável de ambiente ETCDCTL_API como 3.

      Nota: aqui, estamos usando o etcd v3.3.13 desenvolvido para uma máquina com processadores que usam o conjunto de instruções AMD64. Você pode encontrar binários para outros sistemas e outras versões na página oficial de Lançamentos no GitHub.

      Para replicar os mesmos passos em um formato padronizado, podemos adicionar tarefas ao nosso playbook. Abra o arquivo de playbook playbook.yaml no seu editor:

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

      Substitua tudo no arquivo playbook.yaml pelo seguinte conteúdo:

      ~/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 tarefa usa um módulo e para este conjunto de tarefas, estamos usando os seguintes módulos:

      • file: usado para criar o diretório /opt/etcd/bin, e para definir mais tarde as permissões de arquivo para os binários etcd e etcdctl.
      • get_url: usado para baixar o tarball gzipped nos nós gerenciados.
      • unarchive: usado para extrair e descompactar os binários etcd e etcdctl do tarball gzipped.
      • lineinfile: usado para adicionar uma entrada no arquivo .profile.

      Para aplicar essas alterações, feche e salve o arquivo playbook.yaml pressionando CTRL+X e depois Y. Então, no terminal, execute o mesmo comando ansible-playbook novamente:

      • ansible-playbook -i hosts playbook.yaml

      A seção PLAY RECAP da saída mostrará apenas ok e changed (alterado):

      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 se houve uma instalação correta do etcd, entre manualmente via SSH em um dos nós gerenciados e execute o etcd e o etcdctl:

      etcd1_public_ip é o endereço IP público do servidor chamado etcd1. Depois que tiver obtido acesso ao SSH, execute etcd --version para imprimir a versão instalada do etcd:

      Você verá um resultado semelhante ao que será mostrado a seguir, que significa que o binário etcd foi instalado com sucesso:

      Output

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

      Para confirmar se o etcdctl foi instalado com sucesso, execute version etcdctl:

      Você irá obter um resultado similar ao seguinte:

      Output

      etcdctl version: 3.3.13 API version: 3.3

      Observe que o resultado diz API version: 3.3, o que também confirma que nossa variável de ambiente ETCDCTL_API foi definida corretamente.

      Saia do servidor etcd1 para voltar ao seu ambiente local.

      Agora, instalamos com sucesso o etcd e o etcdctl em todos os nossos nós gerenciados. No próximo passo, vamos adicionar mais tarefas à nossa peça para executar o etcd como um serviço em segundo plano.

      Passo 4 — Criando um arquivo de unidade para o etcd

      Aparentemente, a maneira mais rápida de executar o etcd com o Ansible é usando o módulo command para executar /opt/etcd/bin/etcd. No entanto, isso não funcionará porque fará com que o etcd seja executado como um processo em primeiro plano. Usar o módulo command fará com que o Ansible pare enquanto espera que o comando etcd gere um retorno, o que nunca acontecerá. Sendo assim, neste passo, vamos atualizar nosso playbook para que execute nosso binário etcd como um serviço em segundo plano.

      O Ubuntu 18.04 usa o systemd como seu sistema init, o que significa que podemos criar novos serviços criando arquivos de unidade e colocando-os dentro do diretório /etc/systemd/system/.

      Primeiro, dentro do nosso diretório de projeto, crie um novo diretório chamado files/:

      Em seguida, usando seu editor, crie um novo arquivo chamado etcd.service dentro desse diretório:

      Depois disso, copie o seguinte bloco de código no arquivo 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 arquivo de unidade define um serviço que executa o arquivo executável /opt/etcd/bin/etcd, notifica o systemd assim que tiver terminado a inicialização e sempre reinicializa-se caso seja fechado.

      Nota: se quiser entender mais sobre o systemd e os arquivos de unidade, ou quiser adaptar o arquivo de unidade às suas necessidades, leia o guia Entendendo as unidades e arquivos de unidade do Systemd.

      Feche e salve o arquivo files/etcd.service pressionando CTRL+X e depois Y.

      Em seguida, é necessário adicionar uma tarefa dentro do nosso playbook que irá criar uma cópia do arquivo local files/etcd.service no diretório /etc/systemd/system/etcd.service para cada nó gerenciado. Podemos fazer isso usando o módulo copy.

      Abra seu playbook:

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

      Adicione a seguinte tarefa destacada no final das tarefas já 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
      

      Ao copiar o arquivo de unidade em /etc/systemd/system/etcd.service, um serviço agora está definido.

      Salve e saia do playbook.

      Execute novamente o mesmo comando ansible-playbook para aplicar as novas alterações:

      • ansible-playbook -i hosts playbook.yaml

      Para confirmar se as alterações foram aplicadas, primeiro entre via SSH em um dos nós gerenciados:

      Em seguida, execute systemctl status etcd para consultar o systemd sobre o status do serviço etcd:

      Você receberá o seguinte resultado, que afirma que o serviço está carregado:

      Output

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

      Nota: a última linha (Active: inactive (dead)) do resultado afirma que o serviço está inativo, o que significa que ele não seria executado automaticamente quando o sistema iniciasse. Isso é esperado e não representa um erro.

      Pressione q para voltar ao shell e então execute exit para sair do nó gerenciado e voltar para seu shell local:

      Neste passo, atualizamos nosso playbook para executar o binário etcd como um serviço do systemd. No próximo passo, vamos continuar a configuração do etcd fornecendo espaço para armazenar seus dados.

      Passo 5 — Configurando o diretório de dados

      O etcd é um armazenamento de dados de chaves-valores, o que significa que devemos fornecer-lhe espaço para armazenar seus dados. Neste passo, vamos atualizar nosso playbook para definir um diretório de dados dedicado para o etcd usar.

      Abra seu playbook:

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

      Acrescente a seguinte tarefa no final da lista de tarefas:

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

      Aqui, estamos usando o /var/lib/etcd/hostname.etcd como o diretório de dados, onde hostname é o nome de host do nó gerenciado em questão e inventory_hostname é uma variável que representa o nome de host do nó gerenciado em questão; seu valor é preenchido pelo Ansible automaticamente. A sintaxe com chaves (ou seja, {{ inventory_hostname }}) é usada para a substituição de variável, suportada pelo mecanismo de modelo padrão para o Ansible, Jinja2.

      Feche o editor de texto e salve o arquivo.

      Em seguida, é necessário instruir o etcd a usar este diretório de dados. Fazemos isso passando o parâmetro data-dir para o etcd. Para definir parâmetros do etcd, podemos usar uma combinação de variáveis de ambiente, sinalizadores de linha de comando e arquivos de configuração. Para este tutorial, usaremos um único arquivo de configuração, pois é muito mais simples isolar todas as configurações em apenas um arquivo, do que ter partes da configuração espalhadas em todo o nosso playbook.

      Em seu diretório de projeto, crie um novo diretório chamado templates/:

      Em seguida, usando seu editor, crie um novo arquivo chamado etcd.conf.yaml.j2 dentro do diretório:

      • nano templates/etcd.conf.yaml.j2

      Depois disso, copie a linha a seguir e cole no arquivo:

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

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

      Esse arquivo usa a mesma sintaxe de substituição de variável do Jinja2 que nosso playbook. Para substituir as variáveis e fazer upload do resultado para cada host gerenciado, podemos usar módulo template. Ele funciona de maneira semelhante ao copy, exceto que ele irá realizar a substituição de variáveis antes do upload.

      Saia do etcd.conf.yaml.j2 e abra seu playbook:

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

      Acrescente as seguintes tarefas na lista de tarefas para criar um diretório e fazer upload do arquivo de configuração modelado nele:

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

      Salve e feche esse arquivo.

      Como fizemos essa alteração, precisamos atualizar o arquivo de unidade do nosso serviço para passar-lhe a localização do nosso arquivo de configuração (ou seja, /etc/etcd/etcd.conf.yaml).

      Abra o arquivo do serviço etcd em sua máquina local:

      Atualize o arquivo files/etcd.service adicionando o sinalizador --config-file destacado a seguir:

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

      Salve e feche esse arquivo.

      Neste passo, usamos nosso playbook para fornecer um diretório de dados para o etcd armazenar seus dados. No próximo passo, iremos adicionar mais algumas tarefas para reiniciar o serviço etcd e fazê-lo ser executado na inicialização.

      Passo 6 — Habilitando e iniciando o serviço etcd

      Sempre que fazemos alterações no arquivo de unidade de um serviço, precisamos reiniciar o serviço para elas entrem em vigor. Podemos fazer isso executando o comando systemctl restart etcd. Além disso, para fazer com que o serviço etcd seja iniciado automaticamente na inicialização do sistema, precisamos executar systemctl enable etcd. Neste passo, vamos executar esses dois comandos usando o playbook.

      Para executar comandos, podemos usar o módulo command:

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

      Acrescente as seguintes tarefas no final da lista de tarefas:

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

      Salve e feche o arquivo.

      Execute ansible-playbook -i hosts playbook.yaml mais uma vez:

      • ansible-playbook -i hosts playbook.yaml

      Para verificar se o serviço etcd foi reiniciado e está habilitado, entre via SSH em um dos nós gerenciados:

      Em seguida, execute systemctl status etcd para verificar o status do serviço etcd:

      Você verá enabled e active (running) como destacado a seguir; isso significa que as alterações que fizemos em nosso playbook tiveram efeito:

      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)

      Neste passo, usamos o módulo command para executar comandos systemctl que reiniciam e habilitam o serviço etcd em nossos nós gerenciados. Agora que configuramos uma instalação etcd, vamos testar, no próximo passo, sua funcionalidade realizando algumas operações básicas de criação, leitura, atualização e exclusão (CRUD).

      Passo 7 — Testando o etcd

      Embora tenhamos uma instalação funcional do etcd, ela é insegura e ainda não está pronta para o uso na produção. Mas antes de adicionarmos segurança à nossa configuração do etcd em etapas posteriores, vamos primeiro entender o que o etcd pode fazer em termos de funcionalidade. Neste passo, vamos enviar manualmente solicitações ao etcd para adicionar, coletar, atualizar e excluir dados dele.

      Por padrão, o etcd expõe uma API que escuta na porta 2379 para a comunicação com o cliente. Isso significa que podemos enviar solicitações de API brutas para o etcd usando um cliente HTTP. No entanto, é mais rápido usar o cliente oficial do etcd, o etcdctl, que permite criar/atualizar, recuperar e excluir pares de chave-valor usando os subcomandos put, get e del, respectivamente.

      Certifique-se de ainda estar dentro do nó gerenciado etcd1 e execute os seguintes comandos do etcdctl para confirmar que sua instalação do etcd está funcionando.

      Primeiro, crie uma nova entrada usando o subcomando put.

      O subcomando put possui a seguinte sintaxe:

      etcdctl put key value
      

      No etcd1, execute o seguinte comando:

      O comando que acabamos de executar instrui o etcd a escrever o valor "bar" na chave foo no armazenamento.

      Em seguida, você verá um OK impresso no resultado, que indica que os dados persistiram:

      Output

      OK

      Em seguida, podemos recuperar essa entrada usando o subcomando get, que possui a sintaxe etcdctl get key:

      Você verá este resultado, que mostra a chave na primeira linha e o valor que você inseriu mais cedo na segunda linha:

      Output

      foo bar

      Podemos excluir a entrada usando o subcomando del, que possui a sintaxe etcdctl del key:

      Você verá o seguinte resultado, que indica o número de entradas excluídas:

      Output

      1

      Agora, vamos executar o subcomando get mais uma vez para tentar recuperar um par chave-valor excluído:

      Você não receberá um resultado, o que significa que o etcdctl não é capaz de recuperar o par chave-valor. Isso confirma que depois que a entrada é excluída, ela não pode mais ser recuperada.

      Agora que você testou as operações básicas do etcd e do etcdctl, vamos sair do nosso nó gerenciado e voltar para o ambiente local:

      Neste passo, usamos o cliente etcdctl para enviar solicitações ao etcd. Neste ponto, estamos executando três instâncias separadas do etcd sendo que cada uma age independentemente uma da outra. No entanto, o etcd é projetado como um armazenamento de chaves-valores distribuído, o que significa que várias instâncias do etcd podem se agrupar para formar um único cluster e cada instância torna-se então um membro do cluster. Depois de formar um cluster, seria possível recuperar um par chave-valor que foi inserido a partir de um membro diferente do cluster. No próximo passo, usaremos nosso playbook para transformar nossos 3 clusters de nó único em um único cluster de três nós.

      Passo 8 — Formando um cluster usando a descoberta estática

      Para criar um cluster de três nós ao invés de três clusters de somente 1 nó, devemos configurar essas instalações do etcd para se comunicar entre si. Isso significa que cada uma deve conhecer os endereços IP umas das outras. Este processo é chamado de descoberta. A descoberta pode ser feita usando a configuração estática ou a descoberta de serviço dinâmica. Neste passo, vamos discutir a diferença entre as duas, bem como atualizar nosso playbook para configurar um cluster do etcd usando a descoberta estática.

      A descoberta por configuração estática é o método cuja configuração é mais curta. É aqui que os pontos de extremidade de cada membro são passados no comando etcd antes que ele seja executado. Para usar a configuração estática, as seguintes condições devem ser atendidas antes da inicialização do cluster:

      • o número de membros é conhecido
      • os pontos de extremidade de cada membro são conhecidos
      • os endereços IP para todos os pontos de extremidade são estáticos

      Se essas condições não puderem ser atendidas, então será preciso usar um serviço de descoberta dinâmico. Com a descoberta de serviço dinâmica, todas as instâncias seriam registradas com o serviço de descoberta. Isso permite que cada membro obtenha informações sobre a localização de outros membros.

      Como sabemos que queremos um cluster do etcd de 3 nós e todos os nossos servidores têm endereços IP estáticos, usaremos a descoberta estática. Para iniciar nosso cluster usando a descoberta estática, devemos adicionar diversos parâmetros ao nosso arquivo de configuração. Use um editor para abrir o arquivo modelo templates/etcd.conf.yaml.j2:

      • nano templates/etcd.conf.yaml.j2

      Então, adicione as linhas destacadas a seguir:

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

      Feche e salve o arquivo templates/etcd.conf.yaml.j2 pressionando CTRL+X e depois Y.

      Aqui está uma breve explicação de cada parâmetro:

      • name – um nome humanamente legível para o membro. Por padrão, o etcd usa uma ID único gerado aleatoriamente para identificar cada membro. No entanto, um nome humanamente legível nos permite fazer referência a ele com maior facilidade dentro dos arquivos de configuração e na linha de comando. Aqui, usaremos os nomes de host como os nomes de membro (ou seja, etcd1, etcd2 e etcd3).
      • initial-advertise-peer-urls – uma lista de combinações de endereço IP/porta que outros membros podem usar para se comunicar com este membro. Além da porta da API (2379), o etcd também expõe a porta 2380 para a comunicação ponto a ponto entre membros do etcd. Isso permite que eles enviem mensagens entre si e troquem dados. Observe que essas URLs devem ser acessíveis pelos seus pares (e não ser um endereço IP local).
      • listen-peer-urls – uma lista de combinações de endereço IP/porta onde o membro atual irá escutar a comunicação de outros membros. Isso deve incluir todas as URLs do sinalizador --initial-advertise-peer-urls, mas também as URLs locais como 127.0.0.1:2380. O endereço IP/ porta de destino das mensagens de pares recebidas deve corresponder a uma das URLs listadas aqui.
      • advertise-client-urls – uma lista de combinações de endereço IP/porta que os clientes devem usar para se comunicar com este membro. Essas URLs devem ser acessíveis ao cliente (e não ser um endereço local). Se o cliente estiver acessando o cluster através da internet pública, este deve ser um endereço IP público.
      • listen-peer-urls – uma lista de combinações de endereço IP/porta onde o membro atual irá escutar a comunicação de outros clientes. Isso deve incluir todas as URLs do sinalizador --advertise-client-urls, mas também as URLs locais como 127.0.0.1:2379. O endereço IP/ porta de destino das mensagens de clientes recebidas deve corresponder a uma das URLs listadas aqui.
      • initial-cluster – uma lista de pontos de extremidade para cada membro do cluster. Cada ponto de extremidade deve corresponder a uma das URLs do initial-advertise-peer-urls do membro correspondente.
      • initial-cluster-state – ou new (novo) ou existing (existente).

      Para garantir a consistência, o etcd só pode tomar decisões quando a maioria dos nós estiver em boas condições. Isso é conhecido como estabelecer o quorum. Em outras palavras, em um cluster de três membros, o quorum é alcançado se dois ou mais membros estiverem em boas condições.

      Se o parâmetro initial-cluster-state estiver definido como new, o etcd irá saber que este é um novo cluster sendo inicializado e permitirá que os membros sejam inicializados em paralelo, sem esperar que o quorum seja alcançado. De forma mais concreta, depois que o primeiro membro é iniciado, o quorum não será atingido porque um terço (33,33%) é menor ou igual a 50%. Normalmente, o etcd irá parar e recusar-se a realizar mais ações e o cluster nunca será formado. No entanto, com o initial-cluster-state definido para new, ele irá ignorar a falta inicial de quorum.

      Se for definido como existente, o membro irá tentar se juntar a um cluster existente e espera-se que o quorum já esteja estabelecido.

      Nota: é possível encontrar mais detalhes sobre todos os sinalizadoras de configuração suportados na seção Configuração da documentação do etcd.

      No arquivo modelo templates/etcd.conf.yaml.j2 atualizado, existem algumas instâncias de hostvars. Quando o Ansible for executado, ele irá coletar variáveis vindas de diversas de fontes. Já usamos a variável inventory_hostname anteriormente, mas há muitas outras disponíveis. Essas variáveis estão disponíveis em hostvars[inventory_hostname]['ansible_facts']. Aqui, estamos extraindo os endereços IP privados de cada nó e usamos isso para construir nosso valor de parâmetro.

      Nota: como ativamos a opção de Rede privada quando criamos nossos servidores, cada servidor têm três endereços IP associados a eles:

      • Um endereço IP de loopback – um endereço que é válido apenas dentro da mesma máquina. É usado para que a máquina faça referência a si mesma, por exemplo, 127.0.0.1
      • Um endereço endereço IP público – um endereço que é compartilhado através da internet pública, por exemplo, 178.128.169.51
      • Um endereço IP privado – um endereço que é compartilhável apenas dentro da rede privada; no caso das Droplets da DigitalOcean, existe uma rede privada dentro de cada datacenter, por exemplo, 10.131.82.225

      Cada um desses endereços endereço IP está associado a uma interface de rede diferente. O endereço loopback está associado à interface lo, o endereço IP público à interface eth0, e o endereço IP privado à interface eth1. Estamos usando a interface eth1 para que todo o tráfego fique dentro da rede privada, sem nunca chegar à internet.

      Não é necessário entender as interfaces de rede para este artigo, mas se você quiser aprender mais, Uma introdução à terminologia, interfaces e protocolos de rede é um ótimo lugar para começar.

      A sintaxe {% %} do Jinja2 define a estrutura de loop for que itera através de todos os nós no grupo do etcd para compilar a string initial-cluster em um formato exigido pelo etcd.

      Para formar o novo cluster de três membros, é necessário primeiro interromper o serviço etcd e limpar o diretório de dados antes de inicializar o cluster. Para fazer isso, use um editor para abrir o arquivo playbook.yaml em sua máquina local:

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

      Em seguida, antes da tarefa "Create a data directory", adicione uma tarefa para interromper o serviço 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:
          ...
      

      Em seguida, atualize a tarefa "Create a data directory" para primeiro excluir o diretório de dados e depois recriá-lo:

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

      A propriedade with_items define uma lista de strings através das quais esta tarefa irá iterar. Isso é o equivalente a repetir a mesma tarefa duas vezes, mas com valores diferentes para a propriedade state. Aqui, estamos iterando através da lista com os itens absent e directory, garantindo que o diretório de dados seja excluído primeiro e recriado em seguida.

      Feche e salve o arquivo playbook.yaml pressionando CTRL+X e depois Y. Em seguida, execute o ansible-playbook novamente. Agora, o Ansible irá criar um cluster do etcd único com 3 membros:

      • ansible-playbook -i hosts playbook.yaml

      Você pode verificar isso entrando via SSH em qualquer nó que seja membro do etcd:

      Em seguida, execute etcdctl endpoint health --cluster:

      • etcdctl endpoint health --cluster

      Isso irá listar a integridade de cada membro do cluster:

      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

      Agora, criamos com sucesso um cluster do etcd de 3 nós. Podemos verificar isso adicionando uma entrada ao etcd em um dos nós membros, e recuperando-a em outro nó membro. Em um dos nós membros, execute o etcdctl put:

      Em seguida, use um novo terminal para entrar via SSH em um nó membro diferente:

      Depois disso, tente recuperar a mesma entrada usando a chave:

      Você será capaz de recuperar essa entrada, o que prova que o cluster está funcionando:

      Output

      foo bar

      Por fim, saia de cada um dos nós gerenciados e volte para a sua máquina local:

      Neste passo, criamos um novo cluster de 3 nós. Neste momento, a comunicação entre os membros do etcd e seus pares e clientes é realizada através do HTTP. Isso significa que a comunicação é descriptografada e qualquer pessoa capaz de interceptar o tráfego pode ler as mensagens. Isso não representa um problema grande se o cluster e os clientes do etcd estiverem todos implantados dentro de uma rede privada, ou uma rede privada virtual (VPN) que você tem pleno controle. No entanto, se qualquer um dos sistemas de tráfego precisar viajar através de uma rede compartilhada (privada ou pública), então deve-se garantir que este tráfego seja criptografado. Além disso, um mecanismo precisa ser implantado para que um cliente ou um par verifique a autenticidade do servidor.

      No próximo passo, vamos analisar como proteger a comunicação cliente para servidor, bem como a comunicação por pares usando o TLS.

      Passo 9 — Obtendo os endereços IP privados dos nós gerenciados

      Para criptografar mensagens entre nós membros, o etcd usa o Hypertext Transfer Protocol Secure, ou HTTPS, que é uma camada além do protocolo Transport Layer Security, TLS. O TLS usa um sistema de chaves privadas, certificados e entidades confiáveis chamadas Autoridades de certificação (CAs) para autenticar-se e enviar mensagens criptografadas.

      Neste tutorial, cada nó membro precisa gerar um certificado para se identificar e ter este certificado assinado por uma CA. Vamos configurar todos os nós membros para confiar nesta CA e, dessa forma, também confiar em todos os certificados assinados por ela. Isso permite que os nós membros se autentiquem mutuamente entre si.

      O certificado gerado por um nó membro deve permitir que outros nós membros se identifiquem. Todos os certificados incluem um Nome comum (CN) da entidade com a qual estão associados. Isso é usado com frequência como a identidade da entidade. No entanto, ao verificar um certificado, pode ser que as implementações do cliente comparem se as informações coletadas sobre a entidade correspondem às fornecidas no certificado. Por exemplo, quando um cliente baixa o certificado TLS com a entidade CN=foo.bar.com, mas o cliente está na verdade se conectando ao servidor usando endereço IP (por exemplo, 167.71.129.110), então existe uma incompatibilidade e o cliente pode não confiar no certificado. Ao especificar um nome alternativo de entidade (SAN) no certificado, ele informa ao verificador que ambos os nomes pertencem à mesma entidade.

      Como nossos membros do etcd estão emparelhados uns com os outros usando seus endereços IP privados, quando definirmos nossos certificados, vamos precisar fornecer esses endereços IP privados como os nomes alternativos de entidade.

      Para descobrir o endereço IP privado de um nó gerenciado, entre via SSH nele:

      Em seguida, execute o seguinte comando:

      • ip -f inet addr show eth1

      Você verá um resultado parecido com as seguintes linhas:

      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

      Em nosso exemplo de resultado, 10.131.255.176 é o endereço IP privado do nó gerenciado, e é a única informação na qual estamos interessados. Para filtrar tudo exceto o IP privado, podemos canalizar a saída do comando ip para o utilitário sed, que é usado para filtrar e transformar textos.

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

      Agora, a única saída é o próprio endereço IP privado:

      Output

      10.131.255.176

      Após verificar que o comando anterior funciona, saia do nó gerenciado:

      Para incorporar os comandos anteriores em nosso playbook, abra primeiro o arquivo playbook.yaml:

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

      Em seguida, adicione uma nova peça com uma única tarefa antes da nossa peça 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:
      ...
      

      A tarefa usa o módulo o shell para executar os comandos ip e sed, os quais buscam o endereço IP privado do nó gerenciado. Em seguida, ela registra o valor de retorno do comando shell dentro de uma variável chamada privateIP, que usaremos mais tarde.

      Neste passo, adicionamos uma tarefa ao playbook para obter o endereço IP privado dos nós gerenciados. No próximo passo, vamos usar essas informações para gerar certificados para cada nó membro. Além disso, faremos eles serem assinados por uma Autoridade de Certificação (CA).

      Passo 10 — Gerando as chaves privadas e CSRs dos membros do etcd

      Para que um nó membro receba tráfego criptografado, o remetente deve usar a chave pública do nó membro criptografar os dados. Além disso, o nó membro deve usar sua chave privada para descriptografar o texto cifrado e recuperar os dados originais. A chave pública é empacotada em um certificado e assinada por uma CA para garantir que seja genuína.

      Portanto, vamos precisar gerar uma chave privada e uma solicitação de assinatura de certificado (CSR) para cada nó membro do etcd. Para facilitar o nosso trabalho, vamos gerar todos os pares de chaves e assinar todos os certificados localmente no nó de controle e então copiar os arquivos relevantes para os hosts gerenciados.

      Primeiro, crie um diretório chamado artifacts/, onde vamos colocar os arquivos (chaves e certificados) gerados durante o processo. Abra o arquivo playbook.yaml com um editor:

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

      Nele, use o módulo file para criar o diretório artifacts/:

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

      Em seguida, adicione uma outra tarefa no final da peça para gerar a chave 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:
      ...
      

      Criar chaves privadas e CSRs pode ser feito usando os módulos openssl_privatekey e openssl_csr respectivamente.

      O atributo force: True garante que a chave privada seja regenerada toda vez, mesmo que já exista.

      De maneira similar, adicione a nova tarefa a seguir na mesma peça gerar as CSRs para cada membro, usando o 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 pode estar envolvido em um mecanismo de assinatura digital para fins da autenticação de servidor. Este certificado é associado ao nome do host (por exemplo, etcd1), mas o verificador também deve tratar os endereços IP privados, locais e de loopback de cada nó como nomes alternativos. Observe que estamos usando a variável privateIP que registramos na peça anterior.

      Feche e salve o arquivo playbook.yaml pressionando CTRL+X e depois Y. Em seguida, execute o playbook novamente:

      • ansible-playbook -i hosts playbook.yaml

      Agora, vamos encontrar um novo diretório chamado artifacts dentro do nosso diretório de projeto. Use ls para listar seu conteúdo:

      Você verá as chaves privadas e as CSRs para cada um dos membros do etcd:

      Output

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

      Neste passo, usamos vários módulos do Ansible para gerar chaves privadas e certificados de chave pública para cada um dos nós membros. No próximo passo, vamos analisar como assinar uma solicitação de assinatura de certificado (CSR).

      Passo 11 — Gerando os certificados CA

      Dentro de um cluster do etcd, os nós membros criptografam mensagens usando a chave pública do receptor. Para garantir que a chave pública seja genuína, o receptor empacota a chave pública em uma solicitação de assinatura de certificado (CSR) e tem uma entidade confiável (ou seja, a CA) para assinar a CSR. Como controlamos todos os nós membros e as CA em que eles confiam, não precisamos usar uma CA externa e podemos atuar como nossa própria CA. Neste passo, atuaremos como nossa própria CA. Isso significa que vamos precisar gerar uma chave privada e um certificado auto-assinado que funcionarão como a CA.

      Primeiro, abra o arquivo playbook.yaml com seu editor:

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

      Em seguida, de maneira similar ao passo anterior, adicione uma tarefa à peça localhost para gerar uma chave privada para a 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"
      ...
      

      Em seguida, use o módulo openssl_csr para gerar uma nova CSR. O processo é semelhante ao passo anterior, mas neste CSR, estamos adicionando a restrição básica e a extensão de uso de chave para indicar que este certificado pode ser usado como um certificado 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 fim, use o módulo openssl_certificate para auto-assinar a 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"
      ...
      

      Feche e salve o arquivo playbook.yaml pressionando CTRL+X e depois Y. Em seguida, execute o playbook para aplicar as alterações:

      • ansible-playbook -i hosts playbook.yaml

      Você também pode executar o ls para verificar o conteúdo do diretório artifacts/:

      Agora, você verá o certificado CA recém-gerado (ca.crt):

      Output

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

      Neste passo, geramos uma chave privada e um certificado auto-assinado para a CA. No próximo passo, usaremos o certificado CA para assinar a CSR de cada membro.

      Passo 12 — Assinando as CSRs dos membros do etcd

      Neste passo, vamos assinar a CSR de cada nó membro. Isso será feito de maneira semelhante a como usamos o módulo openssl_certificate para auto-assinar o certificado CA, mas em vez de usar o provedor selfsigned, usaremos o provedor ownca. Isso nos permite assinar usando nosso próprio certificado CA.

      Abra seu playbook:

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

      Acrescente a tarefa destacada a seguir na tarefa "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"
      ...
      

      Feche e salve o arquivo playbook.yaml pressionando CTRL+X e depois Y. Em seguida, execute o playbook novamente para aplicar as alterações:

      • ansible-playbook -i hosts playbook.yaml

      Agora, liste o conteúdo do diretório artifacts/:

      Você verá a chave privada, a CSR e o certificado para cada membro do etcd, além da 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

      Neste passo, assinamos as CSRs de cada nó membro usando a chave da CA. No próximo passo, vamos copiar os arquivos relevantes em cada nó gerenciado, para que o etcd tenha acesso às chaves e certificados relevantes para configurar as conexões TLS.

      Passo 13 — Copiando chaves privadas e certificados

      Todos os nós precisam ter uma cópia do certificado auto-assinado da CA (ca.crt). Cada nó membro do etcd também precisa ter sua própria chave privada e certificado. Neste passo, vamos fazer upload desses arquivos e colocá-los em um novo diretório /etc/etcd/ssl/.

      Para começar, abra o arquivo playbook.yaml com seu editor:

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

      A fim de fazer essas alterações em nosso playbook do Ansible, atualize primeiro a propriedade path da tarefa Create directory for etcd configuration para criar o diretório /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:
      ...
      

      Em seguida, adicione mais três tarefas após a tarefa modificada para copiar os arquivos:

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

      Feche e salve o arquivo playbook.yaml pressionando CTRL+X e depois Y.

      Execute o ansible-playbook novamente para fazer essas alterações:

      • ansible-playbook -i hosts playbook.yaml

      Neste passo, fizemos upload das chaves privadas e certificados para os nós gerenciados. Após termos copiado os arquivos, precisamos agora atualizar nosso arquivo de configuração do etcd para fazer uso deles.

      Passo 14 — Habilitando o TLS no etcd

      No último passo deste tutorial, vamos atualizar algumas configurações do Ansible para habilitar o TLS em um cluster do etcd.

      Primeiro, abra o arquivo modelo templates/etcd.conf.yaml.j2 usando seu editor:

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

      Uma vez dentro, mude todas as URLs para que usem o https como protocolo em vez de http. Além disso, adicione uma seção no final do modelo para especificar a localização do certificado CA, certificado do servidor e chave do 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
      

      Feche e salve o arquivo templates/etcd.conf.yaml.j2.

      Em seguida, execute seu playbook do Ansible:

      • ansible-playbook -i hosts playbook.yaml

      Depois disso, entre via SSH em um dos nós gerenciados:

      Uma vez dentro, execute o comando etcdctl endpoint health para verificar se os pontos de extremidade estão usando o HTTPS e se todos os membros estão íntegros:

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

      Como nosso certificado CA não é, por padrão, um certificado CA root confiável instalado no diretório /etc/ssl/certs/, precisamos passá-lo para o etcdctl usando o sinalizador --cacert.

      Isso dará o seguinte 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 se o cluster do etcd está realmente funcionando, podemos mais uma vez criar uma entrada em um nó membro, e recuperá-la em outro nó membro:

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

      Use um novo terminal para entrar via SSH em um nó membro diferente:

      Agora, recupere a mesma entrada usando a chave foo:

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

      Isso irá retornar a entrada, exibindo o resultado abaixo:

      Output

      foo bar

      Você pode fazer o mesmo no terceiro nó para garantir que todos os três membros estejam operacionais.

      Conclusão

      Agora, você provisionou com sucesso um cluster de 3 nós do etcd, protegeu ele com o TLS e confirmou que está funcionando.

      O etcd é uma ferramenta originalmente criada pelo CoreOS. Para entender o uso do etcd em relação ao CoreOS, consulte Como usar o etcdctl e o etcd, o armazenamento de chaves-valores distribuído do CoreOS. O artigo também dá orientação na configuração de um modelo de descoberta dinâmico, algo que foi discutido, mas não demonstrado neste tutorial.

      Como mencionado no início deste tutorial, o etcd é uma parte importante do ecossistema do Kubernetes. Para aprender mais sobre o papel do Kubernetes e do etcd dentro dele, leia Uma introdução ao Kubernetes. Se estiver implantando o etcd como parte de um cluster do Kubernetes, saiba que existem outras ferramentas disponíveis, como o kubespray e o kubeadm. Para mais detalhes sobre o kubeadm, leia Como criar um cluster do Kubernetes usando o Kubeadm no Ubuntu 18.04.

      Por fim, deve-se destacar que este tutorial fez uso de muitas ferramentas, mas não conseguiu se aprofundar em todos eles com muitos detalhes. A seguir, estão disponíveis links que irão fornecer uma análise mais detalhada sobre cada ferramenta:



      Source link