Cómo acceder al puerto host desde el contenedor docker

281

Tengo un contenedor acoplable con jenkins. Como parte del proceso de compilación, necesito acceder a un servidor web que se ejecuta localmente en la máquina host. ¿Hay alguna manera de que el servidor web host (que se puede configurar para ejecutarse en un puerto) pueda exponerse al contenedor jenkins?

EDITAR: estoy ejecutando Docker de forma nativa en una máquina Linux.

ACTUALIZAR:

Además de la respuesta @larsks a continuación, para obtener la dirección IP de la IP del host de la máquina host, hago lo siguiente:

ip addr show docker0 | grep -Po 'inet \K[\d.]+'
Tri Nguyen
fuente
Usar un comentario ya que esta es una respuesta terrible, pero creo que generalmente puede acceder a él en 172.17.1.78, a menos que sea una configuración de boot2docker.
CashIsClay
@CashIsClay Intenté eso y aún recibí este errorcurl: (7) Failed to connect to 172.17.1.78 port 7000: No route to host
Tri Nguyen
No especificaste; ¿Estás ejecutando boot2docker, o estás ejecutando Docker de forma nativa en Linux?
larsks
@larsks lo siento, acabo de actualizar la pregunta: la estoy ejecutando de forma nativa en Linux.
Tri Nguyen

Respuestas:

206

Al ejecutar Docker de forma nativa en Linux, puede acceder a los servicios de host utilizando la dirección IP de la docker0interfaz. Desde el interior del contenedor, esta será su ruta predeterminada.

Por ejemplo, en mi sistema:

$ ip addr show docker0
7: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default 
    link/ether 00:00:00:00:00:00 brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
       valid_lft forever preferred_lft forever
    inet6 fe80::f4d2:49ff:fedd:28a0/64 scope link 
       valid_lft forever preferred_lft forever

Y dentro de un contenedor:

# ip route show
default via 172.17.0.1 dev eth0 
172.17.0.0/16 dev eth0  src 172.17.0.4 

Es bastante fácil extraer esta dirección IP usando un simple script de shell:

#!/bin/sh

hostip=$(ip route show | awk '/default/ {print $3}')
echo $hostip

Es posible que deba modificar las iptablesreglas en su host para permitir conexiones desde contenedores Docker. Algo así hará el truco:

# iptables -A INPUT -i docker0 -j ACCEPT

Esto permitiría el acceso a cualquier puerto en el host desde los contenedores Docker. Tenga en cuenta que:

  • Las reglas de iptables están ordenadas, y esta regla puede o no hacer lo correcto dependiendo de qué otras reglas vengan antes.

  • solo podrá acceder a los servicios de host que (a) escuchan INADDR_ANY(también conocido como 0.0.0.0) o que escuchan explícitamente en la docker0interfaz.

larsks
fuente
1
Gracias. Lo único que necesitaba era obtener la dirección IP, sin modificar las iptables :)
Tri Nguyen
77
¿Qué tal Docker para MAC? AFAIK no hay una red docker0 disponible para "Docker for MAC". En ese caso, ¿cómo puedo conectarme al host desde el contenedor?
Vijay
17
Si usa Docker para MAC v 17.06 o superior, simplemente use en docker.for.mac.localhostlugar de localhosto 127.0.0.1. Aquí está el doc .
merito
1
He usado el nombre de host de mi host en lugar de obtener la dirección IP (comando de nombre de host en el host)
Marek F
313

Para macOS y Windows

Docker v 18.03 y superior (desde el 21 de marzo de 2018)

Utilice su dirección IP interna o conéctese al nombre DNS especial host.docker.internalque se resolverá en la dirección IP interna utilizada por el host.

Soporte de Linux pendiente https://github.com/docker/for-linux/issues/264

MacOS con versiones anteriores de Docker

Docker para Mac v 17.12 a v 18.02

Igual que el anterior, pero úsalo docker.for.mac.host.internalen su lugar.

Docker para Mac v 17.06 a v 17.11

Igual que el anterior, pero úsalo docker.for.mac.localhosten su lugar.

Docker para Mac 17.05 y versiones inferiores

Para acceder a la máquina host desde el contenedor acoplable, debe adjuntar un alias de IP a su interfaz de red. Puede vincular la IP que desee, solo asegúrese de no usarla para nada más.

sudo ifconfig lo0 alias 123.123.123.123/24

Luego, asegúrese de que su servidor esté escuchando la IP mencionada anteriormente o 0.0.0.0. Si está escuchando en localhost 127.0.0.1, no aceptará la conexión.

¡Entonces solo apunte su contenedor acoplable a esta IP y podrá acceder a la máquina host!

Para probar puede ejecutar algo como curl -X GET 123.123.123.123:3000dentro del contenedor.

El alias se restablecerá en cada reinicio, por lo tanto, cree un script de inicio si es necesario.

Solución y más documentación aquí: https://docs.docker.com/docker-for-mac/networking/#use-cases-and-workarounds

Janne Annala
fuente
He probado con éxito esto. No es necesario apagar el cortafuegos
alvaro g
15
A partir del 17.06 en adelante, lanzada en junio de 2017, ¡su recomendación es conectarse al nombre DNS especial solo para Mac docker.for.mac.localhostque se resolverá en la dirección IP interna utilizada por el host! ... Lo he probado y en realidad está funcionando! :)
Kyr
Respuesta directa para usuarios de Mac como yo. Gracias. Sin embargo, una cosa que no entiendo es por qué dar ip route show | awk '/default/ {print $3}'una IP y docker.for.mac.localhostotra.
dmmd
8
cambiado a host.docker.internal docs.docker.com/docker-for-mac/networking/…
Snowball
1
Se recomienda su uso host.docker.internalen el contenedor docker y la configuración 127.0.0.1 host.docker.internalen el archivo hosts .
junlin
85

Use --net="host"en su docker runcomando, luego localhosten su contenedor docker apuntará a su host docker.

samthebest
fuente
15
Por lo que yo entiendo, no funcionará para aquellos que usan Dpcker para Windows / Docker para Mac debido al hecho de que los contenedores se ejecutan en entornos virtualizados: Hyper-V (Windows) / xhyve (Mac)
ninjaboy
66
¡ESTA! ¡Esta es la respuesta!
user3751385
15
Solo para el registro: dentro de Docker Componse, network_mode: "host"
jbarros
Esto no funciona para mí en Docker para Mac versión 19.03.1 en cliente y servidor. Desearía que funcionara, pero no lo es.
arcilla
1
Incluso si estoy en Mac, funciona cuando se desea la comunicación entre dos contenedores de acopladores y, cuando transformo todas las aplicaciones en la imagen del
acoplador
28

Solución con docker-compose: para acceder al servicio basado en host puede usar el network_modeparámetro https://docs.docker.com/compose/compose-file/#network_mode

version: '3'
services:
  jenkins:
    network_mode: host

EDITAR 2020-04-27: recomendado para usar solo en el entorno de desarrollo local.

vovan
fuente
Entonces, ¿cómo acceder a jenkins? Parece que el reenvío de puertos no funciona si usa el modo de red host
Jeff Tian
1
Es una solución muy arriesgada y no se recomienda en absoluto. NO debemos abrir nuestra red host a contenedores a menos que sea explícitamente necesario
Fatemeh Majd
12

Creé un contenedor docker para hacer exactamente eso https://github.com/qoomon/docker-host

Luego puede simplemente usar el nombre del contenedor dns para acceder al sistema host, por ejemplo curl http://dockerhost:9200

qoomon
fuente
Esa es una solución inteligente. ¿Eres consciente de algo que use esto con mucho tráfico? Puede haber una sobrecarga para enviar todo el tráfico a través de este contenedor.
bcoughlan
3
Sí, funciona bastante bien, casi sin sobrecarga, ya que solo funciona sobre un dispositivo de bucle invertido
qoomon
11

Descubrimos que una solución más simple para toda esta basura de redes es usar el socket de dominio para el servicio. Si de todos modos está intentando conectarse al host, simplemente monte el zócalo como un volumen y estará en camino. Para postgresql, esto fue tan simple como:

docker run -v /var/run/postgresql:/var/run/postgresql

Luego, simplemente configuramos nuestra conexión de base de datos para usar el socket en lugar de la red. Literalmente así de fácil.

mlissner
fuente
Esta es una gran solución. ¡Buen trabajo!
un nerd pagado el
2
Para su información, nos encontramos con un gran problema con esto: Docker para Mac no admite sockets como volúmenes montados. Esto fue muy bien hasta que una persona Mac lo intentó. :(
mlissner
¡Gracias! ¡Funcionó de maravilla en Linux!
Marcelo Cardoso
7

He explorado las diversas soluciones y encuentro que esta es la solución menos hacky:

  1. Defina una dirección IP estática para la puerta de enlace IP del puente.
  2. Agregue la puerta de enlace IP como una entrada adicional en la extra_hostsdirectiva.

El único inconveniente es que si tiene varias redes o proyectos haciendo esto, debe asegurarse de que su rango de direcciones IP no entre en conflicto.

Aquí hay un ejemplo de Docker Compose:

version: '2.3'

services:
  redis:
    image: "redis"
    extra_hosts:
      - "dockerhost:172.20.0.1"

networks:
  default:
    ipam:
      driver: default
      config:
      - subnet: 172.20.0.0/16
        gateway: 172.20.0.1

A continuación, puede acceder a los puertos en el host desde el interior del contenedor utilizando el nombre de host "dockerhost".

bcoughlan
fuente
3

Puede acceder al servidor web local que se ejecuta en su máquina host de dos maneras.

  1. Enfoque 1 con IP pública

    Utilice la dirección IP pública de la máquina host para acceder al servidor web en el contenedor acoplable Jenkins.

  2. Enfoque 2 con la red host

    Utilice "--net host" para agregar el contenedor acoplable Jenkins en la pila de red del host. Los contenedores que se implementan en la pila del host tienen acceso completo a la interfaz del host. Puede acceder al servidor web local en el contenedor acoplable con una dirección IP privada de la máquina host.

NETWORK ID          NAME                      DRIVER              SCOPE
b3554ea51ca3        bridge                    bridge              local
2f0d6d6fdd88        host                      host                local
b9c2a4bc23b2        none                      null                local

Inicie un contenedor con la red host Eg: docker run --net host -it ubuntuy ejecútelo ifconfigpara enumerar todas las direcciones IP de red disponibles a las que se puede acceder desde el contenedor acoplable.

Por ejemplo: inicié un servidor nginx en mi máquina host local y puedo acceder a las URL del sitio web nginx desde el contenedor acoplador de Ubuntu.

docker run --net host -it ubuntu

$ docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
a604f7af5e36        ubuntu              "/bin/bash"         22 seconds ago      Up 20 seconds                           ubuntu_server

Acceder al servidor web Nginx (que se ejecuta en la máquina host local) desde el contenedor acoplable de Ubuntu con una dirección IP de red privada.

root@linuxkit-025000000001:/# curl 192.168.x.x -I
HTTP/1.1 200 OK
Server: nginx/1.15.10
Date: Tue, 09 Apr 2019 05:12:12 GMT
Content-Type: text/html
Content-Length: 612
Last-Modified: Tue, 26 Mar 2019 14:04:38 GMT
Connection: keep-alive
ETag: "5c9a3176-264"
Accept-Ranges: bytes
Sreenivasa Reddy
fuente
3

Para docker-composeusar la red de puente para crear una red privada entre contenedores, la solución aceptada docker0no funciona porque la interfaz de salida de los contenedores no lo es, docker0sino que es una identificación de interfaz generada aleatoriamente, como:

$ ifconfig

br-02d7f5ba5a51: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.32.1  netmask 255.255.240.0  broadcast 192.168.47.255

Desafortunadamente, esa identificación aleatoria no es predecible y cambiará cada vez que componer tenga que recrear la red (por ejemplo, en un reinicio del host). Mi solución a esto es crear la red privada en una subred conocida y configurarla iptablespara aceptar ese rango:

Redactar fragmento de archivo:

version: "3.7"

services:
  mongodb:
    image: mongo:4.2.2
    networks:
    - mynet
    # rest of service config and other services removed for clarity

networks:
  mynet:
    name: mynet
    ipam:
      driver: default
      config:
      - subnet: "192.168.32.0/20"

Puede cambiar la subred si su entorno lo requiere. Seleccioné arbitrariamente 192.168.32.0/20usando docker network inspectpara ver lo que se estaba creando por defecto.

Configure iptablesen el host para permitir la subred privada como fuente:

$ iptables -I INPUT 1 -s 192.168.32.0/20 -j ACCEPT

Esta es la iptablesregla más simple posible . Es posible que desee agregar otras restricciones, por ejemplo, por puerto de destino. No olvides mantener tus reglas de iptables cuando estés contento de que estén funcionando.

Este enfoque tiene la ventaja de ser repetible y, por lo tanto, automatizable. Uso el templatemódulo de ansible para implementar mi archivo de redacción con sustitución de variables y luego uso los módulos iptablesy shellpara configurar y mantener las reglas del firewall, respectivamente.

Andy Brown
fuente
Vi múltiples respuestas sugiriendo iptables -A INPUT -i docker0 -j ACCEPT, pero eso no me ayudó, mientras que lo iptables -I INPUT 1 -s 192.168.32.0/20 -j ACCEPTsugerido aquí resolvió mi problema.
Terry Brown
1

Esta es una pregunta antigua y tenía muchas respuestas, pero ninguna de ellas se ajustaba lo suficientemente bien a mi contexto. En mi caso, los contenedores son muy delgados y no contienen ninguna de las herramientas de red necesarias para extraer la dirección IP del host desde el contenedor.

Además, usar el --net="host"enfoque es un enfoque muy aproximado que no es aplicable cuando se desea tener una configuración de red bien aislada con varios contenedores.

Entonces, mi enfoque es extraer la dirección de los hosts en el lado del host y luego pasarla al contenedor con el --add-hostparámetro:

$ docker run --add-host=docker-host:`ip addr show docker0 | grep -Po 'inet \K[\d.]+'` image_name

o guarde la dirección IP del host en una variable de entorno y use la variable más adelante:

$ DOCKERIP=`ip addr show docker0 | grep -Po 'inet \K[\d.]+'`
$ docker run --add-host=docker-host:$DOCKERIP image_name

Y luego docker-hostse agrega al archivo de hosts del contenedor, y puede usarlo en las cadenas de conexión de la base de datos o URL de API.

Passiday
fuente
0

Cuando tiene dos imágenes acopladas "ya" creadas y desea colocar dos contenedores para comunicarse entre sí.

Para eso, puede ejecutar convenientemente cada contenedor con su propio --name y usar el indicador --link para permitir la comunicación entre ellos. Sin embargo, no obtienes esto durante la construcción de Docker.

Cuando estás en un escenario como yo, y es tu

docker build -t "centos7/someApp" someApp/ 

Eso se rompe cuando intentas

curl http://172.17.0.1:localPort/fileIWouldLikeToDownload.tar.gz > dump.tar.gz

y te quedas atascado en "curl / wget" devolviendo ninguna "ruta al host".

La razón es la seguridad establecida por la ventana acoplable que, de forma predeterminada, prohíbe la comunicación desde un contenedor hacia el host u otros contenedores que se ejecutan en su host. Esto fue bastante sorprendente para mí, debo decir que esperaría que el ecosistema de las máquinas acoplables que se ejecutan en una máquina local simplemente pueda acceder sin problemas entre sí sin demasiados obstáculos.

La explicación de esto se describe en detalle en la siguiente documentación.

http://www.dedoimedo.com/computers/docker-networking.html

Se ofrecen dos soluciones rápidas que lo ayudan a moverse al reducir la seguridad de la red.

La alternativa más simple es simplemente apagar el firewall, o permitir todo. Esto significa ejecutar el comando necesario, que podría ser systemctl stop firewalld, iptables -F o equivalente.

Espero que esta información le ayude.

99Sono
fuente
3
Solo como una nota, --linkahora está en desuso
Mr.Budris
0

Para mí (Windows 10, Docker Engine v19.03.8) fue una mezcla de https://stackoverflow.com/a/43541732/7924573 y https://stackoverflow.com/a/50866007/7924573 .

  1. cambie el host / ip a host.docker.internal,
    por ejemplo: LOGGER_URL = " http: //host.docker.internal: 8085 / log "
  2. configure network_mode para puentear (si desea mantener el reenvío de puertos; si no usa host ):
    version: '3.7' services: server: build: . ports: - "5000:5000" network_mode: bridge o alternativamente: use --net="bridge"si no está usando docker-compose (similar a https://stackoverflow.com/a/48806927/7924573 )
    Como se señaló en respuestas anteriores: Esto solo debe usarse en un entorno de desarrollo local .
    Para obtener más información, lea: https://docs.docker.com/compose/compose-file/#network_mode y https://docs.docker.com/docker-for-windows/networking/#use-cases-and-workarounds
tschomacker
fuente