Разрешение имен доменов из контейнеров Docker в сложных случаях

в 12:05, , рубрики: DNS, dnsmasq, docker, docker-compose, emulator, web-разработка, виртуализация

В вопросе связанном с DNS попил мне Docker кровушки основательно, ибо столько там разных мест в которых тебе предлагают написать заветные цифры, что прямо-таки глаза разбегаются.

Итак, задача: необходимо поднять окружение разработчика для одновременной работы с несколькими веб-проектами висящими на доменах вида example.app. При этом данные домены должны быть доступны из контейнеров, с хоста, а также, например, из эмулятора Genymotion. Кроме того, из контейнеров должен быть доступен и внешний интернет. Все это усугубляется корпоративным Intranet со своим внутренним DNS сервером.

Для решения нам потребуется:

  1. docker — движок для работы с контейнерами
  2. docker-compose — работа с мульти-контейнерными решениями
  3. dnsmasq — легкий DNS (а также DHCP и TFTP) сервер
  4. laradock (опционально) — готовое мульти-контейнерное решение для разработчика

Установку пп.1-3 пропускаю, как очевидную. Обратите внимание на версию docker-compose, нам нужна версия 1.8 или выше (на момент написания в репозитариях лежала старая версия).

Для опытного работника дока докера нет никакой проблемы выбрать нужные образы, слинковать их, пробросить порты, установить дополнительные библиотеки — информации на эту тему достаточно. Но если вы хотите начать работу прямо сейчас, то давайте воспользуемся продуктом коллективного сознательного — laradock.

Из названия laradock можно выявить намек на смычку Laravel + Docker. Так и было изначально, но в данный момент такая направленность ушла и теперь проект ориентируется на любые веб-приложения.

Итак, laradock — это yml-файл и набор сопутствующих сконфигурированных образов для docker-compose, позволяющий разворачивать набор контейнеров с nginx, mysql, mongoDB и еще кучей разных плюшек типа Memcached, Aerospike и т.д. (уже сейчас набор очень широкий и постоянно расширяется).
Немаловажным является то, что установка некоторых дополнительных плагинов или библиотек сводится к простой правке yml-файла, например:

- INSTALL_XDEBUG=true

и пересборке образа:

docker-compose build php-fpm

Кроме того, в laradock все готово, чтобы засунуть туда одновременно столько проектов, сколько душе угодно — просто обратите внимание на примеры конфигурации в ./nginx/sites.

laradock прокидывает порт 80 из контейнера nginx/apache на хост-машину, поэтому чтобы после поднятия нужных контейнеров (набор вы выбираете самостоятельно)

docker-compose up -d nginx mysql phpmyadmin

увидеть сконфигурированные заранее в nginx/apache сайты example.app и mysite.app нужно просто прописать для них локальный IP в файле /etc/hosts

127.0.0.1  example.app
127.0.0.1  mysite.app

Контейнер phpmyadmin прокидывется на порт 8080, поэтому увидеть его в работе можно по адресу http://localhost:8080. Ну и имейте в виду, что контейнер с мускулом работает для всех остальных контейнеров на хосте mysql, к которому и следует подключаться.

На самом деле, если у вас изолированные проекты и нет корпоративного intranet, то вы можете прерваться в данном месте и уже начать разработку. Проблема с DNS вас не коснулась.

Если вы читаете дальше, то вам видимо нужно:

  1. Из своего приложения обратиться к своему же отдельному backend на другом домене.
  2. Из приложения, запущенного в эмуляторе Android (iOS и т.д.) обратиться к своему backend.
  3. Из своего приложения обратиться к стороннему API, но вы сидите в intranet с местным DNS-сервером.

Итак, решаем пункты 1 и 2. Пункт 3 решится сам собой, добавлением всего одной строки — адреса вашего intranet DNS. Сделаем так, чтобы наши домены были доступны из контейнеров (в частности из php-fpm). Для этого нам потребуется dnsmasq.

Устанавливаем его, если вы еще этого не сделали:

sudo apt-get install dnsmasq

Для того, чтобы разрешить DNS имя dnsmasq заглядывает в /etc/resolv.conf в поисках IP вышестоящего DNS-сервера. В принципе, мы могли бы туда его и прописать, но в этом файле четко написано, что изменять его не стоит, т.к. он может быть перезаписан. Поэтому создадим собственный файл /etc/resolv.ext.conf со следующим содержимым:

# Гугловские DNS
nameserver 8.8.8.8
nameserver 8.8.4.4

# Корпоративный DNS (это и есть та самая срока, которая выведет вас из застенков интранет)
nameserver <IP корпоративного DNS>

Теперь нужно чтобы dnsmasq стал его использовать. В файле /etc/dnsmasq.conf нужно найти, раскомментировать и исправить параметр, указав там наш файл:

resolv-file=/etc/resolv.ext.conf

Однако не все так просто… хитрый dnsmasq не будет его использовать пока мы не выполним еще одну манипуляцию.

Здесь нужно сделать маленькую ремарку: можно не заморачиваться с собственным файлом конфигурации, а засунуть nameserver'а в оригинальный resolv.conf, но засунуть правильно. Засовывать их нужно в /etc/resolvconf/resolv.conf.d/head и тогда они будут автоматически добавляться в начале resolv.conf Однако имейте в виду, что там может быть только 3 неймсервера, а в своем файле — сколько угодно.

Но вернемся к нашему случаю. Для того, чтобы наш resolv.ext.conf всеже был прочитан необходимо в файле /etc/default/dnsmasq указать:

IGNORE_RESOLVCONF=yes

Что за бред, скажете вы… Игнорировать файл, чтобы его прочитать? Не совсем так. Комментарий перед этой опцией четко разъясняет что она делает. Мы заставляем dnsmasq игнорировать вывод утилиты resolvconf, если она установлена и обратить внимание именно на наш файл.

В целом это все. Осталось только объяснить Docker, что в нашем маленьком мирке за разрешение DNS отвечает dnsmasq. Открываем /etc/default/docker и указываем:

DOCKER_OPTS="--dns 127.0.0.1"

Рестартуем сервисы:

sudo service dnsmasq restart
sudo service docker restart

и...

… опять заминка:

все заработает только если в вашем линуксе используется система инициализации Upstart или SysVinit (об этом четко написано в данном файле). Но если вы используете, например, Ubuntu 15.04 или выше, то у вас используется уже более продвинутая systemd и нам потребуются дополнительные телодвижения. Но для начала проверим, что же мы используем:

$ sudo stat /proc/1/exe
Файл: '/proc/1/exe' -> '/lib/systemd/systemd'

Как видите, из вывода команды становится ясно что именно используется у нас. В данном случае systemd.

Ну что же, вылечим и этого. Конфигурация сервиса изначально лежит в /lib/systemd/system/docker.service. Можно его просто скопировать и подправить:

sudo cp /lib/systemd/system/docker.service /etc/systemd/system/docker.service

но мы поступим более порядочно — изменим только то, что следует изменить:

sudo [ ! -e /etc/systemd/system/docker.service.d/ ] && mkdir -p /etc/systemd/system/docker.service.d/
sudo cat << 'EOF' > /etc/systemd/system/docker.service.d/docker.conf
[Service]
EnvironmentFile=-/etc/default/docker
ExecStart=
ExecStart=/usr/bin/dockerd -H fd:// $DOCKER_OPTS

EOF

Как вы уже поняли, мы создали файл /etc/systemd/system/docker.service.d/docker.conf (так называемый drop-in) с изменениями стандартных параметров и передаем наши параметры dns, прописанные в /etc/default/docker при старте сервиса.

Осталось только перезапустить сервисы:

sudo systemctl daemon-reload
sudo service docker restart

и убедиться что все в порядке:

sudo service docker status

В выводе этой команды вы увидите много уже знакомого: и подключенный drop-in, и стартовавший сервис с нашим dns. Вот теперь действительно ВСЕ!

Полезные ссылки:

Автор: dlnsk

Источник


* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js