Día 43 - Docker Port Mapping
Problema / Desafío
El equipo de Nautilus necesita exponer una aplicación nginx en App Server 3 (stapp03). Requisitos:
- Descargar la imagen
nginx:alpine - Crear un contenedor llamado
cluster - Mapear el puerto
5004del host al puerto80del contenedor - Mantener el contenedor en ejecución
Conceptos clave
Port mapping (-p)
Por defecto un contenedor está aislado — sus puertos no son accesibles desde el host ni desde la red. El flag -p publica un puerto del contenedor en el host:
El orden importa: siempre es host:contenedor. Invertirlo es el error más común — 5004:80 expone el 80 del contenedor en el 5004 del host, no al revés.
-d (detached mode)
Sin -d, docker run ocupa la terminal mostrando stdout del proceso del contenedor. Al presionar Ctrl+C, el contenedor se detiene.
# Sin -d: bloquea la terminal, Ctrl+C detiene el contenedor
docker run -p 5004:80 --name cluster nginx:alpine
# Con -d: corre en background, devuelve el container ID
docker run -p 5004:80 --name cluster -d nginx:alpine
En modo detached, los logs se acceden con docker logs.
docker logs
Reemplaza la lectura directa de archivos de log cuando el contenedor corre en modo detached:
docker logs <container_id|name> # todos los logs
docker logs -f <container_id|name> # seguir logs en tiempo real (como tail -f)
docker logs --tail 20 <container_id> # últimas 20 líneas
docker inspect
Muestra la configuración completa de un contenedor en formato JSON: estado, red, puertos, volúmenes, variables de entorno, comando de inicio, etc.
Para extraer un campo específico sin parsear el JSON completo se usan Go templates con --format:
# IP del contenedor en la red bridge
docker inspect --format "{{ .NetworkSettings.Networks.bridge.IPAddress }}" cluster
# 172.17.0.2
# Estado del contenedor
docker inspect --format "{{ .State.Status }}" cluster
# running
# Puertos publicados (NetworkSettings.Ports — muestra host + contenedor)
docker inspect --format "{{ .NetworkSettings.Ports }}" cluster
# map[80/tcp:[{0.0.0.0 5004} {:: 5004}]]
# Configuración de port bindings (HostConfig.PortBindings)
docker inspect --format "{{ .HostConfig.PortBindings }}" cluster
# map[80/tcp:[{invalid IP 5004}]]
Nota sobre "invalid IP": En
HostConfig.PortBindings, el campoHostIpestá vacío cuando el puerto está publicado en todas las interfaces (0.0.0.0). Go templates intenta renderizar esa cadena vacía como IP y muestrainvalid IP— no es un error, significa exactamente "sin IP específica → todas las interfaces".NetworkSettings.Portses más legible para este caso.
nginx:alpine vs nginx
| Imagen | Base | Tamaño aprox. | libc |
|---|---|---|---|
nginx:alpine |
Alpine Linux | ~45 MB | musl |
nginx (latest) |
Debian | ~190 MB | glibc |
Alpine es la opción estándar cuando no se necesitan herramientas del sistema Debian. La diferencia de ~145 MB importa cuando la imagen se descarga o distribuye frecuentemente.
Pasos
- Descargar la imagen con
docker pull - Ejecutar el contenedor con port mapping y nombre
- Verificar que el contenedor está corriendo
- Confirmar accesibilidad con
curl
Comandos / Código
1. Pull de la imagen
2. Ejecutar el contenedor
3. Verificar el contenedor
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
aa9b041a1854 nginx:alpine "/docker-entrypoint.…" 3 seconds ago Up 2 seconds 0.0.0.0:5004->80/tcp, :::5004->80/tcp cluster
La columna PORTS confirma el mapeo: 0.0.0.0:5004->80/tcp (IPv4) y :::5004->80/tcp (IPv6).
4. Ver logs del contenedor
/docker-entrypoint.sh: Configuration complete; ready for start up
2026/05/02 12:51:33 [notice] 1#1: nginx/1.29.8
2026/05/02 12:51:33 [notice] 1#1: start worker processes
...
El 1#1 en los logs de nginx indica worker_id#master_pid — el master process corre con PID 1, directamente como proceso principal del contenedor.
5. Probar el acceso
Respuesta HTTP confirma que el mapeo de puertos funciona correctamente.
6. Inspeccionar el contenedor
El campo Args en el output muestra cómo nginx fue iniciado:
daemon off; indica a nginx que no haga fork al background. Sin esta flag, nginx normalmente se convierte en daemon: el proceso padre termina y los workers quedan como hijos. En un contenedor, si PID 1 (el proceso padre) termina, el contenedor se detiene — por eso todos los servidores en contenedores deben correr en foreground.
Troubleshooting
| Problema | Solución |
|---|---|
docker: Error response from daemon: Conflict. The container name "/cluster" is already in use |
Ya existe un contenedor con ese nombre — eliminarlo con docker rm cluster o usar docker rm -f cluster si está corriendo |
curl: (7) Failed to connect to localhost port 5004 |
El contenedor no está corriendo — verificar con docker ps -a y revisar logs con docker logs cluster |
| El contenedor se detiene inmediatamente | El proceso principal terminó — revisar con docker logs cluster para ver el error |
| Puerto 5004 ya en uso en el host | Otro proceso usa ese puerto — identificarlo con ss -tlnp \| grep 5004 |