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.

4.7 6 votos
Article Rating
Subscribe
Notificarme de
guest

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.

10 Comments
más antiguo
más nuevo más votado
Reacciones en línea
Ver todos los comentarios
dryant

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.

rafa

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.

Jose

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

Hector

Esta pesimamente explicado, de lo peor que he visto.

Whor

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