Локальные имена хостов для контейнеров Docker
Вопрос начинающего докера,
Итак, у меня есть среда разработки, в которой я запускаю модульное приложение, оно работает с помощью Docker Compose для запуска 3 контейнеров: сервера, клиента, базы данных.
docker-compose.yml
выглядит следующим образом:
#############################
# Server
#############################
server:
container_name: server
domainname: server.dev
hostname: server
build: ./server
working_dir: /app
ports:
- "3000:3000"
volumes:
- ./server:/app
links:
- database
#############################
# Client
#############################
client:
container_name: client
domainname: client.dev
hostname: client
image: php:5.6-apache
ports:
- "80:80"
volumes:
- ./client:/var/www/html
#############################
# Database
#############################
database:
container_name: database
domainname: database.dev
hostname: database
image: postgres:9.4
restart: always
environment:
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=root
- POSTGRES_DB=dbdev
- PG_TRUST_LOCALNET=true
ports:
- "5432:5432"
volumes:
- ./database/scripts:/docker-entrypoint-initdb.d # init scripts
Вы можете видеть, что я назначаю имя домена .dev для каждого из них, это прекрасно работает, чтобы увидеть один компьютер из другого (внутренняя сеть Docker), например, здесь я пинговаю server.dev
из client.dev
CLI:
[email protected]:/var/www/html# ping server.dev
PING server.dev (127.0.53.53): 56 data bytes
64 bytes from 127.0.53.53: icmp_seq=0 ttl=64 time=0.036 ms
Это работает отлично, но не в моей сети хост-системы.
Для удобства я хотел бы назначить домены в МОЙ локальной сети, а не в сети контейнеров Docker, чтобы я мог, например, набрать: client.dev на моем URL-адрес браузера и загрузить контейнер Docker.
Прямо сейчас, я могу получить доступ только, если я использую Docker IP, который является динамическим:
client: 192.168.99.100:80
server: 192.168.99.100:3000
database: 192.168.99.100:5432
Есть ли автоматизированный/удобный способ сделать это, что не предполагает, что я вручную добавляю IP-адрес в файл /etc/hosts?
BTW Я на OSX, если это имеет какое-то значение.
Спасибо!
Изменить: Я нашел эту проблему Github, которая кажется связанной: https://github.com/docker/docker/issues/2335
Насколько я понял, они, похоже, говорят, что это то, что недоступно вне коробки, и предлагают такие внешние инструменты, как:
Это правильно? И если да, то какой из них я должен использовать в своем конкретном сценарии?
Ответы
Ответ 1
OK
так как кажется, что нет никакого способа сделать это с помощью Docker, я, наконец, выбрал это альтернативное решение от Райана Армстронга, которое состоит в динамическом обновлении файла /etc/hosts
.
Я выбрал это, потому что это было удобно для меня, поскольку это работает как script, и у меня уже был запуск script, поэтому я мог просто добавить эту функцию в него.
В следующем примере создается запись хоста с именем docker.local, которая будет разрешен для вашего IP-телефона док-машины:
update-docker-host(){
# clear existing docker.local entry from /etc/hosts
sudo sed -i '' '/[[:space:]]docker\.local$/d' /etc/hosts
# get ip of running machine
export DOCKER_IP="$(echo ${DOCKER_HOST} | grep -oE '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}')"
# update /etc/hosts with docker machine ip
[[ -n $DOCKER_IP ]] && sudo /bin/bash -c "echo \"${DOCKER_IP} docker.local\" >> /etc/hosts"
}
update-docker-host
Это автоматически добавит или удушит строку /etc/hosts
на моей ОС хоста, когда я запустил машину Docker через мой запуск script.
В любом случае, как я узнал во время своих исследований, кроме редактирования файла hosts, вы также можете решить эту проблему настройка настраиваемого DNS-сервера:
Также найдено несколько проектов на Github, которые, по-видимому, направлены на решение этой проблемы, хотя я их не пробовал:
Ответ 2
Расширение на @eduwass собственный ответ, вот что я делал вручную (без скрипта).
- Как упоминалось в вопросе, определите имя
domainname: myapp.dev
и hostname: www
в файле docker-compose.yml
- Поднимите свои контейнеры Docker как обычно
- Запустите
docker-compose exec client cat/etc/hosts
чтобы получить вывод файла хоста контейнера (где client
- имя вашей службы) (Пример вывода: 172.18.0.6 www.myapp.dev
) - Откройте ваш локальный (хост-компьютер) файл
/etc/hosts
и добавьте эту строку: 172.18.0.6 server.server.dev
Если ваш сервисный контейнер Docker меняет IP-адреса или делает что-то необычное, вам понадобится более сложное решение, но на данный момент это работает для моих простых потребностей.
Ответ 3
Чтобы создать полный домен для localhost, вы можете использовать dnsmasq. В этом случае, если вы выбрали домен .dev, любой поддомен будет указывать на ваш контейнер. Но вы должны знать о проблемах с зоной .dev
Или вы можете использовать bash script для запуска вашего docker-compose, который при запуске добавит строку в /etc/hosts, и после того, как вы убьете этот процесс, эта строка будет удалена
#!/usr/bin/env bash
sudo sed -i '1s;^;127.0.0.1 example.dev\n;' /etc/hosts
trap 'sudo sed -i "/example.dev/d" /etc/hosts' 2
docker-compose up
Ответ 4
Другим решением было бы использование браузера с расширением прокси, отправляющего запросы через прокси-контейнер, который будет знать, где разрешить домены. Если вы решите использовать jwilder/nginx-proxy для производственного режима, то ваша проблема может быть легко решена с помощью mitm-nginx-proxy-companion.
Вот пример, основанный на вашем исходном стеке:
version: '3.3'
services:
server:
build: ./server
working_dir: /app
volumes:
- ./server:/app
client:
environment:
- VIRTUAL_HOST: client.dev
image: php:5.6-apache
volumes:
- ./client:/var/www/html
database:
image: postgres:9.4
restart: always
environment:
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=root
- POSTGRES_DB=dbdev
- PG_TRUST_LOCALNET=true
volumes:
- ./database/scripts:/docker-entrypoint-initdb.d # init scripts
nginx-proxy:
image: jwilder/nginx-proxy
labels:
- "mitmproxy.proxyVirtualHosts=true"
volumes:
- /var/run/docker.sock:/tmp/docker.sock:ro
nginx-proxy-mitm:
dns:
- 127.0.0.1
image: artemkloko/mitm-nginx-proxy-companion
ports:
- "8080:8080"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- Запустите
docker-compose up
- Добавьте расширение прокси в ваш браузер, с прокси-адресом
127.0.0.1:8080
- Доступ
http://client.dev
Запрос будет следовать маршруту:
- Доступ к локальному домену разработки в браузере
- Расширение прокси направляет этот запрос в
mitm-nginx-proxy-companion
вместо "реального" интернета -
mitm-nginx-proxy-companion
пытается разрешить доменное имя через DNS-сервер в том же контейнере - Если домен не является "локальным", он перенаправит запрос в "реальный" интернет
- Но если домен является "локальным", он перенаправит запрос на
nginx-proxy
-
nginx-proxy
в свою очередь, перенаправляет запрос в соответствующий контейнер, содержащий сервис, к которому мы хотим получить доступ
Примечания стороны:
-
links
удалены как устаревшие и заменены сетями Docker - Вам не нужно добавлять доменные имена в
server
и контейнеры database
. client
сможет получить к ним доступ на доменах server
и database
поскольку все они находятся в одной сети (аналогично тому, что ранее делала link
) - вам не нужно использовать
ports
на контейнерах server
и database
потому что он только перенаправляет порты, которые будут использоваться через 127.0.0.1
. PHP в client
контейнере будет выполнять только "внутренние" запросы к другим контейнерам, и поскольку эти контейнеры находятся в одной сети, вы уже можете получить к ним доступ с помощью database:5432
и server:3000
. То же самое касается соединений с server <-> database
. - Я автор
mitm-nginx-proxy-companion
Ответ 5
TL; DR: --name
и --link
- это то, что вам нужно.
Проблема
Как связать контейнеры с помощью /etc/hosts
Решение
Проблема github, с которой вы связаны, действительно напрямую связана с вашей проблемой. Этот comment указывает на PR, который включил это. Несмотря на то, что он довольно старый и устаревший (я не слишком глубокий, просто быстрый взгляд), во всяком случае, то, что вы ищете, это параметр --link
команды docker run
.
Команда docker run --link container-name:alias my-image bash
добавит IP-адрес контейнера с именем container-name
в /etc/hosts
с именем alias
, поэтому, если вы выполните из этого контейнера (запущенный из команды) ping alias
, он будет работа.
Обратите внимание, что имя контейнера не нужно имя контейнера! Я предлагаю вам использовать оба явно в вашем docker-compose.yml
.
Bonus
Вы должны просмотреть страницу руководства docker run
, вы наверняка найдете много других интересных вариантов!