Como acessar a porta do host do contêiner de docker

280

Eu tenho um contêiner docker executando jenkins. Como parte do processo de compilação, preciso acessar um servidor Web que é executado localmente na máquina host. Existe uma maneira de o servidor Web host (que pode ser configurado para ser executado em uma porta) ser exposto ao contêiner jenkins?

Edição: Estou executando docker nativamente em uma máquina Linux.

ATUALIZAR:

Além da resposta do @larsks abaixo, para obter o endereço IP do IP do host da máquina host, faça o seguinte:

ip addr show docker0 | grep -Po 'inet \K[\d.]+'
Tri Nguyen
fonte
Usar um comentário, já que esta é uma resposta terrível, mas acredito que você possa acessá-lo em 172.17.1.78 - a menos que seja uma configuração do boot2docker.
CashIsClay
@CashIsClay Eu tentei isso, e tenho esse erro aindacurl: (7) Failed to connect to 172.17.1.78 port 7000: No route to host
Tri Nguyen
Você não especificou; você está executando o boot2docker ou o Docker nativamente no Linux?
Larsks
Larsks @ desculpe, eu apenas atualizei a pergunta - eu estou executando nativamente no Linux.
Tri Nguyen

Respostas:

206

Ao executar o Docker nativamente no Linux, você pode acessar os serviços de host usando o endereço IP da docker0interface. De dentro do contêiner, essa será sua rota padrão.

Por exemplo, no meu 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

E dentro de um contêiner:

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

É bastante fácil extrair esse endereço IP usando um simples script de shell:

#!/bin/sh

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

Pode ser necessário modificar as iptablesregras do seu host para permitir conexões de contêineres do Docker. Algo assim fará o truque:

# iptables -A INPUT -i docker0 -j ACCEPT

Isso permitiria o acesso a qualquer porta no host a partir de contêineres do Docker. Observe que:

  • As regras do iptables são ordenadas, e essa regra pode ou não fazer a coisa certa, dependendo de outras regras que vierem antes dela.

  • você só poderá acessar serviços de host que (a) estão ouvindo INADDR_ANY(também conhecido como 0.0.0.0) ou que estão ouvindo explicitamente na docker0interface.

larsks
fonte
1
Obrigado. Obter o endereço IP foi tudo o que era necessário para mim, sem modificar as iptables :)
Tri Nguyen
7
E o Docker for MAC? AFAIK não há rede docker0 disponível para "Docker for MAC". Nesse caso, como posso conectar ao host do contêiner?
Vijay
17
Se você usar o Docker for MAC v 17.06 ou superior, use em docker.for.mac.localhostvez de localhostou 127.0.0.1. Aqui está o doc .
Merito #
1
Eu tenho hostname usado de meu anfitrião em vez de obter o endereço IP (comando hostname no host)
Marek F
313

Para macOS e Windows

Docker v 18.03 e superior (desde 21 de março de 2018)

Use seu endereço IP interno ou conecte-se ao nome DNS especial host.docker.internalque resolverá o endereço IP interno usado pelo host.

Suporte para Linux pendente https://github.com/docker/for-linux/issues/264

MacOS com versões anteriores do Docker

Docker para Mac v 17.12 a v 18.02

O mesmo que acima, mas use em seu docker.for.mac.host.internallugar.

Docker para Mac v 17.06 a 17.11

O mesmo que acima, mas use em seu docker.for.mac.localhostlugar.

Docker para Mac 17.05 e abaixo

Para acessar a máquina host a partir do contêiner do docker, você deve anexar um alias de IP à sua interface de rede. Você pode vincular o IP que desejar, apenas certifique-se de não usá-lo com mais nada.

sudo ifconfig lo0 alias 123.123.123.123/24

Em seguida, verifique se o servidor está ouvindo o IP mencionado acima ou 0.0.0.0. Se estiver ouvindo no host local 127.0.0.1, não aceitará a conexão.

Em seguida, basta apontar o contêiner do docker para esse IP e você poderá acessar a máquina host!

Para testar, você pode executar algo como curl -X GET 123.123.123.123:3000dentro do contêiner.

O alias será redefinido a cada reinicialização, portanto, crie um script de inicialização, se necessário.

Solução e mais documentação aqui: https://docs.docker.com/docker-for-mac/networking/#use-cases-and-workarounds

Janne Annala
fonte
Eu testei isso com sucesso. Não há necessidade de desligar o firewall
alvaro g
15
A partir de 17.06, lançada em junho de 2017, sua recomendação é conectar-se ao nome DNS especial somente para Mac, docker.for.mac.localhostque resolverá o endereço IP interno usado pelo host! ... Eu testei e está realmente funcionando! :)
Kyr
Resposta imediata para usuários de Mac como eu. Obrigado. Uma coisa que não entendo é por que ip route show | awk '/default/ {print $3}'dar um IP e docker.for.mac.localhostoutro.
dmmd
8
alterado para host.docker.internal docs.docker.com/docker-for-mac/networking/…
Snowball
1
Recomenda o uso host.docker.internalno contêiner do docker e a configuração 127.0.0.1 host.docker.internalno arquivo hosts .
Junlin
85

Use --net="host"em seu docker runcomando e, localhostem seguida, em seu contêiner de docker apontará para o host do docker.

samthebest
fonte
15
Não vai funcionar para aqueles que utilizam Dpcker para Windows / Docker para Mac, tanto quanto eu entendo devido ao fato de que os recipientes executar nos ambientes virtualizados: Hyper-V (Windows) / xhyve (Mac)
ninjaboy
6
ISTO! Esta é a resposta!
user3751385
15
Apenas para o registro: dentro Docker Componse, network_mode: "host"
jbarros
Isso não está funcionando para mim no Docker for Mac versão 19.03.1 no cliente e no servidor. Eu gostaria que estivesse funcionando, mas não está.
argila
1
Mesmo se eu estou no mac, ele funciona quando você quer a comunicação entre dois recipientes estivadores e como eu transformar toda a aplicação imagem janela de encaixe para ele é suficiente para mim
bormat
27

Solução com docker-compose: Para acessar o serviço baseado em host, você pode usar o network_modeparâmetro https://docs.docker.com/compose/compose-file/#network_mode

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

EDIT 2020-04-27: recomendado para uso somente em ambiente de desenvolvimento local.

vovan
fonte
Então, como acessar Jenkins? Parece que o encaminhamento de porta não está funcionando se usar o modo de rede host
Jeff Tian
1
é uma solução muito arriscada e nem um pouco recomendada. NÃO devemos abrir nossa rede host para contêineres, a menos que seja explicitamente necessário
Fatemeh Majd
12

Criei um contêiner de docker para fazer exatamente isso https://github.com/qoomon/docker-host

Você pode simplesmente usar o nome do container dns para acessar o sistema host, por exemplo curl http://dockerhost:9200

qoomon
fonte
Essa é uma solução inteligente. Você está ciente de alguma coisa usando isso com muito tráfego? Pode haver uma sobrecarga na proxy de todo o tráfego através desse contêiner.
precisa saber é o seguinte
3
Sim ele funciona muito agradável quase nenhuma sobrecarga em todos, porque ele só funciona através de auto-retorno do dispositivo
qoomon
11

Descobrimos que uma solução mais simples para todo esse lixo de rede é usar apenas o soquete do domínio para o serviço. Se você estiver tentando se conectar ao host de qualquer maneira, basta montar o soquete como um volume e estará a caminho. Para o postgresql, isso era tão simples quanto:

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

Depois, configuramos nossa conexão com o banco de dados para usar o soquete em vez da rede. Literalmente tão fácil.

mlissner
fonte
Esta é uma otima soluçao. Bom trabalho!
um nerd pago
2
Para sua informação, enfrentamos um grande problema com isso: o Docker for Mac não suporta soquetes como volumes montados. Isso foi ótimo até que uma pessoa do Mac tentou. :(
mlissner
Obrigado! Funcionou como um encanto no Linux!
Marcelo Cardoso
7

Eu explorei as várias soluções e considero a solução menos hacky:

  1. Defina um endereço IP estático para o IP do gateway da ponte.
  2. Adicione o IP do gateway como uma entrada extra na extra_hostsdiretiva.

A única desvantagem é que, se você tiver várias redes ou projetos, precisará garantir que o intervalo de endereços IP deles não entre em conflito.

Aqui está um exemplo do 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

Você pode acessar as portas no host de dentro do contêiner usando o nome do host "dockerhost".

bcoughlan
fonte
3

Você pode acessar o servidor da web local que está sendo executado em sua máquina host de duas maneiras.

  1. Abordagem 1 com IP público

    Use o endereço IP público da máquina host para acessar o servidor da web no contêiner do docker Jenkins.

  2. Abordagem 2 com a rede host

    Use "--net host" para adicionar o contêiner da janela de encaixe Jenkins na pilha de rede do host. Os contêineres implantados na pilha do host têm acesso completo à interface do host. Você pode acessar o servidor da web local no contêiner do docker com um endereço IP privado da máquina host.

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

Inicie um contêiner com a rede host Eg: docker run --net host -it ubuntue execute ifconfigpara listar todos os endereços IP de rede disponíveis acessíveis a partir do contêiner de docker.

Por exemplo: iniciei um servidor nginx na minha máquina host local e posso acessar os URLs do site nginx a partir do contêiner do docker do 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

Acessando o servidor da web Nginx (em execução na máquina host local) a partir do contêiner do docker do Ubuntu com o endereço IP da rede 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
fonte
3

Para docker-composeusar a rede em ponte para criar uma rede privada entre contêineres, a solução aceita docker0não funciona porque a interface de saída dos contêineres não é, docker0mas sim um ID de interface gerado 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

Infelizmente, esse ID aleatório não é previsível e será alterado sempre que a composição for necessária para recriar a rede (por exemplo, em uma reinicialização do host). Minha solução para isso é criar a rede privada em uma sub-rede conhecida e configurar iptablespara aceitar esse intervalo:

Redigir fragmento de arquivo:

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"

Você pode alterar a sub-rede se o seu ambiente exigir. Selecionei arbitrariamente 192.168.32.0/20usando docker network inspectpara ver o que estava sendo criado por padrão.

Configure iptablesno host para permitir a sub-rede privada como uma fonte:

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

Esta é a iptablesregra mais simples possível . Você pode querer adicionar outras restrições, por exemplo, pela porta de destino. Não se esqueça de manter as regras do iptables quando estiver feliz que elas estejam funcionando.

Essa abordagem tem a vantagem de ser repetível e, portanto, automatizável. Uso o templatemódulo da ansible para implantar meu arquivo de composição com substituição de variável e, em seguida, uso os módulos iptablese shellpara configurar e manter as regras de firewall, respectivamente.

Andy Brown
fonte
Vi várias respostas sugerindo iptables -A INPUT -i docker0 -j ACCEPT, mas isso não me ajudou, enquanto as iptables -I INPUT 1 -s 192.168.32.0/20 -j ACCEPTsugeridas aqui resolveram meu problema.
Terry Brown
1

Esta é uma pergunta antiga e tinha muitas respostas, mas nenhuma delas se encaixa bem o suficiente no meu contexto. No meu caso, os contêineres são muito enxutos e não contêm nenhuma das ferramentas de rede necessárias para extrair o endereço IP do host de dentro do contêiner.

Além disso, o uso da --net="host"abordagem é muito aproximado e não é aplicável quando se deseja ter uma configuração de rede bem isolada com vários contêineres.

Portanto, minha abordagem é extrair o endereço dos hosts no lado do host e depois passá-lo para o contêiner com o --add-hostparâmetro:

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

ou salve o endereço IP do host em uma variável de ambiente e use a variável posteriormente:

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

E então, ele docker-hosté adicionado ao arquivo de hosts do contêiner e você pode usá-lo nas cadeias de conexão do banco de dados ou nos URLs da API.

Sexta-feira
fonte
0

Quando você tem duas imagens do docker "já" criadas e deseja colocar dois contêineres para se comunicar um com o outro.

Para isso, você pode executar convenientemente cada contêiner com seu próprio nome - e usar o sinalizador --link para ativar a comunicação entre eles. Você não consegue isso durante a construção do docker.

Quando você está em um cenário como eu, e é o seu

docker build -t "centos7/someApp" someApp/ 

Isso quebra quando você tenta

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

e você fica preso no "curl / wget", retornando nenhuma "rota para o host".

O motivo é a segurança estabelecida pelo docker que, por padrão, está banindo a comunicação de um contêiner para o host ou outros contêineres em execução no host. Isso foi bastante surpreendente para mim, devo dizer, que você esperaria que o ecossistema de máquinas docker rodando em uma máquina local simplesmente pudesse acessar uma à outra sem problemas demais.

A explicação para isso é descrita em detalhes na documentação a seguir.

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

São fornecidas duas soluções rápidas que ajudam você a se mover diminuindo a segurança da rede.

A alternativa mais simples é apenas desativar o firewall - ou permitir tudo. Isso significa executar o comando necessário, que pode ser systemctl stop firewalld, iptables -F ou equivalente.

Espero que esta informação te ajude.

99Sono
fonte
3
Apenas como uma nota, --linkagora está obsoleta.
Mr.Budris 22/09
0

Para mim (Windows 10, Docker Engine v19.03.8), foi uma mistura de https://stackoverflow.com/a/43541732/7924573 e https://stackoverflow.com/a/50866007/7924573 .

  1. altere o host / ip para host.docker.internal,
    por exemplo: LOGGER_URL = " http: //host.docker.internal: 8085 / log "
  2. configure o network_mode para bridge (se você deseja manter o encaminhamento de porta; se não usar host ):
    version: '3.7' services: server: build: . ports: - "5000:5000" network_mode: bridge ou alternativamente: Use --net="bridge"se você não estiver usando o docker-compose (semelhante a https://stackoverflow.com/a/48806927/7924573 )
    Conforme indicado nas respostas anteriores: Isso deve ser usado apenas em um ambiente de desenvolvimento local .
    Para obter mais informações, leia: https://docs.docker.com/compose/compose-file/#network_mode e https://docs.docker.com/docker-for-windows/networking/#use-cases-and-workarounds
tschomacker
fonte