Как настроить связь между контейнерами Docker, чтобы перезапуск не сломал ее?

У меня есть несколько Docker-контейнеров, работающих как:

  • Nginx
  • Веб-приложение 1
  • Веб-приложение 2
  • PostgreSQL

Поскольку Nginx необходимо подключаться к серверам веб-приложений в веб-приложениях 1 и 2, а веб-приложениям необходимо взаимодействовать с PostgreSQL, у меня есть такие связи:

  • Nginx --- ссылка ---> Веб-приложение 1
  • Nginx --- ссылка ---> Веб-приложение 2
  • Веб-приложение 1 --- ссылка ---> PostgreSQL
  • Веб-приложение 2 --- ссылка ---> PostgreSQL

Сначала это работает очень хорошо. Однако когда я разрабатываю новую версию веб-приложения 1 и веб-приложения 2, мне необходимо заменить их. Что я делаю, так это удаляю контейнеры веб-приложений, настраиваю новые контейнеры и запускаю их.

Для контейнеров веб-приложений их IP-адрес сначала будет выглядеть примерно так:

  • 172.17.0.2
  • 172.17.0.3

И после того, как я заменю их, у них будут новые IP-адреса:

  • 172.17.0.5
  • 172.17.0.6

Теперь эти открытые переменные среды в контейнере Nginx по-прежнему указывают на старые IP-адреса. Здесь возникает проблема. Как заменить контейнер, не нарушая связи между контейнерами? Та же проблема будет и с PostgreSQL. Если я хочу обновить версию образа PostgreSQL, мне, безусловно, нужно удалить ее и запустить новую, но затем мне нужно перестроить весь контейнерный граф, так что это не идеально для реальной работы сервера.

Ответ 1

Эффект --link является статическим, поэтому он не будет работать для вашего сценария (в настоящее время нет повторной привязки, хотя вы можете удалить ссылки).

Мы использовали два разных подхода к dockerize.it, чтобы решить это, без ссылок или послов (хотя вы могли бы также добавить послов).

1) Использовать динамический DNS

Общая идея заключается в том, что вы указываете одно имя для своей базы данных (или любой другой службы) и обновляете недолгое DNS-сервер с фактическим IP при запуске и остановке контейнеров.

Мы начали с SkyDock. Он работает с двумя контейнерами докеров, DNS-сервером и монитором, который поддерживает его обновление автоматически. Позже мы перешли к чему-то более обыденному, используя Consul (также используя докционированную версию: docker-consul).

Эволюция этого (который мы еще не пробовали) заключалась бы в настройке etcd или подобном и использовании его пользовательского API для изучения IP-адресов и портов. Программное обеспечение также должно поддерживать динамическую реконфигурацию.

2) Используйте докер-мост ip

При экспонировании портов контейнера вы можете просто привязать их к мосту docker0, который имеет (или может иметь) известный адрес.

При замене контейнера новой версией просто создайте новый контейнер для публикации того же порта на одном и том же IP-адресе.

Это проще, но также более ограничено. У вас могут быть конфликты портов, если вы запускаете аналогичное программное обеспечение (например, два контейнера не могут прослушивать порт 3306 на мосте docker0), и т.д., Поэтому наш текущий фаворит - это вариант 1.

Ответ 2

Ссылки предназначены для конкретного контейнера, а не для имени контейнера. Итак, как только вы удалите контейнер, ссылка будет отключена, и новый контейнер (даже с тем же именем) автоматически не займет свое место.

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

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, новая сеть не будет создавать переменные среды и не переносить переменные среды с другими контейнерами.

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

Ответ 3

Вы можете использовать контейнер посольский. Но не связывайте контейнер посла с вашим клиентом, так как это создает ту же проблему, что и выше. Вместо этого используйте открытый порт контейнера посла на хостере докеров (как правило, 172.17.42.1). Пример:

объем постгрейса:

$ docker run --name PGDATA -v /data/pgdata/data:/data -v /data/pgdata/log:/var/log/postgresql phusion/baseimage:0.9.10 true

Postgres-контейнер:

$ docker run -d --name postgres --volumes-from PGDATA -e USER=postgres -e PASS='postgres' paintedfox/postgresql

Посол-контейнер для postgres:

$ docker run -d --name pg_ambassador --link postgres:postgres -p 5432:5432 ctlc/ambassador

Теперь вы можете запустить клиентский контейнер postgresql без привязки контейнера посла и доступа postgresql к хосту шлюза (обычно 172.17.42.1):

$ docker run --rm -t -i paintedfox/postgresql /bin/bash
[email protected]:/# PGHOST=$(netstat -nr | grep '^0\.0\.0\.0 ' | awk '{print $2}')
[email protected]:/# echo $PGHOST
172.17.42.1
[email protected]:/#
[email protected]:/# psql -h $PGHOST --user postgres
Password for user postgres: 
psql (9.3.4)
SSL connection (cipher: DHE-RSA-AES256-SHA, bits: 256)
Type "help" for help.

postgres=#
postgres=# select 6*7 as answer;
 answer 
--------
     42
(1 row)

bpostgres=# 

Теперь вы можете перезапустить контейнер посла, не перезагружая клиент.

Ответ 4

Если кому-то все еще интересно, вы должны использовать записи хоста в файле /etc/hosts каждого контейнера докера и не должны зависеть от переменных ENV, поскольку они не обновляются автоматически.

Будет существовать запись файла хоста для каждого связанного контейнера в формате LINKEDCONTAINERNAME_PORT_PORTNUMBER_TCP и т.д.

Ниже приведена docker docs

Важные замечания о переменных среды Docker

В отличие от записей хоста в файле /etc/hosts, IP-адреса, хранящиеся в переменные среды не обновляются автоматически, если источник контейнер перезапускается. Мы рекомендуем использовать записи хоста в /etc/hosts для разрешения IP-адреса связанных контейнеров.

Эти переменные среды устанавливаются только для первого процесса в контейнер. Некоторые демоны, такие как sshd, будут вычищать их при нерестах оболочки для подключения.

Ответ 5

Это включено в экспериментальную сборку докеров 3 недели назад с внедрением сервисов: https://github.com/docker/docker/blob/master/experimental/networking.md

Вы должны иметь возможность получить динамическую ссылку, запустив контейнер докера с аргументами --publish-service <name>. Это имя будет доступно через DNS. Это повторяется при перезагрузке контейнера (если вы перезагружаете контейнер с тем же именем службы, который, конечно)

Ответ 6

Вы можете использовать dockerlinks с именами для решения этой проблемы.

Большинство базовых настроек состоят в том, чтобы сначала создать именованный контейнер базы данных:

$ sudo docker run -d --name db training/postgres

затем создайте веб-контейнер, подключающийся к db:

$ sudo docker run -d -P --name web --link db:db training/webapp python app.py

При этом вам не нужно вручную подключать контейнеры со своими IP-адресами.

Ответ 7

с помощью подхода OpenSVC, вы можете обходным путем:

  • используйте службу с собственным именем ip address/dns (к которому будут подключаться ваши конечные пользователи)
  • сообщите docker об открытии портов на этот конкретный IP-адрес (опция "-ip" докеры)
  • настроить приложения для подключения к IP-адресу службы

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

Учебник здесь = > Контейнеры Docker Multi с OpenSVC

не пропустите часть "сложной оркестровки" в конце tuto, которая может помочь вам запустить/остановить контейнеры в правильном порядке (1 подмножество postgresql + 1 подмножество webapp + 1 подмножество nginx)

Основной недостаток заключается в том, что вы публикуете порты webapp и PostgreSQL для общего доступа, и на самом деле нужно публиковать только порт tgin nginx публично.

Ответ 8

Вы также можете попробовать метод посла с промежуточным контейнером только для сохранения целостности ссылки... (см. https://docs.docker.com/articles/ambassador_pattern_linking/) для больше информации

Ответ 9

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

У этого есть свои недостатки, но он может работать в вашем случае.

Ответ 10

Другой вариант - использовать параметр --net container:$CONTAINER_ID.

Шаг 1. Создание контейнеров "сеть"

docker run --name db_net ubuntu:14.04 sleep infinity
docker run --name app1_net --link db_net:db ubuntu:14.04 sleep infinity
docker run --name app2_net --link db_net:db ubuntu:14.04 sleep infinity
docker run -p 80 -p 443 --name nginx_net --link app1_net:app1 --link app2_net:app2 ubuntu:14.04 sleep infinity

Шаг 2. Внедрение служб в "сетевые" контейнеры

docker run --name db --net container:db_net pgsql
docker run --name app1 --net container:app1_net app1
docker run --name app2 --net container:app1_net app2
docker run --name nginx --net container:app1_net nginx

Пока вы не прикасаетесь к "сетевым" контейнерам, IP-адреса ваших ссылок не должны меняться.

Ответ 11

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

Он не добавляет какой-либо зависимости между контейнерами - они могут общаться до тех пор, пока обе работают, независимо от перезапуска и замены и порядка запуска. Я полагаю, он использует DNS внутри, а не /etc/hosts

Используйте его так: docker run --net=some_user_definied_nw --net-alias postgres ..., и вы можете подключиться к нему, используя этот псевдоним из любого контейнера в той же сети.

Не работает в сети по умолчанию, к сожалению, вам нужно создать один с docker network create <network>, а затем использовать его с --net=<network> для каждого контейнера (compose поддерживает его также).

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

Все это не очень хорошо документировано, но сложно понять, просто прочитав страницу руководства.