¡Acceso ilimitado 24/7 a todos nuestros libros y vídeos! Descubra la Biblioteca Online ENI. Pulse aquí
¡Acceso ilimitado 24/7 a todos nuestros libros y vídeos! Descubra la Biblioteca Online ENI. Pulse aquí
  1. Libros y videos
  2. Alta disponibilidad en Linux
  3. Los contenedores
Extrait - Alta disponibilidad en Linux De la infraestructura a la orquestación de servicios (Heartbeat, Docker, Ansible, Kubernetes…)
Extractos del libro
Alta disponibilidad en Linux De la infraestructura a la orquestación de servicios (Heartbeat, Docker, Ansible, Kubernetes…) Volver a la página de compra del libro

Los contenedores

Especificaciones

El capítulo Aplicación standalone reveló las deficiencias de una arquitectura monolítica. El capítulo Infraestructura y servicios básicos sentó las bases de una infraestructura tolerante a fallos capaz de alojar aplicaciones en condiciones mucho mejores. Ahora tenemos que ser capaces de desplegar la aplicación en los servidores de la forma más sencilla y rápida posible. Hay muchos pasos a seguir para instalarla, configurarla y ponerla en marcha. Es hora de completar las especificaciones:

  • La aplicación debe ser fácil de instalar.

  • La aplicación debe ser reutilizable.

  • La aplicación se debe poder desplegar en varios servidores.

  • El mismo servidor debe poder ejecutar varias instancias de la aplicación.

  • El mantenimiento de la aplicación debe ser mínimo.

  • La aplicación no debe ocupar todos los recursos del servidor.

Este último punto puede sorprender, pero también es un factor de disponibilidad y tolerancia a fallos. Un proceso que consume todos los recursos de la CPU impide que otras aplicaciones y servicios funcionen correctamente.

Si consume toda la memoria, corre el riesgo de que Linux mate el proceso, mediante el mecanismo OOMKiller (Out-Of-Memory Killer). No hay que olvidar que el papel de un sistema operativo es preservar a toda costa el funcionamiento global del sistema y conservar los recursos, aunque ello implique sacrificar...

Aislamiento y contenedores

1. Principio

El aislamiento es una técnica que consiste en ejecutar aplicaciones en sus propios contextos, aisladas de las demás. Es una forma de virtualización. La primera implementación de este tipo en Unix se remonta a 1979, con chroot.

El núcleo de Linux utiliza dos mecanismos para aislar uno o más procesos: namespaces y cgroups. Los namespaces se introdujeron en 2002, y los cgroups en 2007. Todos los núcleos Linux incluyen estas características por defecto. La llegada de los namespaces a los usuarios en Kernel 3.8 en febrero de 2013 allanó el camino para el soporte completo de contenedores.

Los namespaces agrupan procesos en un espacio aislado de los demás. Se pueden utilizar para:

  • un aislamiento de procesos y un proceso con PID 1 como primer proceso en su espacio (PID 1 del primer proceso en un espacio de nombres),

  • un aislamiento de la red, con direcciones IP y puertos propios,

  • un aislamiento de volúmenes de datos: almacenamiento,

  • un aislamiento de permisos y usuarios en relación con el host: ser root dentro de un contenedor puede corresponder así a un usuario sin permiso sobre el host;

Los cgroups controlan los recursos del sistema que un grupo de procesos puede utilizar, con los siguientes controles, por ejemplo:

  • limitar el consumo de memoria,

  • limitar el uso de procesadores,

  • gestionar prioridades,

  • obtener información "contable" sobre este grupo,

  • controlar varios procesos en su conjunto (detenerlos, por ejemplo),

  • aislar este grupo asociándolo a un espacio de nombres.

La combinación de estos dos mecanismos es la base del principio del contenedor.

2. Contenedor y máquina virtual

Los contenedores se comparan a menudo con las máquinas virtuales, poder facilitar la comprensión, pero se trata de dos tipos de tecnología de virtualización bien distintas. El concepto es diferente.

images/cap3_pag4.png

Figura 1: Hipervisor y máquinas virtuales

Las máquinas virtuales se ejecutan en hipervisores. Tanto si utilizan emulación como si no, las máquinas virtuales ejecutan un sistema operativo completo, con un núcleo y controladores de dispositivos (incluso si utilizan paravirtualización). Las aplicaciones se instalan en el sistema operativo de la máquina virtual. Desde el punto de vista del hipervisor, la máquina virtual es un único proceso....

Preparar el entorno

1. Instalar Docker

No utilizaremos la versión publicada por Ubuntu. Añadiremos un repositorio a la configuración de apt.

Los ejemplos que vamos a presentar se han probado con la versión 25.0.4. Con una versión inferior a la 20.10.3, tendrá que activar las funciones experimentales.

Primero necesita instalar algunas dependencias. Esto implica comenzar de manera limpia eliminando cualquier paquete que pueda haber sido instalado por la distribución y, posteriormente, instalar la clave GPG del repositorio, agregar el repositorio oficial de Docker, actualizar apt y, finalmente, instalar la versión oficial.

A continuación, se explica cómo instalar Docker:

Instalamos algunas dependencias previamente:

$ sudo apt install apt-transport-https ca-certificates curl 
software-properties-common 

Añadimos la clave GPG del almacén oficial: 

$ sudo install -m 0755 -d /etc/apt/keyrings 
$ sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o 
/etc/apt/keyrings/docker.asc 
$ sudo chmod a+r /etc/apt/keyrings/docker.asc 

Añadimos el almacén oficial:

$ echo \ 
 "deb [arch=$(dpkg --print-architecture) signed- 
by=/etc/apt/keyrings/docker.asc] 
https://download.docker.com/linux/ubuntu \ 
 $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \ 
 sudo tee /etc/apt/sources.list.d/docker.list > /dev/null 
$ sudo apt update 

Para terminar, instalamos la aplicación Docker:

$ sudo apt-get install docker-ce docker-ce-cli containerd.io 
docker-buildx-plugin docker-compose-plugin 

Compruebe que se ha iniciado el servicio:

$ sudo systemctl status docker 
 
 docker.service - Docker Application Container Engine 
    Loaded: loaded (/lib/systemd/system/docker.service; enabled; 
preset: enabled) 
    Active: active (running) since Fri 2024-03-15 20:37:05 CET; 23min ago 
TriggeredBy:  docker.socket 
      Docs: https://docs.docker.com 
  Main PID: 855 (dockerd) 
     Tasks: 10 
    Memory: 116.7M 
       CPU: 404ms 
    CGroup: /system.slice/docker.service 
            └─855...

Construir la imagen

1. Adaptar el código

Pasar de un modelo standalone u on-premise (aplicaciones instaladas de manera clásica en un servidor físico o una máquina virtual) a los contenedores, la nube o Kubernetes no resuelve por arte de magia todos los problemas de arquitectura, escalabilidad y disponibilidad de una aplicación. Sin una reflexión y la necesaria adaptación de la arquitectura y el código, es probable que la operación cause más problemas de los que resuelve.

Una imagen se construye utilizando un Dockerfile en el que se especifican todos los comandos necesarios para instalar e iniciar el producto. Como se explica en el capítulo Aplicación standalone, hay varias etapas en la construcción de una aplicación funcional:

  • construcción de la propia aplicación con Java, Maven, perfiles, etc,

  • instalación y configuración de Tomcat,

  • configuración de la aplicación, recuperación del archivo WAR de la primera etapa e inicio de Tomcat.

Sabiendo que no necesita todo el entorno de compilación (todas las dependencias instaladas en la primera etapa), no querrá crear una imagen de la aplicación con todo este contenido inútil. La imagen final sólo debe contener lo estrictamente necesario para iniciar la aplicación:

  • Java,

  • dependencias de la aplicación,

  • Tomcat,

  • la aplicación eni-todo.

El programa se modifica para aceptar parámetros a través de variables de entorno. Cuando construimos el archivo eni-todo.war, especificamos valores estáticos para el acceso a la base de datos MariaDB. Ahora puede especificarlos al inicio con las siguientes variables:

  • SPRING_PROFILES_ACTIVE: el perfil Spring que se va a utilizar.

  • MULTIPART_LOCATION: la ruta para almacenar los elementos adjuntos de la tarea. Idealmente, un recurso compartido de red para la persistencia.

  • DB_DTB_JDBC_URL: la URL de conexión a la base de datos, de la forma jdbc:mariadb://<ip>/<base>.

  • DB_DTB_USERNAME: el usuario de la base de datos.

  • DB_DTB_PASSWORD: la contraseña de la base de datos.

2. Dockerfiles

El objetivo es disponer de una imagen Docker que, una vez instanciada con los parámetros adecuados, inicie la aplicación conectándose a la base de datos correcta y escribiendo sus archivos en la carpeta adecuada.

Vamos...

Despliegue de contenedores

1. MariaDB

MariaDB es una dependencia de la aplicación eni-todo. Por el momento, estamos dejando deliberadamente la alta disponibilidad de la base de datos a un lado: vamos a ver cómo configurar una base de datos en clúster en el resto de este tutorial, en particular en el capítulo Despliegue con Kubernetes y el capítulo Integración final. Hasta que tengamos una versión de alta disponibilidad de MariaDB, podemos:

  • reutilizar el servicio MariaDB instalado en el capítulo Aplicación standalone, que se ha explicado previamente,

  • iniciar un servicio MariaDB en forma de contenedor.

Si está reutilizando el servicio MariaDB existente, puede saltarse esta sección e ir directamente a la sección eni-todo.

a. Almacenamiento

Las siguientes operaciones se realizan arbitrariamente en infra01.

MariaDB se puede iniciar como un contenedor, cuyas imágenes proporciona gratuitamente la comunidad Docker, en el Docker Hub: https://hub.docker.com/_/mariadb

Como se explicó anteriormente, el contenedor utiliza su propio almacenamiento a través del montaje en unión, donde la última capa se puede escribir. Si iniciamos un contenedor MariaDB, la base de datos se creará en el contenedor y cuando se destruya, su contenido se borrará, perdiéndose permanentemente.

Docker permite montar volúmenes de datos en un contenedor. Hay varios tipos de montaje. Vamos a utilizar un montaje de tipo bind, que permite asociar un directorio del sistema de archivos host con otro directorio dentro del contenedor: entonces podemos almacenar la base de datos en el host en el directorio /opt/mysql, que vamos a montar en el contenedor en /var/lib/mysql.

 Empiece por deteniendo la instancia MariaDB si ha arrancado en infra01:

$ sudo systemctl stop mariadb 
$ sudo systemctl disable mariadb 

 Cree la carpeta en el servidor:

$ sudo mkdir -p /opt/mysql 

/opt/mysql también puede ser un recurso compartido de red NFS o CIFS desde un NAS o un almacenamiento de alta disponibilidad. En este caso, si el servidor falla, MariaDB se puede reiniciar en otro servidor sin perder la base de datos. El capítulo Almacenamiento en alta disponibilidad presenta una de estas soluciones. Sin embargo, debe ser consciente de las limitaciones de E/S (velocidad, latencia, etc.).

b. Inicio

A continuación, se inicia el contenedor...

Conclusión

Ahora podemos ejecutar nuestra aplicación eni-todo en varios servidores. El uso compartido de NFS para los archivos descargados y una base de datos común y externa garantizan que tengamos el mismo estado de la aplicación tanto si accedemos a ella desde un servidor como desde otro, pero sólo si almacenamos la información en la base de datos: sólo se puede acceder a la información mediante sesiones desde la instancia de la aplicación en la que se crearon.

Hemos avanzado mucho, porque ahora podemos crear, desplegar y reutilizar fácilmente nuestra aplicación en varios servidores. Pero aún nos quedan varios problemas por resolver:

  • La base de datos sigue siendo un punto único de fallo (SPOF), porque si la única instancia de MariaDB se cae, la aplicación falla.

  • Por el momento, el recurso compartido NFS procede de una única máquina y también es un único punto de fallo: si se cae, los archivos descargados quedan inaccesibles.

  • Sólo se puede acceder a cada contenedor de forma individual. Se requiere una dirección IP única para distribuir la conexión a uno de los servidores y contenedores.

  • Las sesiones son únicas para cada contenedor desplegado. Como mínimo, necesitamos un sistema de persistencia para asegurarnos de que siempre acabamos con la misma sesión o incluso, a largo plazo, que los contenedores...