Локальные имена хостов для контейнеров 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 собственный ответ, вот что я делал вручную (без скрипта).

  1. Как упоминалось в вопросе, определите имя domainname: myapp.dev и hostname: www в файле docker-compose.yml
  2. Поднимите свои контейнеры Docker как обычно
  3. Запустите docker-compose exec client cat/etc/hosts чтобы получить вывод файла хоста контейнера (где client - имя вашей службы) (Пример вывода: 172.18.0.6 www.myapp.dev)
  4. Откройте ваш локальный (хост-компьютер) файл /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, вы наверняка найдете много других интересных вариантов!