Как настроить связь между контейнерами Docker, чтобы перезапуск не сломал ее?
У меня есть несколько Docker-контейнеров, работающих как:
- Nginx
- Веб-приложение 1
- Веб-приложение 2
- PostgreSQL
Поскольку Nginx необходимо подключаться к серверам веб-приложений в веб-приложениях 1 и 2, а веб-приложениям необходимо взаимодействовать с PostgreSQL, у меня есть такие связи:
- Nginx --- ссылка ---> Веб-приложение 1
- Nginx --- ссылка ---> Веб-приложение 2
- Веб-приложение 1 --- ссылка ---> PostgreSQL
- Веб-приложение 2 --- ссылка ---> PostgreSQL
Сначала это работает очень хорошо. Однако когда я разрабатываю новую версию веб-приложения 1 и веб-приложения 2, мне необходимо заменить их. Что я делаю, так это удаляю контейнеры веб-приложений, настраиваю новые контейнеры и запускаю их.
Для контейнеров веб-приложений их IP-адрес сначала будет выглядеть примерно так:
И после того, как я заменю их, у них будут новые IP-адреса:
Теперь эти открытые переменные среды в контейнере 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 поддерживает его также).
В дополнение к контейнеру, который недоступен и, следовательно, недоступен в псевдониме, несколько контейнеров также могут совместно использовать псевдоним, и в этом случае он не гарантирует, что он будет разрешен к правильному. Но в некоторых случаях это может помочь с плавным обновлением, возможно.
Все это не очень хорошо документировано, но сложно понять, просто прочитав страницу руководства.