Ви запускаєте контейнер, він стартує нормально, healthcheck зелений… а потім не може розв’язати ім’я хоста.
apt update зависає. У логах додатка видно “Temporary failure in name resolution.”
Хост сам при цьому ідеально розв’язує імена, що особливо принизливо.
На Ubuntu 24.04 це часто спрацьовує на одній й тій самій біді: systemd-resolved виконує свою роботу,
Docker виконує свою, а їхній перетин створює DNS-ситуацію, яка виглядає як чаклунство. Насправді — ні.
Це — мережеве підкапання. І виправлення не вимагає відключати пів-мережевого стека зопалу.
Що саме ламається: stub-резолвери, неймспейси та DNS у Docker
Назвемо рухомі частини, тому що збій виглядає містично тільки якщо розглядати DNS як одну коробку.
На Ubuntu 24.04 хост зазвичай використовує systemd-resolved, щоб надати локальний «stub-резолвер»,
прив’язаний до 127.0.0.53. Файл /etc/resolv.conf хоста часто є симлінком на файл,
який вказує на цей stub.
Docker, між тим, зазвичай підсовує резолвер у контейнери, копіюючи або генеруючи
/etc/resolv.conf всередині мережевого неймспейсу контейнера. Якщо Docker бачить nameserver у файлі
resolv.conf хоста, він спробує використати його. Ось де підступ: 127.0.0.53 в контейнері — не хост.
Це сам контейнер. Отже коли контейнер намагається запитати 127.0.0.53, він по суті питає
самого себе про DNS, і ніхто не відповідає.
Docker має вбудований DNS-сервер (зазвичай доступний як 127.0.0.11 всередині контейнерів)
для discovery між контейнерами у user-defined мережах. Але цей вбудований DNS все одно потребує upstream-серверів,
щоб форвардити запити у реальний світ. Якщо upstream вказано на хостовий stub (127.0.0.53), форвардинг ламається.
Є інші варіанти:
-
Docker підхоплює
nameserver 127.0.0.53з хоста і записує його в контейнери. Миттєве падіння. -
Docker використовує свій вбудований DNS (
127.0.0.11), але upstream недоступний — внутрішні імена працюють, зовнішні — ні. -
Split DNS (корпоративні домени, спрямовані на конкретні резолвери) працює на хості через
systemd-resolved,
але контейнери минають цю логіку і запитують публічний DNS для внутрішніх зон, отримуючи NXDOMAIN або таймаути.
Мета тут не в тому, щоб «перемогти», відключивши systemd-resolved. Він дає реальну користь:
per-link DNS, стан DNSSEC, кешування та хорошу інтеграцію з NetworkManager і netplan. Мета — змусити
Docker використовувати resolv.conf з доступними upstream-серверами й робити це передбачувано.
Одна цитата для правильного налаштування мислення (перефразована думка): Джон Оллспоу наполягав, що надійність походить із розуміння систем, а не з пошуку винних.
DNS-збої майже завжди — це взаємодії систем, а не недбалість оператора.
Жарт №1: DNS — єдина частина стека, де «вчора працювало» вважається форматом конфігурації.
Цікаві факти та історичний контекст (щоб це не здавалося випадковим)
-
Stub systemd-resolved на 127.0.0.53 існує, щоб уникнути колізій з локальними резолверами типу dnsmasq,
і щоб зберегти консистентну loopback-крапку доступу навіть коли upstream-адреси змінюються. -
Вбудований DNS Docker (127.0.0.11) був введений для підтримки discovery контейнерів між собою в
user-defined мережах, а не як загального клієнта «корпоративного DNS». -
Бібліотека резолвера glibc читає
/etc/resolv.confі має правила типу «максимум 3 nameserver»,
які можуть мовчки відкидати записи, що весело, коли VPN-клієнти додають п’ять резолверів. -
Ubuntu все глибше інтегрується з systemd у останніх релізах; симлінк на
/etc/resolv.conf
тепер нормальний, а не екзотичний. -
Split DNS раніше був рідкістю поза підприємствами. Тепер навіть домашні користувачі натрапляють на нього з mesh-VPN, dev‑інструментами
і «магічним» DNS для внутрішніх доменів. -
Мережеві неймспейси означають, що loopback є на рівні неймспейсу.
127.0.0.1в контейнері — не хост.
Якщо запам’ятати тільки одне — запам’ятайте це. -
resolv.conf в контейнерах не святиня. Він генерується при старті контейнера і може змінюватися залежно від
налаштувань Docker, драйвера мережі та runtime. -
systemd-resolved може показувати «реальний» resolv.conf, який перераховує upstream-сервери через
/run/systemd/resolve/resolv.conf, що часто є найчистішим джерелом для Docker.
Швидкий план діагностики
Коли продакшн сповіщає і потрібно швидко отримати сигнал, не починайте з редагування конфігів.
Почніть з того, щоб з’ясувати, де саме розпадається резолювання: всередині контейнера, на вбудованому DNS Docker чи на шарі резолвера хоста.
Перший крок: підтвердьте, що і де падає
- Всередині контейнера: чи можете ви щось розв’язати? Публічні імена? Внутрішні домени?
- Всередині контейнера: що насправді містить
/etc/resolv.conf? - На хості: чи
systemd-resolvedздоровий і які upstream-сервери він знає?
Другий крок: класифікуйте збій
- nameserver 127.0.0.53 всередині контейнера → майже напевно некоректна передача resolv.conf.
- nameserver 127.0.0.11 всередині контейнера, але таймаути → upstream вбудованого DNS Docker неправильний/недоступний.
- Публічні домени працюють, корпоративні не працюють → розбіжність split DNS між хостом і контейнерами.
- Падають тільки певні мережі → маршрути VPN, firewall або MTU/фрагментація впливають на DNS-трафік.
Третій крок: виправляйте на найвужчому шарі
- Віддавайте перевагу вказанню Docker справжнього resolv.conf або явних DNS-серверів.
- Використовуйте DNS-на рівні мережі або per-compose, коли це доречно.
- Уникайте відключення
systemd-resolved, якщо лише ви не замінюєте його краще визначеним резолвером і приймаєте зону впливу змін.
Практичні завдання: команди, очікуваний вивід і як прийняти рішення
Це перевірки, які я реально виконую на Ubuntu-хостах, коли контейнери починають падати DNS. Кожне завдання містить (1) команду, (2) що означає вивід і (3) яке рішення з цього випливає.
Виконуйте їх по черзі, поки не отримаєте впевнену діагностику.
Завдання 1: Підтвердіть симптом Docker DNS всередині мінімального контейнера
cr0x@server:~$ docker run --rm alpine:3.20 sh -lc 'cat /etc/resolv.conf; nslookup -type=a example.com 2>&1 | sed -n "1,12p"'
nameserver 127.0.0.53
options edns0 trust-ad
nslookup: can't resolve '(null)': Name does not resolve
Значення: контейнер намагається говорити з 127.0.0.53 (собою), а не з резолвером хоста.
Запит відразу невдалий.
Рішення: перестаньте звинувачувати upstream DNS. Виправте спосіб, у який Docker заповнює /etc/resolv.conf контейнера (або DNS демона Docker).
Завдання 2: Перевірте, чи resolv.conf хоста — це systemd stub
cr0x@server:~$ ls -l /etc/resolv.conf
lrwxrwxrwx 1 root root 39 Oct 10 09:12 /etc/resolv.conf -> ../run/systemd/resolve/stub-resolv.conf
Значення: хост вказує на stub-резолвер.
Рішення: якщо Docker наслідує це, він передасть контейнерам тупиковий nameserver. Ви хочете, щоб Docker використовував «реальний» resolv.conf.
Завдання 3: Перегляньте «реальний» resolv.conf, який перераховує upstream
cr0x@server:~$ sed -n '1,20p' /run/systemd/resolve/resolv.conf
# This is /run/systemd/resolve/resolv.conf managed by man:systemd-resolved(8).
nameserver 10.20.0.10
nameserver 10.20.0.11
search corp.example
Значення: це доступні upstream-резолвери з погляду мережі хоста.
Рішення: найкраща практика зазвичай: змусити Docker читати цей файл замість stub-файла.
Завдання 4: Переконайтеся, що systemd-resolved працює і не в деградованому стані
cr0x@server:~$ systemctl status systemd-resolved --no-pager -l
● systemd-resolved.service - Network Name Resolution
Loaded: loaded (/usr/lib/systemd/system/systemd-resolved.service; enabled; preset: enabled)
Active: active (running) since Thu 2025-12-28 10:20:18 UTC; 3h 12min ago
Docs: man:systemd-resolved.service(8)
Main PID: 812 (systemd-resolve)
Status: "Processing requests..."
Tasks: 1 (limit: 38291)
Memory: 6.1M
CPU: 1.228s
Значення: resolved здоровий.
Рішення: не «виправляйте» це відключенням сервісу. Проблема — у інтеграції Docker, а не в резолвері.
Завдання 5: Подивіться, які DNS-сервери resolved вважає потрібними для підключення
cr0x@server:~$ resolvectl status
Global
Protocols: -LLMNR -mDNS -DNSOverTLS DNSSEC=no/unsupported
resolv.conf mode: stub
Link 2 (ens160)
Current Scopes: DNS
Protocols: +DefaultRoute -LLMNR -mDNS -DNSOverTLS DNSSEC=no/unsupported
Current DNS Server: 10.20.0.10
DNS Servers: 10.20.0.10 10.20.0.11
DNS Domain: corp.example
Значення: хост використовує DNS через інтерфейс ens160 і має search-домен.
Рішення: якщо контейнерам потрібна резолюція corp.example, потрібно змусити Docker використовувати ці upstream-сервери, інакше ви втратите поведінку split DNS.
Завдання 6: Перевірте, який resolv.conf Docker вважає за потрібне використовувати
cr0x@server:~$ docker info 2>/dev/null | sed -n '/DNS/,+3p'
DNS: 127.0.0.53
Значення: демон Docker налаштований (явно або неявно) на використання stub-резолвера.
Рішення: перевизначте DNS Docker, щоб використовувати реальні upstream або вкажіть Docker на non-stub resolv.conf.
Завдання 7: Підтвердіть, який resolv.conf отримав запущений контейнер
cr0x@server:~$ cid=$(docker run -d alpine:3.20 sleep 300); docker exec "$cid" cat /etc/resolv.conf; docker rm -f "$cid" >/dev/null
nameserver 127.0.0.53
options edns0 trust-ad
Значення: це не вбудований DNS Docker; це пряме успадкування stub.
Рішення: виправте вхідні дані, які використовує Docker (resolv.conf хоста або налаштування демона).
Завдання 8: Перевірте, чи присутній вбудований DNS Docker (сценарій 127.0.0.11)
cr0x@server:~$ docker network create testnet >/dev/null
cr0x@server:~$ cid=$(docker run -d --network testnet alpine:3.20 sleep 300)
cr0x@server:~$ docker exec "$cid" cat /etc/resolv.conf
nameserver 127.0.0.11
options ndots:0
cr0x@server:~$ docker rm -f "$cid" >/dev/null; docker network rm testnet >/dev/null
Значення: у user-defined мережах Docker використовує свій вбудований DNS.
Рішення: якщо запити і тут падають, проблема в upstream, який використовує вбудований DNS Docker.
Завдання 9: Перегляньте iptables/nft правила, які дозволяють форвардинг DNS від контейнерів
cr0x@server:~$ sudo nft list ruleset | sed -n '1,80p'
table inet filter {
chain input {
type filter hook input priority filter; policy accept;
}
chain forward {
type filter hook forward priority filter; policy accept;
}
}
table ip nat {
chain POSTROUTING {
type nat hook postrouting priority srcnat; policy accept;
oifname "ens160" ip saddr 172.17.0.0/16 masquerade
}
}
Значення: існує NAT masquerade, політика forward — accept (у цьому фрагменті).
Рішення: якщо ви бачите суворі політики forward або відсутній masquerade, DNS-запити можуть ніколи не покидати хост. Виправляйте firewall перед зміною DNS-конфіг.
Завдання 10: Дивіться логи systemd-resolved під час спроби DNS з контейнера
cr0x@server:~$ sudo journalctl -u systemd-resolved -n 30 --no-pager
Dec 28 13:21:04 server systemd-resolved[812]: Using degraded feature set UDP instead of UDP+EDNS0 for DNS server 10.20.0.10.
Dec 28 13:21:10 server systemd-resolved[812]: DNS server 10.20.0.11 does not support DNSSEC, disabling DNSSEC validation for this server.
Значення: resolved узгоджує можливості; ці повідомлення самі по собі не фатальні.
Рішення: якщо є таймаути, хвилі SERVFAIL або часте перемикання серверів, у вас також є проблеми з upstream DNS. Не зупиняйтеся лише на проблемі stub у Docker.
Завдання 11: Перевірте, що хост розв’язує те, що не може контейнер (перевірка split DNS)
cr0x@server:~$ resolvectl query registry.corp.example
registry.corp.example: 10.30.40.50 -- link: ens160
-- Information acquired via protocol DNS in 12.7ms.
-- Data is authenticated: no
Значення: хост розв’язує корпоративне ім’я через налаштований DNS інтерфейсу.
Рішення: якщо контейнери не можуть це розв’язати, треба передати ті корпоративні резолвери у Docker, а не публічні.
Завдання 12: Перевірте розв’язання в контейнері через явні DNS-сервери (швидке підтвердження)
cr0x@server:~$ docker run --rm --dns 10.20.0.10 --dns 10.20.0.11 alpine:3.20 sh -lc 'nslookup example.com | sed -n "1,8p"'
Server: 10.20.0.10
Address: 10.20.0.10:53
Non-authoritative answer:
Name: example.com
Address: 93.184.216.34
Значення: з явними upstream контейнер розв’язує нормально.
Рішення: постійне виправлення — налаштувати DNS демона Docker (або DNS у Compose/мережі) на доступні сервери або вказувати реальний resolv.conf.
Завдання 13: Перевірте наявність файлу конфігурації демона Docker і його ефективні налаштування
cr0x@server:~$ sudo test -f /etc/docker/daemon.json && sudo cat /etc/docker/daemon.json || echo "no /etc/docker/daemon.json"
no /etc/docker/daemon.json
Значення: явної конфігурації демона немає.
Рішення: ви можете безпечно додати мінімальний daemon.json, щоб керувати DNS, замість того, щоб гратися з симлінками.
Завдання 14: Підтвердіть, який resolv.conf Docker використовує при зміні файлу на хості
cr0x@server:~$ sudo readlink -f /etc/resolv.conf
/run/systemd/resolve/stub-resolv.conf
Значення: Docker зазвичай підхоплює це при запуску демона, а не динамічно для кожного контейнера.
Рішення: якщо ви змінюєте симлінк resolv.conf на хості, перезапустіть Docker, щоб він перечитав файл (плануйте вікно обслуговування при потребі).
Жарт №2: Нічого не каже «high availability», як розгортання DNS‑фіксу, що потребує перезапуску демона о 14:00 у вівторок.
Патерни виправлення, що працюють (без відключення systemd-resolved)
Є три розумні підходи. Оберіть один залежно від того, наскільки «корпоративний» у вас DNS і наскільки ви цінуєте збереження стандартної мережевої конфігурації хоста.
Я підкажу, що б я робив у продакшні для кожного випадку.
Патерн виправлення A (рекомендовано): скажіть Docker використовувати реальні upstream DNS
Це найчистіше в сенсі «зробити відмови нудними». Ви припиняєте успадкування stub-резолвера Docker і даєте йому
резолвери, до яких він має форвардити.
Створіть або відредагуйте /etc/docker/daemon.json:
cr0x@server:~$ sudo install -d -m 0755 /etc/docker
cr0x@server:~$ sudo tee /etc/docker/daemon.json >/dev/null <<'EOF'
{
"dns": ["10.20.0.10", "10.20.0.11"],
"dns-search": ["corp.example"]
}
EOF
cr0x@server:~$ sudo systemctl restart docker
cr0x@server:~$ docker info 2>/dev/null | sed -n '/DNS/,+3p'
DNS: 10.20.0.10
DNS: 10.20.0.11
Що це робить: вбудований DNS Docker і/або resolv.conf контейнерів тепер вказують на резолвери, доступні з контейнерів (через NAT хоста).
Чому це добре: детерміноване, без ігор із симлінками, стійке до оновлень Ubuntu.
Мінус: якщо upstream DNS змінюється (DHCP, роумінг ноутбуків), потрібно оновлювати конфіг або автоматизувати процес.
Патерн виправлення B: вкажіть /etc/resolv.conf хоста на non-stub файл systemd-resolved
Це поширено і добре працює на серверах зі стабільною мережею. Хитрість у тому, що ви не відключаєте resolved; ви просто
робите так, щоб /etc/resolv.conf перераховував реальні upstream-сервери замість 127.0.0.53.
Docker тоді наслідує працездатні резолвери.
cr0x@server:~$ sudo mv /etc/resolv.conf /etc/resolv.conf.bak
cr0x@server:~$ sudo ln -s /run/systemd/resolve/resolv.conf /etc/resolv.conf
cr0x@server:~$ ls -l /etc/resolv.conf
lrwxrwxrwx 1 root root 32 Dec 28 14:01 /etc/resolv.conf -> /run/systemd/resolve/resolv.conf
cr0x@server:~$ sudo systemctl restart docker
Що це робить: glibc‑резолвер хоста тепер запитує upstream напряму.
Чому це добре: Docker наслідує коректні nameserver без додаткової конфігурації демона.
Мінус: деякі налаштування покладаються на stub-режим для певних поведінок; також ноутбуки/VPN можуть часто змінювати DNS.
Якщо хост роумить по мережах, я б радше явно конфігурував Docker.
Патерн виправлення C: per-Compose / per-container перевизначення DNS (сурогатне, іноді необхідне)
Це те, що роблять, коли одному стеку потрібен корпоративний DNS, а іншому — публічні резолвери, або коли вендорський контейнер
робить щось дивне з DNS. Також це корисно, коли ви не володієте налаштуваннями демона хоста (наприклад, shared runners).
Приклад для Docker Compose:
cr0x@server:~$ cat compose.yaml
services:
app:
image: alpine:3.20
command: ["sh", "-lc", "cat /etc/resolv.conf; nslookup example.com | sed -n '1,8p'; sleep 3600"]
dns:
- 10.20.0.10
- 10.20.0.11
dns_search:
- corp.example
Що це робить: перевизначає resolv.conf всередині контейнера.
Чому це добре: обмежена зона впливу; підходить для контролю змін.
Мінус: розростання конфігів. Якщо у вас 40 compose-проєктів, ви якийсь забудете.
Чого уникати: «просто відключити systemd-resolved» як рефлекс
Ви цілком можете видалити systemd-resolved і використовувати статичний resolv.conf або інший локальний резолвер.
Іноді це правильний крок, особливо якщо ви вже запускаєте dnsmasq/unbound і хочете простіший стек.
Але як стандартна відповідь це надмірно грубо.
Відключення resolved часто ламає:
- Поводження split DNS у VPN, інтегроване з NetworkManager
- Per-interface DNS на хостах з кількома інтерфейсами
- Інструменти, які очікують stub від resolved (різні desktop та dev інструменти)
Якщо ви хочете надійності — не видаляйте компоненти, поки чітко не поясните, що саме вони робили для вас.
Одна тонка деталь для продакшну: поведінка при перезапусках і життєвий цикл контейнерів
Docker зазвичай записує /etc/resolv.conf контейнера при старті. Якщо ви виправили DNS і не перезапустили контейнери,
деякі з них збережуть стару поломанту конфігурацію. Отже план розгортання має значення:
- Виправте DNS демона Docker.
- Перезапустіть Docker, якщо потрібно.
- ПереDEPLOY/перезапустіть контейнери, щоб оновити їхні resolv.conf.
Три міні-історії з корпоративного світу (як це ламається в реальних організаціях)
Міні-історія 1: інцидент через хибне припущення
Команда, з якою я працював, запускала build-агенти на Ubuntu-хостах. Нічого особливого: Docker builds, кілька інтеграційних тестів,
потім пуш артефактів у внутрішній реєстр. Рутинне оновлення ОС пройшло: Ubuntu 24.04, оновлення ядра,
ребут, і все здавалося ок. Хост міг розв’язувати будь-що. CI-агенти? Половина з них почала падати
з помилкою «could not resolve host» посеред пайплайну.
Хибне припущення було болісно просте: «Якщо хост розв’язує DNS, то і контейнери теж».
Це правило трималося роками на старіших конфігураціях, бо /etc/resolv.conf хоста містив реальні upstream.
Оновлення змінило це на systemd stub, і Docker старанно скопіював адресу stub у контейнери.
Контейнери потім питали себе про DNS. Вони не були такими розумними, як здавалося.
Початкова реакція була класичним корпоративним театром ескалації: тікети в мережеву команду, питання, чи упав DNS,
повторні збірки, звинувачення «флейкій інфраструктури». Тим часом підказка лежала на виду: nameserver 127.0.0.53
всередині контейнера. Виправлення полягало в тому, щоб задати DNS демону Docker на корпоративні резолвери і перезапустити сервіс Docker
у вікні обслуговування.
Висновок не був «systemd-resolved — поганий». Він був таким: «неймспейси змінюють значення localhost».
На кожному перегляді інциденту команда додала одну лінію: завжди перевіряти /etc/resolv.conf контейнера
перед діагностикою upstream DNS.
Міні-історія 2: оптимізація, що обернулася проти
Інша організація вирішила «зменшити латентність», жорстко прописавши публічні DNS-сервери в Docker на всіх дев-машинах.
Мотив звучав непогано: менше рухомих частин, незалежність від корпоративного DNS і швидша резолюція ніж через VPN.
Вони запровадили стандартний /etc/docker/daemon.json з двома публічними резолверами.
Це працювало тиждень. Потім внутрішні інструменти почали падати в контейнерах: mirror-репозиторії, внутрішні Git-хости,
discovery під приватним доменом. На хості все і далі працювало, бо systemd-resolved
застосовував split DNS: корпоративні домени шли на корпоративні резолвери, решта — у публічні.
Контейнери обминали цю логіку і зверталися до публічного DNS по приватних зонах, отримуючи NXDOMAIN швидко і впевнено.
«Оптимізація» не просто провалилася; вона провалилася швидко, найводно у найзаплутаніший спосіб. Розробники бачили «хост працює, контейнер — ні»,
думали, що мережа Docker зламана, і почали додавати хаки: додаткові --add-host записи, специфічні DNS-настройки для проєктів,
і кеші, які маскували проблему до наступної зміни мережі.
Відкат полягав у припиненні глобального примусу публічного DNS, і замість цього налаштуванні Docker на ті ж upstream-резолвери, що й хост,
або використанні per-project DNS лише коли є справжня необхідність. Глибша вигода — політика: DNS є частиною environment parity.
Якщо ваш контейнеризований дев-оточення не дотримується тих же правил маршрутизації DNS, що і продакшн, ви будете дебажити примарні проблеми.
Міні-історія 3: нудна, але правильна практика, що врятувала день
Одна SRE‑команда мала неефектну, але корисну звичку: на кожному хості був невеликий “network sanity” скрипт, який запускався через cron і був доступний як фрагмент runbook.
Він перевіряв три речі: резолюцію DNS на хості через resolvectl, резолюцію DNS в контейнері через відомий мінімальний контейнер,
і чи /etc/resolv.conf хоста — stub чи upstream.
Коли Ubuntu 24.04 розгорнули, оповіщення почали спрацьовувати ще до того, як клієнти помітили щось: «виявлено невідповідність DNS контейнер/хост».
Нічого ще не було впало, бо більшість сервісів використовували вже запущені контейнери з кешованими результатами, але команда знала,
що наступний деплой зазнає невдачі.
Вони використали скриптовий вивід, щоб класифікувати проблему як «stub потрапив у контейнери» і застосували стандартне виправлення:
задали DNS демону Docker на upstream-резолвери з /run/systemd/resolve/resolv.conf, потім у контрольованому вікні перезапустили Docker,
а далі перезапускали лише безстанні сервіси першими.
Практика була не гламурною. Вона не включала сервіс-меші чи ШІ. Це була базова валідація на межах між хостом і контейнером.
І вона перетворила потенційний багатомовний інцидент на планову зміну.
Типові помилки: симптоми → корінь проблеми → виправлення
1) Контейнери видають «Temporary failure in name resolution» миттєво
Симптом: будь-який DNS-запит миттєво падає в контейнерах; хост працює.
Корінь: /etc/resolv.conf контейнера має nameserver 127.0.0.53.
Виправлення: налаштуйте DNS демона Docker (/etc/docker/daemon.json) або вкажіть resolv.conf хоста на
/run/systemd/resolve/resolv.conf, потім перезапустіть Docker і перезапустіть уражені контейнери.
2) Внутрішні імена сервісів резольвляться, зовнішні — таймаутяться
Симптом: ping other-container працює в user-defined мережі, але apt update падає.
Корінь: вбудований DNS Docker (127.0.0.11) працює для внутрішнього discovery, але форвардинг upstream ламається
через успадкування stub або недоступні upstream-сервери.
Виправлення: задайте DNS демону Docker на реальні upstream-резолвери; переконайтеся, що NAT/firewall дозволяє вихідний трафік на UDP/TCP 53.
3) Працює без VPN, падає з VPN (або навпаки)
Симптом: корпоративні домени падають тільки коли VPN підключено.
Корінь: хост використовує split DNS через resolved; контейнери спрямовані на публічні або старі резолвери.
Виправлення: передайте Docker ті самі корпоративні резолвери (і search-домени), що й хост, або додайте per-Compose DNS для стеків, які цього потребують.
4) Тільки деякі контейнери падають після зміни DNS
Симптом: старі контейнери продовжують працювати; нові розгортання падають.
Корінь: контейнери зберігають resolv.conf, з яким були створені; нові отримують нову поламанту конфігурацію.
Виправлення: після виправлення DNS демона виконайте ролловий перезапуск контейнерів (в пріоритеті — безстанні), або пересоздайте compose-стеки.
5) DNS працює для малих відповідей, але падає для більших
Симптом: деякі запити проходять; інші зависають або повертають SERVFAIL; часто гірше при VPN.
Корінь: MTU/фрагментація призводять до втрати UDP-фрагментів, або проблеми з EDNS0 розміром на шляху.
Виправлення: зменшіть MTU на docker-бріджі або VPN-інтерфейсі, або забезпечте роботу через TCP; перевірте логи resolved на повідомлення “degraded feature set.”
6) Ви «виправили» це, встановивши 8.8.8.8, тепер внутрішнє не працює
Симптом: публічні домени резольвляться; внутрішні — ні.
Корінь: ви обійшли корпоративний DNS і split-horizon зони.
Виправлення: використовуйте корпоративні резолвери для Docker, а не публічні, якщо тільки ви впевнені, що ваші робочі навантаження ніколи не потребуватимуть внутрішнього DNS.
7) DNS хоста ламається після зміни симлінка /etc/resolv.conf
Симптом: хост раптово перестає розв’язувати або поводиться нестабільно після зміни симлінка.
Корінь: інструменти очікували режим stub; або ви перезаписали resolv.conf так, що він конфліктує з NetworkManager/netplan.
Виправлення: віддавайте перевагу конфігу демона Docker (патерн A). Якщо змінюєте симлінк, збережіть бекап і перевіряйте через resolvectl.
Чеклісти / покроковий план
План A (рекомендований): стабільні сервери, ви контролюєте демон Docker
-
Підтвердіть збій: запустіть одноразовий контейнер і перевірте
/etc/resolv.conf.
Якщо бачите127.0.0.53, далі. -
Визначте upstream-резолвери з
/run/systemd/resolve/resolv.confабоresolvectl status. -
Налаштуйте DNS демона Docker у
/etc/docker/daemon.jsonз цими upstream-серверами. - Перезапустіть Docker у контрольованому вікні.
- Перезапустіть контейнери (рестарт/рекреат) щоб оновити їхній resolv.conf.
- Провалідуйте як публічні, так і внутрішні домени (якщо релевантно).
- Задокументуйте рішення в runbook: «Ми фіксуємо DNS Docker на upstream-резолверах; не використовувати 127.0.0.53».
План B: потрібно зберегти точну поведінку split DNS
- Використайте корпоративні резолвери, які показує resolved для конкретного інтерфейсу, і додайте search-домени за потреби.
- Для стеків, яким потрібно по-різному резольвити декілька зон, віддавайте перевагу per-Compose DNS замість глобального налаштування.
-
Провалідуйте через
resolvectl queryна хості іnslookupвсередині контейнерів для тих же імен.
План C: тимчасове пом’якшення, поки чекаєте change control
-
Запускайте критичні контейнери з
--dnsявно, щоб відновити функціональність. - Задокументуйте, які сервіси ви змінили і чому, бо це інакше перетвориться на «таємну конфігурацію» через три місяці.
- Заплануйте правильне виправлення на рівні демона, щоб уникнути постійних «сніжинок» конфігурації.
FAQ
1) Чому 127.0.0.53 працює на хості, але не в контейнерах?
Тому що loopback існує в межах мережевого неймспейсу. 127.0.0.53 контейнера — не резолвер хоста; це адреса всередині контейнера.
Якщо ви не запускаєте resolved у тому контейнері (не робіть цього), там ніхто не слухає.
2) Хіба Docker не повинен використовувати 127.0.0.11 для DNS?
Зазвичай так, особливо на user-defined мережах. Але вбудований DNS Docker і далі форвардить на upstream-резолвери.
Якщо Docker дізнався upstream як 127.0.0.53 з хоста, форвардинг все одно ламається.
3) Чи можна просто змінити /etc/resolv.conf на non-stub файл?
Можна, і це часто добре працює на серверах. На ноутбуках або в середовищах з активним VPN поведінка може відрізнятися від режиму stub.
Якщо ви хочете найменше несподіванок — конфігуруйте DNS демона Docker явно.
4) Чи потрібно перезапускати Docker після зміни DNS-настроєк?
Так, для змін на рівні демона. Також перезапустіть або пересоздайте контейнери, щоб їхній /etc/resolv.conf було згенеровано заново з новими налаштуваннями.
Інакше ви виправите хост, але будете мати працюючі поламані контейнери.
5) Що робити, якщо мої DNS-сервери видаються DHCP і часто змінюються?
Утримування DNS у /etc/docker/daemon.json тоді крихке, якщо ви не автоматизуєте оновлення.
Для хостів, що роумлять, розгляньте невеликий автомат, який оновлює DNS Docker з /run/systemd/resolve/resolv.conf і перезапускає Docker у безпечні вікна.
6) Чому б не встановити Docker на публічний резолвер і не забути?
Тому що split-horizon DNS існує. Внутрішні зони не будуть доступні публічно, і деякі організації навмисно повертають інші відповіді внутрішньо та зовні.
Публічний DNS може «працювати», поки ви не торкнетеся нічого корпоративного — тоді він дасть збій заплутано.
7) Чи стосується це також Kubernetes/containerd?
Так, по суті. Механіка відрізняється (CNI, налаштування kubelet resolvConf), але основна проблема залишається:
контейнери потребують резолверів, досяжних з їхнього неймспейсу. Loopback‑stub — повторювана пастка.
8) Я бачу таймаути, а не миттєві відмови. Це теж проблема зі stub?
Іноді. Миттєві «can’t resolve» часто вказують на 127.0.0.53 в контейнері.
Таймаути також можуть свідчити про firewall/NAT, маршрутизацію VPN, проблеми MTU або здоров’я upstream. Використовуйте швидкий план діагностики, щоб класифікувати.
9) Чи можна запустити DNS-кеш на хості і вказати Docker на нього?
Так, але робіть це свідомо. Якщо ви запускаєте unbound/dnsmasq, прив’язаний до не-loopback адреси, доступної з мереж контейнерів,
контейнери можуть його використовувати. Прив’язка тільки до 127.0.0.1 створить ту саму проблему неймспейсу в іншій формі.
Висновок: наступні кроки, які можна реально запровадити
Повторювана помилка Ubuntu 24.04 + Docker DNS — це не історія «Docker зламався». Це прогнозована невідповідність:
systemd-resolved рекламуватиме loopback stub, а Docker (або ваші контейнери) не зможуть дістатися до цього loopback в іншому неймспейсі.
Як тільки ви це побачите, ви вже не зможете цього не помітити.
Практичні наступні кроки:
- Запустіть одноразовий контейнер і подивіться на
/etc/resolv.conf. Не вгадуйте. - Якщо бачите
127.0.0.53, оберіть патерн виправлення: DNS демона (переважний) або симлінк resolv.conf на non-stub файл. - Перезапустіть Docker у контрольованому вікні, потім перезапустіть/пересоздайте контейнери, щоб вони підхопили новий конфіг резолвера.
- Перевірте як публічні, так і внутрішні домени (якщо у вас є split DNS — вважайте, що він є).
- Запишіть рішення в runbook, щоб наступне оновлення не навчило ту саму жорстку науку знову.
Найкраще DNS‑виправлення — те, що робить DNS знову нудною інфраструктурою. Залиште адреналін на ті речі, що його справді заслуговують.