Saltar al contenido principal

Imágenes y contenedores

Con docker run hello-world, Docker creó un contenedor cuyo programa mostró un mensaje y terminó. ¿Qué quedó después: la imagen, el contenedor o ambos?

Practicaremos con Nginx, un servidor web que permanece funcionando: podremos detenerlo, iniciarlo y observarlo. El acceso desde el navegador queda para la siguiente unidad.

Usa el entorno de contenedores Linux y el acceso preparados en la unidad anterior. Si tu instalación requiere sudo, aplícalo a los comandos que contactan con el motor.

Para practicar: trabaja únicamente con los contenedores que crees en esta lección. Si ya tienes otros, no los detengas ni elimines. Anota los identificadores de tus pruebas para distinguirlos.

Una base y varias instancias​

Una imagen es una base preparada para crear contenedores. Puede incluir la aplicación, archivos, librerías, un runtime si hace falta y configuración inicial. Tenerla guardada no significa que la aplicación esté ejecutándose.

Un contenedor es una instancia concreta creada a partir de esa imagen. Tiene identidad y estado propios: puede existir aunque sus procesos no estén ejecutándose.

Piensa en un molde que utilizamos para fabricar varios objetos. El molde sigue disponible después de crear cada uno. Es solo un modelo mental, pero nos ayuda a recordar que la imagen no se consume ni se transforma en el contenedor.

Imagen de Nginx, reutilizable
│
├── Contenedor A: identidad y estado propios
└── Contenedor B: identidad y estado propios

Vamos a comprobarlo, empezando por conseguir esa base.

Conseguir una imagen sin ejecutar nada​

Usaremos la imagen oficial de Nginx en Docker Hub. Esa página identifica la imagen y sus variantes; no necesitamos buscar una imagen de procedencia desconocida ni instalar Nginx directamente en nuestro sistema.

Para pedir a Docker que obtenga la imagen, ejecuta:

docker pull nginx

pull solicita obtener la imagen indicada; nginx es su nombre. Con esta referencia, Docker la busca en Docker Hub y la deja disponible en el almacenamiento local del motor con el que estamos trabajando.

Puede mostrar progreso de descarga o indicar que ya está actualizada. Si el contenido necesario ya está disponible, no tiene que descargarlo todo otra vez.

Predice el resultado: ¿hay ya un Nginx ejecutándose? No. Hemos obtenido una imagen, sin crear ningún contenedor.

Comprobar qué imágenes tenemos​

Necesitamos ver si la base está disponible:

docker image ls

image ls lista las imágenes locales. Localiza Nginx y observa estos datos, según cómo los muestre tu versión:

DatoQué representa
Repositorio o nombre, como nginxQué imagen estamos identificando.
Tag, como latestQué variante o versión indica esa referencia.
Identificador de imagenIdentidad de la imagen; no es un ID de contenedor.
Fecha de creación, si apareceCuándo se creó la imagen, no cuándo la descargaste.
TamañoEspacio asociado a la imagen, no memoria consumida por una aplicación en ejecución.

Puedes ver nombre y tag separados como REPOSITORY y TAG, o juntos como nginx:latest bajo IMAGE. También pueden cambiar las columnas de tamaño. Los valores concretos variarán; lo importante es reconocer que la imagen está disponible.

Elegir una variante: el tag​

En nginx:latest, nginx es el nombre y latest es el tag, una etiqueta que identifica una variante o versión. Al omitirlo, como hicimos con nginx, Docker utiliza latest.

Importante: latest es un nombre convencional de tag, no una garantía universal de «la versión más reciente». Su significado depende de cómo publique la imagen quien la mantiene.

Otros ejemplos de referencias son nginx:alpine, una variante basada en Alpine Linux, y postgres:17, una referencia de PostgreSQL. Son referencias de imágenes, no nombres de contenedores. No necesitas descargar PostgreSQL para esta práctica.

Obtén también la variante de Nginx que utilizaremos después para practicar la eliminación de una imagen:

docker pull nginx:alpine
docker image ls

Busca las referencias nginx:latest y nginx:alpine. Seguimos sin haber creado contenedores. Para el recorrido principal utilizaremos nginx, es decir, la referencia con tag latest.

Crear el primer contenedor​

Ya tenemos la imagen. Ahora queremos ejecutar el programa preparado en ella:

docker run nginx

run solicita crear un contenedor nuevo a partir de la imagen e iniciarlo. No vuelve a iniciar uno anterior.

Imagen nginx, que sigue disponible
│ docker run
▼
Nuevo contenedor
│ Se inicia su programa
▼
Procesos de Nginx ejecutándose

Verás mensajes de arranque y la terminal quedará ocupada. Nginx sigue trabajando: a diferencia de hello-world, no tiene como tarea mostrar un saludo y terminar. Que no aparezcan nuevos mensajes durante un rato no significa que esté bloqueado.

En este ejemplo estamos conectados a la salida del contenedor desde la terminal: trabajamos en primer plano. Pulsa Ctrl + C para interrumpir esta ejecución de Nginx y recuperar la terminal. En esta prueba, el programa termina y el contenedor queda detenido. Enseguida comprobaremos que sigue existiendo.

Crear otro y recuperar la terminal​

Ahora queremos que Nginx continúe ejecutándose mientras utilizamos la terminal para otras operaciones. Para eso añadimos -d, de detached:

docker run -d nginx

Docker devuelve el identificador del nuevo contenedor y recuperamos la terminal. Anota ese ID. El proceso continúa ejecutándose en segundo plano respecto a nuestra interacción con el cliente.

No hemos cambiado el primer contenedor a segundo plano: hemos creado un segundo contenedor. Hemos utilizado run dos veces sobre la misma imagen.

Ver qué existe y qué está ejecutándose​

Primero pregunta por los contenedores en ejecución:

docker ps

Busca la fila del segundo Nginx. Las columnas más útiles son:

ColumnaQué observar
CONTAINER IDIdentificador del contenedor. Compara con el ID que anotaste.
IMAGEImagen utilizada para crearlo.
STATUSSu estado; Up ... indica que está ejecutándose.
NAMESNombre del contenedor. Docker asigna uno si no lo elegimos.

También aparecen datos como el comando y el momento de creación. La columna PORTS la utilizaremos en la siguiente unidad; que aparezca 80/tcp no significa por sí solo que hayamos preparado el acceso desde el navegador.

¿Dónde está el primer Nginx? Antes de concluir que se ha borrado, consulta también los detenidos:

docker ps -a

-a muestra todos los contenedores que siguen existiendo, incluidos los detenidos. Localiza las dos filas de Nginx: tendrán identificadores y nombres diferentes. Anota también el ID del primero, el detenido, para retirarlo después.

Una lectura resumida de esas filas sería:

CONTENEDOR           IMAGEN   ESTADO
Primer Nginx nginx Exited (...)
Segundo Nginx nginx Up ...

Este esquema resume los datos, no reproduce todas las columnas de tu terminal. Los IDs, nombres y tiempos serán distintos en cada equipo. Si conservas el contenedor de hello-world de la unidad anterior, también lo encontrarás con docker ps -a.

Tres estados, y una diferencia importante​

EstadoQué significa
created / CreatedEl contenedor existe, pero todavía no se ha iniciado.
running / Up ...Su proceso principal sigue ejecutándose.
exited / Exited (...)Su proceso principal ha terminado; el contenedor sigue existiendo.

Con run, crear e iniciar ocurren en una misma operación, así que normalmente veremos directamente el resultado en ejecución. Si el programa termina rápido, veremos el contenedor detenido, como con hello-world.

El número de Exited (...) es el código de salida del proceso: 0 suele indicar una finalización correcta. Terminar no implica necesariamente un fallo; importa lo que tenía que hacer el programa y por qué terminó.

Eliminado es diferente: el contenedor ya no existe y no aparece ni siquiera en docker ps -a.

Un nombre para trabajar con el mismo contenedor​

Podemos dirigir una operación a un contenedor mediante su nombre o su ID. El ID abreviado que muestra el listado sirve si identifica un único contenedor. No lo confundas con el identificador de su imagen.

Copiar IDs o recordar nombres generados resulta incómodo. Para el siguiente recorrido elegiremos un nombre sencillo, mi-nginx:

docker run -d --name mi-nginx nginx
docker ps

--name mi-nginx asigna el nombre al nuevo contenedor; el nginx final sigue siendo la imagen. Localiza su fila y anota el ID. Lo compararemos después de cada cambio.

Los nombres deben ser únicos dentro del mismo daemon, también entre contenedores detenidos. Si ya existe un mi-nginx, no lo borres sin saber de dónde procede: elige otro nombre y sustitúyelo en todo el recorrido. Estos comandos crean un tercer contenedor de práctica; los dos primeros siguen existiendo.

Detenerlo sin borrarlo​

Queremos que mi-nginx deje de ejecutar Nginx. ¿Desaparecerá de ambos listados o solo del de contenedores en ejecución?

docker stop mi-nginx
docker ps
docker ps -a

stop pide detener el contenedor. Comprueba que ya no aparece entre los activos y que sigue en el listado completo con el mismo ID y nombre, normalmente como Exited (...).

Detenerlo no elimina el contenedor ni su imagen. Tampoco detiene automáticamente el segundo Nginx que creaste: son instancias diferentes.

Volver a iniciar el que ya existe​

Para continuar con ese mismo contenedor, utiliza:

docker start mi-nginx
docker ps

Vuelve a aparecer como Up .... Comprueba que conserva el ID: start no ha creado otro contenedor. En esta forma de uso recuperas la terminal, sin quedar conectado a su salida.

Si ejecutases de nuevo docker run -d nginx, crearías otro, con otro ID. Si repitieses el run con --name mi-nginx, encontrarías un conflicto de nombre, no una forma de reanudarlo.

Reiniciar no es crear de nuevo​

Cuando necesitamos detener y volver a iniciar el mismo contenedor, podemos pedir ambas acciones con:

docker restart mi-nginx
docker ps

Comprueba el ID y el estado. La identidad se mantiene y el tiempo que lleva ejecutándose empieza de nuevo. No ha aparecido un contenedor adicional.

Reiniciar puede ser una operación necesaria, pero no explica ni corrige por sí solo la causa de un fallo. Para entender qué ha ocurrido, necesitamos observar.

¿Qué ha dicho el programa? Los logs​

Al utilizar -d no vimos los mensajes de Nginx en la terminal. Podemos consultarlos después:

docker logs mi-nginx

logs muestra la salida registrada del contenedor, lo que el programa envía a su salida estándar y de errores. En esta imagen incluye mensajes de arranque y parada de Nginx. Las fechas, versiones y cantidad de líneas pueden variar.

No es una vista de todos los archivos que una aplicación pueda guardar: depende de qué salida produzca y se registre. Un listado vacío tampoco demostraría por sí solo que el contenedor ha fallado.

Seguir los mensajes mientras aparecen​

Si queremos mantener abierta la consulta para recibir mensajes nuevos, añadimos -f, de follow:

docker logs -f mi-nginx

Ahora la terminal espera más salida. Pulsa Ctrl + C para terminar esta consulta y después comprueba:

docker ps

mi-nginx continúa ejecutándose. No es lo mismo interrumpir el seguimiento de logs que interrumpir el docker run nginx en primer plano del inicio.

Consultar un contenedor detenido​

Predice: si paramos el proceso, ¿desaparecen los mensajes que ya produjo?

docker stop mi-nginx
docker ps -a
docker logs mi-nginx

El contenedor está detenido, pero todavía existe y podemos consultar su salida registrada. Esto resulta útil cuando un programa termina antes de que hayamos podido leer qué dijo.

Eliminar lo que ya no necesitamos​

Hemos terminado el recorrido con mi-nginx. Está detenido y ahora queremos retirar ese contenedor:

docker rm mi-nginx
docker ps -a
docker image ls

rm elimina el contenedor. Comprueba que ya no aparece en el listado completo y que la imagen de Nginx sigue disponible. Hemos eliminado una instancia, no su base reutilizable.

Ya no podremos iniciarlo con start ni consultar sus logs con ese nombre. Si usamos run otra vez, estaremos creando un contenedor nuevo, aunque le asignemos el mismo nombre que acabamos de liberar.

Eliminar también descarta los cambios guardados únicamente en ese contenedor. En esta práctica no hemos creado datos que queramos conservar; no elimines un contenedor de otro trabajo solo para liberar su nombre.

Retirar las dos primeras pruebas​

Consulta docker ps -a para localizar las dos pruebas iniciales.

Sustituye ID_PRIMERO e ID_SEGUNDO por los IDs que anotaste: el primero está detenido y el segundo sigue ejecutándose.

docker rm ID_PRIMERO
docker stop ID_SEGUNDO
docker rm ID_SEGUNDO
docker ps -a

Comprueba que las dos filas han desaparecido. Si alguno sigue ejecutándose, revisa su identidad y detenlo antes de eliminarlo, sin forzar la operación.

Experimento: dos contenedores, una misma imagen​

La imagen nginx sigue localmente disponible. Vamos a demostrar que podemos reutilizarla sin que los contenedores sean el mismo recurso.

Crear A y B​

Comprueba con docker ps -a que los nombres web1 y web2 están libres. Después crea dos contenedores y observa sus filas:

docker run -d --name web1 nginx
docker run -d --name web2 nginx
docker ps
docker image ls

Anota los IDs. ¿Cuántas bases distintas hemos necesitado para crear estas dos instancias? Una: la misma imagen nginx. Los contenedores tienen IDs y nombres diferentes. Descargar también nginx:alpine antes no significa que la estemos utilizando aquí.

Eliminar A y observar B​

Antes de hacerlo, predice qué pasará con web2 y con la imagen:

docker stop web1
docker rm web1
docker ps -a
docker image ls

Comprueba tres cosas: web1 ya no existe, web2 sigue ejecutándose con su ID anterior y la imagen nginx permanece disponible.

Imagen nginx: sigue disponible
├── web1: eliminado
└── web2: sigue ejecutándose

Hemos actuado sobre una instancia. Ni la otra ni la imagen se han eliminado. Termina la prueba retirando también B:

docker stop web2
docker rm web2
docker ps -a

¿Y si ya no necesito una imagen?​

Eliminar imágenes es una operación distinta de eliminar contenedores. Conservaremos nginx:latest para la siguiente unidad y practicaremos con nginx:alpine.

Primero crearemos un contenedor de esa variante para observar su relación con la imagen. Usa un nombre libre:

docker run -d --name prueba-alpine nginx:alpine
docker ps

¿Podemos retirar sin más la imagen que utiliza? En este laboratorio, intenta:

docker image rm nginx:alpine

Docker debería rechazar la eliminación porque el contenedor utiliza esa imagen. El texto concreto puede cambiar. Este error nos ayuda a distinguir recursos relacionados: tener una imagen y tener contenedores creados a partir de ella son cosas diferentes.

Comprueba con docker ps -a y docker image ls que ambos siguen presentes. Un contenedor detenido también puede impedir eliminar su imagen; detener no equivale a quitar esa dependencia.

Retira primero nuestro contenedor de prueba y vuelve a intentarlo:

docker stop prueba-alpine
docker rm prueba-alpine
docker ps -a
docker image rm nginx:alpine
docker image ls

Ahora debe desaparecer la referencia nginx:alpine; nginx:latest permanece. Esta operación es local: no borra la imagen de Docker Hub. Si una imagen tiene más de una referencia local, retirar una referencia no implica borrar todos sus datos.

Si la eliminación sigue bloqueada por otro contenedor, averigua cuál es. No elimines recursos ajenos ni fuerces la operación para completar el ejemplo.

Ideas que pueden llevarnos a error​

Si piensas…Revisa esta diferencia
«La imagen está ejecutándose».Se ejecutan procesos en un contenedor creado a partir de ella.
«Solo puede existir un contenedor por imagen».Hemos creado varias instancias con identidades propias.
«run vuelve a iniciar mi contenedor».Crea otro; para iniciar uno detenido utilizamos start.
«restart crea otro contenedor».Detiene y vuelve a iniciar el mismo; comprueba su ID.
«Si no sale en docker ps, se ha borrado».Ese listado muestra los activos; consulta también docker ps -a.
«Si ha terminado, ha fallado».Puede haber completado su tarea, como hello-world. Observa su salida y código de salida.
«stop y rm hacen lo mismo».Uno detiene; el otro elimina el contenedor.
«Borrar el contenedor borra también la imagen».La imagen es otro recurso y puede seguir utilizándose.
«Un nombre queda libre al detener».Sigue ocupado mientras exista ese contenedor.
«Un error al eliminar se arregla forzando».Revisa si el contenedor está activo o si la imagen está en uso.
«latest siempre garantiza la última versión».Es un tag cuyo significado decide quien publica la imagen.

Mini reto: tres contenedores, tres resultados​

Crea tres contenedores desde nginx llamados web-a, web-b y web-c. Comprueba primero que esos nombres estén libres. Tu objetivo es conseguir este resultado:

RecursoResultado final
web-aSigue ejecutándose.
web-bExiste, pero está detenido.
web-cHa sido eliminado.
Imagen nginxSigue disponible localmente.

Decide la secuencia sin copiar todos los pasos anteriores. Antes de cada operación, anota qué esperas ver; después, compruébalo. Consulta también los logs de web-b cuando esté detenido.

Responde contando únicamente los recursos de este reto:

  1. ¿Cuántas imágenes distintas has necesitado como base?
  2. ¿Cuántos contenedores del reto siguen existiendo? ¿Cuántos están ejecutándose?
  3. ¿Qué listado permite demostrar que web-b existe?
  4. ¿Puedes volver a iniciar web-b? ¿Cambiaría su ID?
  5. ¿Puedes volver a iniciar web-c con start? ¿Por qué?
  6. Si creases otro contenedor con el nombre web-c, ¿sería el anterior?
  7. ¿Por qué la imagen sigue disponible después de eliminar uno de sus contenedores?

Como comprobación conceptual, deben quedar dos contenedores del reto existentes y uno en ejecución, creados desde una misma imagen. Explica por qué los listados de activos y de todos los contenedores muestran cantidades diferentes.

Cuando termines de comprobar el reto, detén web-a y elimina web-a y web-b. Si has iniciado alguno para experimentar, detén también ese antes de eliminarlo. Verifica que no quedan contenedores de este reto y conserva la imagen de Nginx.

El ciclo que acabas de practicar​

No necesitas memorizar una lista de órdenes. Relaciona cada operación con la necesidad que resuelve:

Necesito una base → pull → imagen disponible
│
run
↓
Contenedor nuevo: created
↓ Se inicia
running
│ stop o fin del proceso
↓
exited
├── start → running (el mismo)
└── rm → eliminado

Mientras está running: restart → parada e inicio del mismo

Los listados permiten comprobar qué existe y su estado; los logs permiten observar lo que produjo el programa. docker image rm actúa sobre la imagen, en una operación distinta de eliminar sus contenedores.

Ya sabes gestionar un contenedor desde su creación hasta su eliminación. Ahora queda una necesidad natural: si Nginx funciona dentro del contenedor, ¿cómo accedemos a su servicio desde nuestro ordenador?

En la siguiente unidad conectaremos este recorrido con el navegador y pondremos en marcha nuestro primer servidor accesible desde el host.