Docker

Dockerfile: Creando imágenes Docker

El día de hoy veremos cómo crear una imagen de una aplicación para que pueda ser lanzada en un contenedor usando Docker. Vamos a utilizar la aplicación Ampache[1] para este propósito, que es un servidor de streaming escrito en PHP, que nos permite tener algo así como nuestra implementación de Spotify privada. El proyecto Ampache ya posee un repositorio[2] el cual contiene un Dockerfile para la creación de la imagen; voy a partir de éste para la creación de la nueva imagen pabloxco/ampache que aunque será similar al original, contará con una importante diferencia que comentaré líneas más adelante. He creado un repositorio en Github[3] que complementa la presente entrada. Docker, puede crear imágenes con base en un archivo de texto plano llamado Dockerfile que contiene todas las instrucciones necesarias para la creación de ésta a través del comando docker build. Lo primero que debemos tener en cuenta, es el contexto de creación de la imagen, por ejemplo si ejecutamos:

$ docker build -t pabloxco/ampache .

Estamos indicando que el Dockerfile se encuentra en el directorio de trabajo, por lo tanto, el cliente Docker enviará al Docker daemon el contenido completo del directorio actual, y por tanto, debemos tener especial cuidado de la ubicación del archivo Dockerfile en el proyecto así como asegurar que en la creación de la imagen no se vayan archivos con información sensible. Para ésto último, se cuenta con un archivo llamado .dockerignore el cual cumple la misma función que tenemos en Git con su archivo .gitignore. A continuación el archivo Dockerfile propuesto:

FROM ubuntu:14.04
MAINTAINER Juan Jaramillo <juan@pablox.io>

RUN apt-get update
RUN apt-get -y upgrade
RUN DEBIAN_FRONTEND=noninteractive apt-get -y install wget inotify-tools

RUN echo 'deb http://download.videolan.org/pub/debian/stable/ /' >> /etc/apt/sources.list
RUN echo 'deb http://archive.ubuntu.com/ubuntu trusty main multiverse' >> /etc/apt/sources.list
RUN wget -O - https://download.videolan.org/pub/debian/videolan-apt.asc | sudo apt-key add -

RUN apt-get update
RUN DEBIAN_FRONTEND=noninteractive apt-get -y install apache2 wget php5 php5-gd php5-json php5-curl php5-mysqlnd pwgen lame libvorbis-dev vorbis-tools flac libmp3lame-dev libavcodec-extra* libfaac-dev libtheora-dev libvpx-dev libav-tools git

# Install composer for dependency management
RUN php -r "readfile('https://getcomposer.org/installer');" | php && mv composer.phar /usr/local/bin/composer

# Add last stable Ampache
ADD https://github.com/ampache/ampache/archive/master.tar.gz /opt/master.tar.gz

# extraction / installation
RUN tar -C /var/www -xf /opt/master.tar.gz ampache-master --strip=1
RUN chown -R www-data:nogroup /var/www
USER www-data
RUN cd /var/www && composer install --prefer-source --no-interaction

# setup apache with default ampache vhost
USER root
ADD 001-ampache.conf /etc/apache2/sites-available/
RUN rm -rf /etc/apache2/sites-enabled/*
RUN ln -s /etc/apache2/sites-available/001-ampache.conf /etc/apache2/sites-enabled/
RUN a2enmod rewrite

# Add PHP Settings to avoids Ampache warnings
RUN sed -i -e 's,upload_max_filesize = 2M,upload_max_filesize = 20M,g' /etc/php5/apache2/php.ini
RUN sed -i -e 's,post_max_size = 8M,post_max_size = 20M,g' /etc/php5/apache2/php.ini

# Add job to cron to clean the library every night
RUN echo '30 7 * * * www-data php /usr/src/ampache/bin/catalog_update.inc' >> /etc/crontab

VOLUME ["/media"]
VOLUME ["/var/www/config"]
VOLUME ["/var/www/themes"]

EXPOSE 80

COPY entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh

ENTRYPOINT ["/entrypoint.sh"]

CMD exec /usr/sbin/apachectl -D FOREGROUND

En el anterior archivo, propongo una aproximación diferente a la oficial ya que al interior de la imagen pabloxco/ampache no realizo ninguna instalación del motor de base de datos MySQL, siendo un requisito para poder instanciar un contenedor de la imagen, proveer un servicio de MySQL con la variable de entorno MYSQL_PORT_3306_TCP_ADDR. Se puede realizar mediante un link a un contenedor de base da datos tal cual lo hacen otras imágenes oficiales como WordPress[4], Drupal[5] o Redmine[6]. Lo anterior es para seguir buenas prácticas al usar Docker de mantener un servicio por contenedor. La directivas del Dockerfile no son case-sensitive, pero la convención más aceptada es escribirlas en mayúsculas. A continuación, explico las usadas en el archivo anterior:

  • FROM → Todo Dockerfile debe tener la directiva FROM como su primera instrucción, en caso contrario el build fallará. Indica la imagen base sobre la que se construirá la imagen de la aplicación. La elección de la imagen base depende de nuestra experiencia en diferentes ambientes GNU/Linux o de la necesidades mismas de la aplicación.
  • MAINTAINER → Indica el creador de la imagen. Información que será utilizada si publicamos la imagen en algún registro como Docker Hub.
  • RUN → Ejecutamos comandos necesarios para la instalación de software/librerías requeridas por la aplicación para su correcta ejecución. Los comandos indicados con la directiva RUN solamente se ejecutan al momento de construcción de la imagen (docker build) y varían dependiendo de la imagen base a utilizar indicada en la directiva FROM. Ya que se ha usado Ubuntu como imagen base, podemos ejecutar todo tipo de comandos disponibles en este tipo de ambientes. Por defecto, RUN utilizará el interprete /bin/sh, si queremos usar BASH sería de la siguiente manera:
$ RUN ["/bin/bash", "-c", "apt update”]
  • COPY → Añadimos archivos o carpetas a la imagen. Solamente se pueden referenciar archivos bajo el contexto inicialmente enviado al docker daemon. Se recomienda usar rutas absolutas en el parámetro destino.
  • ADD → Es similar con a la directiva COPY, pero adicionalmente soporta URL y archivos TAR.
  • EXPOSE → Expone el puerto bajo el cual escucha la aplicación, en este caso el puerto 80/TCP del servicio Apache.
  • CMD → Solamente se establece un CMD y se ejecuta en el momento de instanciación del contenedor, por lo que generalmente aquí viene la ejecución de la aplicación en modo FOREGROUND.
  • ENTRYPOINT → Es similar a CMD, con la diferencia que si se definen ambos, CMD tomará el rol de parámetros por defecto que se enviarán a ENTRYPOINT. Al principio la interacción de ENTRYPOINT y CMD puede llevar a equívocos, aunque en la documentación[7] oficial lo explican en mayor detalle.
  • VOLUME → Crea un directorio en el sistema de archivos de la imagen que pueden ser usados para montar volúmenes ( opción -v del cliente Docker ) generando persistencia de la información.
  • USER → Indicamos el cambio de usuario que ejecutará los siguientes comandos en las directivas RUN, CMD y ENTRYPOINT.

El mayor trabajo que se realiza en la construcción de una imagen Docker, es llevado a cabo por las instrucciones RUN al interior del Dockerfile, que serían la automatización del proceso de instalación de cualquier aplicación, en este caso, la automatización del proceso de instalación de Ampache en un servidor Ubuntu. Ya que es un proceso automatizado, tenemos que asegurarnos que los comandos lanzados en cada una de las directivas RUN no requieran ninguna interacción (non-interactive) por parte del usuario porque de lo contrario el build fallará. Sobre cómo lograr lo anterior es muy relativo y depende de la naturaleza del comando que estemos usando. En el caso del comando apt-get se hace uso de flag -y para lograr dicho comportamiento al igual que la opción –no-interaction cuando se llama a composer, pero dependerá de cada comando usado. Aunque no fueron utilizadas, a continuación listo algunas directivas que son importantes tener en cuenta:

  • ENV → Establece variables de entorno para las siguientes directivas RUN, CMD y ENTRYPOINT. También se mantienen en tiempo de ejecución del contenedor.
  • WORKDIR → Cambio el directorio de trabajo de / a la ruta especificada, que será creada en caso de no existir.

Para un lista completa de la directivas soportadas en el Dockerfile, siempre podemos visitar la documentación oficial[8]. En el archivo README.md del repositorio Github[9] que extiende la presente entrada, se puede apreciar la puesta en marcha de la nueva imagen.