====== 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. {{ :clase:smr:sr:1eval:docker-vs.png?600 |}} 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 [[https://hub.docker.com/|Docker Hub]]. Docker Hub es un repositorio público que permite a los usuarios buscar, compartir y gestionar imágenes de Docker. Lo normal es buscar el nombre de la imagen que queremos descargar en Docker Hub, pero, ¿qué pasa si sólo podemos usar una terminal sin entorno gráfico? En ese caso, no tendríamos acceso a un navegador, con lo que no podríamos acceder a Docker Hub. \\ \\ Afortunadamente, podemos utilizar la orden **docker search ** en la terminal para buscar imágenes con ese nombre. Las principales ordenes para trabajar con imágenes de Docker son: ^ Comando ^ Descripción ^ | [[https://docs.docker.com/reference/cli/docker/image/pull/|docker image pull]] | Descarga una imagen desde un registro (por ejemplo, Docker Hub) | | [[https://docs.docker.com/reference/cli/docker/image/ls/|docker image ls]] | Lista todas las imágenes disponibles en el sistema local | | [[https://docs.docker.com/reference/cli/docker/image/rm/|docker image rm]] | Elimina una imagen del sistema local | | [[https://docs.docker.com/reference/cli/docker/buildx/build/|docker image build]] | Construye una imagen a partir de un **Dockerfile** situado en la ruta especificada | | [[https://docs.docker.com/reference/cli/docker/image/push/|docker image push]] | Sube una imagen a un registro de Docker | ==== Descargar ==== Para descargar imágenes de Docker Hub usamos **docker image pull **. 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 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. Si estás utilizando un proxy en tu entorno, es posible que necesites configurar Docker para que funcione correctamente a través de él. La configuración del proxy es necesaria para que Docker pueda acceder a Internet y a los registros de imágenes, entre otros servicios.\\ \\ Para obtener instrucciones detalladas sobre cómo configurar el proxy para Docker, consulta la [[https://docs.docker.com/reference/cli/dockerd/#proxy-configuration|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 ^ | [[https://docs.docker.com/reference/cli/docker/container/run/|docker container run]] | Crea y ejecuta un nuevo contenedor a partir de una imagen | | [[https://docs.docker.com/reference/cli/docker/container/start/|docker container start]] | Inicia un contenedor que ha sido detenido previamente | | [[https://docs.docker.com/reference/cli/docker/container/stop/|docker container stop]] | Detiene un contenedor en ejecución | | [[https://docs.docker.com/reference/cli/docker/container/rm/|docker container rm]] | Elimina un contenedor del sistema local | | [[https://docs.docker.com/reference/cli/docker/container/ls/|docker container ls]] | Lista todos los contenedores en ejecución | | [[https://docs.docker.com/reference/cli/docker/container/exec/|docker container exec]] | Ejecuta un comando en un contenedor en ejecución | | [[https://docs.docker.com/reference/cli/docker/container/logs/|docker container logs]] | Muestra los registros de un contenedor | | [[https://docs.docker.com/reference/cli/docker/container/cp/|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] [comando] Donde: * ****: 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 :**: 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 **: Asigna un nombre al contenedor, facilitando su gestión. * **-v :**: Monta un volumen desde el host en el contenedor, permitiendo persistir datos. * **-e **: 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] [argumentos] Donde: * ****: Es el nombre o ID del contenedor en el que se desea ejecutar el 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. Si el contenedor no está en ejecución, //docker exec// no podrá ejecutar el comando. Asegúrate de que el contenedor esté activo utilizando //docker ps// antes de intentar ejecutar un comando en él. 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; No te olvides del ; final * Comprueba que se ha creado la bbdd con la orden: show databases; ** Ejercicio 4 ** * Para el contenedor //mysql_smr// y vuelve a iniciarlo Para salir de mysql y del contenedor ejecuta la orden **exit** * 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: * 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: {{ :clase:smr:sr:1eval:php.png?400 |}} * 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