Jak komunikować się między kontenerami Docker za pośrednictwem „nazwy hosta”

91

Planuję podzielić mój serwer monolityczny na wiele małych kontenerów dockerowych, ale nie znalazłem jeszcze dobrego rozwiązania dla „komunikacji między kontenerami”. Oto mój docelowy scenariusz:

Scenariusz docelowy

Wiem, jak łączyć ze sobą kontenery i eksponować porty, ale żadne z tych rozwiązań nie jest dla mnie satysfakcjonujące.

Czy istnieje rozwiązanie umożliwiające komunikację za pośrednictwem nazw hostów (nazw kontenerów) między kontenerami, jak w tradycyjnej sieci serwerów?

Patrick Gotthard
źródło
Niedawno napisałem dokument robiąc dokładnie to, czego szukasz. Zasadniczo dokumentuje, jak zainstalować wiele kontenerów (po jednym na proces) i zintegrować je. „komunikacja między kontenerami” jest częścią gry .
xuhdev
Właśnie znalazłem blog Tumtum i natknąłem się na ten akapit w oficjalnej dokumentacji Dockera . Nie wiem, czy ja przeoczył ten akapit cały czas, czy też został niedawno dodany, ale które powinny być dokładnie to, czego potrzebuję :)
Patrick Gotthard
docker 1.10 jest niedostępny, a docker connect jest niesamowity ( github.com/docker/docker/blob/ ... ). Zobacz moją zredagowaną odpowiedź poniżej
VonC
2
Myślę, że powinieneś spróbować docker-compose . Działa bardzo dobrze.
Suhas Chikkanna

Odpowiedzi:

27

Edycja: po Docker 1.9 docker networkpolecenie (patrz poniżej https://stackoverflow.com/a/35184695/977939 ) jest zalecanym sposobem osiągnięcia tego.


Moim rozwiązaniem jest skonfigurowanie dnsmasq na hoście, aby rekord DNS był automatycznie aktualizowany: rekordy „A” mają nazwy kontenerów i automatycznie wskazują adresy IP kontenerów (co 10 sekund). Tutaj wklejono skrypt automatycznej aktualizacji :

#!/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

Upewnij się, że Twoja usługa dnsmasq jest dostępna na docker0. Następnie uruchom swój kontener, --dns HOST_ADDRESSaby korzystać z tej usługi mini dns.

Źródła: http://docs.blowb.org/setup-host/dnsmasq.html

xuhdev
źródło
To wygląda interesująco, dużo bardziej odporne niż moja odpowiedź --link. +1
VonC
@VonC wygląda na to, że nowa libnetwork może zastąpić to obejście. Zobaczmy jednak.
xuhdev
@xuhdev Ustawiam dnsmasq jak w docs.blowb.org/setup-host/dnsmasq.html . Ale napotykam problemy podczas korzystania z kopania z kontenera docker, upłynął limit czasu. Ale ping do IP interfejsu docker0 hosta działa. Kopia również z tym samym ipem docker0 z hosta docker. Masz jakieś sugestie?
Satheesh
1
@Satheesh Może to ustawienia zapory sieciowej uniemożliwiają kontenerowi wysyłanie zapytań do DNS z hosta?
xuhdev
@xuhdev dzięki, że to firewalld na moim komputerze głównym, który spowodował problem. Gdy pochyliłem się nad zaporą ogniową, mój kontener komunikuje się z dnsmasq na hoście
Satheesh
206

Nowa funkcja sieciowa umożliwia łączenie się z kontenerami według ich nazw, więc jeśli utworzysz nową sieć, każdy kontener podłączony do tej sieci może łączyć się z innymi kontenerami po ich nazwie. Przykład:

1) Utwórz nową sieć

$ docker network create <network-name>       

2) Podłącz kontenery do sieci

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

lub

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

3) Ping kontenera według nazwy

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

Zobacz sekcję dokumentacji;

Uwaga: W przeciwieństwie do starszej linkswersji, nowa sieć nie będzie tworzyć zmiennych środowiskowych ani współdzielić zmiennych środowiskowych z innymi kontenerami.

Ta funkcja obecnie nie obsługuje aliasów

Hemerson Varela
źródło
4
Działa świetnie. Dlaczego domyślna sieć nie włącza tego domyślnie?
Stéphane
Mniej oczywistą częścią jest to, że musisz ponownie uruchomić aplikację działającą w innym kontenerze. Jak więc kontener A może spowodować ponowne uruchomienie aplikacji działającej w kontenerze B? Oczywiście wydaje się, że potrzebny jest jakiś rodzaj magistrali komunikacyjnej. Chodzi mi przede wszystkim o to, aby używać Redis do sygnalizacji i komunikacji między kontenerami .. Więc wszystkie kontenery subskrybują kanał redis i tam będą mówić ... A co ze zmianami w opublikowanych portach w docker-compose plik .yml wymagający pełnego docker-compose down,up,restart?
eigenfield
to jest dokładnie to, czego szukałem przez cały dzień! nie wiedziałem, że możesz odwołać się do węzła sieciowego za pomocą jego nazwy / identyfikatora kontenera. Dziękuję Ci!
elliotwesoff
1
@ Stéphane Jest wyłączone w domyślnej bridgesieci ze względu na wsteczną kompatybilność, ale tak, zgadzam się, zdecydowanie powinno być domyślnie włączone!
helmesjo
15

To powinno być to, co --linkjest , przynajmniej dla części hosta.
W przypadku dockera 1.10 i PR 19242 byłoby to:

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

(patrz ostatnia sekcja poniżej)

To właśnie aktualizacja/etc/hosts szczegółów pliku

Oprócz zmiennych środowiskowych Docker dodaje do /etc/hostspliku wpis hosta dla kontenera źródłowego .

Na przykład uruchom serwer LDAP:

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

I zdefiniuj obraz, aby przetestować ten serwer 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

Możesz wyeksponować openldap„kontener jako internalopenldap” w obrazie testowym za pomocą --link:

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

Następnie, jeśli wpiszesz „lds”, ten alias zadziała:

ldapsearch -H ldap://internalopenldap ...

To zwróciłoby ludzi. Znaczenie internalopenldapjest poprawnie osiągnięte z ldaptestobrazu.


Oczywiście, docker 1.7 doda libnetwork, co zapewnia natywną implementację Go do łączenia kontenerów. Zobacz wpis na blogu .
Wprowadził bardziej kompletną architekturę z modelem sieci kontenerowej (CNM)

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

Spowoduje to zaktualizowanie interfejsu wiersza polecenia platformy Docker za pomocą nowych poleceń „sieciowych” i udokumentowanie sposobu -netużycia flagi „ ” do przypisywania kontenerów do sieci.


docker 1.10 ma nową sekcję Alias ​​o zasięgu sieciowym , teraz oficjalnie udokumentowany wnetwork connect :

Podczas gdy łącza zapewniają rozpoznawanie nazw prywatnych, które są zlokalizowane w kontenerze, alias o zasięgu sieci umożliwia wykrycie kontenera przez inną nazwę przez dowolny inny kontener w zakresie określonej sieci.
W przeciwieństwie do aliasu łącza, który jest definiowany przez konsumenta usługi, alias o zasięgu sieci jest definiowany przez kontener, który oferuje usługę w sieci.

Kontynuując powyższy przykład, utwórz kolejny kontener w programie isolated_nwz aliasem sieciowym.

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

--alias=[]         

Dodaj alias o zasięgu sieciowym dla kontenera

Możesz użyć --linkopcji, aby połączyć inny kontener z preferowanym aliasem

Możesz wstrzymywać, ponownie uruchamiać i zatrzymywać kontenery podłączone do sieci. Wstrzymane kontenery pozostają połączone i mogą zostać ujawnione podczas inspekcji sieci. Gdy kontener jest zatrzymany, nie pojawia się w sieci, dopóki nie zostanie ponownie uruchomiony.

Jeśli określono, adres (y) IP kontenera są ponownie stosowane po ponownym uruchomieniu zatrzymanego kontenera. Jeśli adres IP nie jest już dostępny, nie można uruchomić kontenera.

Jednym ze sposobów zagwarantowania dostępności adresu IP jest określenie adresu --ip-rangepodczas tworzenia sieci i wybranie statycznych adresów IP spoza tego zakresu. Gwarantuje to, że adres IP nie zostanie przekazany innemu kontenerowi, gdy ten kontener nie znajduje się w sieci.

$ 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
źródło
3
Problem z --link polega na tym, że nie można ponownie uruchomić kontenera bez ponownego uruchomienia połączonych kontenerów. Kiedy spojrzysz na moją grafikę, ponowne uruchomienie kontenera MySQL spowoduje ponowne uruchomienie kaskady innych kontenerów.
Patrick Gotthard
3

EDYCJA : To już nie jest krwawa krawędź: http://blog.docker.com/2016/02/docker-1-10/

Oryginalna odpowiedź
Walczyłem z nią całą noc. Jeśli nie boisz się krwawienia, skomponuj najnowszą wersję silnika Docker i Dockera implementację libnetwork.

Z odpowiednim plikiem konfiguracyjnym (który należy umieścić w wersji 2), utworzysz usługi, które będą się wzajemnie widzieć. I dodatkowo, możesz je również skalować za pomocą docker-compose (możesz skalować dowolną usługę, która nie wiąże portu na hoście)

Oto przykład plik

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

I odniesienie do nowej wersji pliku tworzenia wiadomości: https://github.com/docker/compose/blob/1.6.0-rc1/docs/networking.md

Dolanor
źródło
1

O ile wiem, przy użyciu tylko Dockera nie jest to możliwe. Potrzebujesz trochę DNS do mapowania adresów IP kontenerów na nazwy hostów.

Jeśli chcesz gotowe rozwiązanie. Jednym z rozwiązań jest użycie na przykład Konteny . Jest wyposażony w technologię nakładki sieciowej firmy Weave, która służy do tworzenia wirtualnych prywatnych sieci LAN dla każdej usługi i każdej usługi można uzyskać service_name.kontena.local-address.

Oto prosty przykład pliku YAML aplikacji Wordpress, w którym usługa Wordpress łączy się z serwerem MySQL z adresem 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
źródło