Как я могу предоставить контейнерам Docker доступ к локальному DNS-разрешению dnsmasq на хосте?

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

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

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

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

Ответ 1

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

  • --interface=docker0 для прослушивания сетевого интерфейса Docker по умолчанию
  • --except-interface=lo, чтобы пропустить неявное добавление интерфейса loopback
  • --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 ожидает, что после того, как сетевой стек и главный демон docker будут доступны для запуска этой службы, в то время как [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 environment

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

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

$ 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

Литература: