Есть много способов, которыми контейнеры Docker могут запутаться в настройках DNS (просто поищите в SO или в более широком Интернете "Docker DNS", чтобы понять, что я имею в виду), и один из распространенных предлагаемых обходных путей:

  1. Настройте dnsmasq как локальный преобразователь DNS в хост-системе
  2. Привяжите его к сетевому интерфейсу docker0
  3. Настройте Docker на использование IP-адреса docker0 для разрешения DNS

Однако попытка наивно применить этот обходной путь на многих современных системах Linux приведет вас в кроличью яму сложности сети Linux и управления процессами, поскольку systemd заверяет вас, что dnsmasq не работает, но netstat сообщает вам это так, и на самом деле попытка запустить dnsmasq завершается неудачей из-за жалобы на то, что порт 53 уже используется.

Итак, как надежно предоставить своим контейнерам доступ к локальному преобразователю, работающему на хосте, даже если в системе он уже запущен по умолчанию?

20
ncoghlan 29 Фев 2016 в 09:21

2 ответа

Лучший ответ

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

  • --interface=docker0 для прослушивания сетевого интерфейса Docker по умолчанию
  • --except-interface=lo, чтобы пропустить неявное добавление интерфейса обратной петли
  • --bind-interfaces, чтобы отключить функцию dnsmasq, при которой он по-прежнему прослушивает все интерфейсы по умолчанию, даже если он обрабатывает трафик только для одного из них

Настройка выделенного экземпляра dnsmasq

Вместо изменения настроек общесистемного экземпляра dnsmasq по умолчанию эти инструкции показывают настройку выделенного экземпляра dnsmasq с помощью systemd в системе, которая уже определяет службу dnsmasq по умолчанию:

$ sudo cp /usr/lib/systemd/system/dnsmasq.service /etc/systemd/system/dnsmasq-docker.service
$ sudoedit /etc/systemd/system/dnsmasq-docker.service

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

[Service]
ExecStart=/usr/sbin/dnsmasq -k

Мы редактируем этот раздел, чтобы определить наши дополнительные параметры:

[Service]
ExecStart=/usr/sbin/dnsmasq -k --interface=docker0 --except-interface=lo --bind-interfaces

На самом деле весь файл довольно короткий:

[Unit]
Description=DNS caching server.
After=network.target
After=docker.service
Wants=docker.service

[Service]
ExecStart=/usr/sbin/dnsmasq -k --interface=docker0 --except-interface=lo --bind-interfaces

[Install]
WantedBy=multi-user.target

Раздел [Unit] сообщает systemd подождать, пока сетевой стек и главный демон докера не станут доступны для запуска этой службы, а [Install] указывает, к какому целевому состоянию системы добавить службу при ее включении.

Затем мы настраиваем нашу новую службу для запуска при загрузке системы, а также запускаем ее явно для немедленного использования:

$ sudo systemctl enable dnsmasq-docker
$ sudo systemctl start dnsmasq-docker

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

$ sudo systemctl status dnsmasq-docker

В этом выводе мы ищем две ключевые строки:

Loaded: loaded (/etc/systemd/system/dnsmasq-docker.service; enabled; vendor preset: disabled)
Active: active (running) since <date & time>

В первой строке обратите внимание на статус «включен», а во второй - статус «активен (работает)». Если служба не запустилась правильно, то дополнительная диагностическая информация, надеюсь, объяснит почему (хотя, к сожалению, иногда она может быть загадочной, отсюда и этот пост).

Примечание. Эта конфигурация может не запуститься dnsmasq-docker при перезапуске системы из-за ошибки, что интерфейс docker0 не определен. Хотя ожидание docker.service кажется довольно надежным средством избежать этой проблемы, если разрешение имен из контейнеров докеров не работает после перезапуска системы, попробуйте запустить:

$ sudo systemctl start dnsmasq-docker

Настройка брандмауэра хоста

Чтобы иметь возможность использовать преобразователь из локальных контейнеров Docker, нам также необходимо отключить сетевой брандмауэр между хостом и системами, работающими в контейнерах:

sudo firewall-cmd --permanent --zone=trusted --change-interface=docker0
sudo firewall-cmd --reload

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

Настройка Docker с помощью файла среды systemd

Теперь, когда у нас запущен локальный преобразователь, нам нужно настроить Docker для его использования по умолчанию. Docker нужен IP-адрес интерфейса docker0, а не имя интерфейса, поэтому мы используем ifconfig для его получения:

$ ifconfig docker0 | grep inet
        inet 172.17.0.1  netmask 255.255.0.0  broadcast 0.0.0.0

Итак, для моей системы интерфейс хоста на мосту docker0 по умолчанию доступен как 172.17.0.1 (добавление | cut -f 10 -d ' ' к этой команде должно отфильтровать вывод только по IP-адресу)

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

$ cat /usr/lib/systemd/system/docker.service

Первое, что мы ищем, - это точная команда, используемая для запуска демона, которая должна выглядеть примерно так:

ExecStart=/usr/bin/docker daemon \
          $OPTIONS \
          $DOCKER_STORAGE_OPTIONS \
          $DOCKER_NETWORK_OPTIONS \
          $INSECURE_REGISTRY

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

EnvironmentFile=-/etc/sysconfig/docker

Когда используется файл среды (как в Fedora 23), то способ изменить настройки демона Docker - отредактировать этот файл и обновить соответствующую переменную среды:

$ sudoedit /etc/sysconfig/docker

Существующая запись OPTIONS в Fedora 23 выглядит так:

OPTIONS='--selinux-enabled --log-driver=journald'

Чтобы изменить настройки разрешения DNS по умолчанию, мы изменили его, чтобы он выглядел следующим образом:

OPTIONS='--selinux-enabled --log-driver=journald --dns=172.17.0.1'

А затем перезапустите демон Docker:

$ sudo systemctl restart docker

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

Вы можете запустить curl внутри контейнера, чтобы проверить правильность работы разрешения имен:

docker run -it centos curl google.com

Замените google.com именем хоста, с которым у вас возникли проблемы (так как вы должны были найти этот ответ только в том случае, если у вас была проблема с разрешением имени при запуске процесса внутри контейнера Docker)

Настройка Docker с помощью подключаемого файла systemd

(Предостережение: поскольку моя система использует файл среды, я не смог протестировать подход, основанный на добавляемых файлах, ниже, но он должен работать - я включил его, поскольку документация Docker, похоже, указывает, что они теперь предпочитают использование файлов systemd, а не файлов среды)

Если файл системной службы не использует EnvironmentFile, тогда вся запись ExecStart может быть заменена с помощью файла конфигурации drop-in:

$ sudo mkdir -p /etc/systemd/system/docker.service.d
$ sudoedit /etc/systemd/system/docker.service.d/daemon.conf

Затем мы говорим Docker очистить существующую запись ExecStart и заменить ее нашей новой с дополнительными настройками:

[Service]
ExecStart=
ExecStart=/usr/bin/docker daemon \
          $OPTIONS \
          --dns 172.17.0.1 \
          $DOCKER_STORAGE_OPTIONS \
          $DOCKER_NETWORK_OPTIONS \
          $INSECURE_REGISTRY

Затем мы говорим systemd загрузить это изменение конфигурации и перезапустить Docker:

$ sudo systemctl daemon-reload
$ sudo systemctl restart docker

Ссылки:

25
ncoghlan 8 Июл 2016 в 04:41

Вы можете использовать локальный DNS-преобразователь хоста (например, dnsmasq) из ваших контейнеров Docker, если они находятся в сеть, определенная пользователем . В этом случае контейнер /etc/resolv.conf будет иметь сервер имен 127.0.0.11 (также известный как Docker встроенный DNS-сервер), который может правильно пересылать DNS-запросы на адрес обратной связи хоста.

$ cat /etc/resolv.conf
nameserver 127.0.0.1
$ docker run --rm alpine cat /etc/resolv.conf
nameserver 8.8.8.8
nameserver 8.8.4.4
$ docker network create demo
557079c79ddf6be7d6def935fa0c1c3c8290a0db4649c4679b84f6363e3dd9a0
$ docker run --rm --net demo alpine cat /etc/resolv.conf
nameserver 127.0.0.11
options ndots:0    

Если вы используете docker-compose, он автоматически настроит индивидуальную сеть для ваших служб (с формат файла v2 +). Однако обратите внимание, что хотя docker-compose запускает контейнеры в сети, определенной пользователем, он по-прежнему создает их в сети по умолчанию . Чтобы использовать настраиваемую сеть для сборок, вы можете указать параметр network в build конфигурация (требуется формат файла v3.4 + ).

6
Eugene Yarmash 9 Сен 2018 в 07:35