Reenviar el puerto host al contenedor acoplable

167

¿Es posible tener un puerto de acceso de contenedor Docker abierto por el host? Concretamente, tengo MongoDB y RabbitMQ ejecutándose en el host y me gustaría ejecutar un proceso en un contenedor Docker para escuchar la cola y (opcionalmente) escribir en la base de datos.

Sé que puedo reenviar un puerto desde el contenedor al host (a través de la opción -p) y tener una conexión con el mundo exterior (es decir, Internet) desde el contenedor Docker, pero me gustaría no exponer los puertos RabbitMQ y MongoDB del anfitrión al mundo exterior.

EDITAR: algunas aclaraciones:

Starting Nmap 5.21 ( http://nmap.org ) at 2013-07-22 22:39 CEST
Nmap scan report for localhost (127.0.0.1)
Host is up (0.00027s latency).
PORT     STATE SERVICE
6311/tcp open  unknown

joelkuiper@vps20528 ~ % docker run -i -t base /bin/bash
root@f043b4b235a7:/# apt-get install nmap
root@f043b4b235a7:/# nmap 172.16.42.1 -p 6311 # IP found via docker inspect -> gateway

Starting Nmap 6.00 ( http://nmap.org ) at 2013-07-22 20:43 UTC
Nmap scan report for 172.16.42.1
Host is up (0.000060s latency).
PORT     STATE    SERVICE
6311/tcp filtered unknown
MAC Address: E2:69:9C:11:42:65 (Unknown)

Nmap done: 1 IP address (1 host up) scanned in 13.31 seconds

Tuve que hacer este truco para obtener cualquier conexión a Internet dentro del contenedor: mi firewall está bloqueando las conexiones de red desde el contenedor acoplable hacia afuera

EDITAR : Finalmente, creé un puente personalizado utilizando tuberías y haciendo que los servicios escucharan en las IP del puente. Seguí este enfoque en lugar de que MongoDB y RabbitMQ escucharan en el puente acoplable porque brinda más flexibilidad.

JoelKuiper
fuente

Respuestas:

54

Su docker host expone un adaptador a todos los contenedores. Suponiendo que está en ubuntu reciente, puede ejecutar

ip addr

Esto le dará una lista de adaptadores de red, uno de los cuales tendrá un aspecto similar

3: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP
link/ether 22:23:6b:28:6b:e0 brd ff:ff:ff:ff:ff:ff
inet 172.17.42.1/16 scope global docker0
inet6 fe80::a402:65ff:fe86:bba6/64 scope link
   valid_lft forever preferred_lft forever

Tendrá que decirle a rabbit / mongo que se una a esa IP (172.17.42.1). Después de eso, debería poder abrir conexiones a 172.17.42.1 desde sus contenedores.

Seldo
fuente
36
¿Cómo sabe el contenedor a qué IP enviar las solicitudes? Puedo codificar el valor (172.17.42.1 aquí y en mi plataforma de prueba, pero ¿es eso siempre cierto?), ¡Pero eso parece ir en contra de los principios de acoplamiento de trabajar con cualquier host!
JP.
2
@Seldo: ¿Esa interfaz necesita configuración para aparecer? Estoy usando docker 1.7.1, y solo tengo loy eth0.
mknecht
8
¿Es posible hacer esto de alguna manera, si el host solo está escuchando en 127.0.0.1?
HansHarhoff
55
"Deberá decirle a conejo / mongo que se una a esa IP (172.17.42.1). Después de eso, debería poder abrir conexiones a 172.17.42.1 desde sus contenedores". Sería bueno que explicaras cómo hacerlo
Novaterata
1
Como mencionó @Novaterata, ¿alguien puede explicar ese proceso?
keskinsaf
122

Una forma simple pero relativamente insegura sería utilizar la --net=hostopción para docker run.

Esta opción hace que el contenedor use la pila de red del host. Luego puede conectarse a los servicios que se ejecutan en el host simplemente usando "localhost" como nombre de host.

Esto es más fácil de configurar porque no tendrá que configurar el servicio para aceptar conexiones desde la dirección IP de su contenedor Docker, y no tendrá que decirle al contenedor Docker una dirección IP específica o un nombre de host para conectarse, solo un puerto

Por ejemplo, puede probarlo ejecutando el siguiente comando, que supone que se llama a my_imagesu imagen, su imagen incluye la telnetutilidad y el servicio al que desea conectarse está en el puerto 25:

docker run --rm -i -t --net=host my_image telnet localhost 25

Si considera hacerlo de esta manera, vea la advertencia sobre seguridad en esta página:

https://docs.docker.com/articles/networking/

Dice:

--net = host - Le dice a Docker que omita colocar el contenedor dentro de una pila de red separada. En esencia, esta elección le dice a Docker que no contenga las redes del contenedor. Si bien los procesos de contenedor seguirán limitados a su propio sistema de archivos y lista de procesos y límites de recursos, un comando rápido de ip addr le mostrará que, en cuanto a la red, viven "fuera" en el host Docker principal y tienen acceso completo a sus interfaces de red . Tenga en cuenta que esto no permite que el contenedor reconfigure la pila de red del host, eso requeriría --privileged = true - pero sí permite que los procesos del contenedor abran puertos de números bajos como cualquier otro proceso raíz. También permite que el contenedor acceda a servicios de red locales como D-bus. Esto puede llevar a que los procesos en el contenedor puedan hacer cosas inesperadas como reiniciar su computadora.

David Grayson
fuente
12
Para cualquiera que no use docker en Linux (por ejemplo, que use algo de virtualización) esto no funcionará, ya que el host será la VM que contiene, no el sistema operativo host real.
Sebastian Graf
13
En particular, en MacOS, esto no es posible (sin algunas soluciones): docs.docker.com/docker-for-mac/networking/…
pje
15
En MacOS, --net=hostno funciona para permitir que su proceso de contenedor se conecte a su máquina host usando localhost. En su lugar, haga que su contenedor se conecte al nombre docker.for.mac.host.internalde host especial de MacOS en lugar de localhost. No se necesitan parámetros adicionales docker runpara que esto funcione. Puede pasar esto como una variable ambiental -esi desea mantener su plataforma de contenedores independiente. De esa manera, puede conectarse al host nombrado en la variable env y pasar docker.for.mac.host.internalen MacOS y localhostLinux.
tul
18
último nombre de host para mac es host.docker.internal, ver doc
xysun
Lo mismo para Windows docker run --rm -it --net=host postgres bashentoncespsql -h host.docker.internal -U postgres
Leo Cavalcante
12

También podría crear un túnel ssh.

docker-compose.yml:

---

version: '2'

services:
  kibana:
    image: "kibana:4.5.1"
    links:
      - elasticsearch
    volumes:
      - ./config/kibana:/opt/kibana/config:ro

  elasticsearch:
    build:
      context: .
      dockerfile: ./docker/Dockerfile.tunnel
    entrypoint: ssh
    command: "-N elasticsearch -L 0.0.0.0:9200:localhost:9200"

docker/Dockerfile.tunnel:

FROM buildpack-deps:jessie

RUN apt-get update && \
    DEBIAN_FRONTEND=noninteractive \
    apt-get -y install ssh && \
    apt-get clean && \
    rm -rf /var/lib/apt/lists/*

COPY ./config/ssh/id_rsa /root/.ssh/id_rsa
COPY ./config/ssh/config /root/.ssh/config
COPY ./config/ssh/known_hosts /root/.ssh/known_hosts
RUN chmod 600 /root/.ssh/id_rsa && \
    chmod 600 /root/.ssh/config && \
    chown $USER:$USER -R /root/.ssh

config/ssh/config:

# Elasticsearch Server
Host elasticsearch
    HostName jump.host.czerasz.com
    User czerasz
    ForwardAgent yes
    IdentityFile ~/.ssh/id_rsa

De esta manera, elasticsearchtiene un túnel hacia el servidor con el servicio en ejecución (Elasticsearch, MongoDB, PostgreSQL) y expone el puerto 9200 con ese servicio.

czerasz
fuente
8
Básicamente, está poniendo la clave privada en la imagen de Docker. Los secretos nunca deberían entrar en una imagen de Docker.
Teoh Han Hui
2
Esta es la única solución sensata utilizable hasta ahora.
helvete
5

Tuve un problema similar al acceder a un servidor LDAP desde un contenedor acoplable. Configuré una IP fija para el contenedor y agregué una regla de firewall.

docker-compose.yml:

version: '2'
services:
  containerName:
    image: dockerImageName:latest
    extra_hosts:
      - "dockerhost:192.168.50.1"
    networks:
      my_net:
        ipv4_address: 192.168.50.2
networks:
  my_net:
    ipam:
      config:
      - subnet: 192.168.50.0/24

regla de iptables:

iptables -A INPUT -j ACCEPT -p tcp -s 192.168.50.2 -d $192.168.50.1 --dport portnumberOnHost

Dentro del acceso al contenedor dockerhost:portnumberOnHost

Arigion
fuente
3

Si MongoDB y RabbitMQ se están ejecutando en el Host, entonces el puerto ya debería estar expuesto, ya que no está dentro de Docker.

No necesita la -popción para exponer puertos del contenedor al host. Por defecto, todos los puertos están expuestos. La -popción le permite exponer un puerto desde el contenedor al exterior del host.

Entonces, supongo que no necesitas -pnada y debería funcionar bien :)

crujir
fuente
1
Lo sabía, pero parece que me falta un poco de información: vea la edición reciente, ya que no puedo llegar a los puertos en el host.
JoelKuiper
2
Debe configurar rabbitmq y mongodb para escuchar también en el puente y no solo en su interfaz de red principal.
crujir
13
@creack, ¿cómo haces que rabbitmq y mongodb escuchen en el puente?
Ryan Walls