Guía para Principiantes sobre Imágenes y Contenedores de Docker
Acabas de unirte a un equipo de frontend y todos hablan sobre “levantar contenedores” y “descargar imágenes”. Tu aplicación de React funciona perfectamente en tu máquina, pero desplegarla parece un misterio. Docker resuelve exactamente este problema—y entender los fundamentos de las imágenes de Docker es más simple de lo que piensas.
Esta guía cubre qué son realmente las imágenes y contenedores de Docker, cómo se relacionan entre sí, y cómo usarlos en tu flujo de trabajo de frontend para desarrollo local, pruebas y despliegues simples.
Puntos Clave
- Las imágenes de Docker son plantillas de solo lectura, mientras que los contenedores son instancias en ejecución de esas imágenes.
- Usa etiquetas explícitas como
node:20-alpineen lugar delatestpara builds predecibles y reproducibles. - Los builds multi-etapa mantienen tus imágenes de producción pequeñas y seguras.
- Nunca almacenes datos persistentes dentro de contenedores—usa volúmenes en su lugar.
- Ejecuta contenedores como usuarios no-root y escanea las imágenes regularmente en busca de vulnerabilidades.
¿Qué Son las Imágenes y Contenedores de Docker?
Una imagen de Docker es un paquete de solo lectura que contiene todo lo necesario para ejecutar una aplicación: código, runtime, librerías y configuración. Piensa en ella como una clase en programación orientada a objetos—un blueprint que define la estructura pero no ejecuta nada por sí misma.
Un contenedor es una instancia en ejecución de esa imagen. Cuando ejecutas docker run, Docker crea un entorno aislado a partir de la imagen donde tu aplicación realmente se ejecuta. Puedes levantar múltiples contenedores desde la misma imagen, cada uno operando de forma independiente.
Esta distinción entre imagen y contenedor de Docker es importante: las imágenes son plantillas estáticas almacenadas en disco, mientras que los contenedores son procesos vivos con su propio sistema de archivos y red.
Las imágenes de Docker siguen la especificación OCI (Open Container Initiative), lo que significa que funcionan en diferentes runtimes de contenedores—no solo Docker. Esta estandarización garantiza que tus imágenes permanezcan portables.
Entendiendo Registros y Etiquetas
Las imágenes residen en registros—siendo Docker Hub el público más común. Cuando referencias una imagen como node:20-alpine, estás especificando un repositorio (node) y una etiqueta (20-alpine).
Aquí hay algo que confunde a los principiantes: la etiqueta latest no es mágica. No apunta automáticamente a la versión más reciente. Es simplemente una etiqueta predeterminada que los mantenedores de imágenes pueden actualizar o no. Siempre usa etiquetas explícitas como node:20-alpine para builds predecibles.
Ejecutando Tu Primer Contenedor
Ejecutemos un contenedor simple usando la imagen oficial de Node.js:
docker run -it --rm node:20-alpine node -e "console.log('Hello from Docker!')"
Los flags -it habilitan el modo interactivo con una terminal. El flag --rm elimina automáticamente el contenedor cuando termina.
Para un ejemplo más práctico, puedes ejecutar un servidor de desarrollo. Primero, crea un directorio de proyecto con tu código de frontend, luego:
docker run -d -p 3000:3000 -v $(pwd):/app -w /app node:20-alpine sh -c "npm install && npm run dev"
El flag -d ejecuta el contenedor en segundo plano. El -p 3000:3000 mapea el puerto 3000 dentro del contenedor al puerto 3000 en tu máquina. El flag -v monta tu directorio actual dentro del contenedor, y -w establece el directorio de trabajo.
Construyendo Imágenes Personalizadas con Dockerfiles
Para proyectos reales, crearás imágenes personalizadas. Aquí hay un Dockerfile para una aplicación React:
FROM node:20-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
FROM nginx:alpine
COPY --from=build /app/dist /usr/share/nginx/html
EXPOSE 80
Esto demuestra un build multi-etapa—una práctica clave de Docker. La primera etapa construye tu aplicación; la segunda etapa copia solo los archivos de producción en una imagen mínima de nginx. Tu imagen final se mantiene pequeña y segura.
Constrúyela y ejecútala:
docker build -t my-frontend-app .
docker run -d -p 8080:80 my-frontend-app
Discover how at OpenReplay.com.
Volúmenes de Docker y Persistencia
Los contenedores son efímeros—cuando se detienen, cualquier dato escrito dentro de ellos desaparece. Para desarrollo local, usa bind mounts para sincronizar tu código fuente:
docker run -v $(pwd)/src:/app/src -p 3000:3000 my-frontend-app
Para datos que necesitan persistir (como archivos de base de datos), usa volúmenes nombrados:
docker volume create app-data
docker run -v app-data:/data my-app
Entender los volúmenes y la persistencia de Docker es esencial: nunca almacenes datos importantes únicamente dentro del sistema de archivos de un contenedor.
Fundamentos de Seguridad en Docker
Algunas prácticas mantienen tus contenedores más seguros:
Ejecuta como no-root. Agrega un usuario en tu Dockerfile:
RUN adduser -D appuser
USER appuser
Usa imágenes base mínimas. Las imágenes basadas en Alpine tienen menos vulnerabilidades que las distribuciones completas.
Escanea imágenes regularmente. Herramientas como Docker Scout o Trivy identifican vulnerabilidades conocidas.
Nunca incluyas secretos en las imágenes. Las variables de entorno o herramientas de gestión de secretos manejan credenciales—codificarlas directamente crea riesgos de seguridad que persisten en las capas de la imagen.
Simplificando Configuraciones Multi-Contenedor con Compose
Cuando tu frontend necesita una API backend y base de datos localmente, Docker Compose orquesta todo:
services:
frontend:
build: ./frontend
ports:
- "3000:3000"
api:
build: ./api
ports:
- "4000:4000"
Ejecuta docker compose up y ambos servicios inician juntos. Usa docker compose down para detener y eliminar todos los contenedores.
Conclusión
Las imágenes de Docker son blueprints; los contenedores son instancias en ejecución. Mantén las imágenes pequeñas con builds multi-etapa, usa etiquetas explícitas en lugar de latest, separa el estado de los contenedores usando volúmenes, y reconstruye las imágenes regularmente para capturar actualizaciones de seguridad. Estos fundamentos te servirán ya sea que estés ejecutando un entorno de desarrollo o desplegando una aplicación frontend simple.
Preguntas Frecuentes
Una imagen de Docker es una plantilla de solo lectura que contiene el código de tu aplicación, dependencias y configuración. Un contenedor es una instancia en ejecución de esa imagen. Puedes crear múltiples contenedores desde la misma imagen, cada uno ejecutándose de forma independiente con su propio sistema de archivos y red aislados.
La etiqueta latest no se actualiza automáticamente a la versión más reciente. Es solo una etiqueta predeterminada que los mantenedores pueden mantener actualizada o no. Usar etiquetas de versión explícitas como node:20-alpine asegura builds reproducibles y previene cambios inesperados que rompen la compatibilidad cuando las imágenes se actualizan.
Usa volúmenes de Docker para persistir datos fuera del sistema de archivos del contenedor. Los volúmenes nombrados almacenan datos en una ubicación gestionada por Docker, mientras que los bind mounts enlazan a directorios específicos en tu máquina host. Nunca confíes en el sistema de archivos interno de un contenedor para datos importantes.
Los builds multi-etapa te permiten usar una imagen para construir tu aplicación y una imagen diferente y más pequeña para ejecutarla. Esto mantiene tus imágenes de producción ligeras al excluir herramientas de build y dependencias, reduciendo tanto el tamaño de la imagen como las potenciales vulnerabilidades de seguridad.
Gain control over your UX
See how users are using your site as if you were sitting next to them, learn and iterate faster with OpenReplay. — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data. Check our GitHub repo and join the thousands of developers in our community.