Guía para Desarrolladores Principiantes en Docker

Video thumbnail

Esta es una formación básica para aprender a usar Docker, por lo tanto, SOLAMENTE tratamos los elementos básicos y necesarios para que puedas empezar ha realizar administrar Docker y hacer el deploy de tus primeros proyectos; en esta página tienes la documentación completa:

https://docs.docker.com/get-started/

Conceptos Claves

Como en todo en la vida, primero debemos tener claros algunos conceptos antes de pasar a la práctica. En los frameworks usualmente hablamos del patrón MVC, MTV, entre otros.
Y aquí ocurre lo mismo: necesitamos entender cómo está formado este programa llamado Docker. ¿Qué demonios es esto? Porque créeme que cuando pasemos a la práctica, todo se entenderá mucho más fácilmente.

Similitudes con Python, los ambientes virtuales, Node y PHP

El uso de contenedores, imágenes y Docker es muy similar a los ambientes virtuales en Python.

  • Python usa Pipenv, Conda, o simplemente venv.
  • Laravel y Node instalan dependencias dentro del proyecto (vendor/, node_modules/).

Pero Python instala por defecto todo a nivel del sistema operativo, lo cual es un problema.

Ejemplo práctico:

Tienes un proyecto hecho en Django hace 3 años (versión Django 4), y hoy necesitas crear otro proyecto pero con Django 7.
Si instalaras todo directo en el sistema operativo, choque asegurado.

Y no solo pasa con Python:

  • No puedes tener 3 PHP diferentes activos en el mismo sistema.
  • Tampoco puedes mezclar dependencias de forma desordenada sin aislar cada proyecto.
  • Por eso, en Python creamos un ambiente virtual por proyecto, lo cual encapsula y aísla sus dependencias. Y es justo ahí donde aparece el paralelismo con Docker.

¿Dónde se guardan las dependencias?

En Laravel tenemos vendor/, en Node tenemos node_modules/.

En Python, el equivalente es una carpeta llamada usualmente .venv o .pvenv, donde se guardan solo las dependencias de ese proyecto.

Eso no ocurre automáticamente: tú debes instalar y activar el ambiente virtual, igual que con Docker, que también debes instalar y configurar manualmente.

Con esto, puedes entender para que podemos usar Docker, como un ambiente en donde podemos ejecutar de manera aislada nuestros proyectos y las similitudes con imágenes y contenedores, en las cuales, las imágenes son simplemente las dependencias, como ocurre con dependencias como Django o Laravel, que son simplemente archivos estáticos y contenedores, que es cuando ya se estan ejecutando o interpretan las imágenes.

Docker

Docker es una plataforma que permite “empaquetar” aplicaciones con todas sus dependencias (librerías, configuración, etc.) para que puedan ejecutarse de forma consistente en cualquier entorno (tu máquina, servidores, nube).

Puedes ver Docker como una especia de ambiente virtual, como los que tenemos en Python mediante los venv, en el cual, podemos instalar paquetes (imágenes sería el equivalente en Docker) que son las dependencias de nuestro proyecto; la ventaja de esto, es que, se ejecutan de manera aislada del sistema operativo; con esto, podemos tener múltiples dependencias con versiones distintas para proyectos distintos instalados en el mismo sistema operativo pero, virtualizado mediante los contenedores en Docker; por ejemplo, podemos tener múltiples versiones de Python instaladas para distintos proyectos.

Otro ejemplo, es como la carpetas vendor de PHP o de node_modules de Node, en la cual, tenemos las dependencias (imágenes sería el equivalente en Docker) para cada proyecto; no es exactamente esto, pero, con esta idea te será más fácil entender como funciona Docker

Docker no es solo una herramienta de desarrollo y se usa ampliamente en producción, y Railway es un ejemplo muy claro de ello.

Imagen (Image)

Una imagen de Docker es como una “plantilla” inmutable, es decir, de solo lectura que contiene todo lo necesario para ejecutar una aplicación: sistema de archivos, dependencias, código, variables de entorno, etc; con esto, se crean los contenedores en Docker que es el siguiente punto que vamos a tratar.

  • Las imágenes están formadas por capas (layers) que representan cambios incrementalmente aplicados unas sobre otras.
  • Una vez creada, no se modifica: si quieres cambiar algo, creas una nueva imagen o haces “build” sobre una base existente. 

Imagina que una imagen de Docker es como un archivo .exe en tu computadora.
El .exe contiene todo el código y los recursos necesarios para ejecutar un programa (bibliotecas, dependencias, configuraciones...), pero mientras no lo ejecutes, el programa no está corriendo ni ocupa recursos.

Del mismo modo, una imagen de Docker es un paquete estático que incluye el sistema base, el entorno, las dependencias y la aplicación lista para funcionar. Pero no hace nada por sí sola hasta que creas un contenedor a partir de ella —que sería el equivalente a ejecutar el .exe.

Otra analogía es con las clases, una clase definida:

class Category(models.Model):
   title = models.CharField(max_length=500)
   slug = models.SlugField(max_length=500)

Es como una imagen, por si, no hacemos nada con ella, pero es cuando la ejecutamos (un contenedor) es decir, creamos una instancia, es que se emplea dicha clase/imagen.
 

Una imagen es como una plantilla inmutable, de solo lectura.
Es una referencia, un entorno con su sistema base y dependencias incluidas.

Ejemplos reales de imágenes disponibles:

  • python
  • ubuntu
  • nginx
  • mysql
  • postgres

Contenedor (Container)

Un contenedor es la instancia en ejecución de una imagen. Piensa en la imagen como el plano o molde, y el contenedor como la casa construida a partir de ese plano.
Una vez que lanzas un contenedor, este funciona como un proceso aislado, con su propio sistema de archivos (basado en la imagen), entorno, red, etc. 
Los contenedores comparten el kernel del sistema operativo host, lo que los hace más ligeros que máquinas virtuales completas.

Al ser un proceso, puedes ejecutarlo, crearlo, detenerlo, moverlo o eliminar el proceso; para eso, se emplea comandos como docker create container, pull, cet:

 $ docker run -i -t ubuntu /bin/bash

Imagina que un contenedor de Docker es como ejecutar un programa .exe en tu computadora.

Mientras la imagen de Docker es el archivo guardado en tu disco (listo pero inactivo), el contenedor es ese mismo programa ya en ejecución: con su ventana abierta, sus procesos corriendo y su propio entorno funcionando de forma independiente.

Cuando cierras el programa, el proceso termina, pero el .exe sigue allí intacto.
De igual forma, cuando detienes o eliminas un contenedor, la imagen original sigue disponible para crear otro contenedor nuevo en cualquier momento.

Los contenedores en Docker:

  • Autocontenidos: cada contenedor incluye todo lo que necesita para funcionar, sin depender de programas o librerías instaladas en la máquina donde se ejecuta.
  • Aislados: los contenedores se ejecutan de forma independiente del sistema y de otros contenedores, lo que mejora la seguridad y evita conflictos.
  • Independientes: puedes crear, detener o eliminar un contenedor sin afectar a los demás. Cada uno se gestiona por separado.
  • Portátiles: un contenedor puede ejecutarse en cualquier lugar. El mismo que usas en tu computadora funcionará igual en un servidor o en la nube.

Otros conceptos funcionales:

Docker Daemon (dockerd)

El Docker Daemon es el proceso que realmente ejecuta y administra todo en Docker.
Se encarga de:

  • Crear y ejecutar contenedores.
  • Descargar o construir imágenes.
  • Manejar redes, volúmenes y otros recursos de Docker.
  • Comunicarse con otros daemons (por ejemplo, en entornos distribuidos o en clústeres).

En resumen:

Es el “motor” de Docker, el que hace el trabajo pesado.

Normalmente corre en segundo plano como un servicio del sistema y es el que recibe los comandos recibidos por el cliente que es el siguiente apartado que vamos a comentar.

Docker Client (docker)

El cliente de Docker es la herramienta con la que tú interactúas — por ejemplo, cuando escribes comandos como:

docker run -d -p 8080:80 nginx

Este comando no ejecuta el contenedor directamente.

El cliente simplemente envía una solicitud al daemon (usando la Docker API) diciéndole qué hacer, y el daemon es quien lo ejecuta realmente.

Además, un mismo cliente puede comunicarse con múltiples daemons (por ejemplo, un Docker local y otro remoto en la nube).

 

| Componente             | Qué hace                                                 | Ejemplo                                                   |
| ---------------------- | -------------------------------------------------------- | --------------------------------------------------------- |
| **Cliente (`docker`)** | Recibe tus comandos.                                     | `docker build`, `docker run`, `docker ps`                 |
| **Daemon (`dockerd`)** | Ejecuta las órdenes del cliente y gestiona los recursos. | Crea imágenes, inicia contenedores, configura redes, etc. |

En esta imagen, tomada desde la web de Docker, puedes ver claramente la distinción entre el cliente Docker Client (docker) (los comandos) y el demonio de Docker (dockerd) que es el que recibe los comandos y hace los cambios a nivel de nuestras imágenes y contenedores, que SON LA PIEZA FUNDAMENTAL Y BÁSICA EN DOCKER:


 

Instalación

La instalación de Docker es muy sencilla. Para empezar, busca en Google “Docker Install”. Entre los resultados, selecciona la página oficial de Docker (Docker Start).

Una vez cargada la página, se mostrará la opción de descarga según tu sistema operativo: macOS, Windows, Linux, macOS con Intel, etc. Simplemente descárgalo e instálalo siguiendo los pasos habituales: Next, Next, Next.

Qué estamos instalando

Al instalar Docker, se instalan dos componentes principales:

  1. Interfaz gráfica: te permite administrar contenedores y configuraciones de manera visual.
  2. CLI (Command Line Interface): la línea de comandos que te permite ejecutar comandos de Docker desde la terminal. Aunque el nombre suene técnico, es muy sencillo de usar.

Verificación de la instalación

Una vez finalizada la instalación, abre Docker y verás la interfaz gráfica cargando:

Docker UI

Para verificar la instalación de la CLI, abre cualquier terminal y escribe:

docker

Si todo está correcto, se mostrará la lista de comandos disponibles, confirmando que ambos componentes están instalados y listos para usar:

Usage:  docker [OPTIONS] COMMAND A self-sufficient runtime for containers Common Commands:   run         Create and run a new container from an image   exec        Execute a command in a running container   ps          List containers   build       Build an image from a Dockerfile   bake        Build from a file   pull        Download an image from a registry   push        Upload an image to a registry   images      List images   login       Authenticate to a registry   logout      Log out from a registry   search      Search Docker Hub for images   version     Show the Docker version information   info        Display system-wide information

Comandos imprescindibles

En este listado, puedes ver algunas acciones que podemos realizar el Docker, como realizarlo mediante la CLI y su equivalente en la UI:

  • docker images: Ver imágenes: docker images (Pestaña Images)
  • docker run <ID / nombre>: Crear contenedor: docker run (Botón “Run” sobre una imagen)
  • docker ps -a: Ver contenedores: docker ps -a (Pestaña Containers)
  • docker logs <Container ID>: Ver logs: docker logs <id> (Sección “Logs”)
  • docker stop <ID / nombre> / docker rm <ID / nombre>: Detener/eliminar: docker stop/docker rm (Botones Stop / Delete)
  • docker rmi <ID / nombre>  elimina una imagen
  • docker exec -it  bash   Entrar dentro de un contenedor ACTIVO en modo interactivo (habilita el bash para lanzar comandos)
    • docker run -it <Container ID>(Ej ubuntu)  Crea el contenedor y lo deja en modo interactivo (habilita el bash para lanzar comandos)

Un comando imprescindible es el siguiente:

docker run -d -p 8080:80 nginx
  • docker run → Crea y arranca un nuevo contenedor.
  • -d → Lo ejecuta en modo “detached” (en segundo plano, no se queda "pegado" en la terminal).
  • -p 8080:80 → Expone el puerto 80 del contenedor en el puerto 8080 de tu PC.
  • nginx → Usa la imagen oficial de Nginx desde Docker Hub.

Funcionamiento básico

Del comando anterior, quitando los aspectos técnicos como el detached, o la configuración de los puertos, tenemos dos aspectos fundamentales en Docker que es la imagen y el contenedor:

  • Las imágenes descargadas (como nginx)
  • Los contenedores que crea a partir de esas imágenes

Ver imágenes

En términos funcionales pasa lo siguiente:

  • Descarga la imagen nginx desde Docker Hub (si no la tienes ya, si quieres ver las imágenes, recuerda docker images).
  • Crea un contenedor en su propio entorno interno y NO en una carpeta en particular.
  • Expone el puerto 80 del contenedor en el puerto 8080 de tu host (tu Mac), gracias a la red virtual que Docker configura.
$ docker images  >> REPOSITORY   TAG       IMAGE ID       CREATED       SIZE >> nginx        latest    3b7732505933   12 days ago   255MB  $ docker run 3b7732505933     O $ docker run nginx      No le estás diciendo ni que se quede activo, ni qué puertos exponer, así que parece “que no pasó nada”. En realidad sí se ejecutó, pero se cerró inmediatamente o quedó corriendo sin exponer nada.

En el caso anterior, NO tiene sentido ejecutar la imagen de nginex ya que, la misma para poder levantar el proceso satifactoriamente, se debe de especificar las opciones del puerto, como hicimos antes:

$ docker run -d -p 8080:80 nginx

O mediante redis:

$ docker run -d redis

Redis si podría trabajar sin exponerle el puerto ya que, por defecto el queda escuchando en el puerto 6379 y por lo tanto, para la implementación anterior, solo sería accesible desde otros contenedores Docker, es decir, de manera interna y no desde el PC perse (fuera de Docker).

Ver Contenedores

Si quisiéramos ver los contenedores de Docker (desde la UI lógicamente la pestaña de Contenedores):

Inicialmente, vamos a ejecutarlo sin el parámetro -a. Para este ejemplo, seguramente en tus pruebas anteriores también tendrás algo. Yo tengo un par de contenedores de las pruebas que hicimos con docker run, ya que, recordemos, cuando ejecutamos docker run con parámetros o sin parámetros, siempre se crea algún contenedor. Entonces, yo tengo al menos dos con los cuales trabajar

Muestra SOLO los contenedores que están corriendo/ejecutandose:

$ docker ps  >> CONTAINER ID   IMAGE          COMMAND                  CREATED      STATUS                     PORTS     NAMES >> e678f12698bf   nginx          "/docker-entrypoint.…"   3 days ago   Exited (0) 5 minutes ago             peaceful_mclaren >> c880881a55f8   3b7732505933   "/docker-entrypoint.…"   3 days ago   Exited (0) 3 days ago                modest_northcutt

Fíjate que nos devuelve información sobre el contenedor o los contenedores. Aquí aparece algo interesante, que se apoya en lo comentado antes: los contenedores son la pieza de ejecución de las imágenes.

Si no tienes nada iniciado, no verás absolutamente nada. Pero, ¿qué pasa si agregamos el parámetro -a?

Muestra TODOS los contenedores:

$ docker ps -a

En ambos casos:

>> CONTAINER ID   IMAGE     COMMAND   CREATED   STATUS    PORTS     NAMES

Ahora aparecen los contenedores que tenemos. La opción -a devuelve todos los contenedores, sin importar si están iniciados o no. Por otro lado, docker ps sin parámetros muestra únicamente los contenedores que se están ejecutando.

Ver las imágenes:

$ docker images  >>> REPOSITORY   TAG       IMAGE ID       CREATED       SIZE >>> nginx        latest    3b7732505933   12 days ago   255MB

En resumen:

  • docker ps -a → todos los contenedores (activos o inactivos).
  • docker ps → solo los contenedores activos.

¿Por qué se llama ps y no containers?

Seguramente te preguntas: “Si para las imágenes usamos docker image, ¿por qué aquí no usamos docker containers?”

Esto es una tradición de Linux: ps significa process status, es decir, el estado del proceso. Un contenedor es, esencialmente, un proceso iniciado.

El término containers es más moderno y se incorporó alrededor de 2017, pero el comando ps se mantiene como legacy, una convención histórica de Docker.

Con estos conceptos, podemos entender mejor los fundamentos de Docker: las imágenes como base y los contenedores como piezas clave para ejecutar esos procesos.

Eliminar imágenes

Al igual que siempre, podemos pasarle el ID o el nombre de la imagen que queremos eliminar.
Por ejemplo, si tenemos una imagen llamada nginx, podemos escribir:

$ docker rmi nginx 
  • rmi significa remove image

¿Qué significa RMI?

RMI significa Remove Image, tal como antes teníamos con Docker RM, pero esta vez la I indica que se trata de una imagen. Con esto, ya queda bastante claro su propósito.

Detener contenedores

Docker Stop, como pueden suponer, lo que hace es detener un contenedor. A mí me gustaría que se llamara docker container stop, que sería un poco más claro, pero bueno, así es como está implementado. En la siguiente clase veremos el comando equivalente para eliminar un contenedor.

Para detener un contenedor, simplemente debemos pasarle una de dos cosas:

  1. El identificador del contenedor (ID).
  2. El nombre del contenedor.

Fíjate que los parámetros, como el nombre, se pueden personalizar, pero eso lo veremos más adelante.

Identificando contenedores

Recordemos que anteriormente usamos docker ps, que nos devuelve información sobre los contenedores en ejecución. Allí podemos ver tanto el ID como el nombre del contenedor, lo que nos permite copiarlos y pegarlos fácilmente para usar con docker stop.

Deteniendo contenedores

Para detener un contenedor, este debe estar iniciado.

Usando el nombre:

$ docker stop nombre_del_contenedor

Resultado: se detuvo correctamente.

Usando el ID:

$ docker stop ID_del_contenedor

Eliminar contenedores

Para eliminar un contenedor es similar al de detener y el contenedor debe estar detenido, ejecutamos:

Usando el nombre:

$ docker rm nombre_del_contenedor

O usando el ID:

$ docker rm ID_del_contenedor

Logs contenedores

Permite ver los logs de un contenedor:

$ docker logs -f --tail 20 -t contenedor
  • --tail = cuántas líneas quieres ver
    • --tail 20 últimas 20 líneas
  • -t = (timestamp) agrega la hora a cada línea
  • -f = (follow) queda "escuchando", y te muestra todo lo nuevo que vaya saliendo en vivo, sin cerrar (el contenedor debe de estar ejecutándose)

Modo Interactivo bash

El siguiente comando que quiero comentar es Docker Exec, que nos permite ejecutar comandos dentro de un contenedor.

Consideraciones previas

Es importante entender que el contenedor debe estar habilitado y en ejecución. Dependiendo de la imagen, ejecutar comandos puede ser extremadamente útil:

  • Si tenemos una imagen como Ubuntu, podemos interactuar directamente con el sistema operativo.
  • Si es un Apache o Nginx, probablemente no sea posible ejecutar comandos de manera interactiva.
    • A menos que quieras modificar archivos: docker exec -it <apache-container> tail -f /var/log/apache2/error.log
  • En el caso de bases de datos, podemos ejecutar comandos SQL directamente.

En resumen, todo depende del propósito de la imagen.

El modo interactivo es simplemente habilitar una terminal en la cual ejecutar comandos; para ello, se emplean los siguiente modos:

  • -i (interactive) Deja escribir comandos en la terminal
  • -t (tty (terminal)) Te da una terminal “real”

Ejemplos:

$ docker run -it ubuntu  $ docker exec -it a1b2c3d4e5 bash # a1b2c3d4e5 EJ de ID de un contenedor ejecutandose

Dependiendo de la imagen, puede que si tenga sentido ejecutar comandos; por ejemplo, un shell interactivo de Ubuntu:

$ docker run -it ubuntu

Un poco más elaborado:

$ docker run -d --name mi-ubuntu ubuntu sleep 1000
  • -d → lo ejecuta en segundo plano
  • --name mi-ubuntu → le da un nombre para no usar el ID
  • ubuntu → la imagen base

Entrar al contenedor con bash:

$ docker exec -it a1b2c3d4e5 bash $ docker exec -it mi-ubuntu bash

Ahora estás dentro del contenedor.

Puedes ejecutar comandos como en cualquier Linux:

$ ls $ pwd $ apt update

Diferencia con Docker Run -it

  • Docker Run -it: inicia un contenedor y automáticamente abre una terminal interactiva.
  • Docker Exec -it: ingresa en un contenedor que ya está activo, permitiéndonos ejecutar comandos.

Para qué sirve sleep 1000

Cuando haces:

docker run -d --name mi-ubuntu ubuntu sleep 1000

sleep 1000 es el comando que corre dentro del contenedor.

Mantiene el contenedor activo por 1000 segundos (unos 16 minutos).

Como usamos -d (detached / en segundo plano), el contenedor necesita un proceso que siga corriendo, si no, Docker lo detendrá inmediatamente.

¿Qué pasa si no pones sleep?

docker run -d --name mi-ubuntu ubuntu

Ubuntu no tiene un proceso por defecto que se quede corriendo.

Entonces el contenedor arranca y se detiene al instante, porque no hay nada que mantener activo.

Si luego haces docker ps, no lo verás en la lista de contenedores activos, solo en docker ps -a como Exited.

✅ Por eso usamos sleep para mantenerlo vivo y poder entrar con docker exec -it <id> bash.

Construir Imágenes Personalizadas: Dockerfile

Recordemos que una imagen como un paquete único que contiene todo lo necesario para ejecutar un proceso Y PODEMOS CREAR LOS PROPIOS, en nuestro caso, serían nuestros proyecto en PHP, Node Python, etc; las imágenes personalizadas por nosotros contendrá un entorno Node/Python/PHP, el código de la misma. Para crear nuestras propias imágenes, debemos de crear un archivo especial en nuestro proyecto llamado Dockerfile.

Básicamente, estas son las piezas fundamentales sobre las cuales podemos crear nuestros contenedores para ejecutar aplicaciones. Por ahora, todo puede parecer un poco estático: instalamos imágenes de sistemas operativos o servidores, pero… ¿cómo interactuamos con ellas?

Construyendo Imágenes Personalizadas

Para crear nuestras propias imágenes usamos los famosos archivos llamados Dockerfile. Con estos archivos podemos definir cómo se construye nuestra imagen.

Recuerda: una imagen puede ser cualquier cosa: un sistema operativo, un lenguaje de programación, un servidor… o incluso un proyecto propio. En este caso, vamos a crear una imagen de nuestro proyecto en Flash.

Para construir una imagen personalizada:

  • Creamos un Dockerfile en la raíz de nuestro proyecto.
  • Definimos las reglas necesarias para que la imagen funcione correctamente.
  • Indicamos, si es necesario, qué otras imágenes son requeridas para que nuestra imagen tenga sentido. Por ejemplo, nuestro proyecto en Flash necesita la imagen de Python para funcionar.

Dockerfile

Un Dockerfile es simplemente un archivo de texto que contiene todas las instrucciones necesarias para construir una imagen de Docker. la estructura que tendrá, depende del proyecto y lo que quieras hacer, pero, usualmente cuenta con estos pasos fundamentales:

El Dockerfile es la semilla, la pieza fundamental que nos permite crear nuestras propias imágenes. Por eso, encontrarás muchísimas páginas y ejemplos, porque su funcionamiento es sencillo de comprender: basta con partir de un proyecto base, que puede ser prácticamente cualquier cosa.

1. Crea el Archivo de Instrucciones

En la raíz de tu proyecto, crea un archivo llamado exactamente Dockerfile (con la D mayúscula y sin ninguna extensión).

  • Este archivo se crea en la raíz del proyecto, sea PHP, Laravel, CodeIgniter, Flash, FastAPI, Django, Node… lo que sea.
  • A partir de allí, definimos un conjunto de reglas. La estructura cambia dependiendo del proyecto, pero en la mayoría de los casos sigue un patrón similar.

2. Define la Imagen Base (FROM)

Especifica el punto de partida de tu imagen usando la instrucción FROM. Esto determina el sistema operativo y el entorno inicial. Por ejemplo, para un proyecto Node.js podrías usar:

FROM node:lts-alpine
  • En nuestro ejemplo, usamos Python para Flash, o Node para proyectos en Node.
  • Si tu proyecto necesita, por ejemplo, SQL, también puedes indicar la imagen de MySQL.

3. Establece el Directorio de Trabajo (WORKDIR)

Con la directiva WORKDIR, defines la carpeta dentro del contenedor donde se ejecutarán todos los comandos posteriores y donde se copiarán tus archivos. Por ejemplo:

WORKDIR /app

Este es el lugar donde se instalarán las dependencias, se copiará el proyecto, se ejecutarán comandos y se expondrán puertos.

Por convención, se usa /app, pero puedes nombrarlo como quieras.

4. Copia tus Archivos Locales (COPY)

Utiliza la instrucción COPY para transferir los archivos de tu máquina local al interior de la imagen. Para llevar todos los contenidos del directorio actual (el contexto) al directorio de trabajo del contenedor, harías:

COPY . .
  • Generalmente copiamos todo el contenido de nuestro proyecto al directorio de trabajo.

5. Ejecuta Comandos de Configuración (RUN)

La instrucción RUN ejecuta comandos durante el proceso de construcción de la imagen, ideal para instalar dependencias o configurar el entorno. Por ejemplo, para instalar dependencias de Node.js:

RUN yarn install --production

6. Especifica el Comando de Inicio (CMD)

CMD indica cuál es el comando predeterminado que se ejecutará cuando se inicie un contenedor a partir de esta imagen. Para lanzar una aplicación Node.js:

CMD ["node", "src/index.js"]
  • Para proyectos de servidor, esto suele incluir instalar dependencias y arrancar la aplicación.
  • En Python, por ejemplo, python app.py inicia la aplicación.
  • En Node, usaríamos npm install y luego npm start.

7. Informa sobre los Puertos (EXPOSE)

Si tu aplicación está configurada para escuchar en un puerto específico (ej. 3000), usa EXPOSE. Esto le notifica a Docker qué puertos estarán abiertos para la comunicación en tiempo de ejecución.

EXPOSE 3000

Ejemplo Completo de Dockerfile

Combinando todas estas directivas, un Dockerfile para una aplicación Node.js se vería así:

Dockerfile

# syntax=docker/dockerfile:1 FROM node:lts-alpine WORKDIR /app COPY . . RUN yarn install --production CMD ["node", "src/index.js"] EXPOSE 3000

En resumen, el Dockerfile define cómo se construye tu imagen, desde la base hasta los comandos de ejecución y puertos. Cada proyecto requiere adaptaciones específicas, pero la estructura general sigue los pasos que hemos descrito:

  • Imagen base (FROM)
  • Directorio de trabajo (WORKDIR)
  • Copiar archivos del proyecto (COPY)
  • Exponer puertos (EXPOSE)
  • Ejecutar comandos (RUN o CMD)
  • En la siguiente clase veremos cómo elaborar este Dockerfile paso a paso para nuestro proyecto y cómo construir la imagen lista para ejecutarla en un contenedor.

Comando docker build

El comando docker build construye una imagen de Docker (nuestra imagen personalizado) a partir de las instrucciones contenidas en un Dockerfile y un contexto especificado.

App en Flask

Pensando en la estructura anterior, veamos como podemos crear un Dockerfile, para crear una imagen personalizada de un proyecto en Flask:

https://github.com/libredesarrollo/01-jan-chat

Servidor de desarrollo

 

# Usa una imagen base de Python oficial, por ejemplo la versión 3.12 pero mas pequena que la completa que es simplemente python FROM python:3.12-slim  # Establece el directorio de trabajo dentro del contenedor, puede ser cualquier otro pero, por convension es app WORKDIR /app  # Copia el archivo de requisitos e instala las dependencias # --no-cache-dir desactivar el almacenamiento en caché de los paquetes descargados e instalados. COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt  # Copia TODO el contenido del proyecto (incluyendo app.py) al directorio de trabajo COPY . .  # Expone el puerto en el que corre Flask (por defecto 5050) # if __name__ == '__main__': #    app.run(debug=True, host="0.0.0.0", port=5050) EXPOSE 5050  # Comando para correr la aplicación # Usa `python app.py` si modificaste app.run(host='0.0.0.0') # o usa un comando más robusto si instalaste Gunicorn: # CMD ["gunicorn", "--bind", "0.0.0.0:5000", "app:app"] CMD ["python", "app.py"]

Ya con esto, tenemos la estructura para crear la imagen, falta construir la imagen personalizado de nuestra aplicación, para ello:

$ docker build -t app-flask-chat-01 .
  • -t, Se utiliza la bandera -t (tag), la imagen resultante recibe un nombre y una etiqueta
  • el punto al final (.) significa copiar TODO el proyecto desde el directorio actual que es el que tiene:
    __pycache__             chat_routes.py          llm_service.py          templates
     app.py                  Dockerfile              requirements.txt        test.py

Y para crear el contenedor de nuestra imagen personalizada:

$ docker run -d -p 5050:5050 --name flask-app-contenedor app-flask-chat-01

En cualquier comento, puedes ver el log del contenedor:

$ docker logs flask-app-contenedor 

Ideal por si el proyecto tiene errores, y deberías de ver algo como esto:

 * Running on all addresses (0.0.0.0)  * Running on http://127.0.0.1:5050  * Running on http://172.17.0.2:5050 Press CTRL+C to quit  * Restarting with stat  * Debugger is active!  * Debugger PIN: 148-371-654

Con guicorn

xxx

Agregamos requirements.txt:

pip install gunicorn

Y

pip freeze > requirements.txt

El Dockerfile:

CMD ["gunicorn", "--workers", "4", "--bind", "0.0.0.0:5050", "app:app"] gunicorn -b 0.0.0.0:$PORT run:app

Luego reconstruyes la imagen Docker:

docker build -t flask-app-gunicorn .

 

Acepto recibir anuncios de interes sobre este Blog.

Guía básica para iniciar con Docker, empezar a crear tus primeros contenedores, incluso si nunca has usado Docker antes. Verás cómo funciona internamente y empezar a usar Docker en tus proyectos.

Algunas recomendaciones:

Benjamin Huizar Barajas

Laravel Legacy - Ya había tomado este curso pero era cuando estaba la versión 7 u 8. Ahora con la ac...

Andrés Rolán Torres

Laravel Legacy - Cumple de sobras con su propósito. Se nota el grandísimo esfuerzo puesto en este cu...

Cristian Semeria Cortes

Laravel Legacy - El curso la verdad esta muy bueno, por error compre este cuando ya estaba la versi...

Bryan Montes

Laravel Legacy - Hasta el momento el profesor es muy claro en cuanto al proceso de enseñanza y se pu...

José Nephtali Frías Cortés

Fllask 3 - Hasta el momento, están muy claras las expectativas del curso

| 👤 Andrés Cruz

Por aquí tienes el listado completo de clases que vamos a cubrir en el libro y curso:

Primeros pasos

Imágenes