Como se comunicar entre contêineres Docker por meio de “nome do host”

90

Pretendo dividir meu servidor monolítico em vários pequenos contêineres docker, mas ainda não encontrei uma boa solução para a "comunicação entre contêineres". Este é o meu cenário-alvo:

Cenário alvo

Eu sei como vincular contêineres e como expor portas, mas nenhuma dessas soluções me satisfaz.

Existe alguma solução para se comunicar via hostnames (nomes de contêineres) entre os contêineres como em uma rede de servidor tradicional?

Patrick Gotthard
fonte
Escrevi um documento recentemente fazendo exatamente o que você está procurando. Basicamente, documenta como instalar vários contêineres (um por processo) e torná-los integrados. a "comunicação entre contêineres" faz parte do jogo .
xuhdev
Acabei de encontrar o Tumtum Blog e esbarrei neste parágrafo na documentação oficial do Docker . Não sei se perdi esse parágrafo o tempo todo ou se ele foi adicionado recentemente, mas deve ser exatamente o que eu preciso :)
Patrick Gotthard
docker 1.10 foi lançado, e docker connect é incrível ( github.com/docker/docker/blob/… ). Veja minha resposta editada abaixo
VonC
2
Acho que você deveria tentar docker-compose . Funciona muito bem.
Suhas Chikkanna

Respostas:

27

Editar: depois do Docker 1.9, o docker networkcomando (veja abaixo https://stackoverflow.com/a/35184695/977939 ) é a maneira recomendada de fazer isso.


Minha solução é configurar um dnsmasq no host para que o registro DNS seja atualizado automaticamente: os registros "A" têm os nomes dos contêineres e apontam para os endereços IP dos contêineres automaticamente (a cada 10 segundos). O script de atualização automática é colado aqui:

#!/bin/bash

# 10 seconds interval time by default
INTERVAL=${INTERVAL:-10}

# dnsmasq config directory
DNSMASQ_CONFIG=${DNSMASQ_CONFIG:-.}

# commands used in this script
DOCKER=${DOCKER:-docker}
SLEEP=${SLEEP:-sleep}
TAIL=${TAIL:-tail}

declare -A service_map

while true
do
    changed=false
    while read line
    do
        name=${line##* }
        ip=$(${DOCKER} inspect --format '{{.NetworkSettings.IPAddress}}' $name)
        if [ -z ${service_map[$name]} ] || [ ${service_map[$name]} != $ip ] # IP addr changed
        then
            service_map[$name]=$ip
            # write to file
            echo $name has a new IP Address $ip >&2
            echo "host-record=$name,$ip"  > "${DNSMASQ_CONFIG}/docker-$name"
            changed=true
        fi
    done < <(${DOCKER} ps | ${TAIL} -n +2)

    # a change of IP address occured, restart dnsmasq
    if [ $changed = true ]
    then
        systemctl restart dnsmasq
    fi

    ${SLEEP} $INTERVAL
done

Certifique-se de que seu serviço dnsmasq esteja disponível em docker0. Em seguida, inicie seu contêiner --dns HOST_ADDRESSpara usar este serviço mini dns.

Referência: http://docs.blowb.org/setup-host/dnsmasq.html

xuhdev
fonte
Isso parece interessante, muito resistente do que minha resposta --link. +1
VonC
@VonC parece que a nova libnetwork pode substituir esta solução alternativa. Vamos ver embora.
xuhdev
@xuhdev Eu configurei dnsmasq como em docs.blowb.org/setup-host/dnsmasq.html . Mas eu enfrento problemas ao usar o dig from docker container, ele atinge o tempo limite. Mas o ping para o ip da interface docker0 do host funciona. Também escave com o mesmo ip docker0 do docker host funciona. Você tem alguma sugestão?
Satheesh
1
@Satheesh Talvez sejam as configurações do firewall que impedem o seu contêiner de consultar o DNS do host?
xuhdev
@xuhdev obrigado, é o firewalld na minha máquina host, que causou o problema. Assim que abaixei o firewalld, meu contêiner se comunica com o dnsmasq no host
Satheesh
205

O novo recurso de rede permite que você se conecte a contêineres pelo nome, portanto, se você criar uma nova rede, qualquer contêiner conectado a essa rede pode alcançar outros contêineres pelo nome. Exemplo:

1) Crie uma nova rede

$ docker network create <network-name>       

2) Conecte os contêineres à rede

$ docker run --net=<network-name> ...

ou

$ docker network connect <network-name> <container-name>

3) Fazer ping do contêiner por nome

docker exec -ti <container-name-A> ping <container-name-B> 

64 bytes from c1 (172.18.0.4): icmp_seq=1 ttl=64 time=0.137 ms
64 bytes from c1 (172.18.0.4): icmp_seq=2 ttl=64 time=0.073 ms
64 bytes from c1 (172.18.0.4): icmp_seq=3 ttl=64 time=0.074 ms
64 bytes from c1 (172.18.0.4): icmp_seq=4 ttl=64 time=0.074 ms

Consulte esta seção da documentação;

Nota: Ao contrário do legado, linksa nova rede não criará variáveis ​​de ambiente, nem compartilhará variáveis ​​de ambiente com outros contêineres.

Este recurso atualmente não oferece suporte a aliases

Hemerson Varela
fonte
4
Funciona bem. Por que a rede padrão não habilita isso por padrão ??
Stéphane
A parte menos óbvia é que você precisa reiniciar um aplicativo em execução em um contêiner diferente. Então, como o contêiner A pode fazer um aplicativo em execução no contêiner B reiniciar? Claramente, parece haver necessidade de algum tipo de barramento de comunicação. Penso em usar o Redis para sinalização e comunicação entre contêineres .. Então todos os contêineres assinam o redis chanel e é lá que eles vão falar ... Que tal mudanças nos portos publicados no docker-compose Arquivo .yml que requer um arquivo completo docker-compose down,up,restart?
eigenfield
isto é exatamente o que estive procurando o dia todo! não sabia que você pode se referir a um nó de rede por seu nome / id de contêiner. obrigado!
elliotwesoff
1
@ Stéphane Está desabilitado na bridgerede padrão por causa da compatibilidade com versões anteriores, mas sim, eu concordo, definitivamente deveria ser habilitado por padrão!
helmesjo
15

Isso deve ser o que --linké para , pelo menos para a parte hostname.
Com docker 1.10 e PR 19242 , isso seria:

docker network create --net-alias=[]: Add network-scoped alias for the container

(veja a última seção abaixo)

Isso é o que Atualizando os/etc/hosts detalhes do arquivo

Além das variáveis ​​de ambiente, o Docker adiciona uma entrada de host para o contêiner de origem ao /etc/hostsarquivo.

Por exemplo, inicie um servidor LDAP:

docker run -t  --name openldap -d -p 389:389 larrycai/openldap

E defina uma imagem para testar esse servidor LDAP:

FROM ubuntu
RUN apt-get -y install ldap-utils
RUN touch /root/.bash_aliases
RUN echo "alias lds='ldapsearch -H ldap://internalopenldap -LL -b
ou=Users,dc=openstack,dc=org -D cn=admin,dc=openstack,dc=org -w
password'" > /root/.bash_aliases
ENTRYPOINT bash

Você pode expor o ' openldap' contêiner como ' internalopenldap' dentro da imagem de teste com --link:

 docker run -it --rm --name ldp --link openldap:internalopenldap ldaptest

Então, se você digitar 'lds', esse alias funcionará:

ldapsearch -H ldap://internalopenldap ...

Isso iria devolver as pessoas. O significado internalopenldapé alcançado corretamente a partir da ldaptestimagem.


Obviamente, o docker 1.7 será adicionado libnetwork, o que fornece uma implementação Go nativa para conectar contêineres. Veja a postagem do blog .
Introduziu uma arquitetura mais completa, com o Container Network Model (CNM)

https://blog.docker.com/media/2015/04/cnm-model.jpg

Isso atualizará a Docker CLI com novos comandos “network” e documentará como o -netsinalizador “ ” é usado para atribuir contêineres às redes.


docker 1.10 tem um novo alias com escopo de rede da seção , agora oficialmente documentado emnetwork connect :

Embora os links forneçam resolução de nome privada localizada dentro de um contêiner, o alias com escopo de rede fornece uma maneira de um contêiner ser descoberto por um nome alternativo por qualquer outro contêiner no escopo de uma rede específica.
Ao contrário do alias de link, que é definido pelo consumidor de um serviço, o alias com escopo de rede é definido pelo contêiner que está oferecendo o serviço à rede.

Continuando com o exemplo acima, crie outro contêiner isolated_nwcom um alias de rede.

$ docker run --net=isolated_nw -itd --name=container6 -alias app busybox
8ebe6767c1e0361f27433090060b33200aac054a68476c3be87ef4005eb1df17

--alias=[]         

Adicionar alias com escopo de rede para o contêiner

Você pode usar a --linkopção de vincular outro contêiner com um alias preferido

Você pode pausar, reiniciar e interromper os contêineres que estão conectados a uma rede. Os contêineres pausados ​​permanecem conectados e podem ser revelados por uma inspeção de rede. Quando o contêiner é interrompido, ele não aparece na rede até que você o reinicie.

Se especificado, o (s) endereço (s) IP do contêiner são reaplicados quando um contêiner interrompido é reiniciado. Se o endereço IP não estiver mais disponível, o contêiner falhará ao iniciar.

Uma maneira de garantir que o endereço IP está disponível é especificar um --ip-rangeao criar a rede e escolher o (s) endereço (s) IP estático (s) fora desse intervalo. Isso garante que o endereço IP não seja fornecido a outro contêiner enquanto esse contêiner não estiver na rede.

$ docker network create --subnet 172.20.0.0/16 --ip-range 172.20.240.0/20 multi-host-network

$ docker network connect --ip 172.20.128.2 multi-host-network container2
$ docker network connect --link container1:c1 multi-host-network container2
VonC
fonte
3
O problema de --link é que você não consegue reiniciar um contêiner sem reiniciar os contêineres vinculados também. Quando você olha meu gráfico, reiniciar o contêiner do MySQL resultará em uma cascata de reinicializações de outros contêineres.
Patrick Gotthard
3

EDIT : Não é mais uma tecnologia de ponta: http://blog.docker.com/2016/02/docker-1-10/

Resposta Original Lutei
contra isso a noite toda. Se você não tem medo de sangramento, a versão mais recente do mecanismo Docker e do Docker compor implementam libnetwork.

Com o arquivo de configuração correto (que precisa ser colocado na versão 2), você criará serviços que todos verão uns aos outros. E, bônus, você também pode escaloná-los com docker-compose (você pode escalar qualquer serviço que desejar que não vincule a porta no host)

Aqui está um arquivo de exemplo

version: "2"
services:
  router:
    build: services/router/
    ports:
      - "8080:8080"
  auth:
    build: services/auth/
  todo:
    build: services/todo/
  data:
    build: services/data/

E a referência para esta nova versão do arquivo de composição: https://github.com/docker/compose/blob/1.6.0-rc1/docs/networking.md

Dolanor
fonte
1

Pelo que eu sei, usando apenas o Docker, isso não é possível. Você precisa de algum DNS para mapear o ip: s do contêiner para nomes de host.

Se você quiser uma solução fora da caixa. Uma solução é usar, por exemplo, Kontena . Ele vem com a tecnologia de sobreposição de rede da Weave e essa tecnologia é usada para criar redes LAN privadas virtuais para cada serviço e cada serviço pode ser alcançado service_name.kontena.local-address.

Aqui está um exemplo simples de arquivo YAML do aplicativo Wordpress onde o serviço Wordpress se conecta ao servidor MySQL com o endereço wordpress-mysql.kontena.local:

wordpress:                                                                         
  image: wordpress:4.1                                                             
  stateful: true                                                                   
  ports:                                                                           
    - 80:80                                                                      
  links:                                                                           
    - mysql:wordpress-mysql                                                        
  environment:                                                                     
    - WORDPRESS_DB_HOST=wordpress-mysql.kontena.local                              
    - WORDPRESS_DB_PASSWORD=secret                                                 
mysql:                                                                             
  image: mariadb:5.5                                                               
  stateful: true                                                                   
  environment:                                                                     
    - MYSQL_ROOT_PASSWORD=secret
Lauri
fonte