Ya hace unos dias, aparecieron los contenedores. Si fue para quedarse o no, sólo el tiempo lo dirá, pero mientras tanto, llevan un tiempo dando vueltas, han ido madurando y sin duda pueden encontrarse diversos casos de uso para ellos. Así que en éste post vamos a tratar de introducirlos y a ver algún ejemplo básico. Teoria de DockerArquitectura Docker usa una arquitectura cliente-servidor en la que el cliente de Docker habla con el Docker daemon, el servicio, quien hace realmente las tareas de build, ejecución y distribución de los contenedores Docker. Los contenedores Un contenedor es un proceso ejecutado en una sandbox para aislarlo del resto de procesos del host. Ese aislamiento se produce basándose en los namespaces y cgroups del kernel de Linux. Algunas características de un contenedor:
Un contenedor supone una capa de abstracción más por encima del SO (Sistema Operativo). Añadí "más" porque hasta entonces, se puede decir que se operaba bajo el paradigma de la virtualización (VM o Máquina Virtual), ya de por sí una capa de hardware por software situada entre el hardware real y el SO. Algunas ventajas de los contenedores son:
Existen diversos motores de contenedores: lxc, docker, RKT, CRI-O, podman, containerd... Y por encima de éstos, aunque no entraremos a comentarlos, como soluciones de orquestación y automatización de contenedores tenemos Docker Swarm, OpenShift y Kubernetes. El artículo que nos ocupa es sobre Docker. Las imágenes Una imagen es en realidad un sistema de archivos con todas las dependencias necesarias para ejecutar el servicio/aplicación. Ésta se tiene que construir (docker build). Las imágenes son la base de los contenedores, ya que éstos se crean a partir de las mismas. Son inmutables en el sentido que funcionan como un template/plantilla, lo cual no significa que no las podamos modificar para adaptarlas a nuestras necesidades y posteriormente guardarlas (modificando ese template o creando uno nuevo). Pero es importante entender el contepto de que una imagen es una base; no se ejecuta. Lo que se ejecuta es un contenedor creado a partir de una imagen. Para empezar con Docker, lo más conveniente es realizar los primeros pasos usando imágenes existentes sin modificar y aplicando las configuraciones que requiramos y que estén previstas por las mismas. La biblioteca más grande de imágenes Docker por antonomasia es Docker Hub, y es allí donde hemos de empezar a buscar los servicios que necesitemos. Dockerfile En relación a las imágenes, los archivos Dockerfile permiten modificarlas creando de nuevas. Dockerfile es un archivo con las instrucciones necesarias para realizar las modificaciones deseadas a la imagen. Normalmente se parte de una imagen base, a la que se añaden paquetes, se crean usuarios/archivos/directorios, se asignan permisos... Docker tomará éste archivo para hacer el build de la imagen, para lo cual se descargará (si no la tiene ya) la imagen base, ejecutará las instrucciones y acabará generando (en el repositorio de imágenes local) la imagen resultante. Docker Registry Como acabamos de avanzar, cuando se trabaja con una imagen Docker para lanzar un contenedor, el flujo que sigue Docker es: si no tengo la imagen localmente, la voy a pedir al registry público (Docker registry) para descargarla y posteriormente poder crear un contenedor en base a la misma. Desde ese mismo instante, esa imagen se servirá en local a ese mismo host siempre que la necesite. El entorno local es en cierta medida una biblioteca de imágenes con opciones sencillas que guarda aquellas con las que hemos trabajado. Si queremos opciones avanzadas como publicar esa biblioteca para que otros puedan hacer uso de nuestras imágenes, necesitariamos de un Docker Registry. Redes y almacenamiento Docker permite definir ambos tipos de recursos. Por lo que respecta a redes, ofrece distintos tipos de redes (drivers) en función de las necesidades:
Por lo que respecta a almacenamiento, los archivos que se crean dentro un contenedor, se crean en una capa escribible que se pierde cuando se elimina el contenedor. Esa capa de storage es gestionada por un storage driver (unionfs), pensada más bien para trabajar con las imágenes, y no es conveniente (por performance y espacio ocupado) hacer uso indiscriminado de la misma. Así pues, para la persistencia de datos, mejor performance y menor espacio ocupado, es mejor hacer uso de alguna solución de persistencia, bien sea en la propia imagen (versus contenedor) modificándola para que incluya de serie aquello que queremos, bien sea usando volúmenes. Tipos:
Práctica de DockerInstalación en una Debian $ curl -fsSL https://download.docker.com/linux/debian/gpg | gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg $ echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/debian $(lsb_release -cs) stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null $ apt-get update $ apt-get install docker-ce docker-ce-cli containerd.io Ejemplos de linea de comandos # Listado de contenedores mostrando también los parados $ docker ps -a # Ejecución de contenedores manual (docker run) # Ejecuta un contenedor en base a la imagen "hello world". Éste # escribe un mensaje por pantalla y finaliza su ejecución. $ docker run hello-world # Ejecuta un contenedor en base a la imagen "docker/getting-started". Éste despliega un tutorial # de inicio a Docker que puede ser navegado apuntando nuestro navegador a http://localhost/ $ docker run -d -p 80:80 docker/getting-started # # Aquí nos fijamos en los siguientes parámetros: # -d ejecuta el contenedor en modo detached (lo desvincula/deja libre la consola desde dónde se lanza) # -p publica un puerto del host (el de la izquierda) a otro del contenedor (el de la derecha) # Ejecuta un contenedor en base a la imagen "bitnami/apache". Éste despliega # un servidor apache sirviendo la web alojada en el path /home/web $ docker run -d -v /home/web:/app -p 8080:8080 bitnami/apache # # Aquí se añade el parámetro -v, que mapea un volumen con un directorio del host dentro del contenedor (bind mount). # Inicio/Parada/Reinicio de un contenedor $ docker start|stop|restart nombre_contenedor # Eliminado de un contenedor, sólo posible cuando está parado $ docker rm nombre_contenedor # Listado de imagenes $ docker image ls # Listado de redes $ docker network ls # Listado de volumenes $ docker volume ls Para la ejecución de contenedores organizadamente tenemos el cliente de Docker docker-compose. Ésta herramienta permite organizar la parametrización de cada contenedor, definir las relaciones entre los mismos, el orden de dependencia/arranque, usar uno o varios archivos de entorno (.env) y Dockerfiles, así como crear redes y volúmenes de forma más sencilla. Todo ésto mediante un archivo de definición .yaml, que podría considerarse un archivo de definición de proyecto y despliegue con un formato específico. Para ver un ejemplo sencillo, tomaremos el docker-compose.yml público de la imagen Docker de Wordpress: version: '3.1' services: wordpress: image: wordpress restart: always ports: - 8080:80 environment: WORDPRESS_DB_HOST: db WORDPRESS_DB_USER: exampleuser WORDPRESS_DB_PASSWORD: examplepass WORDPRESS_DB_NAME: exampledb volumes: - wordpress:/var/www/html db: image: mysql:5.7 restart: always environment: MYSQL_DATABASE: exampledb MYSQL_USER: exampleuser MYSQL_PASSWORD: examplepass MYSQL_RANDOM_ROOT_PASSWORD: '1' volumes: - db:/var/lib/mysql volumes: wordpress: db: Como vemos, lo primero a indicar es la versión de la especificación de Docker Compose que vamos a desplegar. Y a continuación va la palabra services seguida por cada uno de los servicios que deseamos; en éste caso 2, wordpress y db. Éste nombre del servicio lo ponemos nosotros. Cada servicio de corresponde a un contenedor. Dentro de cada servicio debemos especificar un mínimo obligatorio: la imagen a usar para cada uno de ellos y según cada imagen base, probablemente sea necesario especificar algunas variables de entorno para que éste inicie por primera vez correctamente. El resto de parámetros (redes, volúmenes, puertos...) ya depende de lo que quedamos hacer/montar. En éste aspecto, en el docker-compose.yml vas usando imágenes de diferentes distribuidores de software y montando un "Tetris" estableciendo puntos de unión entre servicios. Para levantar los servicios con éste sistema en modo detached seria: $ docker-compose up -d El cliente nos irá informando de todo lo que va haciendo (descarga y extracción de imagen, levantamiento de cada servicio...) y finalmente del estado de cada servicio. Creación de imágenes No vamos a profundizar en éste punto, pero sí que podemos ver un ejemplo básico de modificación de una imagen mediante la instalación de un paquete para una distribución base (Debian): FROM debian:latest-slim RUN apt-get update && \ apt-get install --no-install-recommends -y htop iotop procps psutils && \ rm -rf /var/lib/apt/lists/* En éste caso, estamos diciendo que vamos a usar la imagen slim de la última versión de la imagen de Docker de Debian y a instalar una serie de utilidades en la nueva imagen resultante. Si quisiéramos usarla, tendríamos que crear un contenedor en base a la misma, bien con docker run, bien con docker-compose o con cualquier otro cliente de Docker. Comentarios (0) |