Ya hemos visto algunas de las instrucciones disponibles para
utilizar en el fichero Dockerfile, como RUN y EXPOSE. Pero también hay una gran
variedad de instrucciones que podemos utilizar en nuestro Dockerfile. Estos incluyen CMD, ENTRYPOINT, ADD,
COPY, VOLUME, WORKDIR, USER, ONBUILD, LABEL, STOPSIGNAL, ARG y ENV.
Vamos
a ver la lista completa.
CMD
La instrucción CMD especifica el comando a ejecutar cuando
se inicia un contenedor. Es similar a la instrucción RUN, pero en lugar de
ejecutarse el comando cuando se está construyendo el contenedor, se
especificará el comando a ejecutar cuando se inicia el contenedor, al igual que
la especificación de un comando para ejecutar al iniciar un contenedor con el
comando “docker run”.
Por último, es importante entender que podemos anular la
instrucción CMD usando el comando “docker
run”. Si especificamos un CMD en nuestro Dockerfile y uno en la línea de
comando del comando “docker run”, la
línea de comandos anulará la instrucción CMD del Dockerfile.
ENTRYPOINT
En estrecha relación con la instrucción CMD, y que puede
crear confusión con el mismo, está la instrucción ENTRYPOINT. Entonces vamos a
ver cuál es la diferencia entre los dos y porque necesitamos a ambos para poder
anular la instrucción CMD en la línea de comando de “docker run”. A veces, esto
no es suficiente cuando queremos que un contenedor se comporte de una
determinada manera. La instrucción ENTRYPOINT, ofrece un comando que no se
anulará con la misma facilidad. Sin embargo, cualquier argumento que
especifique en la línea de comandos del comando “docker run” serán pasados como argumentos para el comando
especificado en el ENTRYPOINT o punto de entrada. Vamos a ver un ejemplo de una
instrucción ENTRYPOINT.
ENTRYPOINT [“/usr/sbin/nginx”]
Como con la instrucción CMD, también podemos especificar
parámetros añadiéndolos a la matriz. Por ejemplo:
ENTRYPOINT[“/usr/sbin/nginx”,”-g”,”daemon off;”]
En caso de requerirlo en tiempo de ejecución, nosotros
podríamos llegar a sobrescribir la instrucción ENTRYPOINT usando junto con el
comando “docker run” el parámetro
“--entrypoint”.
WORKDIR
La instrucción WORKDIR ofrece una manera de establecer el
directorio de trabajo para el contenedor y para la instrucción ENTRYPOINT y / o
CMD para que sea ejecutado cuando un contenedor se inicia desde la imagen.
Podemos utilizarlo para establecer el directorio de trabajo
para una serie de instrucciones o para el contenedor final. Por ejemplo, para
establecer el directorio de trabajo para una instrucción específica podría ser:
WORKDIR
/opt/webapp/db
RUN
bundle install
WORKDIR
/opt/webapp
ENTRYPOINT
[“rackup”]
Aquí hemos cambiado al directorio de trabajo /opt/webapp/db
y hemos ejecutado bundle install y
luego cambiamos el directorio de trabajo a /opt/webapp antes de especificar
nuestra instrucción ENTRYPOINT de rackup.
Podemos reemplazar el directorio de trabajo en tiempo de
ejecución con el parámetro -w, por ejemplo:
$ docker
run –ti –w /var/log ubuntu pwd /var/log
Esto configurará el
directorio de trabajo por defecto del contenedor a /var/log
ENV
La instrucción ENV es usada para configurar variables de
entorno durante el proceso de construcción de la imagen. Por ejemplo:
Estas nuevas variables de entorno, podrán ser usadas por
cualquier instrucción RUN posterior.
Podemos especificar varias variables de entorno con una sóla
instrucción ENV, como por ejemplo:
ENV
RAPP_PATH=/home/rapp RAPP_FLAGS=”-arch i386”
También podemos utilizar estas variables de entorno en otras
instrucciones:
ENV DESTINO_DIR /opt/app
WORKDIR $DESTINO_DIR
Hemos especificado una nueva variable de entorno,
DESTINO_DIR, y hemos usado su valor para definir el directorio de trabajo con
la instrucción WORKDIR, el cual quedará establecido como /opt/app.
Estas variables de entorno serán persistentes en los
contenedores creados desde la imagen. Pero si nosotros ejecutamos el comando ENV
dentro del contenedor construido alteraremos su valor.
También podemos pasar variables de entorno con el comando “docker run” utilizando el parámetro –e.
Estas variables sólo se utilizaran en tiempo de ejecución.
USER
La instrucción USER especifica un usuario en el que la imagen
se ejecutará, por ejemplo:
Esto hará que los contenedores creados a partir de la imagen
se ejecuten por el usuario nginx. Podemos especificar un nombre de usuario o un
UID y grupo o GID. O incluso una combinación de los mismos, por ejemplo:
USER user
USER
user:group
USER
uid
USER
uid:gid
USER
user:gid
USER
uid:group
También podemos sobrescribir ésto en tiempo de ejecución
especificando el parámetro –u con el comando “docker run”
VOLUME
La instrucción VOLUME añade volúmenes a cualquier contenedor
creado a partir de una imagen. Un volumen es un directorio especialmente
designado dentro de uno o más contenedores que no utiliza el sistema de
archivos del contenedor, aunque se integra en el mismo para proporcionar varias
funciones útiles para que los datos sean persistentes y se puedan compartir con
facilidad:
• Los volúmenes pueden ser compartidos y reutilizados entre
los contenedores.
• Un contenedor no tiene que estar en ejecución para
compartir sus volúmenes.
• Los cambios en un volumen se hacen directamente.
• Los cambios en un volumen no se incluirán al actualizar
una imagen.
• Los volúmenes persisten incluso cuando dejan de usarlo los
contenedores.
Esto nos permite añadir datos (como el código fuente), una
base de datos, o cualquier otro contenido en una imagen sin comprometer la
imagen y nos permite compartir los datos entre los contenedores. Esto se puede
utilizar para hacer pruebas con los contenedores y el código de una aplicación,
administrar registros, o manejar bases de datos dentro de un contenedor.
La instrucción VOLUME se puede utilizar de la siguiente
forma:
Esta instrucción crea un punto de montaje en “/opt/app” a
cualquier contenedor creado desde esta imagen.
También podemos especificar múltiples volúmenes
especificando un array:
VOLUME [“/opt/app”,”/datos”]
ADD
La instrucción ADD agrega archivos y directorios de nuestro
entorno de compilación en nuestra imagen; por ejemplo, al instalar una
aplicación. La instrucción ADD especifica un origen y un destino para los
archivos, así por ejemplo:
ADD
licencia.lic /opt/application/licencia.lic
Esta instrucción ADD copiará el archivo licencia.lic desde
el directorio de construcción de la imagen a “/opt/application/licencia.lic” dentro
de la propia imagen. El origen del archivo puede ser una URL, nombre de archivo
o directorio, siempre y cuando esté accesible dentro del entorno de construcción.
No se pueden añadir archivos desde fuera del directorio de construcción.
Cuando añadimos archivos con la instrucción ADD, Docker
utiliza el carácter final de la ruta del destino para determinar cuál es la
fuente. Si el destino termina en /, entonces se considera que la fuente es un
directorio. Si no termina en un /, considera que la fuente es un archivo.
La fuente del archivo también puede ser un URL; por ejemplo:
ADD
http://wordpress.org/latest.zip /root/wordpress.zip
Por último, la instrucción ADD tiene algo de especial para
el manejo de archivos tar locales. Si un archivo tar (tipos de archivo válidos
son gzip, bzip2, xz) se especifica como el archivo de origen, entonces Docker
automáticamente lo descomprimirá:
ADD
latest.tar.gz /var/www/wordpress/
Esto descomprimiría el archivo latest.tar.gz en el
directorio /var/www/wordpress/. El archivo es extraído con el mismo
comportamiento que ejecutar tar con la opción -x: la salida es la unión de todo
lo que existe en el destino, más el contenido del archivo. Si un archivo o
directorio con el mismo nombre ya existe en el destino, no se sobrescribirá.
Actualmente esto no funciona con un archivo tar especificado
en una dirección URL.
Por último, si el destino no existe, Docker creará la ruta
completa para nosotros, incluyendo cualquier directorio. Los nuevos archivos y
directorios se crearán con un modo de permisos 0755 y un UID y GID de 0.
También es importante tener en cuenta que la memoria caché
de compilación puede ser invalidada por las instrucciones ADD. Si los archivos
o directorios agregados por una instrucción ADD cambiaran, entonces esto va a
invalidar la caché para todas las siguientes instrucciones del Dockerfile.
COPY
La instrucción COPY está estrechamente relacionado con la
instrucción ADD. La diferencia clave es que la instrucción COPY está pensado
para la copia de archivos locales del entorno de construcción y no tiene
ninguna capacidad de extracción o de descompresión de archivos.
Ejemplo:
COPY conf.d/ /etc/apache2/
Esto copiará los archivos desde el directorio conf.d al
directorio /etc/apache2/.
El origen de los archivos debe ser la ruta a un archivo o
directorio en relación con el entorno de construcción, el directorio de origen
local en el que está localizado el fichero Dockerfile. No se puede copiar nada
que esté fuera de este directorio, porque el entorno de construcción se carga
en el demonio de Docker y la copia se lleva a cabo allí. El destino debe ser
una ruta absoluta dentro del contenedor.
Los archivos y directorios creados por la copia tendrá un
UID y GID de 0.
Si el origen es un directorio, todo el directorio se copia,
incluidos los metadatos del sistema de archivos; si la fuente es cualquier otro
tipo de archivo, se copia de forma individual junto con sus metadatos. En
nuestro ejemplo, el destino termina con una barra inclinada /, por lo que se
considera un directorio y copia en el directorio de destino.
Si el destino no existe, se crea junto con todos los
directorios que faltan en su camino, al igual que funciona el comando mkdir -p.
LABEL
La instrucción LABEL añade metadatos a una imagen Docker.
Los metadatos se presenta en forma de parejas clave/valor. Por ejemplo:
LABEL version= "1.0"
LABEL localizacion= “Murcia” tipo="CPD "
rol = "Servidor Web"
La instrucción LABEL se escribe en el formato etiqueta = "valor".
Se puede especificar un elemento de metadatos por instrucción LABEL o varios
elementos separados por espacios en blanco. Se recomienda combinar todos sus
metadatos en una sola instrucción LABEL para evitar la creación de múltiples
capas con cada etiqueta de metadatos. Podemos inspeccionar las etiquetas en una
imagen usando el comando “docker inspect”.
$ docker inspect jalapuente/apache
Y podemos ver los metadatos que acabamos de definir mediante
el uso de la instrucción LABEL.
STOPSIGNAL
La instrucción de instrucciones STOPSIGNAL establece la señal
de llamada al sistema que será enviado al contenedor cuando se le indica que se
detenga. Esta señal puede ser un número válido permitido por el kernel, por
ejemplo 9, o un nombre de señal en el SIGNAME formato, por ejemplo SIGKILL.
ARG
La instrucción ARG define variables que se pueden pasar en
tiempo de compilación a través del comando “docker
build”. Esto se hace utilizando el parámetro --build-arg. Sólo puede
especificar argumentos en tiempo de compilación que han sido definidos en el fichero
DOCKERFILE, aquí tenemos un ejemplo de uso:
ARG build
ARG webapp_user = usuario
La segunda instrucción ARG establece valor por defecto, si
no se especifica ningún valor para el argumento en tiempo de construcción, se
utiliza el valor predeterminado. Aquí tenemos un ejemplo de uso de estos argumentos en en la ejecución
del comando “docker build”.
$ docker
build -- build-arg constru=1234 -t jalapuente/webapp .
Cuando se construye la imagen jalapuente/webapp la variable constru
se establecerá en 1234 y la variable webapp_user heredará el valor
predeterminado de usuario.
Docker tiene un conjunto de variables ARG predefinidos que
se pueden utilizar en tiempo de compilación, sin que exista una instrucción ARG
correspondiente en el Dockerfile.
HTTP_PROXY
http_proxy
HTTPS_PROXY
https_proxy
FTP_PROXY ftp_proxy
NO_PROXY no_proxy
Para utilizar estas variables predefinidas, pasarlas con el parámetro
“--build-arg <variable> = <valor>” al comando docker build.
ONBUILD
La instrucción ONBUILD añade disparadores (también conocidos
como triggers) a las imágenes. Un disparador se ejecuta cuando se utiliza la
imagen como base de otra imagen (por ejemplo, si tenemos una imagen que
necesita agregar código fuente desde una ubicación específica que podría no
estar disponible aún, o si necesitamos ejecutar un script que es específico
para el entorno en el que se construye la imagen).
El disparador inserta una nueva instrucción en el proceso de
construcción, como si se especifica inmediatamente después de la instrucción
FROM. El desencadenante puede ser cualquier instrucción de construcción.
Por ejemplo:
ONBUILD
ADD ./app/src
ONBUILD
RUN cd /app/src && make
Esto añadiría un disparador ONBUILD a la imagen que se está
creando, lo que podemos ver ejecutando el comando docker inspect sobre la
imagen.
$
docker inspect imagen
… “OnBuild”: [“ADD ./app/src”, “RUN cd /app/src
/ && make”] …
Por ejemplo, vamos a construir un nuevo Dockerfile de una
imagen Apache2 que llamaremos jalapuente/apache2.
FROM
Ubuntu:14.04
MAINTAINER
Javier Hernandez “jalapuente@example.com”
RUN
apt-get update && apt-get install –y apache2
ENV
APACHE_RUN_USER www-data
ENV
APACHE_RUN_GROUP www-data
ENV APACHE_LOG_DIR /var/log/apache2
ONBUILD
ADD . /var/www/
EXPOSE
80
ENTRYPOINT
[“/usr/sbin/apache2”]
CMD
[“D”, “FOREGROUND”]
Ahora vamos a construir esta imagen.
$ docker
build –t=”jalapuente/apache2”.
Ahora tenemos una imagen con una instrucción ONBUILD que
utiliza la instrucción ADD para agregar el contenido del directorio que estamos
construyendo desde el directorio /var/www/ a nuestra imagen. Esto podría ser
fácilmente nuestra plantilla de aplicación web genérica para la construcción de
aplicaciones web.
Vamos a utilizar esto mediante la construcción de una nueva
imagen llamada webapp con el siguiente fichero Dockerfile:
FROM
jalapuente/apache2
ENV
APPLICATION_NAME webapp
ENV
ENVIRONMENT development
Si nosotros ahora ejecutáramos el comando para construir
esta imagen:
$ docker
build -t=”jalapuente/webapp” .
entre los
pasos veríamos:
# Executing
1 build triggers Step onbuild-0: ADD . /var/www/
Se puede ver que inmediatamente después de la instrucción
FROM, Docker ha insertado la instrucción ADD, especificado por el disparador
ONBUILD y, a continuación, se procedió a ejecutar los pasos restantes. Esto permitiría
añadir siempre la fuente local y especificar alguna configuración o construir
información para cada aplicación; por lo tanto, esto se convierte en una de imagen
plantilla bastante útil.
Los disparadores ONBUILD se ejecutan en el orden
especificado en la imagen padre y sólo se heredan de una vez (es decir, por los
niños y no los nietos). Si construimos otra imagen de esta nueva imagen, un
nieto de la imagen jalapuente/apache2, los disparadores no serían ejecutados
cuando se construye esa nueva imagen.
Existen varias instrucciones que no se pueden utilizar en ONBUILD,
entre ellas FROM, MAINTAINER, y ONBUILD a si mismo. Esto se hace para evitar la
recursividad en la compilación del fichero Dockerfile.