Guia Docker Parte I

En esta guía tutorial, vamos a introducirnos en el mundo de Docker y contenedores.
La guía se encuentra dividida en tres partes y hoy compartimos con ustedes esta primera entrega.

Parte Uno

Antes de comenzar con el armado de Dockerfiles, vamos a hacer una breve descripción de los elementos básicos de Docker como también de algunos conceptos que creemos necesarios para entender su funcionamiento.

¿Qué es Docker? ¿Para qué sirve?

Docker es un “generador de Máquinas Virtuales”, permite correr aplicaciones en entornos reducidos y auto-contenidos. Está basado en un sistema de virtualización de Linux.
Es el empaquetador “universal” de aplicaciones ya que puede crear réplicas auto-contenidas de aplicaciones, e incluso máquinas completas, capaces de ser utilizadas en otros entornos corriendo Docker.

¿Por qué es bueno entenderlo?

Docker permite replicar aplicaciones en “casi” cualquier entorno, sin estar atado a librerías o herramientas instaladas en el sistema, solamente se necesita Docker.
Esto lleva a que la instalación de aplicaciones pase a estar definida en archivos de Configuración, los cuáles son sólamente secuencias de instrucciones. Mejor aún nos deja la posibilidad de versionarlos de una manera descriptiva.
Además, facilita la automatización y creación de aplicaciones o stacks completos, lo cuál es muy importante para la implementación de la Integración Contínua (CI) y despliegue contínuo de aplicaciones(CD). [Referencia]

¿Qué cosas hay que tener en cuenta?

Es importante tener un conocimiento general de instalación de aplicaciones y herramientas de sistemas como también manejo de comandos básicos de la terminal Linux, ya que Docker se basa en muchos de estos comandos para definir las instrucciones al empaquetar aplicaciones.
También ayuda tener un conocimiento básico de infraestructura de sistemas. En particular, manejo de puertos(Port Forwarding), manejo de red (Networking, resolución de IPs en Redes Privadas /etc/hosts, localhost vs domain.com, dominios o dns)*, servicios o procesos comúnes de sistemas (SSH 22, WEB 80, POSTGRES 5432), manejo de directorios (File Systems) y algunas cosas más que mencionaremos durante este tutorial.

Términos generales de Docker

  • Docker Hub, antes de comenzar con los conceptos teóricos, tenemos que saber que Docker funciona de manera similar a Git, es decir permite crear repositorios y comitear a nivel local, imágenes en el caso de Docker. Pero además existe un servidor público Github o Gitlab (Registry Global) en donde finalmente se comparten estos repositorios. Docker Hub es el servidor público y oficial de Imágenes de Docker, y nos permite pushear y pullear las Imágenes creadas. [Ver Dockerhub]
  • Dockerfiles, son los archivos donde se definen las secuencias de instrucciones para instalar una aplicación, estos pasos se ejecutan dentro del contexto de armado (ver abajo) de la máquina host. Un Dockerfile define el empaquetado de aplicaciones el cuál genera finalmente una Imágen Docker. [Ver docker build]
  • Imágenes, son las aplicaciones empaquetadas generadas a partir de un Dockerfile, están listas para ser ejecutadas y representan al Blueprint de la versión específica de un Dockerfile (Similar a un tag de Github). Una imágen tiene todo lo necesario para correr en cualquier máquina con Docker, una vez iniciada pasa a ser un proceso dentro del sistema.
    [Ver docker image lsdocker run image]
  • Contenedores, son las aplicaciones o procesos corriendo dentro del sistema. Los cuales fueron creados a partir de Imágenes que corren en sus propios entornos auto-contenidos (“Máquinas Virtuales” o Sandbox) dentro de la Máquina Host, lo cuál permite modificarlas en tiempo de ejecución. El contenido de un contenedor persiste mientras el proceso sigue vivo(no eliminado explícitamente) de la Máquina Host.
    [Ver docker psdocker exec container_id command]
  • Volúmenes, en algunos casos se necesita persistir cambios por fuera del ciclo de vida de los contenedores, para eso existen los volúmenes.
    Un volúmen es un espacio en disco de la Máquina Host y puede ser utilizado por uno o más contenedores.
    Además, permite mapear archivos existentes de la Máquina Host adentro del sistema de archivos de un contenedor, es muy útil para ambientes de desarrollo. También se suele utilizar para persistir los archivos generados por las bases de datos, los cuáles tienen que vivir fuera del ciclo de vida del mismo proceso.
    [Ver docker volume lsdocker volume create]
  • Redes, si vemos a los contenedores como pequeñas Máquinas Virtuales entonces necesitamos poder configurar el ruteo (Networking) de cada una ellas para conectarlas entre sí. De manera que, puedan resolver tanto los puertos como las IPs dentro de la red interna de la Máquina Host donde correrán los contenedores.
    Por esta razón, se puede configurar el tipo o driver de red a utilizar, los más conocidos son: bridge, permite la configuración de fragmentos de red a los que debe rutear Docker dentro de la Máquina Host; host, quita la capa de red entre un contenedor y la Máquina Host, es decir usan la misma interfáz de red; existen otros no muy conocidos.
    [Ver docker network ls]

Términos generales de Dockerfiles

Hasta el momento, sabemos que un Dockerfile es un archivo que contiene una serie de instrucciones y sirve para generar la Imágen de una aplicación.
Antes de continuar con el listado de instrucciones, existen dos conceptos importantes: Contexto de Armado y Cache de Capas.

  • Contexto de Armado, el directorio donde se encuentra el archivo Dockerfile pasa a ser el contexto de armado actual al momento de generar una imágen, lo cuál permite el uso de la carpeta actual y sus subdirectorios.
    [Ver Build Context Docker]
  • Cache de Capas, cada una de las instrucciones definidas dentro de un Dockerfile son cacheadas por defecto, aún más el cacheo de cada instrucción depende de las instrucciones anteriores (Similar a como funcionan los commits en git). Esto impacta en el orden en el cuál es conveniente definir cada instrucción, ya que Docker no necesita aplicar las instrucciones que ya fueron cacheadas. Si detecta algún cambio se necesitaría correr alguna o todas las instrucciones siguientes. Para esta implementación Docker genera un tipo de dato temporal conocido como capa o layer (mediante los comandos RUNCOPYADD) y también imágenes intermedias para las lineas de las instrucciones dentro del Dockerfile. Una fuerte recomendación es separar el Dockerfile en diferentes secciones, comenzando con instalaciones generales de la Máquina Virtual(apt-install), instalación de dependencias de la aplicación(npm install), generación del empaquetado(npm build) y finalmente inicio de la aplicación con su comando de ejecución(npm run start).
  • Comandos Dockerfile, para la construcción de una aplicación, se pueden utilizar las siguientes instrucciones dentro del archivo Dockerfile: FROM, WORKDIR, ADD, COPY, RUN, ENTRYPOINT, CMD, VOLUME, ENV, ARG, EXPOSEReferencia
    • FROM, permite configurar la Imágen Base a utilizar dentro del Dockerfile, por lo cuál su argumento principal es una Imágen Docker válida que será pulleada al momento de buildearlo. Tiene otro uso, conocido como Multi Stage Build, el cuál explicaremos en otro momento. Ejemplo,
      • FROM node:12.20
    • WORKDIR, permite modificar la posición del directorio actual al momento de armar la Imágen. Su uso se puede ver como la combinación de cd y mkdir. Es más fácil de visualizar dentro de un Dockerfile. Ejemplo,
      • WORKDIR /usr/src/app/
    • ADD y COPY, ambos tienen el mismo fin, permitir insertar nuevos archivos o carpetas dentro de la Imágen a buildear. Siguen un patrón similar a los comandos cp y scp de Linux, COMMAND <src> <dest>. Siendo dest el sistema de archivos de la Imágen y src los archivos y carpetas del Contexto de Armado. En el caso de ADD también es posible usar URLs apuntando a archivos o archivos comprimidos siendo descomprimidos al momento de insertarse. Por último, las carpetas no existentes en la ruta de inserción son creadas automáticamente dentro del sistema de archivos de la Imágen(Muy útil). Algunos ejemplos,
      • ADD package.json /usr/src/app
      • COPY package.json /usr/src/app
    • RUN, permite ejecutar comandos al momento de buildear la Imágen, creando una nueva layer. Se recomienda agrupar múltiples comandos en un único RUN aprovechando el cacheo de Docker, para esto se pueden agregar múltiples lineas separadas por “\”. Por ejemplo, RUN npm install && \ npm build Por último, este comando tiene dos posibles formas, modo comando(shell) o modo ejecutable(brackets), el primero se muestra en el ejemplo y funciona como argumento al comando /bin/sh -c, es decir es enviado por defecto a la shell. El segundo en cambio tiene que ser un comando ejecutable completo, por ejemplo, RUN ["/bin/sh", "-c", "npm install"].
    • ENTRYPOINT y CMD, ambos tienen un fin similar, ejecutar comandos al momento de instanciar una Imágen(un Contenedor). Un Dockerfile requiere tener definido al menos un Entrypoint o CMD, y pueden ser utilizados de manera combinada(no es muy común). Generalmente suelen utilizarse para ejecutar el comando para iniciar el proceso de la aplicación, por ejemplo npm run start. Ambas instrucciones pueden utilizar los modos comando y ejecutable. Algunos ejemplos, ENTRYPOINT ["entrypoint.sh"]CMD ["node", "app.js"]. Para una definición más precisa pueden ver esta Guia.
    • VOLUME, permite generar volúmenes para una Imágen, funciona de la siguiente manera, se le pasa como argumento la carpeta src dentro del sistema de archivos de la Imágen que persistirá en la Máquina Host y estará asociada al Contenedor una vez instanciado. No es muy común su uso, desaconsejado en realidad, ya que en un mundo ideal una Imágen Docker debería ser autocontenida y reproducible pero un volúmen siempre está asociado al contenido dentro de la Máquina Host por lo tanto puede haber una dependencia y conflicto en posteriores instanciaciones. Ejemplo de uso, VOLUME /usr/src/app/data
    • ARG, permite definir las variables a utilizar como argumentos al momento de buildear la Imágen. Se pueden configurar valores por defecto. Por ejemplo, ARG NODEJS_VER=12.20-alpine FROM node:$NODEJS_VER Luego ejecutar, $ docker build --build-arg NODEJS_VER=12.20 ., para modificar la versión de NODEJS, o $ docker build ., para usar la versión por defecto.
    • ENV, permite definir las variables de ambiente que pueden ser utilizadas dentro del Contenedor instanciado. Existen dos maneras de definirlas, clave=valor o clave valor. Por ejemplo, ENV PORT=3000 o ENV PORT 3000, el primero permite definir múltiples variables separadas por “\”.
    • EXPOSE, permite exponer los puertos a utilizar dentro de la Imágen. No es requerimiento definir cada puerto a utilizar sino que sirve como documentación de los mismos. Por ejemplo, EXPOSE 3000.

Puede parecer muy extenso pero no es necesario acordarse todos los comandos y definiciones, solamente es un gran vistazo del mundo de Docker. En la siguiente guía utilizaremos los comandos comunes explicados.

Con esto terminamos la Parte Uno de Docker, continúara…

Referencias

Deja una respuesta