Как настроить связь между контейнерами 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, мне, безусловно, нужно удалить ее и запустить новую, но затем мне нужно перестроить весь контейнерный граф, это не очень хорошая идея для реального работа сервера жизни.

11 ответов

Решение

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

объем postgres:

$ 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
root@b94251eac8be:/# PGHOST=$(netstat -nr | grep '^0\.0\.0\.0 ' | awk '{print $2}')
root@b94251eac8be:/# echo $PGHOST
172.17.42.1
root@b94251eac8be:/#
root@b94251eac8be:/# 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=# 

Теперь вы можете перезапустить посол без необходимости перезапуска клиента.

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

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

Следующее от докеров документации

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

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

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

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

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

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

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

Наиболее простой настройкой было бы сначала создать именованный контейнер базы данных:

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

затем создайте веб-контейнер, соединяющийся с БД:

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

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

С подходом OpenSVC вы можете обойти:

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

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

Учебник здесь => Docker Multi Containers с OpenSVC

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

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

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

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

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

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

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

Это все еще не очень хорошо документировано, трудно понять, просто прочитав страницу руководства.

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

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

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

Другие вопросы по тегам