Aprende a exponer y publicar eficazmente puertos en Docker para permitir la comunicación entre tus contenedores y el mundo exterior. Esta guía lo cubre todo, desde la configuración de Dockerfile y los indicadores de tiempo de ejecución hasta la orquestación de Docker Compose y las técnicas de resolución de problemas.
Al trabajar con contenedores Docker, uno de los aspectos más cruciales que he encontrado es habilitar la comunicación de red entre los contenedores y el mundo exterior. Sin una configuración adecuada de los puertos, las aplicaciones en contenedores permanecen aisladas e inaccesibles. En este artículo, te guiaré a través del proceso de exposición de puertos Docker, una habilidad fundamental para cualquiera que trabaje con aplicaciones en contenedores.
Esta guía está diseñada específicamente para programadores de software, ingenieros de DevOps y profesionales de TI que necesitan que sus aplicaciones en contenedores sean accesibles a los usuarios, a otros servicios o a sistemas externos. Tanto si estás desplegando un servidor web, una API o una base de datos dentro de un contenedor, comprender la exposición de puertos es esencial para construir sistemas funcionales basados en Docker.
Al final de este artículo, comprenderás claramente cómo funciona la red Docker, los distintos métodos de exposición de puertos y las técnicas prácticas para aplicar estos conceptos en tus propios proyectos.
Antes de entrar en los detalles técnicos, es importante entender qué significa la exposición de puertos en el ecosistema Docker.
Principios de aislamiento de la red de contenedores
Los contenedores Docker están diseñados con el aislamiento como principio básico. Este aislamiento se consigue mediante funciones del núcleo de Linux como los espacios de nombres y los cgroups. Los espacios de nombres proporcionan aislamiento de procesos, asegurando que los procesos de un contenedor no puedan ver ni interactuar con procesos de otros contenedores o del sistema anfitrión. Los Cgroups, en cambio, controlan la asignación de recursos, limitando cuánta CPU, memoria y otros recursos puede consumir un contenedor.
Desde el punto de vista de la red, cada contenedor recibe su propio espacio de nombres de red, completo con una interfaz Ethernet virtual (par veth). Esta interfaz se conecta por defecto a una red puente Docker, lo que permite la comunicación entre contenedores manteniendo el aislamiento de la red anfitriona. Piensa que cada contenedor tiene su propia dirección de red privada, invisible para el mundo exterior.
En su configuración por defecto, un contenedor Docker está completamente aislado. Los servicios que se ejecutan dentro pueden comunicarse entre sí, pero son inalcanzables desde fuera del contenedor, incluso desde la máquina anfitriona. Aquí es donde se hace necesaria la exposición portuaria.
Exponer vs publicar: distinción semántica
Cuando trabajes con la configuración de puertos Docker, te encontrarás con dos conceptos relacionados pero distintos: exponer y publicar puertos.
Exponer los puertos es principalmente una función de documentación. Cuando expones un puerto en Docker, esencialmente estás añadiendo metadatos a tu imagen de contenedor, indicando que la aplicación en contenedor escucha en puertos específicos.
Sin embargo, exponer un puerto no lo hace realmente accesible desde fuera del contenedor. Sirve de documentación para los usuarios de tu imagen.
Publicar puertos es lo que realmente hace que tus servicios en contenedores estén disponibles para el mundo exterior. La publicación crea un mapeo entre un puerto de la máquina anfitriona y un puerto dentro del contenedor. Cuando publicas un puerto, Docker configura la red del host para reenviar el tráfico del puerto del host especificado al puerto del contenedor correspondiente.
A continuación te indicamos cuándo utilizar cada enfoque:
Utiliza exponer cuando construyas imágenes destinadas a ser utilizadas por otros usuarios de Docker, para documentar qué puertos utiliza tu aplicación.
Utiliza publicar cuando necesites que sistemas externos (incluido el anfitrión) accedan a servicios que se ejecutan dentro de tu contenedor.
Utiliza ambos juntos para obtener una documentación y funcionalidad completas, especialmente en entornos de producción.
Cómo exponer puertos Docker
Ahora que entendemos los conceptos, veamos los aspectos prácticos de la exposición de puertos Docker en diferentes contextos.
Estrategias de declaración de Dockerfile
La forma más básica de exponer un puerto es utilizando la instrucción EXPOSE en tu Dockerfile. Declara los puertos que debe utilizar el contenedor.
En este ejemplo, he especificado que el contenedor utilizará los puertos 80 y 443, que son estándar para el tráfico HTTP (Protocolo de Transferencia de Hipertexto) y HTTPS (Protocolo de Transferencia de Hipertexto Seguro). También puedes combinarlas en una sola instrucción:
Al especificar los puertos, es una buena práctica incluir el protocolo, TCP (Protocolo de Control de Transmisión) o UDP (Protocolo de Datagramas de Usuario) si tu aplicación utiliza uno específico:
Si no se especifica ningún protocolo, se asume TCP por defecto. Esto es apropiado para la mayoría de las aplicaciones web, pero servicios como DNS (Sistema de Nombres de Dominio) o servidores de juegos suelen requerir UDP.
Desde el punto de vista de la seguridad, te recomiendo que sólo expongas los puertos que tu aplicación necesita realmente. Cada puerto expuesto representa un vector de ataque potencial, por lo que minimizar el número de puertos expuestos sigue el principio del mínimo privilegio.
Gestión de puertos en tiempo de ejecución
Aunque la instrucción EXPOSE documenta qué puertos utiliza un contenedor, para que estos puertos sean accesibles desde el host u otras máquinas, tienes que publicarlos al ejecutar el contenedor.
He aquí cómo vincular explícitamente un puerto de contenedor a un puerto de host específico:
La bandera -p (o --publish) te permite vincular un único puerto o rango de puertos del contenedor al host.
Este comando asigna el puerto 80 dentro del contenedor al puerto 8081 en el host. Después de ejecutar este comando, puedes acceder al servidor web navegando a http://localhost:8081 en tu navegador.
Para mayor comodidad durante el desarrollo, Docker proporciona la bandera -P, P mayúscula o --publish-all, que publica automáticamente todos los puertos expuestos en puertos aleatorios con números altos del host:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
a7c53d9413bf image "/docker-.…"10 s ago Up 9 s 0.0.0.0:49153->80/tcp, :::49153->80/tcp wizardlCon la tecnología de
Aquí, el puerto 80 dentro del contenedor se asigna al puerto 49153 en el host.
Cuando trabajes con servicios que utilicen tanto TCP como UDP en el mismo puerto (como los servidores DNS), deberás especificar el protocolo al publicar:
Para las aplicaciones multicontenedor, Docker Compose proporciona una forma más manejable de configurar la red y los puertos.
En un archivo YAML de Docker Compose, puedes especificar asignaciones de puertos bajo la clave ports para cada servicio:
services:web:image: my-image
ports:-"8081:80"-"443:443"api:build: ./api
ports:-"3000:3000"Con la tecnología de
Docker Compose hace una distinción importante entre ports y expose. La sección ports crea mapeos de puertos publicados accesibles desde el exterior, mientras que expose sólo pone puertos a disposición de los servicios enlazados dentro de la misma red Compose:
services:web:image: my-image
ports:-"8081:80"database:image: postgres
expose:-"5432"Con la tecnología de
En este ejemplo, el servicio web es accesible desde el host en el puerto 8081, pero la base de datos PostgreSQL sólo es accesible para otros servicios dentro del archivo Compose, no directamente desde el host.
Para configuraciones flexibles, especialmente en distintos entornos, puedo utilizar variables de entorno en las asignaciones de puertos:
Esta sintaxis me permite especificar el puerto del host a través de una variable de entorno (WEB_PORT), por defecto 8081 si no se establece.
Exponer puertos Docker - Ejemplo práctico
Veamos un ejemplo completo de exposición de puertos Docker para una aplicación web con un backend de base de datos.
Imagina que estamos construyendo una aplicación web sencilla que consiste en un servicio API Flask de Python y una base de datos PostgreSQL. El servicio API escucha en el puerto 5000, y PostgreSQL utiliza su puerto por defecto 5432.
Este es el aspecto que podría tener nuestra sencilla aplicación Flask (app.py). Esta aplicación creará una base de datos myapp y obtendrá los datos de la tabla items, una vez creada esta tabla dentro de la base de datos.
from flask import Flask, jsonify
import os
import psycopg2
app = Flask(__name__)# Database connection parameters from environment variables
DB_HOST = os.environ.get('DB_HOST','db')
DB_NAME = os.environ.get('DB_NAME','myapp')
DB_USER = os.environ.get('DB_USER','postgres')
DB_PASS = os.environ.get('DB_PASSWORD','postgres')@app.route('/')defindex():return jsonify({'message':'API is running'})@app.route('/items')defget_items():# Connect to the PostgreSQL database
conn = psycopg2.connect(
host=DB_HOST,
database=DB_NAME,
user=DB_USER,
password=DB_PASS
)# Create a cursor and execute a query
cur = conn.cursor()
cur.execute('SELECT id, name FROM items')# Fetch results and format as list of dictionaries
items =[{'id': row[0],'name': row[1]}for row in cur.fetchall()]# Close connections
cur.close()
conn.close()return jsonify(items)if __name__ =='__main__':# Run the Flask app, binding to all interfaces (important for Docker)
app.run(host='0.0.0.0', port=5000)Con la tecnología de
A continuación, crearé nuestro archivo requirements.txt:
Ahora tengo que crear un archivo Dockerfile para nuestro servicio API de Python:
FROM python:3.9-slimWORKDIR /app# Install dependenciesCOPY requirements.txt .RUN pip install --no-cache-dir -r requirements.txt# Copy application codeCOPY . .# Expose the port Flask runs onEXPOSE 5000# Command to run the applicationCMD ["python", "app.py"]Con la tecnología de
Por último, crearé un archivo Docker Compose para orquestar ambos servicios:
Ahora puedo acceder a la API en http://localhost:5000, pero no se puede acceder directamente a la instancia PostgreSQL desde fuera de la red Docker, lo cual es una buena práctica de seguridad para los servicios de bases de datos. Pero puedes acceder a PostgreSQL desde el contenedor en ejecución:
Este comando accederá a la CLI de PostgreSQL, donde puedo ver la base de datos y crear una tabla items. Una vez creado, puedo ver los datos en http://localhost:5000/items
Cómo exponer varios puertos Docker
Si mi aplicación Python necesita exponer varios puertos, puedo especificarlos todos en el archivo Dockerfile:
FROM python:3.9-slimWORKDIR /appCOPY requirements.txt .RUN pip install --no-cache-dir -r requirements.txtCOPY . .EXPOSE 5000 8081 9090CMD ["python", "app.py"]Con la tecnología de
Este Dockerfile expone:
Puerto 5000 para la aplicación principal de Flask (actualmente activa)
Puerto 8081 para un posible cuadro de mandos de vigilancia
Puerto 9090 para una futura interfaz de depuración
Es importante señalar que, aunque he expuesto los puertos 8081 y 9090 en el Dockerfile como documentación, mi aplicación Flask actual sólo está configurada para escuchar en el puerto 5000. Los puertos adicionales expuestos indican dónde podrían implementarse futuras funcionalidades.
Cómo publicar puertos expuestos
Para publicar los puertos expuestos en un Dockerfile, utilizo la bandera -p o -P al ejecutar el contenedor:
Esto asigna el puerto 5000 en el contenedor (que está expuesto en el Dockerfile y donde Flask está escuchando) al puerto 8081 en el host. Después de ejecutar este comando, puedo acceder a mi aplicación Flask navegando a http://localhost:8081 en mi navegador.
También puedo utilizar la bandera -P para publicar automáticamente todos los puertos expuestos en puertos aleatorios del anfitrión:
5000/tcp ->0.0.0.0:32768
8081/tcp ->0.0.0.0:32769
9090/tcp ->0.0.0.0:32770Con la tecnología de
Este comando muestra todas las asignaciones de puertos de un contenedor concreto. Por ejemplo, si mi aplicación Flask sólo escucha en el puerto 5000 pero he expuesto varios puertos en el Dockerfile, Docker port me mostrará exactamente qué puertos del host están asignados a qué puertos del contenedor. Así, si el puerto 5000 se ha asignado a 32768, puedo acceder a mi aplicación Flask bajo http://localhost:32768
Técnicas de diagnóstico y resolución de problemas
Incluso con una configuración cuidadosa, pueden surgir problemas con la asignación de puertos Docker. He aquí algunas técnicas que utilizo para diagnosticar y resolver problemas comunes.
Inspección de la cartografía portuaria
El primer paso para diagnosticar problemas relacionados con los puertos es verificar que las asignaciones están configuradas correctamente. Utilizo estos comandos:
Lista todos los contenedores en ejecución con asignaciones de puertos:
El comando docker inspect proporciona información detallada, incluida la configuración de red. Para centrarte en los detalles relacionados con la red, utiliza
docker inspect --format='{{json .NetworkSettings.Ports}}'[container_id]| jqCon la tecnología de
Si necesitas realizar un análisis más profundo del tráfico, herramientas como tcpdump y netstat pueden serte muy útiles. En primer lugar, asegúrate de que están instalados dentro del contenedor. Si el contenedor aún no incluye estas herramientas, puedes instalarlas ejecutando el siguiente comando:
dockerexec-u root -it f5c6cac71492 bash-c"apt-get update && apt-get install -y net-tools tcpdump"Con la tecnología de
Para ver en qué puertos está escuchando la aplicación dentro del contenedor:
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State
tcp 000.0.0.0:5000 0.0.0.0:* LISTEN
tcp 00127.0.0.11:42265 0.0.0.0:* LISTEN
udp 00127.0.0.11:34267 0.0.0.0:*Con la tecnología de
Esto muestra, por ejemplo, que la aplicación Flask está escuchando en todas las interfaces en el puerto 5000.
Para controlar el tráfico entrante y saliente en la interfaz de red principal del contenedor (normalmente eth0):
listening on eth0, link-type EN10MB (Ethernet), snapshot length 262144 bytesCon la tecnología de
Este comando te permite rastrear el tráfico e identificar si las peticiones están llegando al contenedor o si los paquetes se están descartando o desviando.
Análisis de reglas iptables
Docker aprovecha iptables para gestionar la Traducción de Direcciones de Red (NAT) y controlar el flujo de tráfico entre el host y los contenedores. Inserta automáticamente reglas para reenviar los puertos del host a las IP y puertos apropiados del contenedor, permitiendo un acceso sin fisuras a los servicios en contenedores.
En hosts Linux nativos, puedes inspeccionar estas reglas utilizando comandos como:
Esto muestra cómo Docker asigna el tráfico entrante en puertos específicos del host a los puntos finales del contenedor. Basándote en el archivo Docker Compose de antes, verás los puertos reenviados:
RETURN garantizan el correcto enrutamiento del tráfico interno de la red Docker.
Sin embargo, dependiendo de tu entorno (por ejemplo, Docker Desktop en Windows o WSL2), Docker puede gestionar el reenvío de puertos de forma diferente, y las reglas de iptables pueden no ser visibles o modificables desde dentro del sistema. En este caso, puedes comprobar el reenvío de puertos con el siguiente comando:
Netid State Recv-Q Send-Q Local Address:Port Peer Address:Port
tcp LISTEN 04096 *:5000 *:*
tcp LISTEN 04096 *:8081 *:*
tcp LISTEN 04096 *:9090 *:* Con la tecnología de
Consejos para solucionar problemas:
Confirma las asignaciones de puertos con docker ps y docker port [container].
Comprueba si el cortafuegos del sistema anfitrión o el software de seguridad está bloqueando puertos.
Utiliza los registros del contenedor y herramientas de red como netstat y tcpdump para verificar el comportamiento de la red de tu aplicación.
Conclusión
Exponer puertos Docker es una habilidad fundamental que tiende un puente entre el aislamiento en contenedores y la usabilidad práctica. A lo largo de este artículo, he explicado cómo funciona el modelo de red de Docker, la diferencia entre exponer y publicar puertos, y he proporcionado ejemplos prácticos para diversos escenarios.
Ten en cuenta estos puntos clave:
Utiliza EXPOSE en Dockerfiles para documentar qué puertos utiliza tu aplicación
Utiliza -p o -P cuando ejecutes contenedores para que esos puertos sean accesibles desde el exterior
Aprovecha Docker Compose para gestionar aplicaciones complejas multicontenedor
Utiliza herramientas de diagnóstico para solucionar problemas cuando surjan
Dominar estos conceptos y técnicas te permitirá diseñar aplicaciones en contenedores más robustas que se comuniquen eficazmente tanto internamente como con el mundo exterior. Tanto si estás desarrollando una sencilla aplicación web como una compleja arquitectura de microservicios, una correcta gestión de puertos es fundamental para el éxito.
A medida que los contenedores sigan dominando las estrategias modernas de despliegue de aplicaciones, la capacidad de gestionar con confianza las redes y puertos Docker seguirá siendo una habilidad esencial en el conjunto de herramientas de todo programador.
Para seguir aprendiendo, no dejes de consultar los siguientes recursos:
Preguntas frecuentes sobre la exposición de un puerto Docker
¿Qué significa exponer un puerto en Docker?⌄
Exponer un puerto en Docker es una forma de documentar en qué puertos escucha tu aplicación en contenedor, pero no hace que el puerto sea accesible fuera del contenedor.
¿Cómo hago que el puerto de un contenedor Docker sea accesible desde mi máquina anfitriona?⌄
Puedes publicar un puerto utilizando la bandera -p con docker run o la sección ports en Docker Compose, que asigna un puerto de host al puerto del contenedor.
¿Cuál es la diferencia entre `EXPOSE` y `-p` en Docker?⌄
EXPOSE se utiliza en el Dockerfile para declarar puertos para la documentación, mientras que -p publica y mapea esos puertos para el acceso externo.
¿Puedo exponer varios puertos para un único contenedor Docker?⌄
Sí, puedes exponer varios puertos utilizando varias instrucciones EXPOSE en el Dockerfile o especificando varios puertos en la sección ports de Docker Compose.
¿Cómo puedo solucionar los problemas de mapeo de puertos de Docker?⌄
Utiliza comandos como docker ps, docker port, docker inspect, y herramientas de red dentro del contenedor (por ejemplo, netstat, tcpdump) para verificar las asignaciones de puertos y el flujo de tráfico.
Como fundador de Martin Data Solutions y científico de datos autónomo, ingeniero de ML e IA, aporto una cartera diversa en Regresión, Clasificación, PNL, LLM, RAG, Redes Neuronales, Métodos de Ensemble y Visión por Ordenador.
Desarrolló con éxito varios proyectos de ML de extremo a extremo, incluyendo la limpieza de datos, análisis, modelado y despliegue en AWS y GCP, ofreciendo soluciones impactantes y escalables.
Construí aplicaciones web interactivas y escalables utilizando Streamlit y Gradio para diversos casos de uso de la industria.
Enseñó y tuteló a estudiantes en ciencia de datos y analítica, fomentando su crecimiento profesional mediante enfoques de aprendizaje personalizados.
Diseñó el contenido del curso para aplicaciones de generación aumentada por recuperación (RAG) adaptadas a los requisitos de la empresa.
Es autora de blogs técnicos de IA y ML de gran impacto, que tratan temas como MLOps, bases de datos vectoriales y LLMs, logrando un compromiso significativo.
En cada proyecto que asumo, me aseguro de aplicar prácticas actualizadas en ingeniería de software y DevOps, como CI/CD, code linting, formateo, monitorización de modelos, seguimiento de experimentos y una sólida gestión de errores. Me comprometo a ofrecer soluciones completas, convirtiendo los datos en estrategias prácticas que ayuden a las empresas a crecer y a sacar el máximo partido de la ciencia de datos, el aprendizaje automático y la IA.
Explora 13 proyectos prácticos de AWS para todos los niveles. Mejora tus conocimientos sobre la nube con aplicaciones prácticas del mundo real y la orientación de expertos.
Aprende a instalar y configurar la base de datos MySQL dentro de contenedores Docker. El tutorial incluye conceptos como conectarse a servidores MySQL, ejecutar clientes MySQL para conectarse a contenedores, etc.
Este artículo explica qué son los blocs de notas y por qué deberías utilizarlos. También profundizamos en los cuadernos alojados, que facilitan el intercambio y la colaboración. Este artículo también incluye consejos, trucos y atajos de teclado.
Esta guía completa te enseña los fundamentos del backend con Python. Aprende conceptos básicos, frameworks y buenas prácticas para empezar a crear aplicaciones web.
Profundiza en habilidades avanzadas y herramientas de última generación que están transformando los puestos de ingeniería de datos con nuestro programa de Ingeniería de datos profesional.
Al trabajar con contenedores Docker, uno de los aspectos más cruciales que he encontrado es habilitar la comunicación de red entre los contenedores y el mundo exterior. Sin una configuración adecuada de los puertos, las aplicaciones en contenedores permanecen aisladas e inaccesibles. En este artículo, te guiaré a través del proceso de exposición de puertos Docker, una habilidad fundamental para cualquiera que trabaje con aplicaciones en contenedores.
Esta guía está diseñada específicamente para programadores de software, ingenieros de DevOps y profesionales de TI que necesitan que sus aplicaciones en contenedores sean accesibles a los usuarios, a otros servicios o a sistemas externos. Tanto si estás desplegando un servidor web, una API o una base de datos dentro de un contenedor, comprender la exposición de puertos es esencial para construir sistemas funcionales basados en Docker.
Al final de este artículo, comprenderás claramente cómo funciona la red Docker, los distintos métodos de exposición de puertos y las técnicas prácticas para aplicar estos conceptos en tus propios proyectos.
Si eres nuevo en Docker, considera la posibilidad de seguir uno de nuestros cursos, como Introducción a Docker, Containerización y Virtualización con Docker y Kubernetes, Docker Intermedio o Conceptos de Containerización y Virtualización.
¿Qué es exponer un puerto en Docker?
Antes de entrar en los detalles técnicos, es importante entender qué significa la exposición de puertos en el ecosistema Docker.
Principios de aislamiento de la red de contenedores
Los contenedores Docker están diseñados con el aislamiento como principio básico. Este aislamiento se consigue mediante funciones del núcleo de Linux como los espacios de nombres y los cgroups. Los espacios de nombres proporcionan aislamiento de procesos, asegurando que los procesos de un contenedor no puedan ver ni interactuar con procesos de otros contenedores o del sistema anfitrión. Los Cgroups, en cambio, controlan la asignación de recursos, limitando cuánta CPU, memoria y otros recursos puede consumir un contenedor.
Desde el punto de vista de la red, cada contenedor recibe su propio espacio de nombres de red, completo con una interfaz Ethernet virtual (par veth). Esta interfaz se conecta por defecto a una red puente Docker, lo que permite la comunicación entre contenedores manteniendo el aislamiento de la red anfitriona. Piensa que cada contenedor tiene su propia dirección de red privada, invisible para el mundo exterior.
En su configuración por defecto, un contenedor Docker está completamente aislado. Los servicios que se ejecutan dentro pueden comunicarse entre sí, pero son inalcanzables desde fuera del contenedor, incluso desde la máquina anfitriona. Aquí es donde se hace necesaria la exposición portuaria.
Exponer vs publicar: distinción semántica
Cuando trabajes con la configuración de puertos Docker, te encontrarás con dos conceptos relacionados pero distintos: exponer y publicar puertos.
Exponer los puertos es principalmente una función de documentación. Cuando expones un puerto en Docker, esencialmente estás añadiendo metadatos a tu imagen de contenedor, indicando que la aplicación en contenedor escucha en puertos específicos.
Sin embargo, exponer un puerto no lo hace realmente accesible desde fuera del contenedor. Sirve de documentación para los usuarios de tu imagen.
Publicar puertos es lo que realmente hace que tus servicios en contenedores estén disponibles para el mundo exterior. La publicación crea un mapeo entre un puerto de la máquina anfitriona y un puerto dentro del contenedor. Cuando publicas un puerto, Docker configura la red del host para reenviar el tráfico del puerto del host especificado al puerto del contenedor correspondiente.
A continuación te indicamos cuándo utilizar cada enfoque:
Cómo exponer puertos Docker
Ahora que entendemos los conceptos, veamos los aspectos prácticos de la exposición de puertos Docker en diferentes contextos.
Estrategias de declaración de Dockerfile
La forma más básica de exponer un puerto es utilizando la instrucción
EXPOSEen tu Dockerfile. Declara los puertos que debe utilizar el contenedor.En este ejemplo, he especificado que el contenedor utilizará los puertos
80y443, que son estándar para el tráfico HTTP (Protocolo de Transferencia de Hipertexto) y HTTPS (Protocolo de Transferencia de Hipertexto Seguro). También puedes combinarlas en una sola instrucción:Al especificar los puertos, es una buena práctica incluir el protocolo, TCP (Protocolo de Control de Transmisión) o UDP (Protocolo de Datagramas de Usuario) si tu aplicación utiliza uno específico:
Si no se especifica ningún protocolo, se asume TCP por defecto. Esto es apropiado para la mayoría de las aplicaciones web, pero servicios como DNS (Sistema de Nombres de Dominio) o servidores de juegos suelen requerir UDP.
Desde el punto de vista de la seguridad, te recomiendo que sólo expongas los puertos que tu aplicación necesita realmente. Cada puerto expuesto representa un vector de ataque potencial, por lo que minimizar el número de puertos expuestos sigue el principio del mínimo privilegio.
Gestión de puertos en tiempo de ejecución
Aunque la instrucción
EXPOSEdocumenta qué puertos utiliza un contenedor, para que estos puertos sean accesibles desde el host u otras máquinas, tienes que publicarlos al ejecutar el contenedor.He aquí cómo vincular explícitamente un puerto de contenedor a un puerto de host específico:
La bandera
-p(o--publish) te permite vincular un único puerto o rango de puertos del contenedor al host.Este comando asigna el puerto 80 dentro del contenedor al puerto 8081 en el host. Después de ejecutar este comando, puedes acceder al servidor web navegando a
http://localhost:8081en tu navegador.Para mayor comodidad durante el desarrollo, Docker proporciona la bandera
-P, P mayúscula o--publish-all, que publica automáticamente todos los puertos expuestos en puertos aleatorios con números altos del host:Para saber qué puertos host se asignaron, puedo utilizar el comando
docker ps:Esto mostrará resultados como:
Aquí, el puerto 80 dentro del contenedor se asigna al puerto 49153 en el host.
Cuando trabajes con servicios que utilicen tanto TCP como UDP en el mismo puerto (como los servidores DNS), deberás especificar el protocolo al publicar:
Orquestación Docker Compose
Para las aplicaciones multicontenedor, Docker Compose proporciona una forma más manejable de configurar la red y los puertos.
En un archivo YAML de Docker Compose, puedes especificar asignaciones de puertos bajo la clave
portspara cada servicio:Docker Compose hace una distinción importante entre
portsyexpose. La secciónportscrea mapeos de puertos publicados accesibles desde el exterior, mientras queexposesólo pone puertos a disposición de los servicios enlazados dentro de la misma red Compose:En este ejemplo, el servicio
webes accesible desde el host en el puerto 8081, pero la base de datos PostgreSQL sólo es accesible para otros servicios dentro del archivo Compose, no directamente desde el host.Para configuraciones flexibles, especialmente en distintos entornos, puedo utilizar variables de entorno en las asignaciones de puertos:
Esta sintaxis me permite especificar el puerto del host a través de una variable de entorno (
WEB_PORT), por defecto 8081 si no se establece.Exponer puertos Docker - Ejemplo práctico
Veamos un ejemplo completo de exposición de puertos Docker para una aplicación web con un backend de base de datos.
Imagina que estamos construyendo una aplicación web sencilla que consiste en un servicio API Flask de Python y una base de datos PostgreSQL. El servicio API escucha en el puerto
5000, y PostgreSQL utiliza su puerto por defecto5432.Este es el aspecto que podría tener nuestra sencilla aplicación Flask (
app.py). Esta aplicación creará una base de datosmyappy obtendrá los datos de la tablaitems, una vez creada esta tabla dentro de la base de datos.A continuación, crearé nuestro archivo
requirements.txt:Ahora tengo que crear un archivo Dockerfile para nuestro servicio API de Python:
Por último, crearé un archivo Docker Compose para orquestar ambos servicios:
En esta configuración:
5000, haciéndolo accesible desde la máquina anfitriona.5432pero no lo publica, por lo que sólo es accesible para otros servicios de la red Compose.5432.Para ejecutar esta aplicación, utiliza
Ahora puedo acceder a la API en
http://localhost:5000, pero no se puede acceder directamente a la instancia PostgreSQL desde fuera de la red Docker, lo cual es una buena práctica de seguridad para los servicios de bases de datos. Pero puedes acceder a PostgreSQL desde el contenedor en ejecución:Este comando accederá a la CLI de PostgreSQL, donde puedo ver la base de datos y crear una tabla
items. Una vez creado, puedo ver los datos enhttp://localhost:5000/itemsCómo exponer varios puertos Docker
Si mi aplicación Python necesita exponer varios puertos, puedo especificarlos todos en el archivo Dockerfile:
Este Dockerfile expone:
5000para la aplicación principal de Flask (actualmente activa)8081para un posible cuadro de mandos de vigilancia9090para una futura interfaz de depuraciónEs importante señalar que, aunque he expuesto los puertos
8081y9090en el Dockerfile como documentación, mi aplicación Flask actual sólo está configurada para escuchar en el puerto5000. Los puertos adicionales expuestos indican dónde podrían implementarse futuras funcionalidades.Cómo publicar puertos expuestos
Para publicar los puertos expuestos en un Dockerfile, utilizo la bandera
-po-Pal ejecutar el contenedor:Esto asigna el puerto
5000en el contenedor (que está expuesto en el Dockerfile y donde Flask está escuchando) al puerto8081en el host. Después de ejecutar este comando, puedo acceder a mi aplicación Flask navegando ahttp://localhost:8081en mi navegador.También puedo utilizar la bandera -P para publicar automáticamente todos los puertos expuestos en puertos aleatorios del anfitrión:
Para comprobar qué puertos están publicados, puedo utilizar:
Para obtener información más detallada, puedo utilizar
Este comando muestra todas las asignaciones de puertos de un contenedor concreto. Por ejemplo, si mi aplicación Flask sólo escucha en el puerto
5000pero he expuesto varios puertos en el Dockerfile, Docker port me mostrará exactamente qué puertos del host están asignados a qué puertos del contenedor. Así, si el puerto5000se ha asignado a 32768, puedo acceder a mi aplicación Flask bajohttp://localhost:32768Técnicas de diagnóstico y resolución de problemas
Incluso con una configuración cuidadosa, pueden surgir problemas con la asignación de puertos Docker. He aquí algunas técnicas que utilizo para diagnosticar y resolver problemas comunes.
Inspección de la cartografía portuaria
El primer paso para diagnosticar problemas relacionados con los puertos es verificar que las asignaciones están configuradas correctamente. Utilizo estos comandos:
Lista todos los contenedores en ejecución con asignaciones de puertos:
Obtén información detallada sobre un contenedor concreto:
Comprueba las asignaciones de puertos de un contenedor concreto:
El comando docker inspect proporciona información detallada, incluida la configuración de red. Para centrarte en los detalles relacionados con la red, utiliza
Si necesitas realizar un análisis más profundo del tráfico, herramientas como
tcpdumpynetstatpueden serte muy útiles. En primer lugar, asegúrate de que están instalados dentro del contenedor. Si el contenedor aún no incluye estas herramientas, puedes instalarlas ejecutando el siguiente comando:Para ver en qué puertos está escuchando la aplicación dentro del contenedor:
Esto muestra, por ejemplo, que la aplicación Flask está escuchando en todas las interfaces en el puerto
5000.Para controlar el tráfico entrante y saliente en la interfaz de red principal del contenedor (normalmente eth0):
Este comando te permite rastrear el tráfico e identificar si las peticiones están llegando al contenedor o si los paquetes se están descartando o desviando.
Análisis de reglas iptables
Docker aprovecha
iptablespara gestionar la Traducción de Direcciones de Red (NAT) y controlar el flujo de tráfico entre el host y los contenedores. Inserta automáticamente reglas para reenviar los puertos del host a las IP y puertos apropiados del contenedor, permitiendo un acceso sin fisuras a los servicios en contenedores.En hosts Linux nativos, puedes inspeccionar estas reglas utilizando comandos como:
Esto muestra cómo Docker asigna el tráfico entrante en puertos específicos del host a los puntos finales del contenedor. Basándote en el archivo Docker Compose de antes, verás los puertos reenviados:
Toma:
DNATredirigen el tráfico del host al contenedor.RETURNgarantizan el correcto enrutamiento del tráfico interno de la red Docker.Sin embargo, dependiendo de tu entorno (por ejemplo, Docker Desktop en Windows o WSL2), Docker puede gestionar el reenvío de puertos de forma diferente, y las reglas de
iptablespueden no ser visibles o modificables desde dentro del sistema. En este caso, puedes comprobar el reenvío de puertos con el siguiente comando:Consejos para solucionar problemas:
docker psydocker port [container].netstatytcpdumppara verificar el comportamiento de la red de tu aplicación.Conclusión
Exponer puertos Docker es una habilidad fundamental que tiende un puente entre el aislamiento en contenedores y la usabilidad práctica. A lo largo de este artículo, he explicado cómo funciona el modelo de red de Docker, la diferencia entre exponer y publicar puertos, y he proporcionado ejemplos prácticos para diversos escenarios.
Ten en cuenta estos puntos clave:
EXPOSEen Dockerfiles para documentar qué puertos utiliza tu aplicación-po-Pcuando ejecutes contenedores para que esos puertos sean accesibles desde el exteriorDominar estos conceptos y técnicas te permitirá diseñar aplicaciones en contenedores más robustas que se comuniquen eficazmente tanto internamente como con el mundo exterior. Tanto si estás desarrollando una sencilla aplicación web como una compleja arquitectura de microservicios, una correcta gestión de puertos es fundamental para el éxito.
A medida que los contenedores sigan dominando las estrategias modernas de despliegue de aplicaciones, la capacidad de gestionar con confianza las redes y puertos Docker seguirá siendo una habilidad esencial en el conjunto de herramientas de todo programador.
Para seguir aprendiendo, no dejes de consultar los siguientes recursos: