02 - Docker

Los proyectos no viven solo del código: alrededor hay una base de datos, un almacenamiento de ficheros, un proxy… Cada pieza es un servidor ajeno, con su instalación y su configuración. Docker permite empaquetar cada pieza con todo lo que necesita y ejecutarla aislada y reproducible, en cualquier máquina y sin instalar nada más.

Instalar y configurar cada servidor a mano tiene dos problemas: el trabajo no se traslada (lo que funciona en una máquina no va igual en otra: versiones y configuraciones distintas) y las piezas comparten el sistema, así que una actualización toca a todas. Docker resuelve ambos: cada servidor se empaqueta en una imagen con su versión exacta y corre en un contenedor aislado del resto.

Concepto Qué es
Imagen Plantilla inmutable con el sistema de ficheros y el comando de arranque; tiene una versión (*tag*)
Contenedor Proceso aislado en ejecución, creado a partir de una imagen
Registro Repositorio de imágenes; Docker Hub es el público más usado

La imagen es la plantilla; el contenedor, la instancia. De una imagen salen tantos contenedores como se quieran.

Máquina virtual Contenedor
Qué incluye SO invitado completo + aplicación Solo la aplicación y sus dependencias
Arranque Minutos (arranca un SO) Milisegundos (es un proceso)
Coste Gigabytes por VM Megabytes; comparte el kernel del host

Ambos aíslan; el contenedor comparte el kernel del host y por eso es tan ligero. Para servidores de aplicación es el enfoque dominante.

Los nombres de imagen siguen el patrón usuario/imagen:tag: usuario identifica al publicador y es opcional —si falta, la imagen es oficial—; imagen es obligatorio; tag señala la versión y también es opcional. Sin tag se descarga latest, que no siempre es la más nueva: es la versión que los creadores etiquetaron como tal. Fijar la versión (mariadb:11, no mariadb) es lo reproducible.

Docker Hub es el registro público: en la web se busca y se distinguen las imágenes oficiales. En una terminal sin entorno gráfico, lo mismo lo hace docker search.

Comando Qué hace
docker search <imagen> busca imágenes con ese nombre en el registro
docker pull <imagen> descarga la imagen
docker images lista las imágenes del sistema local
docker rmi <imagen> elimina una imagen local, por REPOSITORY:TAG o IMAGE ID
docker tag crea un alias de una imagen existente
docker save / docker load exporta o importa imágenes en un fichero .tar, sin registro

$ docker search mariadb
$ docker pull mariadb:11
$ docker images
$ docker rmi mariadb:11

  • La salida de docker images muestra REPOSITORY, TAG, IMAGE ID, fecha y tamaño.
  • Los comandos tienen variantes largas equivalentes (docker image pull, docker image rm…); las cortas son de uso habitual.
  • Una imagen en uso por algún contenedor no se deja eliminar: primero hay que eliminar el contenedor.

docker run crea y arranca un contenedor desde una imagen; si no está en local, la descarga antes. Sus opciones completan la configuración del contenedor en el momento de crearlo:

Opción Efecto
-d en segundo plano (*detached*), sin ocupar la terminal
-p host:contenedor publica el puerto del contenedor en el host
–name nombre para referirse al contenedor sin depender del ID
-e VAR=valor variable de entorno: así se configura el servidor (contraseñas, rutas…)
-v volumen:ruta guarda datos fuera del contenedor (sección 5)
–restart always lo vuelve a arrancar si el contenedor o la máquina se reinician

$ docker run -d -p 8080:80 nginx                       # un servidor web en el 8080 del host
$ docker run -d --name db \
    -e MARIADB_ROOT_PASSWORD=secreto \
    -p 3306:3306 mariadb:11

El contenedor se para y se arranca sin perderse; se elimina y ya no hay nada:

$ docker ps                    # contenedores en ejecución (con -a, también los parados)
$ docker stop db               # lo detiene
$ docker start db              # lo vuelve a arrancar: el mismo contenedor, con lo que tiene dentro
$ docker logs -f db            # sigue la salida del contenedor
$ docker rm db                 # lo elimina (con -f también si está en marcha)

  • Solo rm destruye el contenedor y con él lo que guardó dentro; los datos valiosos van en volúmenes (sección 5).

docker exec ejecuta comandos en un contenedor en marcha, sin detenerlo:

$ docker exec -it db bash      # una terminal dentro del contenedor (para salir, exit)
$ docker exec db ls /app       # un comando suelto, sin terminal

  • -it asigna un terminal interactivo: es lo que permite abrir una shell.
  • El contenedor debe estar en ejecución; si no, no se puede hacer exec.

Puertos: -p 9000:9000 conecta el puerto del host con el del contenedor. Sin él, el servicio no es accesible desde fuera; entre contenedores no hace falta: se ven por la red interna (tema 08).

Volúmenes: el sistema de ficheros del contenedor es efímero: al eliminar el contenedor se pierde lo que guardó dentro. Un volumen vive fuera del contenedor y persiste:

$ docker run -d --name db -v db-data:/var/lib/mysql mariadb:11

Los datos de MariaDB quedan en el volumen db-data y sobreviven al contenedor.

Forma Sintaxis Cuándo usarla
Volumen nombrado -v db-data:/var/lib/mysql datos que deben sobrevivir al contenedor; los gestiona Docker y no los liga a una ruta del host
Bind mount -v ./html:/usr/share/nginx/html trabajar con una carpeta concreta de la máquina: el host es dueño de los ficheros

$ docker volume create db-data     # crear el volumen por adelantado
$ docker volume inspect db-data    # sus datos: el Mountpoint es la carpeta real del host

  • Los nombrados son la forma normal de persistir datos de servidores (bases de datos, almacenamiento).
  • Los bind mounts sirven para compartir una carpeta del host con el contenedor: configuración, código, contenido estático.

Los servidores oficiales ya vienen empaquetados; la aplicación propia se define en un Dockerfile: un fichero de texto que describe cómo se construye una imagen —de qué parte, qué añade y con qué comando arranca.

Instrucción Qué hace
FROM imagen base de la que se parte; toda imagen empieza aquí
RUN ejecuta un comando durante la construcción (instalar paquetes…)
COPY / ADD copia ficheros del host a la imagen (ADD admite URLs y descomprime)
WORKDIR directorio de trabajo para las siguientes instrucciones
ENV variables de entorno fijas de la imagen
EXPOSE documenta el puerto que usa la aplicación; no lo publica (eso lo hace -p)
CMD comando por defecto al arrancar el contenedor; se puede sobreescribir
ENTRYPOINT el programa principal, fijo; lo que se añade en docker run son sus argumentos

FROM python:3
WORKDIR /app
COPY app.py .
CMD ["python", "app.py"]

$ docker build -t miapp:1.0 .      # construye la imagen desde este directorio
$ docker run miapp:1.0

  • La imagen base oficial de python ya trae el intérprete; el Dockerfile solo añade el código y el arranque.
  • El fichero se llama exactamente Dockerfile, sin extensión: Docker lo busca con ese nombre.
  • -t fija nombre y *tag* de la imagen (miapp:1.0); sin tag tomaría latest.
  • La imagen construida corre igual en cualquier máquina. Si la aplicación espere peticiones, publica su puerto como cualquier contenedor (-p).
La forma no cambia aunque crezca lo que hay que instalar: el Dockerfile es la lista de pasos —base, añadidos, arranque— que sustituye a toda la instalación manual del servidor.

Cada contenedor nace aislado en su propia red. Para que dos contenedores se vean, comparten una red: un conmutador virtual —de tipo bridge por defecto— donde se alcanzan por el nombre del contenedor, como un hostname.

$ docker network create mi-red
$ docker run -d --name db --network mi-red mariadb:11
$ docker run -d --name app --network mi-red alpine
$ docker exec app ping db          # db responde a su nombre: la red los une

Comando Qué hace
network create crea la red (bridge por defecto)
network connect / disconnect añade o retira un contenedor en marcha a/de la red
network ls lista las redes
network inspect detalle de la red: driver, contenedores conectados y sus IP
network rm elimina la red, sin contenedores conectados
  • Otros *drivers* existen —host comparte la pila de red de la máquina, none aísla del todo, overlay conecta varias máquinas—; para el uso habitual basta el bridge de siempre.
  • La clave del módulo está aquí: entre contenedores no son necesarias las direcciones ni los puertos publicados; los nombres bastan. Lo retoma la arquitectura del tema 08.

Con varios contenedores, los comandos manuales no escalan: hay que recordar orden, puertos y variables de cada uno. Docker Compose describe el sistema completo —servicios, redes y volúmenes— en un fichero compose.yaml y lo levanta con un solo comando:

services:
  web:
    image: nginx:alpine
    ports:
      - "8080:80"
    volumes:
      - ./html:/usr/share/nginx/html

  db:
    image: mariadb:11
    environment:
      MARIADB_ROOT_PASSWORD: secreto
      MARIADB_DATABASE: miapp
    volumes:
      - db-data:/var/lib/mysql

volumes:
  db-data:

  • Cada entrada de services es un servicio: la receta de su contenedor —imagen, puertos, entorno, volúmenes—.
  • Los volúmenes nombrados se declaran al final, una vez, y los servicios los comparten.
  • El *bind mount* de la sección 5 se usa igual; en compose.yaml, una ruta que empieza por . es relativa al propio fichero.
  • Compose crea una red propia del proyecto entre los servicios: web alcanza a db por su nombre, con la red de la sección 7 hecha automáticamente.
Comando Qué hace
docker compose up -d crea, si faltan, y arranca todos los servicios, en segundo plano
docker compose start / stop arranca o para los contenedores existentes, sin eliminarlos
docker compose ps estado de los servicios
docker compose logs -f la salida de todos, ordenada por servicio
docker compose down para y elimina contenedores y red; con -v, también los volúmenes
docker compose down elimina contenedores pero conserva los volúmenes nombrados: los datos sobreviven. Es el modo normal de reiniciar el sistema; -v solo si se quieren borrar de verdad.

Con un solo servicio Compose parece excesivo; su valor aparece al describir sistemas con varios servidores: es el caso del tema de arquitectura (tema 08).

  • clase/daw/si/1eval/docker.txt
  • Última modificación: 2026/10/17 10:37
  • por cesguiro