UD06 Teoría PDF
Document Details
![EffortlessAntigorite5305](https://quizgecko.com/images/avatars/avatar-12.webp)
Uploaded by EffortlessAntigorite5305
Tags
Summary
This document appears to be a set of lecture notes or study materials on containerization technology, specifically Docker. It covers topics like Docker installation, images, containers, and persistence, as well as using Docker with WordPress.
Full Transcript
Unidad 5: Contenedores 1. Introducción........................................................................................................2 1.1. ¿A quién le puede interesar usar docker?........................................................... 2 1.2. ¿Docker es virtualización?............
Unidad 5: Contenedores 1. Introducción........................................................................................................2 1.1. ¿A quién le puede interesar usar docker?........................................................... 2 1.2. ¿Docker es virtualización?....................................................................................3 1.3. Conceptos básicos............................................................................................... 3 1.3.1 Objetos de docker......................................................................................... 4 2. Instalación.......................................................................................................................... 4 2.1. Disponibilidad.............................................................................................................. 4 2.2. Instalación................................................................................................................... 5 2.3. Configuración del usuario............................................................................................6 2.4. Imágenes.....................................................................................................................6 2.5. Herramientas...............................................................................................................7 3. Imágenes.............................................................................................................................7 3.1. Buscar imágenes.........................................................................................................7 3.2. Gestión de imágenes................................................................................................ 10 3.2.1. Descarga.......................................................................................................... 10 3.2.2. Listado.............................................................................................................. 11 3.2.3. Borrado............................................................................................................. 11 4. Contenedores................................................................................................................... 12 4.1. Listado.......................................................................................................................12 4.2. Iniciar un contenedor.................................................................................................12 4.3. Detener un contenedor..............................................................................................13 4.4. Borrar un contenedor................................................................................................ 13 4.5. Ejecutar comandos dentro de un contenedor........................................................... 13 5. Persistiendo datos........................................................................................................... 15 5.1. Crear un volumen......................................................................................................15 5.2. Listar volúmenes....................................................................................................... 16 5.3. Visualizar volúmenes................................................................................................ 16 5.4. Borrar volúmenes...................................................................................................... 16 6. Levantar WordPress con Docker....................................................................................17 6.1. Crear un contenedor con MariaDB............................................................................17 6.2. Creando nuestro blog................................................................................................19 7. Levantar un WordPress con Docker Compose............................................................. 20 7.1. Docker Compose.......................................................................................................20 7.2. Creación de contenedores automatizada..................................................................21 7.3. Iniciar servicios..........................................................................................................22 7.4. Detener servicios.......................................................................................................23 7.5. Borrar servicios......................................................................................................... 23 7.6. Estructura de la configuración...................................................................................23 8. Crear imágenes propias.................................................................................................. 27 8.1. Mi primer Dockerfile.................................................................................................. 27 8.2. Creando aplicaciones en contenedores.................................................................... 29 8.2.1. Probar nuestro contenedor..............................................................................31 8.2.2. Creando la aplicación.......................................................................................31 8.2.3. Balanceo de carga........................................................................................... 32 8.3. Compartir imágenes.................................................................................................. 34 9. Trucos............................................................................................................................... 35 9.1. Portainer....................................................................................................................35 9.2. Limpieza.................................................................................................................... 35 9.3. Copias de seguridad................................................................................................. 36 10. Fuentes de esta página................................................................................................. 37 11. Imágenes base................................................................................................................37 1. Introducción Según la Wikipedia: "Docker es un proyecto de código abierto que automatiza el despliegue de aplicaciones dentro de contenedores de software, proporcionando una capa adicional de abstracción y automatización de virtualización de aplicaciones en múltiples sistemas operativos. Docker utiliza características de aislamiento de recursos del kernel Linux, tales como cgroups y espacios de nombres (namespaces) para permitir que 'contenedores' independientes se ejecuten dentro de una sola instancia de Linux, evitando la sobrecarga de iniciar y mantener máquinas virtuales” 1.1. ¿A quién le puede interesar usar docker? Docker es útil a administradores de sistemas, pero también a desarrolladores. Uno de los problemas que se presentan durante el desarrollo y despliegue de aplicaciones es encontrarnos con sistemas heterogéneos, no ya entre los desarrolladores, también entre los sistemas de pruebas, pre-producción y producción. Es decir, que los desarrolladores y los sistemas donde se ejecuta la aplicación tienen librerías y sistemas operativos diferentes. ¿Y por qué es un problema? Pues porque la aplicación puede funcionar bien en una distribución de GNU/Linux pero no bien en otra, o ejecutarse bien con la versión de un lenguaje pero no con otra. Para asegurar la calidad de desarrollo tenemos que asegurar que todo el mundo use las mismas versiones de todas las aplicaciones y librerías necesarias. Esto es más complicado de lo que parece, porque hay desarrolladores que prefieren una distribución concreta, o incluso sistemas privativos. Incluso los sistemas de pruebas, pre-producción y producción suelen ser distintos. Los sistemas de producción suelen ser más nuevos y potentes y los antiguos se dejan para pruebas y pre-producción. Otro problema es que un mismo desarrollador o un mismo sistema de despliegue tenga que trabajar en más de un proyecto que requiera versiones distintas de librerías, complicándolo aún más. Docker viene a solucionar todos estos problemas, tanto para los desarrolladores como para los administradores de sistemas. Con Docker podemos crear entornos aislados con configuraciones que serán exactamente igual siempre. 1.2. ¿Docker es virtualización? En GNU/Linux Docker no es virtualizado, no hay un hipervisor. Los procesos que corren dentro de un contenedor de docker se ejecutan con el mismo kernel que la máquina anfitrión. Linux lo que hace es aislar esos procesos del resto de procesos del sistema, ya sean los propios de la máquina anfitrión o procesos de otros contenedores. Además, es capaz de controlar los recursos que se le asignan a esos contenedores (cpu, memoria, red, etc.). Internamente, el contenedor no sabe que lo es y a todos los efectos es una distribución GNU/Linux independiente, pero sin la penalización de rendimiento que tienen los sistemas virtualizados. Así que, cuando ejecutamos un contenedor, estamos ejecutando un servicio dentro de una distribución construida a partir de una "receta". Esa receta permite que el sistema que se ejecuta sea siempre el mismo, independientemente de si estamos usando Docker en Ubuntu, Fedora o, incluso, sistemas privativos compatibles con Docker. De esa manera podemos garantizar que estamos desarrollando o desplegando nuestra aplicación, siempre con la misma versión de todas las dependencias. Obviamente, si ejecutamos contenedores GNU/Linux dentro de sistemas privativos, sí habrá virtualización. 1.3. Conceptos básicos Antes de comenzar a instalar y usar docker es importante tener una serie de conceptos claros: Demonio de docker (docker daemon) : Es el proceso principal de docker. Escucha peticiones a la API y maneja los objetos de docker: imágenes, contenedores, redes, volúmenes. También es capaz de comunicarse con otros demonios para controlar servicios docker. Cliente de docker (docker client) : Es la principal herramienta que usan los administradores de sistema para interaccionar con el sistema Docker. Registro de docker (docker registry) : Es el lugar donde se almacenan las imágenes de Docker y poder descargarlas para reutilizarlas. Docker Hub es el principal registro público de Docker y contiene ya un montón de imágenes listas para ser usadas de multitud de servicios (mysql, wordpress, etc). 1.3.1 Objetos de docker Cuando usamos Docker, estamos creando y usando imágenes, contenedores, redes o volúmenes, entre otros. A todo esto se le denominan objetos. Veamos los más importantes: Imagen (image) : Plantilla de solo lectura que contiene las instrucciones para crear un contenedor Docker. Pueden estar basadas en otras imágenes, lo cual es habitual. Contenedor (container) : Es una instancia ejecutable de una imagen. Esta instancia puede ser creada, iniciada, detenida, movida o eliminada a través del cliente de Docker o de la API. Las instancias se pueden conectar a una o más redes, sistemas de almacenamiento, o incluso se puede crear una imagen a partir del estado de un contenedor. Se puede controlar cómo de aislado está el contenedor del sistema anfitrión y del resto de contenedores. El contenedor está definido tanto por la imagen de la que procede como de las opciones de configuración que permita. Por ejemplo, la imagen oficial de MariaDb permite configurar a través de opciones la contraseña del administrador, de la primera base de datos que se cree, del usuario que la maneja, etc. Servicios (services) : Los servicios permiten escalar contenedor a través de múltiples demonios de Docker, los cuales trabajarán conjuntamente como un enjambre (swarm). 2. Instalación Existen dos versiones de Docker, una libre y otra que no lo es. Nos ocuparemos exclusivamente de la primera: Docker CE (Community Edition). 2.1. Disponibilidad Docker CE está disponible para los siguientes sistemas GNU/Linux: CentOS, Debian, Fedora y Ubuntu. No todas están en múltiples arquitecturas, pero sí todas soportan x86_64/amd64. Si tienes otra arquitectura u otro sistema es mejor que uses una máquina virtual para arrancar una distribución compatible. Para más información sobre sistemas operativos soportados, leer la sección de plataformas soportadas de la documentación oficial. 2.2. Instalación Debido a que, dependiendo de la distribución, la forma de instalarlo difiere, es mejor consultar la documentación oficial para saber como instalar Docker en tu máquina. Ubuntu: https://docs.docker.com/install/linux/docker-ce/ubuntu/ Debian: https://docs.docker.com/install/linux/docker-ce/debian/ CentOS: https://docs.docker.com/install/linux/docker-ce/centos/ Fedora: https://docs.docker.com/install/linux/docker-ce/fedora/ Para saber si tienes Docker bien instalado, los tutoriales oficiales siempre te indican que inicies un contenedor de ejemplo. sudo docker run hello-world El resultado es el siguiente: $ sudo docker run hello-world Unable to find image 'hello-world:latest' locally latest: Pulling from library/hello-world d1725b59e92d: Pull complete Digest: sha256:0add3ace90ecb4adbf7777e9aacf18357296e799f81cabc9fde470971e499788 Status: Downloaded newer image for hello-world:latest Hello from Docker! This message shows that your installation appears to be working correctly. To generate this message, Docker took the following steps: 1. The Docker client contacted the Docker daemon. 2. The Docker daemon pulled the "hello-world" image from the Docker Hub. (amd64) 3. The Docker daemon created a new container from that image which runs the executable that produces the output you are currently reading. 4. The Docker daemon streamed that output to the Docker client, which sent it to your terminal. To try something more ambitious, you can run an Ubuntu container with: $ docker run -it ubuntu bash Share images, automate workflows, and more with a free Docker ID: https://hub.docker.com/ For more examples and ideas, visit: https://docs.docker.com/get-started/ En la línea 1 estamos ejecutando el cliente de Docker, y estamos indicando que queremos ejecutar un contenedor a partir de la imagen hello-world del registro público de Docker. Si es la primera vez que hemos ejecutado esa imagen, nos aparecerá la línea 2, que indica que la imagen no puede ser encontrada y va a proceder a buscarla, por defecto, en el registro público. Si tenemos conexión a Internet se descargará la imágen (línea 6) y automáticamente creará un contenedor. Tanto si se ha descargado la imagen o ya estaba descargada, el contenedor se ejecutará, obteniendo el texto de bienvenida que se ve en el cuadro anterior. 2.3. Configuración del usuario Si estamos usando Docker en nuestro ordenador personal, podemos configurar nuestro usuario para usar el cliente sin tener que poner sudo delante. Para ello, añade tu usuario al grupo de docker ejecutando lo siguiente: sudo usermod -aG docker $USER Para que los nuevos permisos surtan efecto, debes cerrar y volver a abrir la sesión. Para problemas relacionados con los permisos visitad la página del manual oficial. 2.4. Imágenes Es necesario traer ya instaladas ciertas imágenes de contenedores. Ejecuta los siguientes comandos en tu equipo (si te da error de permisos asegúrate que has hecho el apartado anterior y abierto y cerrado la sesión). Para instalar WordPress: docker pull wordpress:latest Para instalar MariaDB: docker pull mariadb:latest 2.5. Herramientas También es necesario traer una herramienta llamada Docker Compose. Puedes instalarla con las instrucciones que hay en la página de Instalación de Docker Compose. Sin embargo, si usas Ubuntu o Debian puedes instalarlo de forma más fácil con apt: sudo apt install docker-compose 3. Imágenes Las imágenes son la base de Docker. Nuestros contenedores se iniciarán a partir de ellas. Como se indicó en la introducción, es una plantilla de solo lectura, que se crea incorporando los requisitos necesarios para cumplir el objetivo para el cual fue creada. Por ejemplo, si estamos creando un proyecto con PHP, incorporará el intérprete del lenguaje de PHP. Si es una página web, incorporará el servidor web (apache, nginx, etc.). 3.1. Buscar imágenes Crear una imagen desde cero supone un esfuerzo demasiado grande, así que lo normal es partir o usar una ya creada. Para ellos buscaremos en los registros, el lugar donde se almacenan. Hay un registro oficial (https://hub.docker.com), pero nada impide a otras organizaciones, o a nosotros mismos, tener un registro propio. Estos registros pueden ser privados o públicos. Imaginemos que queremos crear una web con WordPress. Si buscamos en el registro encontraremos una imagen llamada wordpress, con la etiqueta oficial. La recomendación es que siempre busquemos imágenes oficiales, están mantenidas y bien documentadas. En la página encontraremos las diferentes opciones que tiene esta imagen para configurarla, aunque las veremos con más detalle más adelante. Por ahora iniciemos la imagen como se indica: docker run -p 8080:80 wordpress Y comprobaremos cómo se inicia el contenedor: $ docker run -p 8080:80 wordpress Unable to find image 'wordpress:latest' locally latest: Pulling from library/wordpress 802b00ed6f79: Pull complete 59f5a5a895f8: Pull complete 6898b2dbcfeb: Pull complete 8e0903aaa47e: Pull complete 2961af1e196a: Pull complete 71f7016f79a0: Pull complete 5e1a48e5719c: Pull complete 7ae5291984f3: Pull complete 725b65166f31: Pull complete e90b121f9520: Pull complete b5a272809bbd: Pull complete 8ac3e8ada01a: Pull complete d3da911b920f: Pull complete 94c7e0af5b20: Pull complete e1f39ac90dec: Pull complete Digest: sha256:7121cdf8e9f01816653a3b2d2f4fc7bfe1dab956f00db5c7e7689e5f1454029a Status: Downloaded newer image for wordpress:latest WordPress not found in /var/www/html - copying now... Complete! WordPress has been successfully copied to /var/www/html AH00558: apache2: Could not reliably determine the server's fully qualified domain name, using 10.17.0.1. Set the 'ServerName' directive globally to suppress this message AH00558: apache2: Could not reliably determine the server's fully qualified domain name, using 10.17.0.1. Set the 'ServerName' directive globally to suppress this message [DDD mmm dd hh:mm:ss.iiiiii yyyy] [mpm_prefork:notice] [pid 1] AH00163: Apache/2.4.25 (Debian) PHP/7.2.10 configured -- resuming normal operations [DDD mmm dd hh:mm:ss.iiiiii yyyy] [core:notice] [pid 1] AH00094: Command line: 'apache2 -D FOREGROUND' Vemos en la línea nueva un nuevo parámetro: -p 8080:80. Por defecto, un contenedor está totalmente aislado. Pero si estamos montando un blog con WordPress vamos a necesitar acceder a él desde el navegador. Con el parámetro -p, versión corta de --publish, podemos indicar que estamos enlazando un puerto de la máquina anfitrión con el contenedor. En este caso estamos enlazando el puerto 8080 de la máquina anfitrión con el 80 del contenedor. Vamos a abrir la siguiente página web en nuestro navegador: http://localhost:8080 La cual nos mostrará el asistente de instalación de WordPress, el cual no vamos a instalar porque necesitamos una base de datos que aún no tenemos. En su lugar vamos a la consola e interrumpimos la ejecución del contenedor con Control+C. 3.2. Gestión de imágenes 3.2.1. Descarga Las imágenes que nos descargamos se identifican, además de por el nombre, por una versión. De esa manera podemos tener distintas versiones de una misma imagen. En la página del registro de WordPress veremos una pestaña con el nombre Tags, con las versiones disponibles. Para usar una en concreto se usa dos puntos seguido del nombre de la versión. Si no se indica nada, como hasta ahora, por defecto se descarga la etiquetada como latest. Podemos descargar imágenes con la orden docker pull: En el primer caso no hay descarga porque esa versión ya estaba descargada, en la segunda nos descargamos la versión de la imagen que usa php7.1 en vez de php7.2 3.2.2. Listado Para ver el listado de images disponibles usamos docker images: 3.2.3. Borrado Si queremos dejar de usar alguna imagen usaremos docker rmi: Si una imagen está en uso por algún contenedor, no nos dejará eliminarla. 4. Contenedores Los contenedores son instancias de las imágenes que hemos creado o hemos descargado que se ejecutan de forma aislada. 4.1. Listado La orden para ver el listado de contenedores del sistema es docker container ls o la forma abreviada docker ps. Si lo ejecutamos nos dará un listado vacío porque no hay ningún contenedor activo. Probemos con el parámetro --all o -a. Estos contenedores están parados y se pueden volver a ejecutar, con el mismo estado que tuviera el sistema de archivos cuando se detuvieron. 4.2. Iniciar un contenedor Con docker container start podemos iniciar un contenedor parado: Veremos que la web de instalación de WordPress está de nuevo disponible. Solo que ahora el contenedor se ejecuta en segundo plano y no lo podemos detener como antes. 4.3. Detener un contenedor Con docker container stop podemos detener un contenedor iniciado, indicando su id o su nombre: 4.4. Borrar un contenedor Un contenedor detenido ocupa espacio. Si hemos dejado de necesitar un contenedor podemos borrarlo con docker container rm. Igualmente hay que indicar id o nombre. Hay que tener cuidado al borrar contenedores. Cuando un contenedor se borra se elimina cualquier información que contenga y no esté almacenada en algún lugar externo al propio contenedor. 4.5. Ejecutar comandos dentro de un contenedor Ya hemos usado docker run para crear e iniciar un contenedor. También podemos usar este comando para ejecutar programas que estén dentro del contenedor. Por ejemplo: docker run --name ubuntu_bash --rm -i -t ubuntu bash Las primeras versiones de Docker eran más limitadas, respecto a la creación de objetos. Así que salió con comandos como docker start, docker stop, etc. relacionados con los contenedores. Cuando surgieron más objetos no había consistencia entre los comandos de otros objetos (como docker volumes ls) y los de los contenedores. Así que se ha creado una jerarquía nueva de subcomandos bajo el comando container que son equivalentes y se mantienen por compatibilidad: Antiguo Nuevo docker run docker container run docker start docker container start docker stop docker container stop docker rm docker container rm docker inspect docker container inspect docker exec docker container exec No hay más diferencia entre ellos que el nombre. Pero esta forma de ejecutar cosas, crea un nuevo contenedor. Si queremos ejecutar un comando en un contenedor que ya esté iniciado, debemos usar docker container exec. Ejecuta lo siguiente en otro terminal (no cierres el anterior). docker exec -w /tmp ubuntu_bash touch my_file.sh El parámetro -w indica el directorio de trabajo, después indicamos el contenedor donde queremos ejecutar el comando (ubuntu_bash) y por último el comando a ejecutar (touch my_file.sh). Si en el primer terminal ejecutamos un listado del directorio tmp: # ls /tmp my_file.sh Vemos cómo podemos modificar un contenedor ya iniciado con docker container exec. Pulsa Control+C en el primer terminal para cerrar y borrar el contenedor. 5. Persistiendo datos Por defecto ya hemos indicado que un contenedor está aislado de todo. Hemos visto cómo podemos conectar el contenedor a un puerto de red para poder acceder a él. Eso incluye al sistema de archivos que contiene. De tal manera que si se elimina el contenedor, se eliminan también sus archivos. Si queremos almacenar datos (una web, una base de datos, etc.) dentro de un contenedor necesitamos una manera de almacenarlos sin perderlos. Docker ofrece tres maneras: A través de volúmenes, que son objetos de Docker como las imágenes y los contenedores. Montando un directorio de la máquina anfitrión dentro del contenedor. Almacenándolo en la memoria del sistema (aunque también se perderían al reiniciar el servidor). Lo normal es usar volúmenes, pero habrá ocasiones en que es preferible montar directamente un directorio de nuestro espacio de trabajo. Por ejemplo, para guardar los datos de una base de datos usaremos volúmenes, pero para guardar el código de una aplicación o de una página web montaremos el directorio. La razón para esto último es que tanto nuestro entorno de desarrollo como el contenedor tengan acceso a los archivos del código fuente. Los volúmenes, al contrario que los directorios montados, no deben accederse desde la máquina anfitrión. 5.1. Crear un volumen Como necesitamos crear una base de datos para nuestro blog con WordPress vamos a crear un volumen donde guardar la información: 5.2. Listar volúmenes Con docker volume ls podemos visualizar todos los volúmenes disponibles. 5.3. Visualizar volúmenes Los volúmenes se crean en un directorio del sistema y no es recomendable acceder a él, no al menos mientras haya un contenedor usándolo. En cualquier caso, si queremos ver los metadatos de un volumen podemos usar docker volume inspect 5.4. Borrar volúmenes Como todos los objetos de Docker, los volúmenes también pueden ser borrados, pero solo si no están en uso. Mucha precaución al borrar los volúmenes, porque perderíamos todos los datos que contenga. Para borrar un contenedor usaremos docker volume rm y el nombre del contenedor. 6. Levantar WordPress con Docker Para crear un blog con WordPress necesitamos tener una base de datos dónde almacenar las entradas. Así que empezaremos creándola y después crearemos el contenedor de nuestro blog. 6.1. Crear un contenedor con MariaDB WordPress soporta los motores relaciones MySQL y MariaDB. Usaremos este último. Vamos a crear nuestra base de datos usando este volumen. docker run -d --name wordpress-db \ --mount source=wordpress-db,target=/var/lib/mysql \ -e MYSQL_ROOT_PASSWORD=secret \ -e MYSQL_DATABASE=wordpress \ -e MYSQL_USER=manager \ -e MYSQL_PASSWORD=secret mariadb:10.3.9 La imagen se descargará, si no lo estaba ya, y se iniciará nuestro contenedor de MariaDB: El principal cambio en docker run con respecto a la última vez es que no hemos usado -p (el parámetro para publicar puertos) y hemos añadido el parámetro -d. Lo primero que habremos notado es que el contenedor ya no se queda en primer plano. El parámetro -d indica que debe ejecutarse como un proceso en segundo plano. Así no podremos pararlo por accidente con Control+C. Lo segundo es que vemos que el contenedor usa un puerto, el 3306/tcp, pero no está linkado a la máquina anfitrión. No tenemos forma de acceder a la base de datos directamente. Nuestra intención es que solo el contenedor de WordPress pueda acceder. Luego una serie de parámetros -e que nos permite configurar nuestra base de datos. Los contenedores se configuran a través de variables de entorno, que podemos configurar con el parámetro -e que vemos en la orden anterior. Gracias a ellos hemos creado una base de datos, un usuario y configurado las contraseñas. Se recomienda buscar en el registro de Docker la imagen oficial de MariaDB para entender el uso de los parámetros. Por último, el parámetro --mount nos permite enlazar el volumen que creamos en el paso anterior con el directorio /var/lib/mysql del contenedor. Ese directorio es donde se guardan los datos de MariaDB. Eso significa que si borramos el contenedor, o actualizamos el contenedor a una nueva versión, no perderemos los datos porque ya no se encuentran en él, si no en el volumen. Solo lo perderíamos si borramos explícitamente el volumen. El parámetro --mount se empezó a utilizar desde la versión 17.06 para contenedores independientes (los que no pertenecen a un enjambre o swarm). Los que conozcan Docker de versiones más antiguas estarán más acostumbrados a usar el parámetro --volume que hace algo similar. Sin embargo la documentación aconseja usar ya --mount, sobre todo para nuevos usuarios. 6.2. Creando nuestro blog Vamos a crear otra vez nuestro contenedor de WordPress, pero esta vez vamos a conectarlo con nuestra base de datos. Además, queremos poder editar los ficheros de las plantillas, por si tenemos que modificar algo, así que necesitaremos montar el directorio del contenedor donde está instalado WordPress con nuestra cuenta de usuario en la máquina anfitrión. Vamos a crear el espacio de trabajo: mkdir -p ~/Sites/wordpress/target && cd ~/Sites/wordpress Y dentro de este directorio arrancamos el contenedor: docker run -d --name wordpress \ --link wordpress-db:mysql \ --mount type=bind,source="$(pwd)"/target,target=/var/www/html \ -e WORDPRESS_DB_USER=manager \ -e WORDPRESS_DB_PASSWORD=secret \ -p 8080:80 \ wordpress:4.9.8 Cuando termine la ejecución, si accedemos a la dirección http://localhost:8080/, ahora sí podremos acabar el proceso de instalación de nuestro WordPress. Si listamos el directorio target comprobaremos que tenemos todos los archivos de instalación accesibles desde el directorio anfitrión. 7. Levantar un WordPress con Docker Compose El cliente de Docker es engorroso para crear contenedores, así como para crear el resto de objetos y vincularlos entre sí. Para automatizar la creación, inicio y parada de un contenedor o un conjunto de ellos, Docker proporciona una herramienta llamada Docker Compose. Para esta parte vamos a detener y borrar lo que hemos creado: docker container stop wordpress wordpress-db docker container rm wordpress wordpress-db docker volume rm wordpress-db 7.1. Docker Compose Compose es una herramienta para definir y ejecutar aplicaciones multi-contenedor. Con un solo comando podremos crear e iniciar todos los servicios que necesitamos para nuestra aplicación. Los casos de uso más habituales para docker-compose son: Entornos de desarrollo Entornos de test automáticos (integración contínua) Despliegue en host individuales (no clusters) Compose tiene comandos para manejar todo el ciclo de vida de nuestra aplicación: Iniciar, detener y rehacer servicios. Ver el estado de los servicios. Visualizar los logs. Ejecutar un comando en un servicio. 7.2. Creación de contenedores automatizada En el mismo directorio donde estábamos en el paso anterior (~/Sites/wordpress), vamos a crear un fichero llamado docker-compose.yaml con el siguiente contenido: version: '3' services: db: image: mariadb:10.3.9 volumes: - data:/var/lib/mysql environment: - MYSQL_ROOT_PASSWORD=secret - MYSQL_DATABASE=wordpress - MYSQL_USER=manager - MYSQL_PASSWORD=secret web: image: wordpress:4.9.8 depends_on: - db volumes: -./target:/var/www/html environment: - WORDPRESS_DB_USER=manager - WORDPRESS_DB_PASSWORD=secret - WORDPRESS_DB_HOST=db ports: - 8080:80 volumes: data: Los ficheros de Compose están divididos en tres secciones: services, volumes y networks; y deben indicar un número de versión. Nos permite realizar prácticamente lo mismo que podemos hacer con el cliente de docker, pero de forma automática. Con este fichero podemos hacer lo mismo que hemos hecho en el capítulo anterior, pero con la ventaja de describir todos nuestros requisitos en un solo archivo. 7.3. Iniciar servicios Vamos a ejecutar esta aplicación y explicarla: Arranca la aplicación con Compose: docker-compose up -d Cuando arrancamos la aplicación, Compose nos informa de los servicios que ha ido levantando: El parámetro -d es similar al que hemos visto en docker run: nos permite levantar los servicios en segundo plano. Veamos los contenedores activos: También podemos ver los contenedores con Compose: Lo que tenemos que tener en cuenta es lo siguiente: docker-compose ps solo muestra información de los servicios que se define en docker-compose.yaml, mientras que docker muestra todos. Cuando creamos contenedores con docker sin indicar un nombre, por defecto asigna uno aleatorio; mientras que en Compose el prefijo es el nombre del directorio y el sufijo el nombre del servicio: wordpress_db_1. El número indica el número de instancia. Es posible levantar más de una instancia de un mismo servicio. Si accedemos a la dirección http://localhost:8080/, veremos de nuevo la instalación de WordPress. 7.4. Detener servicios Podemos detener servicios con docker-compose stop 7.5. Borrar servicios Podemos borrar servicios con docker-compose down Esto borra los contenedores, pero no los volúmenes. Así que si hemos creado bien la aplicación nuestros datos están a salvo. Si queremos borrar también los volúmenes: docker-compose down -v 7.6. Estructura de la configuración Veamos la configuración por partes: version: '3' Compose se actualiza a menudo, con lo que el archivo de configuración va adquiriendo nuevas funcionalidades. La versión '3' (es una cadena, importante poner comillas) es la última y para conocer todas sus características mira la página de referencia de la versión 3 de Compose. volumes: data: Ya hemos indicado que es importante guardar los datos volátiles de las aplicaciones en volúmenes. En este caso hemos creado un volumen llamado data. Recordemos que Compose siempre añade como prefijo el nombre del directorio, con lo que el nombre real del volumen es wordpress_data. Podemos comprobarlo con el cliente de docker como hicimos en el capítulo de volúmenes: $ docker volume ls DRIVER VOLUME NAME local wordpress_data Nos saltamos la sección de redes (networks) y vamos a la sección de servicios, que son los contenedores que precisa o componen nuestra aplicación. Primero la base de datos: services: db: image: mariadb:10.3.9 volumes: - data:/var/lib/mysql environment: - MYSQL_ROOT_PASSWORD=secret - MYSQL_DATABASE=wordpress - MYSQL_USER=manager - MYSQL_PASSWORD=secret Después de abrir la parte de servicios, el primer nivel indica el nombre del servicio db, que genera el contenedor wordpress_db. Lo que vemos a continuación es lo mismo que hicimos en la sección anterior pero de forma parametrizada. Si recordamos, para levantar nuestra base de datos, indicamos la imagen (línea 3), luego montamos los volúmenes (línea 4), y después indicamos las variables de entorno que configuraban el contenedor (línea 6). Es decir, lo anterior es equivalente, excepto por el nombre, a: Y después nuestro WordPress: services: web: image: wordpress:4.9.8 depends_on: - db volumes: -./target:/var/www/html environment: - WORDPRESS_DB_USER=manager - WORDPRESS_DB_PASSWORD=secret - WORDPRESS_DB_HOST=db ports: - 8080:80 En este caso la equivalencia es al comando: Si reiniciamos el ordenador, los contenedores estarán detenidos (stop), podremos reiniciarlos con docker start o docker-compose start. Este es el comportamiento predeterminado y el que nos interesa en un entorno de desarrollo. Sin embargo, en otros entornos, o para casos concretos, igual queremos que un contenedor tenga el mismo estado en el que estaba antes de reiniciar la máquina (iniciado o parado). Para eso usaremos el parámetro restart. En el caso de la base de datos de nuestro ejemplo, la configuración quedaría como: El equivalente en la consola sería: Otros valores son: no (por defecto), always y on-failure. 8. Crear imágenes propias Ya hemos visto cómo usar imágenes de terceros para crear aplicaciones y servicios. Pero, ¿si no hay ninguna imagen que tenga lo que queremos? ¿O si queremos hacer una imagen de nuestra aplicación para distribuirla? Docker permite crear imágenes propias. Aunque podríamos hacerla partiendo de cero, es un esfuerzo que no tiene sentido. Existen ya imágenes base para crear las nuestras y es mucho más fácil crear una imagen basándose en otra que hacerlo todo nosotros. Podemos partir de una imagen base que parte de un lenguaje de programación (python, php) o de alguna distribución (ubuntu, debian). 8.1. Mi primer Dockerfile Los Dockerfile son los archivos que contienen las instrucciones que crean las imágenes. Deben estar guardados dentro de un build context, es decir, un directorio. Este directorio es el que contiene todos los archivos necesarios para construir nuestra imagen, de ahí lo de build context. Creamos nuestro build context mkdir -p ~/Sites/hello-world cd ~/Sites/hello-world echo "hello" > hello Dentro de este directorio crearemos un archivo llamado Dockerfile con este contenido: FROM busybox COPY /hello / RUN cat /hello Directiva Explicación FROM Indica la imagen base sobre la que se basa esta imagen COPY Copia un archivo del build context y lo guarda en la imagen RUN Ejecuta el comando indicado durante el proceso de creación de imagen. Ahora para crear nuestra imagen usaremos docker build. docker build -t helloapp:v1. El parámetro -t nos permite etiquetar la imagen con un nombre y una versión. El. indica que el build context es el directorio actual. Y podremos ver que una nueva imagen está instalada en nuestro equipo: 8.2. Creando aplicaciones en contenedores Vamos a crear una aplicación en python y la vamos a guardar en un contenedor. Comenzamos creando un nuevo build context: mkdir -p ~/Sites/friendlyhello cd ~/Sites/friendlyhello El código de la aplicación es el siguiente, lo guardaremos en un archivo llamado app.py: from flask import Flask from redis import Redis, RedisError import os import socket # Connect to Redis redis = Redis(host="redis", db=0, socket_connect_timeout=2, socket_timeout=2) app = Flask(__name__) @app.route("/") def hello(): try: visits = redis.incr("counter") except RedisError: visits = "cannot connect to Redis, counter disabled" html = "Hello {name}!" \ "Hostname: {hostname}" \ "Visits: {visits}" return html.format(name=os.getenv("NAME", "world"), hostname=socket.gethostname(), visits=visits) if __name__ == "__main__": app.run(host='0.0.0.0', port=80) Nuestra aplicación tiene una serie de dependencias (librerías de terceros) que guardaremos en el archivo requirements.txt: Flask Redis Y por último definimos nuestro Dockerfile: Para conocer todas las directivas visita la documentación oficial de Dockerfile. En total debemos tener 3 archivos: Ahora construimos la imagen de nuestra aplicación: docker build -t friendlyhello. Y comprobamos que está creada: 8.2.1. Probar nuestro contenedor Vamos a arrancar nuestro contenedor y probar la aplicación: docker run --rm -p 4000:80 friendlyhello Normalmente los contenedores son de usar y tirar, sobre todo cuando hacemos pruebas. El parámetro --rm borra automáticamente un contenedor cuando se para. Recordemos que los datos volátiles siempre se deben guardar en volúmenes. Comprobamos en el puerto 4000 si efectivamente está iniciada o no: http://localhost:4000. Obtendremos un mensaje como este: Ya tenemos una imagen lista para ser usada. Pulsamos Control+C para interrumpir y borrar nuestro contenedor. 8.2.2. Creando la aplicación En este caso nuestro contenedor no funciona por sí mismo. Es muy habitual que dependamos de servicios para poder iniciar la aplicación, habitualmente bases de datos. En este caso necesitamos una base de datos Redis que no tenemos. Como vimos en el apartado anterior, vamos a aprovechar las características de Compose para levantar nuestra aplicación. Vamos a crear el siguiente archivo docker-compose.yaml: La principal diferencia con respecto al capítulo anterior, es que en un servicio podemos indicar una imagen (parámetro imagen) o un build context (parámetro build). Esta es una manera de integrar las dos herramientas que nos proporciona Docker: la creación de imágenes y la composición de aplicaciones con servicios. 8.2.3. Balanceo de carga Vamos a modificar nuestro docker-compose.yaml: En este caso, el servicio web no va a tener acceso al exterior (hemos eliminado el parámetro ports). En su lugar hemos añadido un balanceador de carga (el servicio lb). Vamos a arrancar esta nueva aplicación, pero esta vez añadiendo varios servicios web: docker-composer up -d --scale web=5 Esperamos a que terminen de iniciar los servicios: Podemos comprobar como el servicio web nos ha iniciado 5 instancias, cada una con su sufijo numérico correspondiente. Si usamos docker ps para ver los contenedores disponibles tendremos: Vamos a fijarnos en el CONTAINER ID y vamos a volver a abrir nuestra aplicación: http://localhost:4000. Si en esta ocasión vamos recargando la página, veremos como cambian los hostnames, que a su vez coinciden con los identificadores de los contenedores anteriores. Esta no es la manera adecuada de hacer balanceo de carga, puesto que todos los contenedores están en la misma máquina, lo cual no tiene sentido. Solo es una demostración. Para hacer balanceo de carga real necesitaríamos tener o emular clusters de máquinas y crear un enjambre (swarm). 8.3. Compartir imágenes Si tenemos una imagen que queramos compartir, necesitamos usar un registro. Existe incluso una imagen que nos permite crear uno propio, pero vamos a usar el repositorio público de Docker. Los pasos son: 1. Crear una cuenta de usuario en el repositorio oficial de Docker. 2. Pulsar sobre el botón "Create Repository +". 3. En el formulario hay que rellenar solo un dato obligatoriamente: el nombre. Usaremos el de la imagen: friendlyhello. Nuestro nombre de usuario es el namespace y es obligatorio que tenga uno. Si estuviéramos en alguna organización podríamos elegir entre varios. El resto de campos lo dejamos como está por el momento. La cuenta gratuita solo deja tener un repositorio privado, así que no lo malgastaremos aquí. 4. Ahora tenemos que conectar nuestro cliente de Docker con nuestra cuenta en el Hub. Usamos el comando docker login. 5. Para que las imágenes se puedan guardar, tenemos que etiquetarla con el mismo nombre que tengamos en nuestro repositorio más el namespace. Si nuestra cuenta es 'username' y el repositorio es 'friendlyhello', debemos crear la imagen con la etiqueta 'username/friendlyhello'. $ docker build -t username/friendlyhello. 6. Ahora ya podemos enviar nuestra imagen: $ docker push username/friendlyhello 9. Trucos 9.1. Portainer Portainer es un gestor de contenedores a través de una interfaz web. Para usarlo creamos un directorio donde guardar nuestro docker-compose.yaml. mkdir -p ~/Sites/portainer cd ~/Sites/portainer Guardamos el siguiente fichero como docker-compose.yaml en nuestro directorio: version: '2' services: portainer: image: portainer/portainer command: -H unix:///var/run/docker.sock volumes: - /var/run/docker.sock:/var/run/docker.sock - portainer_data:/data ports: - 127.0.0.1:9000:9000 volumes: portainer_data: Y ejecutamos el contenedor: docker-compose up -d 9.2. Limpieza Para borrar objetos que no están en uso: docker system prune Para borrar volúmenes que no están asociados a ningún contenedor: docker volume rm $(docker volume ls -q -f "dangling=true") Para borrar contenedores que han terminado su ejecución: docker rm $(docker ps -q -f "status=exited") Para borrar imágenes que no están etiquetadas: docker rmi $(docker images -q -f "dangling=true") 9.3. Copias de seguridad Para hacer una copia de seguridad: docker run --rm -v /tmp:/backup \ --volumes-from \ busybox tar -cvf /backup/backup.tar Para restaurar: docker run --rm -v /tmp:/backup \ --volumes-from busybox tar -xvf /backup/backup.tar 10. Fuentes de esta página 1. https://codefresh.io/docker-tutorial/everyday-hacks-docker/ 2. http://blog.labianchin.me/2016/02/15/docker-tips-and-tricks 11. Imágenes base Son las imágenes más conocidas por las que podemos usar para no partir desde cero para crear la nuestra. phusion/baseimage: 209mb centos: 200mb debian: 101mb ubuntu: 84mb alpine: 4.4mb busybox: 1.16mb