Estamos de vuelta. Seguimos con la serie de artículos sobre Docker. En el capítulo de hoy abordaremos Dockerfile. Se trata de una herramienta que nos ofrece la posibilidad de crear una imagen a medida, mediante una serie de instrucciones, ubicadas dentro de un fichero.
Imágenes a medida con Dockerfile
Docker puede construir imágenes automáticamente, leyendo las instrucciones indicadas en un fichero Dockerfile. Se trata de un documento de texto que contiene todas las órdenes a las que un usuario dado puede llamar, desde la línea de comandos, para crear una imagen. En esta página podemos encontrar todas las órdenes que se pueden utilizar. Además, podemos consultar las buenas prácticas recomendadas.
Los pasos principales para crear una imagen a partir de un fichero Dockerfile son:
- Crear un nuevo directorio que contenga el fichero, con el guión y otros ficheros que fuesen necesarios para crear la imagen.
- Crear el contenido.
- Construir la imagen mediante el comando
docker build
.
La sintaxis para el comando es:
docker build [opciones] RUTA | URL | -
Las opciones más comunes son:
- -t, nombre [:etiqueta]. Crea una imagen con el nombre y la etiqueta especificada a partir de las instrucciones indicadas en el fichero. Es una opción muy recomendable.
- –no-cache. Por defecto, Docker guarda en memoria caché las acciones realizadas recientemente. Si se diese el caso de que ejecutamos un
docker build
varias veces, Docker comprobará si el fichero contiene las mismas instrucciones y, en caso afirmativo, no generará una nueva imagen. Para generar una nueva imagen omitiendo la memoria caché utilizaremos siempre esta opción. - –pull. También por defecto. Docker solo descargará la imagen especificada en la expresión FROM si no existe. Para forzar que descargue la nueva versión de la imagen utilizaremos esta opción.
- –quiet. Por defecto, se muestra todo el proceso de creación, los comandos ejecutados y su salida. Utilizando esta opción solo mostrará el identificador de la imagen creada.
Laboratorio de pruebas
La mejor forma de aprender algo es dándole caña, así que vamos al lío. Seguiremos los pasos expuestos en los párrafos anteriores. Vamos a crear un directorio de trabajo y en este crearemos el fichero guión:
mkdir ubuntutest cd ubuntutest vi Dockerfile
Indicamos en el guión qué imagen base vamos a utilizar mediante FROM, después con RUN apuntamos los comandos a ejecutar y con CMD decimos el comando por defecto:
FROM ubuntu:latest RUN apt-get -y update; \ apt-get -y upgrade; \ apt-get -y install apt-utils \ vim \ htop; RUN apt-get -y install dstat CMD ["bash"]
Una vez creado el fichero y editado, lo guardamos. Ahora realizamos la construcción de la imagen:
docker build -t "pruebas9:dockerfile" .
Con el resultado:
[....] Step 6/6 : CMD bash ---> Running in 75324aa704ba ---> b2dcc8d2d69d Removing intermediate container 75324aa704ba Successfully built b2dcc8d2d69d Successfully tagged pruebas9:dockerfile
Lo expuesto anteriormente sólo es la parte final de los registros de creación de la imagen. Ahora ya la podemos ver en la lista de imágenes disponibles:
[root@servdocker /]# docker images REPOSITORY TAG IMAGE ID CREATED SIZE pruebas9 dockerfile b2dcc8d2d69d 7 minutes ago 254MB
Pero esto no para aquí. Vamos a crear un contenedor a partir de la imagen:
[root@servdocker /]# docker run -dti --name pruebacontainer b2dcc8d2d69d cd1fd50239d9c17f91ab1aa739e9230eaf9a17dd273305c5fda9a8a48df194f3 [root@servdocker /]# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES cd1fd50239d9 b2dcc8d2d69d "bash" 33 seconds ago Up 32 seconds pruebacontainer
Y accedemos a él para comprobar que, efectivamente, los programas instalados están disponibles:
[root@servdocker /]# docker exec -i -t pruebacontainer /bin/bash root@cd1fd50239d9:/# dstat You did not select any stats, using -cdngy by default. ----total-cpu-usage---- -dsk/total- -net/total- ---paging-- ---system-- usr sys idl wai hiq siq| read writ| recv send| in out | int csw 2 1 94 3 0 0| 56k 338k| 0 0 | 68B 159B| 152 165 0 0 100 0 0 0| 0 0 | 0 0 | 0 0 | 38 70 0 0 100 0 0 0| 0 0 | 0 0 | 0 0 | 79 94
Ya podéis ver que el comando ‘dstat‘ se ha instalado sin problema y funciona correctamente; lo mismo podemos decir con el resto de programas indicados en el guión del Dockerfile.
Entrando en materia
El guión que hemos creado es bastante sencillo, así que vamos a profundizar un poco más. Vamos a añadir los siguientes conceptos para utilizar en los Dockerfile:
- MAINTAINER: Nos permite configurar datos del autor, principalmente su nombre y su dirección de correo electrónico.
- ENV: Configura las variables de entorno.
- ADD: Esta instrucción se encarga de copiar los ficheros y directorios desde una ubicación especificada y los agrega al sistema de ficheros del contenedor. Si se trata de añadir un fichero comprimido, al ejecutarse el guión lo descomprimirá de manera automática.
- COPY: Es la expresión recomendada para copiar ficheros, similar a ADD.
- EXPOSE: Indica los puertos en los que va a escuchar el contenedor. Hay que tener en cuenta que esta opción no consigue que los puertos sean accesibles desde el host; para esto debemos utilizar la exposición de puertos mediante la opción -p de
docker run
, tal y como explicamos en un artículo anterior. - VOLUME: Esta es una opción que muchos usuarios de la Web estaban esperando como agua de mayo. Nos permite utilizar en el contenedor una ubicación de nuestro host, y así, poder almacenar datos de manera permanente. Los volúmenes de los contenedores siempre son accesibles en el host anfitrión, en la ubicación:
/var/lib/docker/volumes/
- WORKDIR: El directorio por defecto donde ejecutaremos las acciones.
- USER: Por defecto, todas las acciones son realizadas por el usuario root. Aquí podemos indicar un usuario diferente.
- SHELL: En los contenedores, el punto de entrada es el comando
/bins/sh -c
para ejecutar los comandos específicos en CMD, o los comandos especificados en línea de comandos para la acción run. - ARG: Podemos añadir parámetros a nuestro Dockerfile para distintos propósitos.
Veamos una muestra. Vamos a crear un guión donde indicaremos los pasos para obtener una imagen de un servidor Tomcat. De esta manera veremos un ejemplo de la mayoría de los conceptos antes expuestos.
Cosas a tener en cuenta. Crearemos una imagen basada en Centos 7 sobre la que funcionará un servidor Tomcat. Además del Dockerfile, también necesitaremos algunos ficheros de configuración, que añadiremos al proyecto. Todos ellos estarán en la misma carpeta. Además, necesitamos tener la última versión de Tomcat, que nos descargaremos. A fecha de hoy la última versión es la 8.5.27. La podemos descargar desde aquí.
El contenido del fichero Dockerfile es el siguiente:
FROM centos:7 MAINTAINER davidochobits davidochobits@colaboratorio.net ENV container docker RUN yum -y update RUN yum -y install sudo \ tar \ gzip \ openssh-clients \ java-1.7.0-openjdk-devel \ vi \ find RUN groupadd tomcat RUN useradd -M -s /bin/nologin -g tomcat -d /opt/tomcat tomcat ADD apache-tomcat-8.5.27.tar.gz /opt/ RUN mv /opt/apache-tomcat-8.5.27 /opt/tomcat ADD tomcat-users.xml /opt/tomcat/conf ADD context.xml /opt/tomcat/webapps/manager/META-INF/context.xml ADD context.xml /opt/tomcat/webapps/host-manager/META-INF/context.xml RUN cd /opt/tomcat; \ chgrp -R tomcat /opt/tomcat; \ chmod -R g+r conf; \ chmod g+x conf; \ chown -R tomcat /opt/tomcat/webapps/; \ chown -R tomcat /opt/tomcat/work/; \ chown -R tomcat /opt/tomcat/temp/; \ chown -R tomcat /opt/tomcat/logs/ ENV JAVA_HOME /usr/lib/jvm/jre ENV CATALINA_PID /opt/tomcat/temp/tomcat.pid ENV CATALINA_HOME /opt/tomcat ENV CATALINA_BASE /opt/tomcat EXPOSE 8080 VOLUME "/opt/tomcat/webapps" WORKDIR /opt/tomcat #Lanzar Tomcat CMD ["/opt/tomcat/bin/catalina.sh", "run"]
Los otros dos ficheros son de configuración de Tomcat, que son llamados desde el guión del Dockerfile. El primero es un fichero XML, donde indicamos los roles de los usuarios. Se llama «tomcat-users-xml«. El otro es imprescindible añadirlo para poder acceder a él en la consola de administración. Su nombre es «context.xml» Ambos ficheros los he subido a GitHub junto con el resto del proyecto. Podéis consultar su contenido allí.
Debemos tener, en la misma carpeta:
[root@servdocker centostomcat]# ls -l total 9328 -rw-r--r-- 1 root root 9536557 ene 18 21:32 apache-tomcat-8.5.27.tar.gz -rw-r--r-- 1 root root 239 feb 8 15:19 context.xml -rw-r--r-- 1 root root 1312 feb 8 15:25 Dockerfile -rw-r--r-- 1 root root 398 feb 8 15:07 tomcat-users.xml
Para construir la imagen debemos ejecutar la siguiente orden, dentro de la carpeta del proyecto:
docker build -t "nombre:etiqueta" .
Una vez construida la imagen, ya podemos generar un contenedor:
docker run -dti --name "nombre-contenedor" -p 8080:8080 "nombre-imagen"
Si todo ha ido bien, en el navegador deberíamos ver una imagen similar a esta por el puerto 8080:
Lo dejamos por hoy, que el artículo ya me ha quedado muy extenso. En el próximo hablaremos en profundidad de los volúmenes, la forma que tiene Docker de hacer persistente la información de los contenedores. Estad atentos ^.^
Derechos de la imagen de portada by Flickr, con derechos de Dominio Público.
Hola. Gracias por la información, pero no me ha funcionado… 🙁
Cuando esta creando la imagen llega a un punto en el que me dice esto:
After this operation, 12.6 MB of additional disk space will be used.
Do you want to continue? [Y/n] Abort.
The command ‘/bin/sh -c apt-get update && apt-get install apache2’ returned a non-zero code: 1
y se vuelva a mi prompt.
Uso Docker sobre MacOS High Sierra.
Muchas gracias, ha funcionado… SOLO que necesitaba java 8 , y no podia desplegar el war por ese motivo… e intentado meter por bash a java 8, y creo que crujido porque ya no arranca el docker, jejeje. De todas maneras , para Java 7 ha funcionado perfectamente.
Hola David,
Lo he probado y me crea la imagen correctamente, realizo el run y se crea el contenedor pero, no está arrancado el servidor. Luego dentro del contenedor lanzo startup.sh y funciona perfecto.
Sabes por qué no arranca el servidor, no se ejecuta CMD [«/opt/tomcat/bin/catalina.sh», «run»]
Gracias.
Un saludo
Esta pesimamente explicado, de lo peor que he visto.
hola, tengo problema con un FROM tengo java 17.0.3.1 y la app es java 11 pero ningun amazoncorretto funciona….tienen alguna manera de saber cual es el indicado? Saludos Whor