01 - Docker I
La virtualización es una tecnología que permite crear varias máquinas virtuales (VMs) sobre un único hardware físico. Cada VM funciona como una máquina independiente con su propio sistema operativo y aplicaciones. Los hipervisores, como VMware, Hyper-V o VirtualBox, gestionan estas VMs, permitiendo compartir los recursos físicos del servidor (CPU, memoria, almacenamiento) entre las VMs.
Docker es una plataforma de contenedorización que permite a los desarrolladores empaquetar aplicaciones y sus dependencias en contenedores. Los contenedores son ligeros, portátiles y se ejecutan de manera consistente en cualquier entorno que soporte Docker. A diferencia de las VMs, los contenedores comparten el núcleo del sistema operativo del host, lo que los hace más eficientes en términos de recursos.
En Docker hay que diferencias dos conceptos:
- Imagen: Plantilla inmutable que define lo que contiene un contenedor. Incluye el sistema de archivos y las dependencias necesarias para ejecutar una aplicación. Las imágenes son estáticas y se utilizan para crear instancias de contenedores.
- Contenedor: Instancia ejecutable de una imagen. Es una unidad ligera y portátil que puede ser ejecutada en cualquier entorno que tenga Docker instalado. Los contenedores contienen todo lo necesario para ejecutar una aplicación, pero a diferencia de las imágenes, los contenedores son dinámicos y pueden cambiar durante su ejecución (por ejemplo, guardando datos en su sistema de archivos).
Instalación
Puedes instalar Docker siguiendo las instrucciones de su web. Dependiendo del sistema operativo. Por ejemplo, para instalar Docker en Ubuntu:
https://docs.docker.com/engine/install/ubuntu/
Puedes comprobar que todo se ha instalado correctamente comprobando la versión instalada:
docker --version
Docker version 27.0.3, build 7d4bcd8
Imagenes
Las imágenes de Docker son archivos que contienen todo lo necesario para ejecutar una aplicación: código, bibliotecas, dependencias y el sistema de archivos. Actúan como plantillas a partir de las cuales se crean contenedores.
Las imágenes se pueden descargar de registros de Docker, siendo el más popular Docker Hub. Docker Hub es un repositorio público que permite a los usuarios buscar, compartir y gestionar imágenes de Docker.
Afortunadamente, podemos utilizar la orden docker search <imagen> en la terminal para buscar imágenes con ese nombre.
Las principales ordenes para trabajar con imágenes de Docker son:
Comando | Descripción |
---|---|
docker image pull | Descarga una imagen desde un registro (por ejemplo, Docker Hub) |
docker image ls | Lista todas las imágenes disponibles en el sistema local |
docker image rm | Elimina una imagen del sistema local |
docker image build | Construye una imagen a partir de un Dockerfile situado en la ruta especificada |
docker image push | Sube una imagen a un registro de Docker |
Descargar
Para descargar imágenes de Docker Hub usamos docker image pull <imagen>. Los nombres de imágenes de Docker generalmente siguen el patrón usuario/imagen:tag:
- usuario: Opcional. Es el nombre de usuario o la organización que posee la imagen en el registro de Docker. Si se omite, se asume que la imagen es oficial de Docker.
- imagen: Obligatorio. Es el nombre de la imagen.
- tag: Opcional. Es una etiqueta que especifica una versión particular de la imagen. Si se omite, se utiliza la etiqueta por defecto latest.
Por ejemplo, si queremos bajarnos la versión 8.0.39 de la imagen mysql del usuario bitnami:
docker pull bitnami/mysql:8.0.39
Mientras que si quisiéramos bajarnos la última versión lts oficial de mysql:
docker pull mysql
Listar
Para listar las imágenes que tenemos descargadas en local usamos docker image ls (o alguno de sus alias como docker images):
docker images
REPOSITORY TAG IMAGE ID CREATED SIZE ubuntu-dhcp-server latest d884c8118af6 3 weeks ago 83.5MB mysql latest 5cde95de907d 4 weeks ago 586MB mysql 8.0 6c54cbcf775a 4 weeks ago 572MB linuxserver/dokuwiki latest b72ff6b0d7fb 7 weeks ago 117MB mariadb latest 4486d64c9c3b 7 weeks ago 406MB ubuntu latest 35a88802559d 8 weeks ago 78.1MB docker.cloudsmith.io/isc/docker/kea-dhcp4 2.6.0 2fe3592342d0 2 months ago 80.6MB docker.cloudsmith.io/isc/docker/kea-dhcp4 latest 2fe3592342d0 2 months ago 80.6MB docker.cloudsmith.io/isc/docker/kea-dhcp4 2.4.1 f65fe5a3c577 8 months ago 88MB docker/getting-started latest 3e4394f6b72f 19 months ago 47MB networkboot/dhcpd latest e25c872f0fca 24 months ago 139MB mariadb 10.7.1 67a24127bba8 2 years ago 411MB
La salida del comando incluye la siguiente información:
- REPOSITORY: El nombre del repositorio de la imagen.
- TAG: La etiqueta de la imagen, que usualmente indica la versión.
- IMAGE ID: El identificador único de la imagen.
- CREATED: Cuándo se creó la imagen.
- SIZE: El tamaño de la imagen.
Eliminar
Para eliminar imágenes que tenemos descargadas en local usamos docker image rm (o su alias docker rmi):
docker rmi <image_id>
Puedes especificar el IMAGE ID o el REPOSITORY:TAG de la imagen que deseas eliminar:
REPOSITORY TAG IMAGE ID CREATED SIZE ubuntu-dhcp-server latest d884c8118af6 3 weeks ago 83.5MB
docker rmi d884c8118af6
docker image rm ubuntu-dhcp-server
La salida del comando de eliminación confirmará que la imagen ha sido eliminada. Si una imagen está siendo utilizada por algún contenedor, se deberá eliminar primero el contenedor asociado.
Para obtener instrucciones detalladas sobre cómo configurar el proxy para Docker, consulta la documentación oficial. Esta guía te proporcionará los pasos necesarios para ajustar la configuración del proxy según tus necesidades específicas.
Contenedores
Los contenedores de Docker son instancias ejecutables de imágenes. Mientras que las imágenes son plantillas estáticas que contienen todo lo necesario para ejecutar una aplicación, los contenedores son unidades en ejecución que proporcionan un entorno aislado y consistente para que las aplicaciones se ejecuten.
Puedes crear y gestionar contenedores utilizando una variedad de comandos. Aquí están los más importantes:
Comando | Descripción |
---|---|
docker container run | Crea y ejecuta un nuevo contenedor a partir de una imagen |
docker container start | Inicia un contenedor que ha sido detenido previamente |
docker container stop | Detiene un contenedor en ejecución |
docker container rm | Elimina un contenedor del sistema local |
docker container ls | Lista todos los contenedores en ejecución |
docker container exec | Ejecuta un comando en un contenedor en ejecución |
docker container logs | Muestra los registros de un contenedor |
docker container cp | Copia archivos o directorios entre el contenedor y el sistema de archivos del host |
Ejecutar
Para ejecutar un contenedor a partir de una imagen, utilizamos el comando docker run. Este comando no solo crea un nuevo contenedor, sino que también lo inicia y, por defecto, ejecuta el comando especificado en la imagen (además, si no hemos descargado la imagen en local lo primero que haría sería descargarla).
El comando básico tiene el siguiente formato:
docker run [opciones] <imagen> [comando]Donde:
- <imagen>: Es el nombre de la imagen a partir de la cual se creará el contenedor. Puede incluir una etiqueta opcional (por ejemplo, ubuntu:latest).
- [comando]: Es el comando que se ejecutará dentro del contenedor. Si no se especifica, se usará el comando por defecto definido en la imagen.
Algunas opciones comunes que se pueden usar con docker run incluyen:
- -d: Ejecuta el contenedor en segundo plano (modo “detached”).
- -p <puerto_host>:<puerto_contenedor>: Mapea puertos del contenedor al host. Por ejemplo, -p 8080:80 mapea el puerto 80 del contenedor al puerto 8080 del host.
- - -restart always : Añadiremos este flag si queremos que se vuelva a iniciar el contenedor si acaba dicho contenedor. Es útil ya que se iniciará el contenedor si se reinicia la máquina real.
- - -name <nombre>: Asigna un nombre al contenedor, facilitando su gestión.
- -v <volumen_host>:<volumen_contenedor>: Monta un volumen desde el host en el contenedor, permitiendo persistir datos.
- -e <variable=valor>: Define variables de entorno en el contenedor.
Ejemplo de uso:
docker run -d -p 8080:80 --name mi_web nginx
En este ejemplo, el contenedor se ejecutará en segundo plano, exponiendo el puerto 80 del contenedor en el puerto 8080 del host, y se le asignará el nombre mi_web. La imagen utilizada es nginx, y se ejecutará el comando por defecto definido en esta imagen (iniciar el servidor web Nginx).
Otro ejemplo:
docker run --name mysql_web -e MYSQL_ROOT_PASSWORD=my-secret-pw -d mysql
En este caso, el contenedor se ejecutará en segundo plano con el nombre mysql_web. La opción -e se usa para definir variables de entorno; en este ejemplo, se establece la variable MYSQL_ROOT_PASSWORD con el valor my-secret-pw, que configura la contraseña de root para la base de datos MySQL. La imagen utilizada es mysql, y se ejecutará el comando por defecto definido en esta imagen (iniciar el servidor MySQL).
El comando docker run es fundamental para crear y ejecutar contenedores a partir de imágenes, permitiendo la configuración de múltiples aspectos del contenedor en el momento de su creación.
Volumenes
Los volúmenes en Docker son una forma de persistir datos generados o utilizados por los contenedores. A diferencia de los datos almacenados dentro de un contenedor, que se perderán si el contenedor se elimina, los volúmenes permiten que los datos persistan incluso si el contenedor se detiene o se elimina.
Para persistir datos y asegurar que se mantengan entre reinicios o recreaciones de contenedores, Docker ofrece dos formas principales de manejar volúmenes:
- Volúmenes Nombrados: Se crean y gestionan utilizando Docker. Estos volúmenes son independientes del ciclo de vida de un contenedor y pueden ser compartidos entre varios contenedores.
- Volúmenes con Path: Basados en rutas del sistema de archivos del host. Esto permite montar un directorio local en el contenedor.
Crear un volumen nombrado:
docker volume create nombre_volumen
Inspeccionar un volumen:
docker inspect volume nombre_volumen
[ { "CreatedAt": "2024-07-08T11:33:46+02:00", "Driver": "local", "Labels": null, "Mountpoint": "/var/lib/docker/volumes/datos-todo/_data", "Name": "datos-todo", "Options": null, "Scope": "local" } ]
La salida del comando docker inspect muestra detalles como el punto de montaje del volumen y su nombre.
Utilizar un volumen nombrado con un contenedor:
docker run -dp 3000:3000 --name app-cambios -v nombre_volumen:/etc/todos app-cambios
En este ejemplo, el contenedor app-cambios montará el volumen nombre_volumen en el directorio /etc/todos. Los datos almacenados en este directorio persistirán incluso si el contenedor se elimina y se vuelve a crear, siempre y cuando el volumen no sea eliminado.
Montar un directorio local en un contenedor:
docker run -dp 3000:3000 -v ./datos-todo:/etc/todos app-cambios
En este ejemplo, el directorio local ./datos-todo se monta en /etc/todos dentro del contenedor. Los datos guardados en este directorio local se reflejarán dentro del contenedor y viceversa.
¿Cuándo usar cada tipo?
- Volúmenes Nombrados: Ideal para compartir datos entre varios contenedores o para mantener datos persistentes que no están ligados a una ubicación específica en el host.
- Volúmenes con Path: Útil cuando necesitas vincular un directorio específico del host con un contenedor, o cuando cada contenedor debe tener acceso a datos específicos del host.
Ejecutar comandos en el contenedor
La orden docker container exec permite ejecutar comandos en un contenedor en funcionamiento. Esta funcionalidad es útil para realizar tareas como la depuración, administración o inspección de un contenedor en tiempo real.
El formato básico del comando es:
docker exec [opciones] <contenedor> <comando> [argumentos]
Donde:
- <contenedor>: Es el nombre o ID del contenedor en el que se desea ejecutar el comando.
- <comando>: Es el comando que se ejecutará dentro del contenedor. Puede ser cualquier comando que el contenedor soporte.
- [argumentos]: Son los argumentos opcionales que se pasan al comando.
La opción más comun es -it, la cual ejecuta el comando en modo interactivo y asigna un terminal. Esto es útil para ejecutar shell interactivos, como bash o sh.
Por ejemplo, ejecutar bash dentro de un contenedor para abrir una sesión de shell bash dentro de él, permitiendo interacturar con él directamente.
docker exec -it mi_contenedor /bin/bash
Ejecutar un comando no interactivo:
docker exec mi_contenedor ls /app
Aquí, el comando ls /app se ejecuta dentro del contenedor mi_contenedor. Este comando no requiere interacción y muestra el contenido del directorio /app en el contenedor.
La orden docker exec es esencial para la administración y mantenimiento de contenedores en ejecución, permitiendo realizar tareas sin necesidad de detener o reiniciar el contenedor.
Ejercicios
Ejercicio 1
- Instala Docker en tu ordenador
- Comprueba que todo se ha instalado correctamente
Ejercicio 2
- Busca por terminal la imagen de Docker hello-world
- ¿Cuál de todas las imágenes es la oficial?
- Haz lo mismo, pero en DockerHub. Marca a la izquierda la casilla Docker Official Image y comprueba que coincide con tu elección del punto anterior
- Descarga y ejecuta la imagen elegida en tu ordenador
- Haz lo mismo con la imagen hello-seattle
- Muestra las imágenes descargadas en tu ordenador
- Intenta borrar la imagen hello-seattle ¿Por qué te está dando error?
- Lista los contenedores de tu ordenador con la opción ls sin parámetros ¿Ves algún contenedor de la imagen hello-seattle?
- Vuelve a listar los contenedores con la opción ls -a ¿Por qué ahora sí que se muestra el contenedor de la imagen hello-seattle? ¿Para qué sirve la opción -a?
- Elimina el contenedor que usa hello-seattle y la imagen
- Vuelve a listar las imágenes y comprueba que se ha borrado correctamente
Ejercicio 3
- Descarga la imagen oficial de mysql
- Con la imagen que te has descargado, crea un contenedor con nombre mysql_smr y smr como password del usuario root
- Ejecuta desde la terminal la orden:
docker exec -it mysql_smr mysql -u root -p
- Comprueba que puedes conectarte al Mysql del contenedor
- Ejecuta la siguiente orden dentro de Mysql para crear una nueva base de datos:
CREATE DATABASE prueba;
- Comprueba que se ha creado la bbdd con la orden:
show databases;
Ejercicio 4
- Para el contenedor mysql_smr y vuelve a iniciarlo
- Conéctate de nuevo a Mysql y comprueba que la bbdd prueba sigue estando
- Elimina el contenedor mysql_smr
- Vuelve a crearlo con los mismos parámetros y comprueba que la bbdd prueba no existe
- Sal del contenedor y elimínalo
Ejercicio 5
- Crea un directorio llamado mysql_data
- Vuelve a crea el contenedor mysql con los mismos parámetros anteriores pero indicando que la carpeta del contenedor /var/lib/mysql se guardará en el directorio recién creado (mysql_data)
- Crea de nuevo la bbdd pruebas dentro del Mysql del contenedor
- Sal y elimina el contenedor
- Crea de nuevo el contenedor sin mapear ninguna carpeta (sin utilizar volúmenes) y comprueba que la bbdd prueba no existe
- Vuelve a salir y eliminar el contendor
- Crea de nuevo el contenedor pero mapeando la carpeta igual que antes (la carpeta /var/lib/mysql del contenedor se guardará en mysql_data)
- Comprueba que la bbdd sigue estando en Mysql
Ejercicio 6
- Crea un directorio llamado php_data
- Crea un archivo llamado index.php en el directorio recién creado con el siguiente contenido:
<?php phpinfo();
- Arranca un contenedor con la imagen php:8.0-apache y nombre php_smr indicando que la carpeta del contenedor /var/www/html se guardará en la carpeta creada en el primer punto. Además, el puerto 80 del contenedor se mapeará al puerto 11211 de la máquina real
- Abre un navegador accede a http://localhost:11211. Comprueba que se muestra una página similar a ésta:
- Reinicia la máquina real
- Comprueba que accediendo a la anterior url se produce un error
- Borra el contenedor
- Vuelve a crear el contenedor pero ahora añade que siempre se reinicie
- Reinicia la máquina real
- Comprueba que navegando a la anterior url ahora si que funciona