Despliegas una nueva versión del contenedor el viernes. Nada exótico. Misma imagen, mismo archivo de Compose, solo cambias la etiqueta.
Entonces tu aplicación comienza a registrar: “could not resolve db” o “Name or service not known.” Cinco minutos después se “arregla sola.”
O no. Todos coinciden en que es “DNS”, que en lenguaje corporativo significa “todavía nadie sabe.”
El descubrimiento de servicios de Docker es simple cuando te mantienes en la carretera pavimentada, y sorprendentemente raro cuando pones un pie fuera de ella.
La buena noticia: la mayoría de las fallas son deterministas. La mala noticia: esa determinismo está oculto tras valores por defecto como ndots,
el alcance de las redes y el servidor DNS embebido que nunca pediste.
Un modelo mental que coincide con producción
Aquí está el modelo para mantener en la cabeza: el descubrimiento de servicios de Docker es DNS con alcance por red.
Los nombres se resuelven solo dentro de la misma red de Docker, usando el servidor DNS embebido de Docker (usualmente 127.0.0.11 dentro del contenedor),
y el mapeo nombre→IP se construye a partir de los endpoints de los contenedores y los alias de red. Eso es todo. Todo lo demás es consecuencia.
Si recuerdas una frase, que sea esta: si dos contenedores no están en la misma red definida por el usuario, no se “ven” por nombre.
“¡Pero están en el mismo host!” no es un modelo de red; es un grito de ayuda.
Qué puedes esperar que funcione
- En una red bridge definida por el usuario creada por Docker/Compose: los nombres de servicio y alias de red se resuelven a IPs de contenedor.
- En la red por defecto de Compose: cada nombre de servicio se convierte en un nombre DNS (
db,redis,api). - En Swarm: los nombres de servicio se resuelven ya sea a una VIP (balanceada) o a múltiples IPs de tareas (modo DNSRR).
Qué debes asumir que está roto hasta probar lo contrario
- Resolución de nombres a través de diferentes redes de Docker sin conexiones explícitas.
- Usar IPs de contenedores como identidades estables (son ganado, no mascotas).
- Confiar en dominios de búsqueda y nombres sin calificar cuando
ndotsy las políticas DNS corporativas están en juego. - Archivos de Compose “funcionan en mi portátil” que dependen implícitamente de redes por defecto y valores predeterminados amigables.
Una idea más: el DNS de Docker no es un servidor DNS general. Es un registro de nombres para endpoints de contenedores con una capa de reenvío.
Trátalo como una característica del plano de control con dependencia en el plano de datos. Si lo manejas como “solo DNS”, te recordará quién manda.
Idea parafraseada (atribuida): Werner Vogels suele promover la idea de que todo falla; diseña para la falla en lugar de asumir confiabilidad.
Esa es la actitud correcta también para el descubrimiento de servicios en Docker.
Hechos interesantes y un poco de historia
Estos no son datos para concursos. Explican por qué Docker se comporta como lo hace, y por qué tu “simple pregunta de DNS” se convierte en
una pasarela de incidente de dos horas.
- Docker originalmente dependía en gran medida de entradas en
/etc/hostspara las búsquedas de nombres; el descubrimiento de servicios basado en DNS maduró después a medida que evolucionó la red. - El servidor DNS embebido (
127.0.0.11) es por contenedor en el sentido de que es un listener stub dentro del namespace de red de cada contenedor, respaldado por el motor Docker. - Las redes bridge definidas por el usuario trajeron descubrimiento real de servicios: la red
bridgepor defecto históricamente no ofrecía resolución automática de nombres como lo hacen las redes definidas por el usuario. - Compose popularizó “nombre de servicio = nombre DNS”, lo que facilitó el desarrollo de microservicios y también hizo que la gente olvidara que el DNS está acotado por red.
- Swarm introdujo el descubrimiento de servicios basado en VIP: resolver un nombre de servicio a menudo devuelve una IP virtual, no la IP de la tarea, desplazando el balanceo al propio platforma.
- El comportamiento del TTL de DNS no es una promesa en entornos contenedorizados; las respuestas de DNS de Docker y las cachés del lado del cliente pueden hacer que los cambios parezcan “pegajosos”.
ndotsse volvió un agente silencioso de problemas a medida que Kubernetes y los runtimes de contenedores aumentaron el uso de dominios de búsqueda; Docker hereda el mismo comportamiento del resolvedor desde libc.- Los patrones de DNS de horizonte dividido corporativo chocan con los contenedores: lo que tu host puede resolver vía VPN puede no ser alcanzable o resoluble dentro de un contenedor sin configuración adicional.
Cómo funciona realmente el DNS en Docker (bridge, compose, swarm)
El servidor DNS embebido: por qué sigues viendo 127.0.0.11
Dentro de la mayoría de contenedores en redes definidas por el usuario, /etc/resolv.conf apunta a nameserver 127.0.0.11.
Esa dirección no es tu DNS corporativo. Es el stub DNS embebido de Docker. El stub hace dos tareas:
- Responde consultas por nombres de contenedores y alias en la misma red.
- Reenvía otras consultas a los resolvers ascendentes configurados para el daemon Docker (a menudo copiados del host).
Esto importa porque cuando el DNS ascendiente falla, verás fallos incluso para nombres internos si la librería de resolución se confunde
(time-outs, retransmisiones, expansiones por dominios de búsqueda). También importa porque un contenedor puede tener servidores DNS ascendientes diferentes al host.
Redes bridge definidas por el usuario: el valor por defecto sensato
Crea una red, conecta contenedores a ella y Docker registrará sus nombres en el espacio de nombres DNS de esa red.
Compose hace esto por ti creando una red con alcance de proyecto. Por eso db se resuelve en Compose sin que hagas nada especial.
La red bridge por defecto es una conveniencia heredada. Ha mejorado, pero todavía es una trampa en comparación con redes definidas por el usuario.
Si te importa un descubrimiento de servicios predecible, deja de usar el bridge por defecto para aplicaciones multi-contenedor.
Redes overlay (Swarm): la zona de “depende”
En Swarm, el descubrimiento de servicios está integrado en el orquestador. Típicamente obtienes uno de dos modos:
- Modo VIP: el nombre del servicio se resuelve a una IP virtual; el swarm enruta a las tareas. Genial para simplicidad; a veces confuso para depurar.
- Modo DNSRR: el nombre del servicio se resuelve a múltiples registros A (IPs de tareas). Genial para balanceo del lado cliente; fácil de usar mal.
Las redes overlay añaden partes móviles: tráfico de gossip/plano de control, mesh de enrutamiento de ingreso y manejo de DNS por nodo. Cuando falla el descubrimiento de servicios aquí,
“DNS” puede ser en realidad un problema del plano de control del overlay. Tu enfoque diagnóstico debe reflejar eso.
Qué no es el DNS de Docker
- No es un servidor DNS autoritativo completo para tu dominio empresarial.
- No es un service mesh.
- No es una promesa de que el comportamiento del resolvedor de tu aplicación sea sensato.
Broma #1: DNS significa “Did Not Sleep” (No dormí), y Docker se asegurará de que te ganes el acrónimo si ignoras la configuración del resolvedor.
Nombres de servicio, nombres de contenedor, hostnames y alias de red
La gente mezcla estos términos como si fueran sinónimos. No lo son, y la diferencia es la diferencia entre “funciona” y “falla misteriosamente en producción.”
Nombre de servicio (Compose)
En Docker Compose, el nombre de servicio (la clave bajo services:) se convierte en un nombre DNS en la red de Compose.
Por eso esto funciona:
apipuede conectarse adb:5432si ambos están en la misma red de Compose.
Compose también añade un scope de proyecto detrás de escenas, pero los nombres DNS suelen ser el nombre de servicio, no el nombre completo del contenedor.
A menos que lo anules. Y la gente lo hace.
Nombre de contenedor
El nombre de contenedor es lo que ves en docker ps. Compose a menudo lo genera como project-service-1.
Puedes establecer container_name, pero eso tiende a crear más problemas de los que resuelve:
rompe el escalado y fomenta dependencias frágiles en una identidad específica.
Hostname
El hostname de un contenedor es lo que devuelve hostname dentro del contenedor. Puede influir en cómo cierto software se identifica a sí mismo,
pero no es lo mismo que un registro DNS. Establecer hostname: en Compose no crea automáticamente un nombre DNS estable a través de redes.
Alias de red (la herramienta que realmente quieres)
Un alias de red es un nombre DNS asociado con un endpoint de contenedor en una red específica.
Los alias están acotados por red. Ese es el punto. Puedes darle al mismo contenedor distintos alias en redes diferentes.
Usa alias cuando:
- Quieres un nombre “bien conocido” y estable como
dbmientras cambias implementaciones (postgresvscockroach). - Necesitas múltiples nombres para el mismo servicio durante migraciones (
dbypostgres-primary). - Conectas un contenedor a múltiples redes y quieres controlar los nombres visibles en cada una.
Evita alias cuando:
- Los usas para cubrir un mal diseño de red (“solo aliasalo para que se resuelva”).
- Construyes un pseudo-espacio de nombres global. Las redes Docker están pensadas como límites.
Una palabra sobre “localhost”
Dentro de un contenedor, localhost es el propio contenedor. No el host. No el otro contenedor. No tus sentimientos.
Si tu aplicación usa localhost para alcanzar una dependencia, fallará a menos que esa dependencia esté en el mismo contenedor.
Separa procesos y debes cambiar la dirección.
Broma #2: “Funcionó cuando usé localhost” es el equivalente contenedorizado de “el cheque está en el correo.”
Guía de diagnóstico rápido
Cuando falla el descubrimiento de servicios, no tienes tiempo para danza interpretativa. Necesitas una secuencia que encuentre el cuello de botella rápido.
Esta guía asume que depuras desde el host Docker con acceso al CLI.
Primero: confirma que es un problema de nombres, no de TCP
- Si resolver el nombre falla: estás en territorio DNS.
- Si la resolución funciona pero la conexión falla: estás en territorio de enrutamiento/firewall/listening.
Segundo: verifica que ambos contenedores compartan una red definida por el usuario
- Si no comparten red, ningún ajuste de alias ayudará.
- Si sí comparten, inspecciona la red y los endpoints para alias e IPs.
Tercero: inspecciona la configuración del resolvedor del contenedor cliente
- Revisa
/etc/resolv.confen busca de127.0.0.11, dominios de búsqueda yoptions ndots. - Comprueba si el contenedor puede alcanzar el DNS ascendiente (si el nombre es externo).
Cuarto: reproduce la consulta con herramientas deterministas
- Usa
getent hostspara emular el comportamiento de libc. - Usa
dig/nslookuppara ver respuestas DNS crudas (si están instaladas).
Quinto: verifica el estado del engine Docker y los objetos de red
- Busca rarezas: endpoints obsoletos, redes huérfanas, reinicios del daemon, o “ajustes útiles” de DNS.
Tareas prácticas: comandos, salidas y decisiones
Estas son las tareas de campo que realmente ejecuto. Cada una incluye lo que significa la salida y qué decisión tomar a continuación.
Ejecútelas en orden cuando estés bajo presión.
Task 1: Confirmar en qué redes está el contenedor cliente
cr0x@server:~$ docker inspect -f '{{json .NetworkSettings.Networks}}' api | jq
{
"app_net": {
"IPAMConfig": null,
"Links": null,
"Aliases": [
"api",
"3d2c9a8c4c1a"
],
"MacAddress": "02:42:ac:14:00:05",
"DriverOpts": null,
"NetworkID": "c5f2e4b8d0f1...",
"EndpointID": "4c0f2a7c2e0b...",
"Gateway": "172.20.0.1",
"IPAddress": "172.20.0.5",
"IPPrefixLen": 16,
"IPv6Gateway": "",
"GlobalIPv6Address": "",
"GlobalIPv6PrefixLen": 0,
"DNSNames": [
"api",
"3d2c9a8c4c1a"
]
}
}
Significado: El contenedor api está conectado a app_net y tiene los nombres DNS api más su ID de contenedor.
Decisión: Inspecciona si el contenedor objetivo también está en app_net. Si no, arregla la conexión de red, no el DNS.
Task 2: Confirmar que el contenedor objetivo comparte la misma red
cr0x@server:~$ docker inspect -f '{{json .NetworkSettings.Networks}}' db | jq
{
"app_net": {
"IPAMConfig": null,
"Links": null,
"Aliases": [
"db",
"postgres",
"a9b1c2d3e4f5"
],
"NetworkID": "c5f2e4b8d0f1...",
"EndpointID": "d1a6b9aa0f2c...",
"Gateway": "172.20.0.1",
"IPAddress": "172.20.0.10",
"IPPrefixLen": 16
}
}
Significado: Ambos contenedores comparten app_net. El DNS debería resolver db y el alias postgres.
Decisión: Pasa a las comprobaciones del resolvedor dentro del contenedor cliente.
Task 3: Revisar la configuración del resolvedor dentro del contenedor cliente
cr0x@server:~$ docker exec -it api cat /etc/resolv.conf
nameserver 127.0.0.11
options ndots:0
search corp.example
Significado: El contenedor usa el DNS embebido de Docker. ndots:0 significa que incluso nombres de etiqueta única como db se tratan primero como “absolutos”.
Decisión: Si ves ndots:5 y una lista larga de búsqueda, espera retrasos y búsquedas externas extrañas; considera ajustar o usar FQDNs para nombres externos.
Task 4: Reproducir la búsqueda usando el comportamiento de libc
cr0x@server:~$ docker exec -it api getent hosts db
172.20.0.10 db
Significado: la resolución de nombres por libc tiene éxito. Si tu aplicación aún sostiene “cannot resolve”, sospecha caché de DNS a nivel de aplicación, una librería de resolución diferente, o un nombre equivocado.
Decisión: Si getent falla, es un problema real del resolvedor/camino DNS—continúa con herramientas DNS crudas.
Task 5: Preguntar directamente al DNS de Docker (consulta cruda)
cr0x@server:~$ docker exec -it api sh -lc 'apk add --no-cache bind-tools >/dev/null 2>&1; dig @127.0.0.11 db +short'
172.20.0.10
Significado: El DNS de Docker devuelve el registro A esperado.
Decisión: Si esto funciona pero la aplicación falla, estás tratando con caché de DNS en la aplicación, condiciones de carrera en el arranque, o conexión al nombre de red equivocado.
Task 6: Comprobar si la aplicación falla por preferencia de IPv6
cr0x@server:~$ docker exec -it api sh -lc 'getent ahosts db | head -n 5'
172.20.0.10 STREAM db
172.20.0.10 DGRAM
172.20.0.10 RAW
Significado: Solo respuestas IPv4. Si tu aplicación intenta IPv6 primero y tu DNS devuelve registros AAAA en otro lugar, puedes obtener “espera y luego fallback.”
Decisión: Si ves registros AAAA pero no hay enrutamiento IPv6, arregla la configuración IPv6 o fuerza IPv4 en el cliente.
Task 7: Confirmar que la red tiene los endpoints y alias correctos
cr0x@server:~$ docker network inspect app_net | jq '.[0] | {Name, Driver, Containers}'
{
"Name": "app_net",
"Driver": "bridge",
"Containers": {
"3d2c9a8c4c1a...": {
"Name": "api",
"IPv4Address": "172.20.0.5/16",
"IPv6Address": ""
},
"a9b1c2d3e4f5...": {
"Name": "db",
"IPv4Address": "172.20.0.10/16",
"IPv6Address": ""
}
}
}
Significado: La red ve ambos endpoints. Si el contenedor objetivo no aparece listado, el DNS no puede resolverlo en esa red.
Decisión: Adjunta el contenedor a la red o corrige la configuración de redes de Compose.
Task 8: Verificar que Compose creó la red que crees que creó
cr0x@server:~$ docker compose ls
NAME STATUS
payments running(6)
cr0x@server:~$ docker network ls | grep payments
c5f2e4b8d0f1 payments_default bridge local
Significado: Compose creó payments_default. Si tus contenedores están en otra red, puede que estés ejecutando múltiples proyectos o mezclando ejecuciones manuales.
Decisión: Estandariza: ejecuta todo desde Compose (o desde tu orquestador), no una situación a medias.
Task 9: Demostrar si un contenedor está accidentalmente en el bridge por defecto
cr0x@server:~$ docker inspect -f '{{range $k,$v := .NetworkSettings.Networks}}{{$k}} {{end}}' legacy_worker
bridge
Significado: Este contenedor está solo en la red bridge por defecto. No resolverá nombres desde tu red de Compose definida por el usuario.
Decisión: Muévelo a una red definida por el usuario; deja de esperar descubrimiento de nombres entre redes.
Task 10: Adjuntar un contenedor en ejecución a la red correcta (hot fix)
cr0x@server:~$ docker network connect payments_default legacy_worker
cr0x@server:~$ docker inspect -f '{{range $k,$v := .NetworkSettings.Networks}}{{$k}} {{end}}' legacy_worker
bridge payments_default
Significado: Ahora el contenedor comparte la red de Compose y debería resolver nombres de servicio en ella.
Decisión: Trata esto como un parche. Corrige el archivo de Compose o el despliegue para que arranque conectado correctamente la próxima vez.
Task 11: Validar que el nombre que usas está realmente registrado como alias
cr0x@server:~$ docker inspect -f '{{json (index .NetworkSettings.Networks "payments_default").Aliases}}' db | jq
[
"db",
"postgres",
"a9b1c2d3e4f5"
]
Significado: El alias postgres es real en esa red.
Decisión: Si tu app se conecta a postgresql y ese alias no está aquí, añade el alias o actualiza la configuración de la app. No adivines.
Task 12: Detectar expansión por dominio de búsqueda que causa búsquedas lentas
cr0x@server:~$ docker exec -it api sh -lc 'cat /etc/resolv.conf; echo; time getent hosts db >/dev/null'
nameserver 127.0.0.11
options ndots:5
search corp.example svc.corp.example cloud.corp.example
real 0m2.013s
user 0m0.000s
sys 0m0.003s
Significado: Una búsqueda de dos segundos por un nombre local es un indicador. Con ndots:5, el resolvedor intenta primero añadir dominios de búsqueda. Eso puede causar timeouts antes de preguntar por db tal cual.
Decisión: Para nombres internos de Docker, prefiere nombres de etiqueta única con ndots:0 cuando proceda, o establece un punto final explícito (db.) en clientes que lo soporten. Alternativamente, reduce los dominios de búsqueda para esa carga de trabajo.
Task 13: Confirmar la configuración DNS del daemon (lado host)
cr0x@server:~$ docker info | sed -n '/DNS:/,/Registry Mirrors:/p'
DNS: 10.10.0.53
10.10.0.54
Registry Mirrors:
Significado: El daemon Docker reenvía consultas no internas a estos servidores ascendientes.
Decisión: Si las búsquedas externas fallan en contenedores pero funcionan en el host, compara los resolvers ascendientes; puede que necesites configurar /etc/docker/daemon.json con DNS explícitos.
Task 14: Verificar conectividad al servicio objetivo después de que DNS tenga éxito
cr0x@server:~$ docker exec -it api sh -lc 'apk add --no-cache busybox-extras >/dev/null 2>&1; nc -vz db 5432'
db (172.20.0.10:5432) open
Significado: DNS y conectividad TCP están bien. Si la app aún falla, buscas problemas de TLS, credenciales, incompatibilidad de protocolo o configuración de la app.
Decisión: Deja de culpar al DNS. Sube de capa.
Task 15: Comprobación de Sanidad en modo Swarm (VIP vs DNSRR)
cr0x@server:~$ docker service inspect payments_api --format '{{json .Endpoint.Spec.Mode}}'
"vip"
Significado: En Swarm, el servicio se resuelve a una VIP. Verás una IP para el nombre del servicio, no IPs por tarea.
Decisión: Si tu cliente espera múltiples registros A para balanceo, cambia a DNSRR o adapta el cliente para conectarse a la VIP.
Tres mini-historias corporativas desde las trincheras del DNS
Mini-historia 1: El incidente causado por una suposición equivocada
Una empresa mediana ejecutaba una API de pagos en un único host Docker para una línea de producto legacy. Era “temporal” durante un año,
que es la unidad de tiempo estándar en arquitectura empresarial.
Usaban Compose, con un servicio api y un servicio db, además de un contenedor “migration” puntual ejecutado manualmente durante despliegues.
Un día, el job de migración empezó a fallar con “could not translate host name ‘db’ to address.”
El ingeniero on-call revisó el stack de Compose: db estaba arriba, saludable, logs bien.
Reiniciaron la base de datos de todas formas, por tradición. El fallo persistió.
La suposición equivocada fue sutil: creían que “los contenedores en el mismo host pueden resolverse por nombre.”
El contenedor de migración se arrancó con docker run y quedó en el bridge por defecto.
Los servicios de Compose estaban en project_default. Dos universos separados. Mismo host, diferentes redes, sin relación DNS.
La solución fue aburrida: ejecutar las migraciones como un servicio de Compose conectado a la misma red, o adjuntar el contenedor puntual a la red de Compose explícitamente.
Después de eso, el equipo escribió un pequeño wrapper de despliegue que se negaba a ejecutar contenedores ad-hoc sin --network.
Fue levemente molesto para los desarrolladores, lo cual es señal de que funcionó.
Mini-historia 2: La optimización que salió mal
Otra organización tenía una API interna sensible a la latencia. Alguien notó retrasos ocasionales de 1–2 segundos durante el arranque cuando los servicios intentaban alcanzar dependencias.
Un ingeniero bienintencionado concluyó, correctamente, que las reintentos de DNS y la expansión de dominios de búsqueda estaban involucrados.
Su “optimización” fue hardcodear direcciones IP para dependencias en variables de entorno.
Durante una semana, todo se vio bien. El arranque fue más rápido y las gráficas más calmadas. Luego llegó el incidente: un redeploy rutinario barajó las IPs de los contenedores.
Un servicio seguía intentando conectar a la IP antigua, y como la IP ahora pertenecía a otra cosa, el modo de fallo no fue “conexión rechazada.”
Fue “conectado al servicio equivocado”, seguido de errores de autenticación que parecían deriva de credenciales.
La caída no fue catastrófica, pero fue fea: fallos parciales, logs confusos y un rollback que no restauró la cordura porque
la configuración “optimizadora” vivía en una plantilla compartida de CI.
El postmortem fue cortés y profundamente incómodo.
La solución real fue arreglar el comportamiento del DNS, no evitarlo: reducir el ruido de dominios de búsqueda, usar alias de red e implementar reintentos con jitter
en la capa de aplicación para la disponibilidad de dependencias. Las IP volvieron a ser efímeras, como debe ser.
Mini-historia 3: La práctica aburrida pero correcta que salvó el día
Una gran empresa ejecutaba múltiples proyectos de Compose en hosts compartidos durante una fase de migración.
Era un desorden, pero el equipo SRE impuso una regla: cada proyecto define redes explícitas con nombres explícitos, y cada dependencia entre proyectos
usa una red “compartida” dedicada con alias controlados.
También forzaron una convención de nombres: las conexiones servicio-a-servicio deben usar un nombre DNS que sea o bien el nombre de servicio de Compose
en la red local, o un alias de red en la red compartida. No nombres de contenedor. No IPs. No “lo que resuelva.”
Pareció pedante y ralentizó algunos hacks rápidos.
Entonces llegó un contenedor de un proveedor con la expectativa hardcodeada de alcanzar license-server.
Sin la convención, los equipos habrían renombrado servicios, añadido extra_hosts aleatorios o empezado a editar imágenes.
En su lugar, conectaron el contenedor del proveedor a la red compartida y le dieron al servicio real de licencias un alias de red license-server.
La integración del proveedor funcionó a la primera en staging y producción. Nadie celebró porque fue aburrido.
Ese es el mayor cumplido que puedes hacer al trabajo de operaciones.
Errores comunes: síntoma → causa raíz → solución
1) “Name or service not known” desde un contenedor, pero funciona desde otro
Síntoma: api puede resolver db, pero worker no.
Causa raíz: Los contenedores están en redes diferentes (a menudo uno en el bridge por defecto).
Solución: Conecta ambos a la misma red definida por el usuario, o conecta worker a la red de Compose. Prefiere redes explícitas en Compose.
2) “Temporary failure in name resolution” que desaparece tras un reinicio
Síntoma: Arranque inicial falla; reiniciar “lo arregla.”
Causa raíz: Orden de arranque más reintentos del resolvedor más dependencia no lista; a veces también DNS lento por dominios de búsqueda.
Solución: Añade reintentos en la app con backoff; usa healthchecks y patrones wait-for; reduce el ruido de dominios de búsqueda o ajusta ndots adecuadamente.
3) El nombre de servicio se resuelve a una IP, pero las conexiones cuelgan o fallan
Síntoma: getent hosts db funciona; nc -vz db 5432 falla.
Causa raíz: Servicio no escuchando, puerto equivocado, reglas de firewall, o el contenedor no es alcanzable por enrutamiento/políticas/iptables.
Solución: Revisa sockets en escucha (ss -lntp en el objetivo), logs del contenedor y reglas iptables/nft del host Docker. El DNS ya no es tu problema.
4) Dominios externos se resuelven en el host pero no en contenedores
Síntoma: El host puede resolver corp-internal; el contenedor no.
Causa raíz: El daemon Docker usa servidores DNS ascendientes distintos al resolver VPN del host; o las rutas de la VPN no están disponibles para contenedores.
Solución: Configura DNS del daemon explícitamente; asegúrate de que la VPN y las rutas DNS sean accesibles desde los contenedores (a veces requiere ejecutar la VPN en el host de forma que los contenedores puedan usarla).
5) “Funcionó hasta que escalamos a 2 réplicas”
Síntoma: Tras escalar, las conexiones van al backend equivocado o son inconsistentes.
Causa raíz: Uso indebido de container_name o dependencia en la identidad de un único contenedor; en Swarm, confusión entre VIP y DNSRR.
Solución: Elimina container_name en servicios escalables; usa nombres de servicio; elige VIP o DNSRR intencionalmente.
6) Colisión de nombres entre proyectos
Síntoma: db se resuelve, pero al “otro” DB.
Causa raíz: Red compartida con alias que se solapan o múltiples stacks conectados a la misma red con nombres casuales.
Solución: Usa alias específicos por proyecto (payments-db), o aísla redes y comparte solo a través de una red dedicada y curada.
7) Búsquedas lentas para nombres de etiqueta única
Síntoma: Cada búsqueda interna cuesta ~1–5 segundos.
Causa raíz: ndots demasiado alto más lista de búsqueda larga causa múltiples consultas fallidas antes de intentar el nombre tal cual.
Solución: Reduce ndots para la carga, acorta dominios de búsqueda o usa un punto final donde esté soportado.
8) “Añadimos extra_hosts y ahora todo está raro”
Síntoma: Un servicio se conecta intermitentemente a endpoints antiguos después de redeploys.
Causa raíz: extra_hosts fija nombres a IPs, evitando las actualizaciones del DNS de Docker.
Solución: Elimina extra_hosts para servicios internos; usa alias de red y redes adecuadas. Usa extra_hosts solo para casos especiales que estés dispuesto a mantener indefinidamente.
Listas de verificación / plan paso a paso
Lista: diseñando descubrimiento de servicios Docker que no te despierte a medianoche
- Usa redes definidas por el usuario en todas partes. En Compose, defínelas explícitamente; no confíes en el bridge por defecto.
- Mantén el descubrimiento acotado por red. Trata las redes como límites de confianza y dominios de falla.
- Usa alias de red para nombres estables de “interfaz”. Especialmente durante migraciones e integraciones con proveedores.
- No codifiques direcciones IP de contenedores. Si crees necesitarlo, en realidad necesitas un nombre estable o una arquitectura distinta.
- Evita
container_namepara servicios escalables. Rompe el escalado y fomenta dependencias frágiles. - Haz que el arranque sea resiliente. Que el DNS esté “up” no significa que la dependencia esté lista. Implementa reintentos con jitter y timeouts sensatos.
- Controla el comportamiento del resolvedor. Observa
ndotsy dominios de búsqueda; alinéalo con tu estrategia de nombres. - Separa preocupaciones de nombres internos y externos. Servicios internos: nombres cortos/alias. Dependencias externas: FQDNs.
- Decide el modo Swarm conscientemente. VIP para simplicidad; DNSRR si tus clientes pueden manejar múltiples registros A correctamente.
- Escribe runbooks con comandos. Si tus pasos de diagnóstico viven solo en la cabeza de alguien, no existen.
Paso a paso: migrando de “nombres aleatorios” a alias sensatos
- Elige un nombre canónico de dependencia por servicio (
db,cache,queue). - Implementa alias de red en la red interna de la app para esos nombres.
- Actualiza las aplicaciones para usar solo esos nombres (ni nombres de contenedor ni IPs).
- Distribuye un servicio a la vez; mantiene alias duales temporales para compatibilidad.
- Elimina alias obsoletos después de un ciclo completo de despliegue y ventana de rollback.
Paso a paso: flujo disciplinado de depuración durante un incidente
- Ejecuta
getent hostsdentro del contenedor con fallo para el nombre de dependencia. - Si falla, inspecciona las redes del contenedor y redes compartidas.
- Inspecciona
/etc/resolv.confporndotsy dominios de búsqueda. - Usa
dig @127.0.0.11(si es posible) para validar el comportamiento del DNS embebido. - Si el DNS funciona, prueba la conectividad TCP con
nc -vz. - Si TCP funciona, para. No es DNS. Pasa a TLS/configuración/autenticación de la app.
Preguntas frecuentes
1) ¿Por qué mis contenedores usan nameserver 127.0.0.11?
Ese es el stub DNS embebido de Docker dentro del namespace del contenedor. Resuelve nombres/alias de contenedores en redes Docker y reenvía otras consultas a ascendientes.
2) ¿Por qué el descubrimiento de servicios funciona en Compose pero no con docker run?
Compose adjunta servicios a una red de proyecto definida por el usuario donde el descubrimiento basado en DNS está habilitado. Un docker run simple suele caer en el bridge por defecto
a menos que especifiques --network. Red diferente significa espacio de nombres DNS diferente.
3) ¿Es container_name una buena forma de obtener nombres DNS estables?
No. Hace que escalar sea doloroso y fomenta acoplamiento. Usa nombres de servicio y alias de red; expresan intención sin fijar identidad a un único contenedor.
4) ¿En qué se diferencian los alias de red de los hostnames?
El hostname es la identidad local dentro del contenedor. Un alias de red es un nombre DNS registrado en una red Docker específica para ese endpoint.
Los alias son lo que otros contenedores pueden resolver.
5) ¿Por qué las búsquedas a veces tardan segundos?
Causa común: ndots combinado con dominios de búsqueda. Un nombre de etiqueta única como db puede intentarse como db.corp.example,
db.svc.corp.example, etc., con timeouts, antes de intentar db directamente.
6) ¿Debería usar FQDNs para servicios internos de Docker?
Usualmente no. Usa nombres cortos de servicio y alias dentro de la red. Usa FQDNs para servicios externos, especialmente a través de VPNs y DNS empresariales.
Mezclar ambos aumenta la complejidad del resolvedor y modos de falla.
7) En Swarm, ¿por qué mi nombre de servicio se resuelve a una IP aunque haya muchas réplicas?
Probablemente estás en modo VIP. El nombre del servicio se resuelve a una IP virtual; Swarm maneja el balanceo. Si quieres múltiples registros A, usa DNSRR
y asegúrate de que tu cliente pueda manejarlos.
8) ¿Es seguro usar extra_hosts para “arreglar DNS”?
Solo si estás cómodo manteniendo ese mapeo a largo plazo. extra_hosts fija nombres a IPs y evita el descubrimiento dinámico de servicios.
Es aceptable para un endpoint genuinamente estático; es una trampa para servicios internos.
9) ¿Por qué mi app falla en DNS pero getent hosts funciona?
Tu aplicación puede usar una librería de resolución diferente, cachear agresivamente, preferir IPv6 o tener su propio cliente DNS con timeouts distintos.
Confirma qué pila de resolvedor usa el runtime (glibc vs musl vs custom) y prueba con herramientas equivalentes.
10) ¿Pueden dos redes compartir el mismo alias de forma segura?
Sí, porque los alias están acotados por red. Se vuelve inseguro cuando adjuntas un contenedor a múltiples redes y luego asumes que el nombre se resuelve igual en todas partes.
Sé explícito sobre en qué red está el cliente.
Conclusión: próximos pasos que puedes enviar
El descubrimiento de servicios de Docker falla por razones previsibles: los contenedores no están en la misma red, los alias no son lo que crees,
o el resolvedor está haciendo exactamente lo que configuraste (o heredaste) y simplemente no lo leíste.
Arreglarlo es, en su mayoría, elegir un modelo y hacerlo cumplir.
Próximos pasos prácticos
- Audita redes: lista contenedores que aún usan el
bridgepor defecto y migra a redes definidas por el usuario. - Estandariza nombres: elige nombres de servicio y añade alias de red para identidades estables de dependencias.
- Elimina configuraciones por IP: quita cualquier dirección IP de dependencias internas y hacks de
extra_hostsa menos que sean realmente estáticas. - Instrumenta el arranque: añade reintentos con jitter y timeouts acotados; trata el éxito del DNS como necesario pero no suficiente.
- Escribe el runbook: copia la guía de diagnóstico rápido y la sección de tareas en tus documentos de on-call, y mantenlos actualizados.
Si haces esas cinco cosas, la mayoría de los “incidentes DNS de Docker” dejan de ser incidentes. Se vuelven un diagnóstico de cinco minutos y una solución de una línea.
Que es lo que el DNS merece: competencia silenciosa, no drama.