Як взаємодіяти між контейнерами Docker за допомогою “hostname”


91

Я планую розділити свій монолітний сервер на багато невеликих контейнерів докерів, але поки не знайшов хорошого рішення для "міжконтейнерного зв'язку". Це мій цільовий сценарій:

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

Я знаю, як зв'язати контейнери разом і як виставити порти, але жодне з цих рішень не задовольняє мене.

Чи існує якесь рішення для зв'язку між іменами хостів (іменами контейнерів) між контейнерами, як у традиційній серверній мережі?


Нещодавно я написав документ, роблячи саме те, що ви шукаєте. В основному він документує, як встановити кілька контейнерів (по одному на процес) та зробити їх інтегрованими. "міжконтейнерне спілкування" є частиною гри .
xuhdev

Я щойно знайшов блог Tumtum і натрапив на цей параграф в офіційній документації Docker . Я не знаю, чи я весь час пропускав цей абзац, чи його нещодавно додавали, але це має бути саме те, що мені потрібно :)
Патрік Готхард,

docker 1.10 вийшов, а docker connect чудовий ( github.com/docker/docker/blob/… ). Дивіться мою відредаговану відповідь нижче
VonC

2
Я думаю, вам слід спробувати docker-compose . Працює дуже добре.
Suhas Chikkanna

Відповіді:


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


Це виглядає цікаво, набагато стійкіше, ніж моя відповідь --link. +1
VonC

@VonC схоже, що нова мережа libnet може замінити це обхідне рішення. Подивимось все ж.
xuhdev

@xuhdev Я налаштовую dnsmasq, як у docs.blowb.org/setup-host/dnsmasq.html . Але я стикаюся з проблемами при використанні dig з контейнера докера, час очікування закінчується. Але пінг до інтерфейсу docker0 хоста ip працює. Також викопайте з тим самим docker0 ip від докера, який працює. Чи є у вас припущення?
Сатіш

1
@Satheesh Можливо, це налаштування вашого брандмауера, які заважають вашому контейнеру запитувати DNS від хоста?
xuhdev

@xuhdev дякую, що саме брандмауер на моїй хост-машині спричинив проблему. Після того, як я нахилив брандмауер, мій контейнер спілкується з dnsmasq на хості
Satheesh

205

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

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

це саме те, що я шукав цілий день! не знав, що ви можете посилатися на мережевий вузол за назвою / ідентифікатором контейнера. Дякую!
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

Це оновить CLI Docker новими командами “мережа” та зафіксує, як -netпрапор “ ” використовується для призначення контейнерів мережам.


Докер 1,10 має новий розділ Network-контекстний псевдонім , в даний час офіційно задокументований в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

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, і ця технологія використовується для створення віртуальних приватних мереж локальної мережі для кожної служби, і кожна послуга може бути доступна 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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.