docker-compose
Práctica 8: Dockerfiles y docker-compose
Creación de imágenes DockerUso de DockerfileFormato de los ficheros DockerfileEjemplo: uso de DockerfileDespliegues multicontenedorInstalación de docker-compose
Uso de docker-compose
Estructura del fichero docker-compose.yml
Comandos docker-compose
Ejemplo: despliegue de dos contenedores con volumen compartidoEjercicio entregableTarea 1: Escenario PHPMyAdmin+MariaDBTarea 2: Escenario HAProxy+Wordpress+MariaDBEntrega
Hay dos mecanismos fundamentales para crear imágenes Docker propias:
docker commit
Dockerfile
que describe sus características mediante el comando docker build
Un Dockerfile
es un fichero de texto donde se indican los prámetros de una imagen Docker y los comandos a ejecutar sobre una imagen base para crear una nueva imagen.
El comandodocker build
construye una nueva imagen (de nombre -t <nombre>
) siguiendo las instrucciones del fichero Dockerfile
docker build
recibe como parámetro el directorio donde se encuentra el Dockerfile
junto con otros ficheros o directorios que puedan ser necesarios para la creación de la nueva imagen (mediante las opciones COPY
y ADD
se incorporarán a la nueva imagen)
docker build
ejecuta las instrucciones del Dockerfile
una a una. Cada instrucción ejecutada crea una imagen intermedia (que normalmente se desecha) y aporta una nuevo layer a la imagen Docker resultante.
La imagen resultante estaŕa disponible en el repositorio de imágenes Docker local
Con el comando docker save <imagen>
se puede volcar una imagen a un fichero .tar
(que después se puede voler a importar con docker load
)
Es posible publicar imágenes propias en Docker Hub (o en otro Docker Registry privado)
docker login
,docker tag
docker pull
FROM: Define la imagen base sobre la que se construirá la nueva imagen
Sintaxis: FROM <imagen>
ó FROM <imagen>:<tag>
LABEL: Permite vincular metadatos (autor, descripción, fecha, etc) a la imagen
Sintaxis: LABEL <campo> <valor>
ó LABEL <campo>=<valor>
ENV: Define variables de entorno que estaŕan disponibles en el contenedor (tanto durante la creación de la imagen, como durante la ejecución del contenendor)
Sintaxis: ENV <variable> <valor>
ó ENV <variable>=<valor>
WORKDIR: Establece el directorio donde se ejecutará los comandos indicados con RUN/CMD/ENTRYPOINT
.
Sintaxis: WORKDIR <ruta>
ADD ó COPY: Permite agregar/copiar archivos desde el equipo local a la imagen
Sintaxis: ADD <origen/es>... <destino>
ó ADD ["<origen/es>",... "<destino>"]
EXPOSE: Indica los puertos (TCP/IP) en los que escuchará el contenedor (es sólo declarativo, la redicción de puertos del anfitrión es independiente y debe hacerse manualmente al iniciar el contenedor)
Sintaxis: EXPOSE <puerto/s>
VOLUME: Establece los volúmenes a montar al ejecutar el contenedor.
Sintaxis: VOLUME <volumen/es>
RUN: Permite ejecutar comandos en la imagen base antes de ser creada (puede haber tantos RUN
como sea necesario)
/bin/sh -c
): RUN <jecutable con parametros>
RUN ["<ejecutable>", "<parámetro1>", "<parámetro2>"]
ENTRYPOINT: Define nuevo "punto de entrada" que es el comando que ejecuta por defecto el contenedor al iniciarse (salvo que especifique otro al crear el contenedor mediante docker run --entrypoint <comando>
) [sólo se permite un ENTRYPOINT
en cada Dockerfile]
RUN
.CMD: Define la acción a ejecutar por defecto al crear el contenedor (salvo que se especifique otro comando con docker run
) [sólo se permite un CMD
en cada Dockerfile]
RUN
.Si hay definido un ENTRYPOINT
, la semanatica de CMD
cambia y se interpreta su valor como parámetros que se pasarán al comando del ENTRYPOINT
(Detalles en https://www.ctl.io/developers/blog/post/dockerfile-entrypoint-vs-cmd/)
Más detalles en la documentación oficial: https://docs.docker.com/engine/reference/builder/
root@docker:~# mkdir miapache
root@docker:~# nano miapache/Dockerfile
xFROM debian:latest
LABEL autor="CDA 2022/23" descripcion="Imagen con Apache2"
EXPOSE 80
RUN apt-get update && apt-get install -y apache2 && apt-get clean
ADD index.html /var/www/html/
CMD ["/usr/sbin/apache2ctl", "-D", "FOREGROUND"]
xxxxxxxxxx
root@docker:~# nano miapache/index.html
xxxxxxxxxx
<html>
<body>
<h1>Prueba index.html por defecto</h1>
</body>
</html>
xxxxxxxxxx
root@docker:~# docker build -t cda/apache:v1.0 miapache
Sending build context to Docker daemon 3.072kB
Step 1/6 : FROM debian:latest
...
Step 2/6 : LABEL autor="CDA 2022/23" descripcion="Imagen con Apache2"
...
Step 3/6 : EXPOSE 80
...
Step 4/6 : RUN apt-get update && apt-get install -y apache2 && apt-get clean
...
Step 5/6 : ADD index.html /var/www/html/
...
Step 6/6 : CMD ["/usr/sbin/apache2ctl", "-D", "FOREGROUND"]
...
Successfully built c8c685b91440
Successfully tagged cda/apache:v1.0
root@docker:~# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
cda/apache v1.0 c8c685b91440 About a minute ago 253MB
root@docker:~# docker run -d -p 8080:80 --name servidor1 cda/apache:v1.0
root@docker:~# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
dcf095a02f1d cda/apache:v1.0 "/usr/sbin/apache2ct…" 18 seconds ago Up 17 seconds 0.0.0.0:8080->80/tcp servidor1
Acceder a http://localhost:8080
xxxxxxxxxx
root@docker:~# docker stop servidor1
root@docker:~# docker rm servidor1
xxxxxxxxxx
root@docker:~# mkdir miphp
root@docker:~# nano miphp/Dockerfile
xxxxxxxxxx
FROM cda/apache:v1.0
LABEL autor="CDA 2022/23" descripcion="Imagen con PHP"
EXPOSE 80
RUN apt-get update && apt-get install -y libapache2-mod-php php && apt-get clean
ENV MENSAJE "valor por defecto"
ADD phpinfo.php /var/www/html/
ADD start.sh /root
RUN chmod +x /root/start.sh
CMD ["/root/start.sh"]
xxxxxxxxxx
root@docker:~# nano miphp/start.sh
xxxxxxxxxx
sed -i "s/__HUECO__/$MENSAJE/g" /var/www/html/phpinfo.php
/usr/sbin/apache2ctl -D FOREGROUND
xxxxxxxxxx
root@docker:~# nano miphp/phpinfo.php
xxxxxxxxxx
<html>
<body>
<h1>Prueba PHP</h1>
<h2> Mensaje: __HUECO__ <h2>
<h2> PHPInfo </h2>
<?php echo phpinfo();?>
</body>
</html>
xxxxxxxxxx
root@docker:~# docker build -t cda/php:v1.0 miphp
Sending build context to Docker daemon 4.096kB
Step 1/9 : FROM cda/apache:v1.0
...
Step 2/9 : LABEL autor="CDA 2022/23" descripcion="Imagen con PHP"
...
Step 3/9 : EXPOSE 80
...
Step 4/9 : RUN apt-get update && apt-get install -y libapache2-mod-php php && apt-get clean
...
Step 5/9 : ENV MENSAJE "valor por defecto"
...
Step 6/9 : ADD phpinfo.php /var/www/html/
...
Step 7/9 : ADD start.sh /root
...
Step 8/9 : RUN chmod +x /root/start.sh
...
Step 9/9 : CMD ["/root/start.sh"]
...
Successfully built b37ea74d93b4
Successfully tagged cda/php:v1.0
root@docker:~# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
cda/php v1.0 b37ea74d93b4 About a minute ago 274MB
cda/apache v1.0 c8c685b91440 5 minutes ago 253MB
root@docker:~# docker run -d -p 8081:80 --name servidor2 cda/php:v1.0
root@docker:~# docker run -d -p 8082:80 -e MENSAJE="saludos de Pepe" --name servidor3 cda/php:v1.0
root@docker:~# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
4d2c813cd93b cda/php:v1.0 "/root/start.sh" 50 seconds ago Up 50 seconds 0.0.0.0:8082->80/tcp servidor3
982dc156d204 cda/php:v1.0 "/root/start.sh" 2 minutes ago Up 2 minutes 0.0.0.0:8081->80/tcp servidor2
Desde el anfitrión, acceder a http://localhost:8081/phpinfo.php y http://localhost:8082/phpinfo.php
xxxxxxxxxx
root@docker:~# docker image inspect debian:latest > /tmp/apache2.json
root@docker:~# docker image inspect cda/apache:v1.0 > /tmp/apache.json
root@docker:~# docker image inspect cda/php:v1.0 > /tmp/php.json
root@docker:~# diff -y /tmp/debian.json /tmp/apache.json | less
root@docker:~# diff -y /tmp/apache.json /tmp/php.json | less
Revisar el elemento Root > FSLayers
(1 layer en debian
, 3 layers en apache:v1.0
, 7 layers en php:v1.0
)
xxxxxxxxxx
root@docker:~# docker save cda/apache:v1.0 > /tmp/imagen.tar
root@docker:~# cd /tmp
root@docker:/tmp# tar xvf imagen.tar
root@docker:/tmp# cat manifest.json
root@docker:/tmp# tar tvf <ultimo layer>/layer.tar
drwxr-xr-x root/root 0 2022-11-24 00:34 var/
drwxr-xr-x root/root 0 2022-11-24 00:34 var/www/
drwxr-xr-x root/root 0 2022-11-24 00:35 var/www/html/
-rw-r--r-- root/root 82 2022-11-24 00:34 var/www/html/index.html
Parar y eliminar contenedores creados
xxxxxxxxxx
root@docker:~# docker stop servidor1 servidor2 servidor3
root@docker:~# docker rm servidor1 servidor2 servidor3
La herramienta docker-compose
(en versiones recientes docker compose
) permite desplegar un conjunto de contenedores a partir de las descripciones aportadas en un fichero docker-compose.yml
docker-compose
<pendiente>
Detalles en https://docs.docker.com/compose/install/
Importante: Dependiendo de la versión de Docker instalada el comando a usar en esta sección será:
docker-compose
, en versiones "antiguas" de Docker (es la que estará instalada en la MV de prácticas)docker compose
, en las últimas versiones recientes de Docker (es la que está instalada en los equipos físicos del laboratorio)
docker-compose
docker-compose.yml
Fichero en formato YAML (YAML Ain't Markup Language).
Especificación del fichero docker-compose.yml
en https://docs.docker.com/compose/compose-file/
Elementos de primer nivel del objeto YAML:
Elemento version
(opcional): versión del formato utilizada
Elemento name
(opcional): nombre del escenario
Elemento services
: lista de los contenedores/services a ejecutar, cada uno identificado por su nombre y acompañado de sus parámetros de creación (imagen, redirecciones de puertos, volúmenes, redes a usar, etc).
Cada service
puede tener:
image
: nombre la imagen base del contenedorbuild
: detalles para la creación del contenedor, incluyendo ruta a Dockerfile
para crear su imagencommand
/entrypoint
: "sobreescribe" el comando de inicio por defecto definido en la imagen baseconfigs
: parámetros de configuración adicionalesdepends_on
: lista con otros contenedores de los que depende el contenedor definido (no se incia hasta que esas dependencias se hayan iniciado)environment
: lista con las variables de entorno a establecer en el contenedornetworks
: lista de redes a las que se conectará el contenedor port
: lista de puertos expuestos y/o redireccionadosvolumes
: lista de volúmes a montarcontainer_name
, labels
cpus,
mem_limit`, etcElemento networks
(opcional): define la lista de redes a crear en el escenario descrito, vinculando a cada nombre de red sus parámetros (driver
, ipam
[con detalles de la subred], etc)
Elemento volumes
(opcional): define la lista de volúmes a usar por los contenedores y los detalles de cada uno
Elemento configs
(opcional): define la lista de "configuraciones" usadas por los contenedores declarados, con los detalles de cada una
Elemento secrets
(opcional): tipo particular de "configuraciones" con datos sensibles (contraseñas, claves, certificados, etc)
Nota: En los escenarios creados con docker-compose
se crea por defecto una red definida por el usuario nueva donde se conectan los contenedores.
networks
y conectar cada contenedor a las redes mediante el atributo networks
.docker-compose
l,a resolución de nombres DNS se tanto por el nombre del contenedor (atributo container_name
) como el nombre del service definido en el elemento de primer nivel services.docker-compose
Ejecutable docker-compose
(en versiones recientes docker compose
) debe ejecutarse en el mismo directorio donde se ubica el fichero docker-compose.yml
con la configuración del escenario (o indicar con -f
la ruta del fichero con la configuración del escenario).
docker-compose up
: Crear los contenedores (servicios) descritos en docker-compose.yml
(con la opción -d
crear los contenedores en modo dettach, sin mostar los logs de inicio) (con la opción -f
se puede indicar el fichero YAML con la configuración en caso de no usar el nombre por defecto)docker-compose down
: Para y borra los contenedores y las redes (si las hubiera) creados con docker-compose up
docker-compose config
: Hace una comprobación de sintaxis del fichero docker-compose.yml
, mostrando la "version completa" del fichero si todo está correcto.docker-compose stop|restart
: Detiene/reinicia los contenedores que previamente se han lanzado con docker-compose up
.docker-compose run
: Inicia los contenedores descritos en el docker-compose.yml
que estén parados.docker-compose rm
: Borra los contenedores parados del escenario. docker-compose pause|unpause
: Pausa los contenedores que previamente se han lanzado con docker-compose up
, o reanuda los previamente pausadosdocker-compose build
: En caso de haberse indicado en la sección build
Dockerfile
con definiciones de images usadas en el docker-compose.yml
se construyen esas imágenesdocker-compose logs <servicio (opc.)>
: Muestra los logs de todos los servicios del escenario o de uno en concreto docker-compose exec <servicio> <comando>
: Ejecuta un comando en el contenedor indicado, declarado como <servicio>
en el docker-compose.yml
docker-compose top
: Muestra los procesos que están ejecutándose en cada uno de los contenedores creados.
xxxxxxxxxx
root@docker:~# mkdir ejemplo
root@docker:~# cd ejemplo/
root@docker:~/ejemplo# nano docker-compose.yml
xxxxxxxxxx
version"3.3"
services
contenedor1
container_name apache1
image cda/php v1.0
restart always
hostname apache1
ports
"8081:80"
volumes
./comun_html:/var/www/html/extra
contenedor2
container_name apache2
image cda/php v1.0
restart always
hostname apache2
environment
MENSAJE"Hola desde Apache2"
depends_on
contenedor1
ports
"8082:80"
volumes
./comun_html:/var/www/html/extra
xxxxxxxxxx
root@docker:~/ejemplo# mkdir comun_html
root@docker:~/ejemplo# echo "Servido por: <?php echo gethostname(); ?>" > comun_html/index.php
root@docker:~/ejemplo# docker-compose config #Valida la configuracion
root@docker:~/ejemplo# docker-compose up -d
Creating network "ejemplo_default" with the default driver
Creating apache1 ... done
Creating apache2 ... done
root@docker:~/ejemplo# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
b59d4af02dd2 cda/php:v1.0 "/root/start.sh" 22 seconds ago Up 21 seconds 0.0.0.0:8082->80/tcp apache2
606e69173a63 cda/php:v1.0 "/root/start.sh" 23 seconds ago Up 22 seconds 0.0.0.0:8081->80/tcp apache1
root@docker:~/ejemplo# docker-compose logs contenedor1
root@docker:~/ejemplo# docker-compose logs contenedor2
root@docker:~/ejemplo# docker exec -it apache1 /bin/bash
root@apache1:/# apt-get install -y -q iproute2 iputils-ping
...
root@apache1:/# ip address
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
68: eth0@if69: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:ac:15:00:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 172.XX.0.2/16 brd 172.21.255.255 scope global eth0
valid_lft forever preferred_lft forever
root@apache1:/# ping 172.XX.0.3
PING 172.XX.0.3 (172.XX.0.3) 56(84) bytes of data.
64 bytes from 172.XX.0.3: icmp_seq=1 ttl=64 time=0.416 ms
root@apache1:/# exit
Desde el anfitrión, acceder a
comun_html
, montada /var/www/html/extra
de cada contenedor)MENSAJE
de la imagen base)Eliminar escenario
xxxxxxxxxx
root@docker:~/ejemplo# docker-compose down
Stopping apache2 ... done
Stopping apache1 ... done
Removing apache2 ... done
Removing apache1 ... done
Removing network ejemplo_default
root@docker:~/ejemplo# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
Repetir el "Ejemplo 3: Uso conjunto de contenedores y redes (MariaDB + PHPMyAdmin)" de la Práctica 7 usando docker-compose
.
docker-compose.yml
, junto con los demás elementos necesarios (directorios, ficheros, etc) docker ps
donde se muestren los contenedores en ejecución)Repetir el "Ejercicio entregable" de la Práctica 7 (despliegue de HAProxy con dos Wordpress y una BD MariaDB compartida) usando docker-compose
.
Crear el directorio de configuración y el fichero docker-compose.yml
, junto con los demás elementos necesarios (directorios, ficheros, etc)
Aportar los ficheros de configuración usados y (si es pertinente) explicar su contenido.
Detallar los pasos seguidos, comandos utilizados y salidas obtenidas (incluir salidas de docker ps
donde se muestren los contenedores en ejecución)
Entrega individual, en MOOVI hasta 23/12/2022