Primeros Pasos con Docker para Desarrolladores Web
Docker simplifica el desarrollo al empaquetar tu aplicación y sus dependencias en contenedores portátiles que se ejecutan de manera consistente en cualquier entorno. Esta guía práctica cubre los conceptos básicos, comandos esenciales y flujos de trabajo del mundo real que todo desarrollador web necesita conocer.
Autor
Lucas Dev
Categoría
DevOps
Tiempo de Lectura
02 Mins read
Fecha de Publicación
12 Oct, 2022
Contenido de la página
Compartir
Suscríbete al boletín
“En mi máquina funciona” es una de las frases más frustrantes en desarrollo web, y es exactamente el problema que Docker resuelve: empaquetar la aplicación junto con todo lo que necesita para correr, de forma idéntica en cualquier entorno.
Qué es realmente un contenedor
Un contenedor no es una máquina virtual completa: comparte el kernel del sistema operativo host, lo que lo hace mucho más liviano y rápido de iniciar. Lo que sí incluye es todo lo que la aplicación necesita para ejecutarse —runtime, dependencias, variables de entorno, archivos del sistema— empaquetado como una unidad portátil que se comporta igual en la laptop de un desarrollador, en un servidor de staging o en producción.
El Dockerfile: la receta de tu aplicación
Un Dockerfile describe paso a paso cómo construir la imagen de tu aplicación:
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --production
COPY . .
EXPOSE 3000
CMD ["node", "server.js"]
Cada línea agrega una capa a la imagen final. Ordenar las instrucciones de menos a más cambiantes (dependencias antes que código fuente) permite que Docker reutilice capas en caché y acelere builds sucesivos considerablemente.
Docker Compose para entornos con múltiples servicios
La mayoría de las aplicaciones reales no son un solo contenedor: necesitan una base de datos, quizás un caché en Redis y el servicio de la aplicación en sí. Docker Compose permite definir todos estos servicios en un solo archivo YAML y levantarlos juntos con un comando, replicando de forma fiel la arquitectura de producción en el entorno local de desarrollo.
Buenas prácticas que evitan dolores de cabeza
Usar imágenes base alpine reduce drásticamente el tamaño final de la imagen. Un archivo .dockerignore evita copiar node_modules o archivos de configuración local innecesarios dentro del contenedor. Y nunca guardar secretos (API keys, contraseñas de base de datos) directamente en el Dockerfile, sino inyectarlos como variables de entorno en tiempo de ejecución.
Docker no es solo una herramienta de despliegue: adoptarlo desde el desarrollo local elimina una categoría entera de bugs relacionados con diferencias de entorno, y facilita que cualquier nuevo integrante del equipo tenga el proyecto corriendo en minutos.
Blogs Relacionados
Arquitectura Serverless: Cuándo y Cómo Usarla
"Serverless" es un nombre engañoso: los servidores siguen existiendo, simplemente dejan de ser tu responsabilidad. El proveedor cloud se encarga de aprovisionar, escalar y mantener la infraestructura; tu código solo se ejecuta cuando hay una petición que atender. El modelo de ejecución por evento Una función serverless permanece inactiva —y sin costo— hasta que un evento la dispara: una petición HTTP, un mensaje en una cola, un archivo subido a un bucket de almacenamiento. Al terminar de procesar ese evento, el entorno de ejecución puede destruirse. Esto obliga a diseñar funciones sin estado: cualquier dato que necesite persistir entre invocaciones debe vivir en una base de datos o almacenamiento externo, nunca en memoria local. Cuándo serverless es la elección correcta Serverless funciona especialmente bien para cargas de trabajo esporádicas o impredecibles: procesamiento de webhooks, tareas programadas, transformación de imágenes al subirlas, endpoints de API con tráfico variable. Pagar solo por el tiempo de ejecución real, sin mantener un servidor corriendo 24/7 esperando peticiones, resulta significativamente más económico para este tipo de cargas. Cuándo no lo es Para cargas de trabajo constantes y predecibles, un servidor tradicional (o contenedores con auto-scaling) suele ser más económico y predecible que serverless. El "cold start" —la latencia extra que ocurre cuando una función se invoca después de estar inactiva— también puede ser un problema real para aplicaciones donde cada milisegundo de latencia importa, como APIs de alta frecuencia. Errores comunes en producción // Evitar: conexión a base de datos creada en cada invocación export async function handler(event) { const db = await connectToDatabase(); // se repite en cada llamada ... }Reutilizar conexiones fuera del handler (aprovechando que el entorno de ejecución puede reutilizarse entre invocaciones cercanas en el tiempo) evita agotar el límite de conexiones de la base de datos bajo carga. Otro error frecuente es no definir límites de timeout y memoria ajustados a la carga real, lo que genera facturas inesperadas o funciones que fallan silenciosamente bajo picos de tráfico. Serverless no es una arquitectura universal, es una herramienta más: la pregunta correcta no es si adoptarla, sino en qué partes específicas del sistema su modelo de costos y escalado automático realmente aportan una ventaja sobre la infraestructura tradicional.
Lucas Dev
DevOps Engineer
18 May, 2023
Dominando los Flujos de Trabajo de Git para la Colaboración en Equipo
Git es sencillo de usar en solitario y sorprendentemente fácil de usar mal en equipo. La mayoría de los conflictos y confusiones no vienen de Git en sí, sino de la ausencia de un flujo de trabajo acordado por todos. Branching por funcionalidad, no por persona Cada rama debe representar una unidad de trabajo con propósito claro —una funcionalidad, un fix, un experimento— y no simplemente "la rama de Juan". Nombrar ramas de forma consistente (feature/checkout-paypal, fix/header-mobile) hace que cualquiera del equipo entienda de un vistazo qué contiene sin tener que preguntar. Pull requests como punto de control, no como trámite Un buen pull request incluye una descripción clara de qué cambia y por qué, no solo un enlace al ticket. Mantenerlos pequeños y enfocados en un solo propósito acelera la revisión y reduce la probabilidad de introducir bugs que pasan desapercibidos en un diff de miles de líneas. Rebase vs. merge: elegir según el historial que quieres merge preserva el historial exacto de cómo ocurrieron los cambios, incluyendo commits intermedios; rebase reescribe el historial para que parezca lineal, más limpio de leer pero que oculta el orden real de trabajo. Muchos equipos usan rebase para ramas de feature antes de integrarlas a main, y merge commits para preservar el punto exacto en que cada feature se integró. git checkout feature/checkout-paypal git rebase main git push --force-with-lease--force-with-lease es más seguro que --force porque falla si alguien más subió cambios a esa rama remota mientras tanto, evitando sobrescribir trabajo ajeno sin darse cuenta. Versionado semántico y tags Etiquetar releases con versionado semántico (v1.4.0) en lugar de fechas o nombres arbitrarios permite comunicar automáticamente el impacto de un cambio: mayor para cambios incompatibles, menor para funcionalidades nuevas, parche para correcciones. Esto es especialmente valioso cuando otros equipos o servicios dependen de tu código como librería. Un flujo de trabajo de Git bien definido no elimina los conflictos por completo, pero sí reduce drásticamente su frecuencia y hace que, cuando ocurren, sean fáciles de resolver porque todo el equipo entiende las mismas reglas.
Lucas Dev
Senior Developer
15 Jan, 2023