Как взаимодействовать между контейнерами Docker через «имя хоста»

91

Я планирую разделить свой монолитный сервер на множество небольших контейнеров-докеров, но пока не нашел хорошего решения для «межконтейнерного взаимодействия». Это мой целевой сценарий:

Целевой сценарий

Я знаю, как связывать контейнеры вместе и как открывать порты, но ни одно из этих решений меня не удовлетворяет.

Есть ли какое-либо решение для связи через имена хостов (имена контейнеров) между контейнерами, как в традиционной сети серверов?

Патрик Готтхард
источник
Я недавно написал документ, который делает именно то, что вы ищете. Он в основном описывает, как установить несколько контейнеров (по одному на процесс) и сделать их интегрированными. «межконтейнерная коммуникация» - это часть игры .
xuhdev
Я только что нашел блог Tumtum и наткнулся на этот абзац в официальной документации Docker . Я не знаю, пропускал ли я этот абзац все время или он был добавлен недавно, но это должно быть именно то, что мне нужно :)
Патрик Готтхард
docker 1.10 отсутствует, а подключение докеров - это круто ( github.com/docker/docker/blob/… ). См. Мой отредактированный ответ ниже
VonC
2
Думаю, тебе стоит попробовать docker-compose . Работает очень хорошо.
Сухас Чикканна

Ответы:

27

Изменить: после Docker 1.9 docker networkкоманда (см. Ниже https://stackoverflow.com/a/35184695/977939 ) является рекомендуемым способом достижения этого.


Мое решение - настроить dnsmasq на хосте для автоматического обновления DNS-записи: записи «A» имеют имена контейнеров и автоматически указывают на IP-адреса контейнеров (каждые 10 секунд). Автоматический скрипт обновления наклеен здесь:

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

Убедитесь, что ваша служба dnsmasq доступна на docker0. Затем запустите свой контейнер, --dns HOST_ADDRESSчтобы использовать эту службу mini DNS.

Ссылка: http://docs.blowb.org/setup-host/dnsmasq.html

xuhdev
источник
Это выглядит интересным, гораздо более устойчивым, чем мой ответ --link. +1
VonC
@VonC похоже, что новая libnetwork может заменить это обходное решение. Но посмотрим.
xuhdev
@xuhdev Я настраиваю dnsmasq, как в docs.blowb.org/setup-host/dnsmasq.html . Но я сталкиваюсь с проблемами при использовании копать из контейнера докеров, он истекает. Но пинг до ip интерфейса хоста docker0 работает. И копать с тем же ip docker0 от docker host работает. Есть ли у вас какие-нибудь предложения?
Satheesh
1
@Satheesh Может быть, это настройки вашего брандмауэра, которые не позволяют вашему контейнеру запрашивать DNS с хоста?
xuhdev 05
@xuhdev благодарит, это firewalld на моем хост-компьютере, который вызвал проблему. Как только я
перестал работать
206

Новая сетевая функция позволяет вам подключаться к контейнерам по их имени, поэтому, если вы создаете новую сеть, любой контейнер, подключенный к этой сети, может обращаться к другим контейнерам по их имени. Пример:

1) Создать новую сеть

$ docker network create <network-name>       

2) Подключите контейнеры к сети

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

или

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

3) Пинг контейнера по имени

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

См. Этот раздел документации;

Примечание. В отличие от прежних версий, linksновая сеть не создает переменные среды и не использует переменные среды для других контейнеров.

Эта функция в настоящее время не поддерживает псевдонимы

Хемерсон Варела
источник
4
Работает отлично. Почему сеть по умолчанию не включает это по умолчанию ??
Стефан
Менее очевидная часть заключается в том, что вам необходимо перезапустить приложение, работающее в другом контейнере. Итак, как контейнер A может заставить приложение, работающее в контейнере B, перезапуститься? Очевидно, что существует необходимость в какой-то коммуникационной шине. В первую очередь я думаю об использовании Redis для сигнализации и межконтейнерной связи. Таким образом, все контейнеры подписываются на канал redis, и именно там они будут говорить ... Как насчет изменений в опубликованных портах в docker-compose .yml файл требует полного docker-compose down,up,restart?
eigenfield 04
это именно то, что я искал весь день! не знал, что вы можете ссылаться на сетевой узел по его имени / идентификатору контейнера. Спасибо!
elliotwesoff
1
@ Stéphane Он отключен в bridgeсети по умолчанию из-за обратной совместимости, но да, я согласен, он обязательно должен быть включен по умолчанию!
helmesjo
15

Это должно быть то --link, для чего нужно , по крайней мере, для части имени хоста.
С docker 1.10 и PR 19242 это будет:

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

(см. последний раздел ниже)

Это то, что Обновление/etc/hosts сведений о файле

Помимо переменных среды, Docker добавляет в /etc/hostsфайл запись хоста для исходного контейнера .

Например, запустите сервер LDAP:

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

И определите изображение для тестирования этого сервера 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

Вы можете выставить openldapконтейнер как ' internalopenldap' внутри тестового изображения с помощью --link:

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

Затем, если вы наберете lds, этот псевдоним будет работать:

ldapsearch -H ldap://internalopenldap ...

Это вернет людей. Смысл internalopenldapправильно передан из ldaptestизображения.


Конечно, добавит docker 1.7 libnetwork, который предоставляет встроенную реализацию Go для подключения контейнеров. См. Сообщение в блоге .
Он представил более полную архитектуру с моделью сети контейнеров (CNM).

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

Это обновит интерфейс командной строки Docker новыми «сетевыми» командами и задокументирует, как используется -netфлаг « » для назначения контейнеров сетям.


docker 1.10 имеет новый раздел с псевдонимом в сетевой области , который теперь официально задокументирован вnetwork connect :

В то время как ссылки обеспечивают частное разрешение имен, которое локализовано в контейнере, псевдоним в пределах сети предоставляет способ обнаружения контейнера по альтернативному имени любым другим контейнером в пределах конкретной сети.
В отличие от псевдонима ссылки, который определяется потребителем услуги, псевдоним в области сети определяется контейнером, который предлагает услугу сети.

Продолжая приведенный выше пример, создайте еще один контейнер isolated_nwс сетевым псевдонимом.

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

--alias=[]         

Добавить псевдоним в сетевой области для контейнера

Вы можете использовать --linkопцию, чтобы связать другой контейнер с предпочтительным псевдонимом

Вы можете приостанавливать, перезапускать и останавливать контейнеры, подключенные к сети. Приостановленные контейнеры остаются подключенными и могут быть обнаружены при проверке сети. Когда контейнер остановлен, он не появляется в сети, пока вы его не перезапустите.

Если указано, IP-адрес (а) контейнера повторно применяется при перезапуске остановленного контейнера. Если IP-адрес больше не доступен, контейнер не запускается.

Один из способов гарантировать, что IP-адрес доступен, - это указать --ip-rangeпри создании сети и выбрать статический IP-адрес (а) за пределами этого диапазона. Это гарантирует, что IP-адрес не будет передан другому контейнеру, пока этот контейнер не находится в сети.

$ 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
источник
3
Проблема --link в том, что вы не можете перезапустить контейнер, не перезапустив также связанные контейнеры. Если вы посмотрите на мой рисунок, перезапуск контейнера MySQL приведет к каскаду перезапусков других контейнеров.
Патрик Готтхард,
3

РЕДАКТИРОВАТЬ : это больше не кровотечение: http://blog.docker.com/2016/02/docker-1-10/

Оригинальный ответ
Я боролся с ним всю ночь. Если вы не боитесь передышки , последняя версия движка Docker и Docker составляют обе реализации libnetwork.

С правильным конфигурационным файлом (который должен быть помещен в версию 2) вы создадите службы, которые будут видеть друг друга. И, бонус, вы также можете масштабировать их с помощью docker-compose (вы можете масштабировать любую службу, которую хотите, которая не связывает порт на хосте)

Вот пример файла

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

И ссылка на эту новую версию файла создания: https://github.com/docker/compose/blob/1.6.0-rc1/docs/networking.md

Доланор
источник
1

Насколько мне известно, при использовании только Docker это невозможно. Вам понадобится DNS для сопоставления ip: s контейнера с именами хостов.

Если хотите нестандартное решение. Одно из решений - использовать, например, Kontena . Он поставляется с технологией наложения сети от Weave, и эта технология используется для создания виртуальных частных сетей LAN для каждой службы, и каждая служба может быть доступна service_name.kontena.local-address.

Вот простой пример файла YAML приложения Wordpress, в котором служба Wordpress подключается к серверу MySQL с адресом 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
Лаури
источник