Porta de host de encaminhamento para contêiner de docker

166

É possível ter uma porta de acesso ao contêiner do Docker aberta pelo host? Concretamente, tenho o MongoDB e o RabbitMQ em execução no host e gostaria de executar um processo em um contêiner do Docker para ouvir a fila e (opcionalmente) gravar no banco de dados.

Eu sei que posso encaminhar uma porta do contêiner para o host (através da opção -p) e ter uma conexão com o mundo externo (por exemplo, internet) de dentro do contêiner Docker, mas gostaria de não expor as portas RabbitMQ e MongoDB do host para o mundo exterior.

EDIT: alguns esclarecimentos:

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

Eu tive que fazer esse truque para obter qualquer conexão com a Internet dentro do contêiner: Meu firewall está bloqueando as conexões de rede do contêiner do Docker para fora

EDIT : Eventualmente, eu fui com a criação de uma ponte personalizada usando tubulações e fazendo com que os serviços escutassem os IPs da ponte. Eu segui essa abordagem em vez de o MongoDB e o RabbitMQ ouvirem na ponte de encaixe, porque ela oferece mais flexibilidade.

JoelKuiper
fonte

Respostas:

54

O host da janela de encaixe expõe um adaptador a todos os contêineres. Supondo que você esteja no ubuntu recente, você pode executar

ip addr

Isso fornecerá uma lista de adaptadores de rede, um dos quais será semelhante a

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

Você precisará dizer ao rabbit / mongo para se conectar a esse IP (172.17.42.1). Depois disso, você poderá abrir conexões para 172.17.42.1 de dentro de seus contêineres.

Seldo
fonte
35
Como o contêiner sabe para qual IP enviar solicitações? Posso codificar o valor (172.17.42.1 aqui e no meu equipamento de teste, mas isso sempre é verdade?), Mas isso parece ir contra os princípios do docker de trabalhar com qualquer host!
JP.
2
@ Seldo: Essa interface precisa de configuração para aparecer? Estou usando o docker 1.7.1 e só tenho loe eth0.
Mkecht28
8
É possível fazer isso de alguma forma, se o host estiver apenas ouvindo no 127.0.0.1?
21716 HansHarhoff
5
"Você precisará dizer ao rabbit / mongo para se conectar a esse IP (172.17.42.1). Depois disso, você poderá abrir conexões para 172.17.42.1 de dentro de seus contêineres." Seria bom se você explicou como fazer isso
Novaterata
1
Como o @Novaterata mencionou, alguém pode explicar esse processo?
precisa saber é o seguinte
121

Uma maneira simples, mas relativamente insegura, seria usar a --net=hostopção docker run.

Essa opção permite que o contêiner use a pilha de rede do host. Em seguida, você pode se conectar aos serviços em execução no host simplesmente usando "localhost" como o nome do host.

Isso é mais fácil de configurar, porque você não precisará configurar o serviço para aceitar conexões do endereço IP do seu contêiner de docker e não precisará informar ao contêiner de docking um endereço IP ou nome de host específico ao qual se conectar, apenas uma porta.

Por exemplo, você pode testá-lo executando o seguinte comando, que assume que sua imagem é chamada my_image, sua imagem inclui o telnetutilitário e o serviço ao qual você deseja se conectar está na porta 25:

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

Se você pensa em fazê-lo dessa maneira, consulte os cuidados sobre segurança nesta página:

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

Diz:

--net = host - Diz ao Docker para ignorar a colocação do contêiner dentro de uma pilha de rede separada. Em essência, essa opção diz ao Docker para não containerizar a rede do contêiner! Embora os processos de contêiner ainda estejam confinados ao seu próprio sistema de arquivos e lista de processos e limites de recursos, um comando rápido ip addr mostrará que, em termos de rede, eles vivem "fora" no host principal do Docker e têm acesso total a suas interfaces de rede . Observe que isso não permite que o contêiner reconfigure a pilha da rede host - o que exigiria --privileged = true - mas permite que os processos do contêiner abram portas de número baixo como qualquer outro processo raiz. Também permite que o contêiner acesse serviços de rede local como D-bus. Isso pode levar os processos no contêiner a fazer coisas inesperadas, como reiniciar o computador.

David Grayson
fonte
12
Para quem não estiver usando o docker no Linux (por exemplo, usando alguma virtualização), isso não funcionará, pois o host será a VM que contém, não o SO do host real.
Sebastian Graf
13
Em particular, no MacOS, isso não é possível (sem algumas soluções alternativas): docs.docker.com/docker-for-mac/networking/…
pje
15
No MacOS, --net=hostnão funciona para permitir que o processo do contêiner se conecte à máquina host usando localhost. Em vez disso, faça com que seu contêiner se conecte ao nome docker.for.mac.host.internaldo host especial do MacOS apenas em vez de localhost. Não são necessários parâmetros extras docker runpara que isso funcione. Você pode passar isso como uma var env usando -ese quiser manter sua plataforma de contêiner independente. Dessa forma, você pode se conectar ao host nomeado no env var e transmitir docker.for.mac.host.internalno MacOS e localhostno Linux.
tul
18
é o último nome de host para mac host.docker.internal, consulte doc
xysun
O mesmo para Windows, docker run --rm -it --net=host postgres bashentãopsql -h host.docker.internal -U postgres
Leo Cavalcante
12

Você também pode criar um 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

Dessa forma, ele elasticsearchpossui um túnel para o servidor com o serviço em execução (Elasticsearch, MongoDB, PostgreSQL) e expõe a porta 9200 com esse serviço.

czerasz
fonte
8
Você está basicamente colocando a chave privada na imagem do Docker. Segredos nunca devem aparecer em uma imagem do Docker.
Teoh Han Hui
2
Até agora, esta é a única solução sã utilizável.
helvete
5

Eu tive um problema semelhante ao acessar um servidor LDAP a partir de um contêiner de docker. Eu configurei um IP fixo para o contêiner e adicionei uma regra 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

regra do iptables:

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

Dentro do acesso ao contêiner dockerhost:portnumberOnHost

Arigion
fonte
3

Se o MongoDB e o RabbitMQ estiverem em execução no host, a porta já deverá estar exposta, pois não está no Docker.

Você não precisa da -popção para expor portas do contêiner para o host. Por padrão, todas as portas estão expostas. A -popção permite expor uma porta do contêiner para a parte externa do host.

Então, meu palpite é que você não precisa de -pnada e deve estar funcionando bem :)

creack
fonte
1
Eu sabia disso, mas parece que estou perdendo um pouco de informação: veja a edição recente, pois não consigo acessar as portas no host.
JoelKuiper
2
Você precisa configurar o rabbitmq e o mongodb para ouvir também na ponte e não apenas na sua interface de rede principal.
creack 22/07
13
@creack como você consegue o rabbitmq e o mongodb para ouvir na ponte?
Ryan Walls