12. Integración en entornos de desarrollo

Docker no solo es una herramienta para producción; ha transformado la forma en que los desarrolladores configuran sus entornos de trabajo. Usar contenedores para el desarrollo soluciona el problema de "en mi máquina funciona" desde la raíz, garantizando que el entorno de desarrollo sea idéntico al de producción.

Contenedores como entornos de desarrollo

Imagina que te unes a un nuevo proyecto. En lugar de pasar horas instalando la versión correcta de Node.js, Python, una base de datos específica y un sinfín de dependencias en tu máquina, simplemente clonas el repositorio, ejecutas `docker compose up` y tienes todo el entorno funcionando en minutos.

Ventajas de este enfoque:

  • Consistencia: Todos los miembros del equipo trabajan con exactamente las mismas versiones de software.
  • Aislamiento: No "contaminas" tu sistema local con dependencias de múltiples proyectos. Puedes trabajar en proyectos con versiones de Python o Node.js conflictivas sin problemas.
  • Portabilidad: Tu entorno de desarrollo está definido en código (Dockerfile y docker-compose.yml) y es fácilmente portable a cualquier máquina que tenga Docker.

Desarrollo en vivo (Hot-Reloading) con Volúmenes

Una preocupación común al pasar a un entorno de desarrollo en contenedores es perder la fluidez del desarrollo local, donde guardas un archivo y ves el cambio al instante. Afortunadamente, esto se soluciona de manera elegante con los bind mounts.

Al montar el código fuente de tu proyecto desde tu máquina local al interior del contenedor, cualquier cambio que hagas en tu IDE se reflejará inmediatamente dentro del contenedor. Si además usas una herramienta que vigila los cambios en los archivos (como `nodemon` para Node.js o el modo debug de Flask), el servidor dentro del contenedor se reiniciará automáticamente.

Ejemplo: Aplicación Node.js con Nodemon

Veamos cómo configurar un entorno de desarrollo para una aplicación Express.js con recarga en vivo.

Archivo `package.json`:

{
  "name": "mi-app-node",
  "version": "1.0.0",
  "main": "server.js",
  "scripts": {
    "start": "node server.js",
    "dev": "nodemon server.js"
  },
  "dependencies": {
    "express": "^4.17.1"
  },
  "devDependencies": {
    "nodemon": "^2.0.15"
  }
}

Archivo `docker-compose.yml`:

version: '3.8'
services:
  app:
    image: node:16-alpine
    container_name: node_dev_app
    working_dir: /usr/src/app
    command: npm run dev  # Ejecuta el script de nodemon
    ports:
      - "3000:3000"
    volumes:
      - ./:/usr/src/app  # Monta el directorio actual en el workdir del contenedor
      - /usr/src/app/node_modules # Evita que node_modules local sobreescriba el del contenedor

Análisis de la configuración de `volumes`:

  1. ./:/usr/src/app: Este es el bind mount clave. Mapea todo el directorio del proyecto local a `/usr/src/app` dentro del contenedor. Cuando editas `server.js` en tu máquina, el archivo dentro del contenedor se actualiza al instante.
  2. /usr/src/app/node_modules: Este es un "truco" importante. Se define un volumen anónimo para `node_modules`. Esto evita que la carpeta `node_modules` de tu host (que podría no existir o tener binarios para otro SO) sobreescriba la carpeta `node_modules` que se crea dentro del contenedor con `npm install`.

Con esta configuración, al ejecutar `docker compose up`, `nodemon` se iniciará dentro del contenedor y vigilará los cambios en los archivos montados, reiniciando el servidor automáticamente cada vez que guardes un cambio.

Integración con VSCode y Dev Containers

La experiencia de desarrollo en contenedores se vuelve aún más poderosa con la extensión Dev Containers para Visual Studio Code.

Esta extensión te permite usar un contenedor como un entorno de desarrollo completo y totalmente funcional. Al abrir un proyecto en un "Dev Container", VSCode se conecta al contenedor y te permite editar código, usar la terminal, depurar y usar otras extensiones como si estuvieras trabajando localmente, pero en realidad todo se está ejecutando dentro del contenedor.

Para configurarlo, se añade una carpeta `.devcontainer` al proyecto con un archivo `devcontainer.json` que describe cómo construir y configurar el entorno. VSCode se encarga de todo lo demás.

// .devcontainer/devcontainer.json
{
  "name": "Node.js Dev Container",
  "dockerComposeFile": "../docker-compose.yml",
  "service": "app",
  "workspaceFolder": "/usr/src/app",
  "extensions": [
    "dbaeumer.vscode-eslint"
  ]
}

Cuando abres el proyecto, VSCode detecta este archivo y te pregunta si quieres "Reabrir en Contenedor". Al hacerlo, obtienes una experiencia de desarrollo nativa, pero con la consistencia y el aislamiento de Docker.