Docker: Primeros Pasos

Por Ariel Altamirano Actualizado por Samir Zurita 15 de agosto de 2024
DockerDevOpsContenedores
Docker: Primeros Pasos

Docker fue presentado en marzo de 2013 por Solomon Hykes en PyCon como una herramienta para desarrolladores, la cual permite empaquetar e implementar fácilmente aplicaciones dentro de un contenedor.

Hykes describió a los contenedores como: unidades autónomas de software aisladas a nivel de proceso que empaquetan sus propias dependencias y sistema de archivos. Esto permite desplegarlos de forma coherente en diferentes servidores e infraestructuras compatibles, independientemente de las librerías instaladas en el sistema anfitrión.

Desde entonces, Docker se ha convertido en el estándar de facto para la contenerización, transformando la forma en que desarrollamos, probamos y desplegamos software.

¿Cómo funciona Docker?

Los contenedores ofrecen un mecanismo de aislamiento diferente al de las máquinas virtuales y, en muchos escenarios, pueden complementar o sustituir a las VMs. Y esto es debido a los múltiples beneficios que brindan, entre ellos está que los contenedores son mucho más livianos que las VMs. Docker es la opción más común para implementar contenedores, sin embargo, existen más opciones como Podman, LXD, entre otros.

En la siguiente imagen, podemos ver que cada máquina virtual tiene su propio Sistema Operativo (SO) invitado por encima del Host, a diferencia de los contenedores que comparten el SO del anfitrión (Host) y es por eso que son más livianos.

Funcionamiento de Docker y VMs

El hecho de que los contenedores no usen un SO completo ayuda a que sean más livianos, es decir, reduce de gran manera la carga que soporta la máquina anfitriona, además, el espacio de almacenamiento es menor ya que solamente utiliza lo mínimo para funcionar, y el tiempo que necesita para lanzar una aplicación es considerablemente menor a las máquinas virtuales.

Diferencias entre Contenedores y VMs

Las máquinas virtuales nos permiten crear entornos con sistemas operativos totalmente aislados del sistema operativo host, obteniendo así un control total sobre el sistema operativo invitado, de esta manera se puede mezclar diferentes sistemas operativos entre el host y los invitados.

Por otro lado, los contenedores además de permitirnos desplegar aplicaciones de manera más rápida también ayudan a crear entornos de desarrollo replicables entre los diferentes miembros de un equipo de desarrollo y así asegurar que se ejecutará de la misma manera en todos los equipos, independientemente del sistema operativo cada integrante del equipo tendrá el ambiente funcional.

En la siguiente tabla se presentan las diferencias entre contenedores y VMs:

CaracterísticaContenedoresMáquinas Virtuales
DespliegueEn segundosEn minutos
TamañoLigeros (KB – MB)Pesadas (MB – GB)
Sistema OperativoCompartido con el hostIndependiente (SO invitado completo)
RecursosGeneralmente menor overheadMayor overhead
Tiempo de arranqueSegundosMinutos
ReplicabilidadFácil (imágenes versionables)Moderada (requiere snapshots)
AislamientoKernel compartido, aislamiento de procesosKernel y SO virtualizados

Arquitectura de Docker

Docker utiliza la arquitectura cliente-servidor, en donde el cliente de Docker se comunica con el demonio de Docker para crear, ejecutar o distribuir contenedores. Además, mediante un API REST se pueden acceder a imágenes de contenedor ubicadas en repositorios de imágenes (Registry).

Arquitectura de Docker

Client

El cliente de Docker (CLI) es la interfaz principal que usan los desarrolladores. A través de comandos como docker run, docker build, docker pull, el usuario envía instrucciones al demonio de Docker.

Docker Host

Es el entorno donde se ejecuta el demonio de Docker (dockerd). Aquí residen:

  • Imágenes: Plantillas de solo lectura usadas para crear contenedores.
  • Contenedores: Instancias en ejecución de una imagen.
  • Volúmenes: Almacenamiento persistente para datos.
  • Redes: Conectividad entre contenedores y con el exterior.

Imágenes

Los contenedores de Docker son creados a partir de imágenes, que son una especia de plantilla para la creación del contendor.

Registry

Los registros almacenan y distribuyen imágenes. Docker Hub es el registro público predeterminado, pero existen alternativas como GitHub Container Registry, Amazon ECR, Google Container Registry y registros privados.

Dockerfile: El corazón de Docker

Un Dockerfile es un archivo de texto plano con instrucciones para construir una imagen Docker. Las instrucciones que modifican el filesystem pueden generar capas reutilizables en el proceso de construcción, lo que permite aprovechar la caché.

Ejemplo básico de Dockerfile:

# Imagen base
FROM node:20-alpine

# Directorio de trabajo
WORKDIR /app

# Copiar dependencias
COPY package*.json ./

# Instalar dependencias
RUN npm install

# Copiar código fuente
COPY . .

# Exponer puerto
EXPOSE 3000

# Comando de inicio
CMD ["npm", "start"]

Instrucciones principales

InstrucciónPropósito
FROMDefine la imagen base (ej: node:20-alpine, python:3.12)
WORKDIREstablece el directorio de trabajo dentro del contenedor
COPYCopia archivos del host al contenedor
RUNEjecuta comandos durante la construcción de la imagen
EXPOSEDocumenta el puerto que usa la aplicación
CMDDefine el comando por defecto al ejecutar el contenedor
ENVEstablece variables de entorno
ARGDefine variables de build-time

Docker Compose

Docker Compose permite definir y ejecutar aplicaciones multi-contenedor usando un archivo YAML (compose.yaml o docker-compose.yml).

Ejemplo de una aplicación web con base de datos:

services:
  web:
    build: .
    ports:
      - "3000:3000"
    environment:
      - DATABASE_URL=postgres://user:pass@db:5432/mydb
    depends_on:
      - db

  db:
    image: postgres:<version>-alpine
    environment:
      POSTGRES_USER: user
      POSTGRES_PASSWORD: pass
      POSTGRES_DB: mydb
    volumes:
      - pgdata:/var/lib/postgresql/data

volumes:
  pgdata:

Para levantar todos los servicios: docker compose up -d

Comandos esenciales de Docker

ComandoDescripción
docker pull (imagen)Descarga una imagen del registro
docker run (imagen)Crea y ejecuta un contenedor
docker psLista contenedores en ejecución
docker ps -aLista todos los contenedores
docker stop (id)Detiene un contenedor
docker rm (id)Elimina un contenedor
docker imagesLista imágenes locales
docker rmi (imagen)Elimina una imagen
docker build -t (nombre) .Construye una imagen desde un Dockerfile
docker logs (id)Muestra los logs de un contenedor
docker exec -it (id) shAccede a un contenedor en ejecución
docker compose up -dLevanta servicios definidos en compose.yaml
docker compose downDetiene y elimina los servicios

Buenas prácticas con Docker

Imágenes ligeras

Se puede utilizar imágenes base como Alpine (-alpine) para reducir el tamaño y la superficie de ataque. Una imagen node:<version>-alpine pesa menos que node:<version>.

Multi-stage builds

Permite construir la aplicación en una imagen con herramientas de desarrollo y copiar solo el artefacto final a una imagen más pequeña.

# Etapa de construcción
FROM node:<version>-alpine AS builder
WORKDIR /app
COPY . .
RUN npm run build

# Etapa de producción
FROM nginx:alpine
COPY --from=builder /app/dist /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

No ejecutar como root

Crea un usuario no privilegiado dentro del contenedor:

RUN addgroup -S appgroup && adduser -S appuser -G appgroup
USER appuser

.dockerignore

Excluye archivos innecesarios para reducir el contexto de construcción:

node_modules
.git
.env
*.md

Un solo proceso por contenedor

Un proceso principal por contenedor cuando sea posible. Si necesitas varios servicios independientes, normalmente es preferible separarlos en contenedores diferentes y coordinarlos con Compose u otra plataforma.

Cuándo usar contenedores

Docker facilita el empaquetado y distribución de aplicaciones, incluyendo arquitecturas basadas en microservicios, por lo que es ideal para:

  • Arquitecturas de microservicios: Cada servicio en su propio contenedor, comunicándose por red.
  • Entornos de desarrollo replicables: Todo el equipo usa la misma configuración.
  • Pipelines de CI/CD: Construir, probar y desplegar usando la misma imagen.
  • Aislamiento de aplicaciones: Ejecutar aplicaciones con dependencias conflictivas en el mismo host.
  • Pruebas de integración: Levantar servicios dependientes (bases de datos, colas, etc.) temporalmente.

Conclusiones

  • Docker facilita preparar un nuevo ambiente de trabajo independientemente de la máquina anfitriona.
  • Los contenedores permiten arrancar aplicaciones rápidamente aprovechando los recursos de hardware.
  • Los Dockerfile y archivos Compose pueden mantenerse bajo control de versiones, facilitando la reproducibilidad y trazabilidad de los entornos.
  • Las prácticas como multi-stage builds y el uso de imágenes Alpine ayudan a mantener contenedores seguros y eficientes.
  • Docker Compose simplifica la gestión de aplicaciones multi-contenedor.
Volver al blog

Cuéntanos qué quieres lograr

Te ayudamos a encontrar la mejor solución para tu negocio.

Hablar con un asesor