Docker es una herramienta que se ha posicionado en el corazón de los desarrolladores de Software, y ¿porqué no? Es una herramienta muy poderosa que empaqueta aplicaciones aisladas y autocontenidas en un objeto denominado Imagen. Las aplicaciones autocontenidas permiten administrar ambientes homogéneos, lo que facilita el despliegue de aplicaciones en contenedores hacia ambientes productivos. Escribir Dockerfiles aplicando buenas prácticas te permite obtener imágenes mas portables. 

Si deseas aprender todo sobre Docker, te invito a que te inscribas a mi curso Docker Desde cero. Este curso va dirigido a principiantes y, además de proveer los conceptos fundamentales, podrás desarrollar un proyecto junto a mí para poner en práctica lo aprendido. ¡Te espero!

Entrando a detalles técnicos, Docker es una herramienta que puede catalogarse como “Infraestructura como código” ya que podemos escribir en un archivo de definición el estado deseado de nuestra aplicación y todas sus dependencias, ofreciendo todas las ventajas de la infraestructura como código:

    • Versionamiento
    • Trazabilidad
    • Visibilidad
    • Mantenibilidad

El archivo de definición que utiliza Docker para construir imágenes se denomina Dockerfile, este archivo no tiene un formato o extensión definida, pero tiene la siguiente anatomía:

Anatomía de un dockerfile

Instrucciones más utilizadas 

En la imagen 1 puedes observar la estructura que tiene un Dockerfile, cada línea es una instrucción que se va ejecutando secuencialmente. Las instrucciones más utilizadas son:

  • ARG
    Define una variable que el usuario puede pasar en tiempo de ejecución. Es la única instrucción que puede presidir a la instrucción FROM.
  • FROM 
    Inicializa la construcción de una nueva imagen, tomando como base la imagen especificada.
  • RUN
    Esta instrucción ejecutará cualquier comando dentro del contenedor en construcción.
  • COPY
    Copia nuevos archivos o directorios y los agrega al sistema de archivos del contenedor.
  • ADD
    Esta instrucción copia nuevos archivos, directorios y archivos remotos desde URLs y los agrega al sistema de archivos del contenedor.
  • WORKDIR
    Configura el directorio de trabajo, donde se ejecutarán los comando especificados en la instrucción de CMD | RUN | ENTRYPOINT.
  • USER
    Configura el usuario con el que se ejecutarán las instrucciones seguidas.
  • EXPOSE
    Notifica a Docker que el contenedor escucha en el o los puertos específicos en tiempo de ejecución.
  • ENTRYPOINT
    Permite configurar un contenedor como ejecutable, normalmente con el proceso que queremos que exponga.
  • CMD
    La función principal de esta instrucción es proveer valores por defecto para un contenedor en ejecución.

Buenas prácticas

Te dejo algunos consejos para que puedas escribir Dockerfiles aplicando buenas practicas, verás como realizando pequeños cambios puedes obtener mejores resultados al momento de construir tus imágenes.

Validar la sintaxis

Un paso muy importante cuando escribimos líneas de código es validar la sintaxis. Entre los desarrolladores es muy común el uso de Linters, que permiten validar la sintaxis del código previo a la construcción y Docker no es la excepción. Encontrarás varias opciones de código abierto para ejecutar en la línea de comandos o plugins para hacer estas validaciones desde tu editor de texto favorito.

Optimizar el uso de las instrucciones

Con el comando  “docker run” construimos una imagen desde un Dockerfile. El objetivo es que estas imágenes sean lo más livianas posible. Además de FROM, las instrucciones ADD, COPY Y RUN agregan una nueva capa a la imagen resultante. Lo ideal es optimizar el uso de estas instrucciones para mantener la imagen lo más ligera posible. En la imagen 2 te muestro unos ejemplos óptimos y no óptimos de su implementación.

Buenas practicas para dockerfiles

Construir las imágenes en múltiples etapas.

Docker tiene una funcionalidad llamada multi-stage, lo cual también es útil para reducir el tamaño de las imágenes, ya que permite utilizar imágenes diferentes en cada etapa. Te recomiendo entonces tener una etapa de construcción con una imagen que contenga el SDK completo del lenguaje que estás utilizando para compilar tu aplicación, y en la etapa final utilizar una imagen que solo contenga el runtime para ejecutar la aplicación. Esto definitivamente hará que tu aplicación sea más ligera y fácil de portar.

Excluir archivos irrelevantes

Para excluir archivos que no sean relevantes para la compilación o ejecución de tu aplicación puedes agregar un archivo adicional: .dockerignore. En este archivo puedes incluir patrones de exclusión o rutas específicas de archivos, similar al archivo .gitignore. Esta práctica te permitirá excluir archivos cuando ejecutas la instrucción COPY y ADD.

Mantener las cosas simples

Para reducir la complejidad, dependencias y tiempos de compilación evita instalar paquetes adicionales o innecesarios. Incluso si solo utilizas un paquete para realizar cierta tarea en la etapa de construcción de imagen y luego ya no lo necesitarás, te recomiendo que lo desinstales.