Брехня кешування DNS у Docker: скидайте правильний кеш (всередині чи поза контейнерами)

Було корисно?

Ви змінюєте DNS-запис. Чекаєте. Навіть виконуєте ритуал: «все добре, TTL низький».
І все одно один контейнер продовжує звертатися до старої IP-адреси, ніби він у далекострокових стосунках з вашим списаним балансувальником навантаження.

Пастка проста: ви очищаєте якийсь кеш, але не той кеш. Docker додає щонайменше один шар резолвера, Linux додає ще, а ваш додаток може накопичувати відповіді, ніби він платить оренду за кожний запит. Якщо ви не знаєте, де саме ховається обман, ви будете ганятися за примарами.

Практична модель мислення: де можуть застрягати відповіді DNS

Виправлення DNS у контейнерах рідко зводиться до «DNS впав». Йдеться про час, контекст і кешування в місцях, про які ви забули.
Найшвидший спосіб вирватись — перестати питати «який у мене DNS?» і почати питати «хто відповів, хто закешував, і хто все ще його використовує?»

DNS має кілька «клієнтів», а не одного

Контейнеризований додаток зазвичай не звертається безпосередньо до корпоративних DNS-серверів. Він звертається до чогось ближчого:

  • Власна логіка резолвера додатка (JVM, Go net.Resolver, кешування в Node, Envoy, правила резолювання upstream у nginx).
  • libc + NSS (резолвер glibc, musl, /etc/nsswitch.conf, /etc/hosts, search-домени, ndots, timeout/attempts).
  • Локальний демон кешування (nscd, dnsmasq, systemd-resolved) — іноді всередині контейнера, часто на хості.
  • Вбудований DNS Docker (зазвичай на 127.0.0.11), який пересилає запити й виконує виявлення сервісів по іменах у користувацьких мережах.
  • Upstream-резолвери хоста (корпоративний DNS, VPC resolver, CoreDNS, Unbound, bind).

Якщо ви очищаєте кеш хоста, але додаток кешує вічно, нічого не зміниться. Якщо ви перезапускаєте контейнер, але вбудований резолвер Docker все ще повертає дивні відповіді, теж нічого не зміниться.
Потрібно ізолювати шар, який повертає застарілі відповіді.

«Очистити DNS» — це не одна команда

На Linux сам по собі «очистити DNS» може означати:

  • перезапустити systemd-resolved або обнулити його кеш,
  • перезапустити nscd або dnsmasq,
  • перезапустити Docker (або лише уражені контейнери / мережі),
  • перезапустити процес додатка, щоб очистити його внутрішній кеш,
  • або скинути кеш upstream-резолвера, яким ви не керуєте.

Правильна очистка — та, яка фактично змінює шлях наступного запиту.

Один операційний принцип, що добре працює в часі: спостерігайте перед тим, як втручатися. Очищення ховає докази. Зберіть кілька знімків «до», потім очищайте мінімально необхідне.

Цитата, яку варто тримати під рукою під час on-call: «Hope is not a strategy.» — General Gordon R. Sullivan

Цікаві факти та трохи історії (щоб ви перестали звинувачувати «Docker-магiю»)

  • Раніше кешування DNS на рівні ОС не було загальнопоширеним очікуванням. Ранні Unix-резолвери були простими stub-резолверами; кешування стало поширеним разом із уповільненням мереж і частими викликами імен.
  • TTL — це рекомендація, а не універсальний закон. Резолвер може обмежувати TTL (мін/макс), а додатки можуть кешувати довше, ніж вказано в DNS — особливо коли вони «намагаються допомогти».
  • Docker додав вбудований DNS, щоб зробити service discovery у користувацьких мережах прийнятним. Інакше іменування між контейнерами швидко стало б болючим.
  • За замовчуванням мости Docker історично копіювали поведінку host-ового resolv.conf. Це робило контейнери спадкоємцями поведінки DNS хоста — і хороших, і поганих сторін — аж поки вбудований DNS не став нормою для custom-мереж.
  • systemd-resolved змінив очікування на багатьох дистрибутивах. Він ввів локальний stub-резолвер (часто 127.0.0.53) із розділеним DNS і кешуванням, що взаємодіє з Docker несподіваними способами.
  • Поведінка резолвера Alpine (musl) відрізняється від Debian/Ubuntu (glibc). Моди відмов, search-доменів і ndots можуть виглядати як «випадковий DNS».
  • Поведінка резолвера Go змінювалася між версіями. Залежно від прапорів збірки та середовища, він може використовувати pure Go резолвер або cgo (glibc), що впливає на кешування і парсинг конфігурації.
  • Корпоративний DNS часто робить своє власне кешування і «допоміжні» переписування. Split-horizon DNS, внутрішні зони і політики означають, що те саме ім’я може резолвитися по-різному всередині/поза VPN.

Останній пункт важливий, бо «в мене на ноуті працює» може виявитися буквально правдою: ваш ноутбук знаходиться в іншому DNS-перегляді, ніж хост контейнера.

Шари кешування DNS: всередині контейнера, на хості та upstream

Шар 0: сам додаток (найпоширеніший брехун)

Якщо ви запам’ятаєте лише одну річ: багато додатків кешують DNS довше, ніж ви думаєте, і деякі практично кешують назавжди, якщо не змусити їх повторно резолвити.
Поширені патерни:

  • Пули з’єднань тримають сокети до старих IP. DNS може змінитися, а додаток не помітить цього, поки пул не оновиться.
  • Рuntime кеші DNS (кешування InetAddress у Java, деякі HTTP-клієнти, service meshes).
  • Довгоживучі процеси, які роблять резолюцію при старті і більше не повторюють її.

Операційний висновок: очищення DNS нижче додатка нічого не змінить, якщо додаток більше не запитує.
Ваш кращий «сброс» може бути rolling-restart або сигнал, що змушує перезавантажити налаштування (якщо підтримується).

Шар 1: libc, NSS і конфігурація резолвера

Тут живуть /etc/resolv.conf, /etc/nsswitch.conf, /etc/hosts, search-домени і опції на кшталт ndots.
Цей шар сам по собі зазвичай не «кешує» агресивно (glibc не тримає великий спільний кеш), але може створювати поведінку, яка виглядає як кешування:

  • search-домени + ndots викликають кілька запитів на один lookup. Один із цих запитів може пройти і залишитися в кешах upstream.
  • timeout/attempts призводять до довгих зависань, які виглядають як часткові відмови.
  • /etc/hosts повністю перебиває DNS, в результаті ‒ «застарілий DNS», що фактично є статичним файлом.

Шар 2: демони кешування (nscd, dnsmasq, systemd-resolved)

Якщо на вузлі працює локальний кешуючий резолвер, контейнери можуть звертатися до нього безпосередньо (через скопійований resolv.conf) або опосередковано (Docker форвардить до нього).
Якщо цей демон кешує погані дані, контейнери бачитимуть їх, поки кеш не протермінує або ви не очистите його.

У сучасному Ubuntu systemd-resolved часто сидить на 127.0.0.53, виступаючи як локальний stub і кеш.
Docker іноді має проблеми, якщо він копіює 127.0.0.53 в контейнери: ця адреса знаходиться в неймспейсі контейнера і не вказує на stub хоста, якщо немає спеціального маршруту.
Цей режим відмови — не кешування; це несумісність неймспейсу.

Шар 3: вбудований DNS Docker (127.0.0.11)

У користувацьких bridge-мережах Docker зазвичай вставляє nameserver 127.0.0.11 у /etc/resolv.conf контейнера.
Ця IP не є резолвером хоста; це вбудований резолвер Docker, прив’язаний всередині неймспейсу мережі контейнера.

Що він робить добре:

  • резолює імена контейнерів у IP-адреси всередині Docker-мережі,
  • підтримує псевдоніми й service discovery у Compose,
  • пересилає інші запити на upstream-резолвери, налаштовані для Docker/хоста.

Що він робить погано (або принаймні непрозоро):

  • становиться додатковим вузлом, де таймаути та поведінка кешування можуть неправильно інтерпретуватися,
  • приховує зміни upstream-резолверів, поки контейнери не будуть відтворені або Docker не перезавантажить конфігурацію,
  • робить «очищення DNS» неоднозначним, бо ви не керуєте цим кешем як типовим демоном.

Шар 4: upstream-резолвери (CoreDNS, Unbound, bind, cloud resolvers)

Upstream-резолвери кешують згідно з TTL, але також мають:

  • негативне кешування (NXDOMAIN кешується протягом певного часу),
  • prefetch та можливість serve-stale у деяких реалізаціях,
  • політики / split DNS, що змінюють відповіді залежно від мережі джерела.

Якщо ви не контролюєте upstream-кеш, єдиний надійний «сброс» — тимчасово опитати інший резолвер або дочекатися закінчення TTL/negative TTL.

Жарт №1: DNS означає «Definitely Not Synchronized». Він не синхронізований, але це пояснює більшість on-call вихідних.

Швидкий план діагностики

Мета тут не стати DNS-ученим. Мета — швидко знайти вузьке місце і «брехуна» з мінімальними побічними ефектами.

По-перше: перевірте симптом та охоплення

  • Це один контейнер, один хост чи всі хости? Проблеми в одному контейнері зазвичай означають кеш додатка, конфігурацію контейнера або специфічний шлях резолвера в неймспейсі.
  • Це одне ім’я чи всі імена? Одне ім’я вказує на DNS-запис або кешування; всі імена вказують на проблеми з підключенням резолвера або конфігурацією.
  • Це застаріле значення (стара IP) чи відмова (NXDOMAIN/таймаути)? Застаріле — це кеш; таймаути — мережа/MTU/фаєрвол; NXDOMAIN — негативне кешування або split DNS.

По-друге: з’ясуйте, яким резолвером користується контейнер

  • Перевірте /etc/resolv.conf в контейнері: 127.0.0.11 означає вбудований DNS Docker; все інше — пряме звернення.
  • Перевірте режим мережі Docker: чи це користувацький bridge, host network чи щось незвичне.

По-третє: виконайте порівняльні запити з контейнера і з хоста

  • Запитайте ім’я з контейнера за допомогою dig проти налаштованого резолвера.
  • Запитайте те саме ім’я з хоста проти upstream-резолверів.
  • Якщо відповіді відрізняються, розбіжність десь між контейнерним stub і upstream. Якщо відповіді збігаються, але додаток все ще звертається до старої IP, винен додаток або пул з’єднань.

По-четверте: виберіть найменший скидання, що змінює поведінку

  • Якщо додаток кешує: перезапустіть процес або змусьте повторну резолюцію (якщо підтримується).
  • Якщо кеш хоста: очистіть systemd-resolved/dnsmasq/nscd.
  • Якщо підозрюєте вбудований DNS Docker: пересоздайте контейнер (або мережу), або відкоригуйте DNS налаштування демона Docker і перезапустіть Docker у контрольований час.
  • Якщо upstream-кеш: тимчасово опитайте інший резолвер або дочекайтеся закінчення TTL/negative TTL.

Практичні завдання: команди, значення виводу та рішення (12+)

Завдання 1: Знайти налаштований резолвер контейнера

cr0x@server:~$ docker exec -it web-1 cat /etc/resolv.conf
nameserver 127.0.0.11
options ndots:0

Що це означає: Цей контейнер використовує вбудований DNS Docker (127.0.0.11). Очищення кешів хоста може не змінити те, що він бачить, якщо Docker кешує/форвардить неправильно.

Рішення: Виконайте dig всередині контейнера, щоб побачити, що повертає Docker DNS; також перевірте конфігурацію DNS демона Docker.

Завдання 2: Підтвердити, чи контейнер у користувацькій мережі

cr0x@server:~$ docker inspect -f '{{json .NetworkSettings.Networks}}' web-1
{"app_net":{"IPAMConfig":null,"Links":null,"Aliases":["web-1","web"],"NetworkID":"8d0e...","EndpointID":"0c4c...","Gateway":"172.20.0.1","IPAddress":"172.20.0.10","IPPrefixLen":16,"IPv6Gateway":"","GlobalIPv6Address":"","GlobalIPv6PrefixLen":0,"MacAddress":"02:42:ac:14:00:0a","DriverOpts":null}}

Що це означає: Користувацька bridge-мережа (app_net) зазвичай означає, що активний вбудований DNS і працює service discovery.

Рішення: Якщо проблема «ім’я сервісу вказує на неправильний контейнер», ви налагоджуєте DNS мережі Docker, а не корпоративний DNS.

Завдання 3: Порівняти відповіді DNS всередині контейнера за допомогою dig

cr0x@server:~$ docker exec -it web-1 sh -lc 'apk add --no-cache bind-tools >/dev/null 2>&1 || true; dig +noall +answer api.internal A'
api.internal.         30      IN      A       10.40.12.34

Що це означає: Резолвер Docker (або до кого він форвардить) наразі повертає 10.40.12.34 з TTL 30 секунд.

Рішення: Якщо ви очікували нову IP і TTL пройшов, щось кешує вище або нижче. Тепер запитайте з хоста і проти конкретного upstream-резолвера.

Завдання 4: Запитати те саме ім’я з хоста проти дефолтного резолвера

cr0x@server:~$ dig +noall +answer api.internal A
api.internal.         30      IN      A       10.40.12.99

Що це означає: Хост бачить 10.40.12.99, контейнер бачить 10.40.12.34. Розбіжність реальна й локальна.

Рішення: Вбудований резолвер Docker може форвардити до інших upstream-резолверів, ніж хост, або кешувати застарілі дані.

Завдання 5: Знайти, які DNS-сервери використовує демон Docker

cr0x@server:~$ docker info | sed -n '/DNS:/,/Registry Mirrors:/p'
DNS: 10.0.0.2
  10.0.0.3

Що це означає: Docker налаштовано використовувати 10.0.0.2 і 10.0.0.3 як upstream-резолвери для форвардингу вбудованого DNS.

Рішення: Запитайте ці резолвери безпосередньо з хоста, щоб побачити, що вони повертають і чи мають застарілий кеш.

Завдання 6: Запитати upstream-резолвер Docker безпосередньо

cr0x@server:~$ dig @10.0.0.2 +noall +answer api.internal A
api.internal.         30      IN      A       10.40.12.34

Що це означає: Upstream-резолвер, який використовує Docker, повертає стару IP. Docker не обов’язково бреше; він чесно повторює чиюсь іншу «брехню».

Рішення: Виправте або очистіть кеш upstream-резолвера, або перенаправте Docker на правильні резолвери.

Завдання 7: Виявити участь systemd-resolved на хості

cr0x@server:~$ resolvectl status | sed -n '1,35p'
Global
         Protocols: -LLMNR -mDNS -DNSOverTLS DNSSEC=no/unsupported
  resolv.conf mode: stub
Current DNS Server: 10.0.0.2
       DNS Servers: 10.0.0.2 10.0.0.3
        DNS Domain: corp.internal

Що це означає: Хост використовує режим stub systemd-resolved; він може кешувати і також може бути джерелом вмісту /etc/resolv.conf, який читає Docker.

Рішення: Якщо контейнери копіюють адресу stub, яка недоступна в їхньому неймспейсі, виправте DNS-конфіг Docker; якщо кеш хоста поганий — очистіть resolved.

Завдання 8: Очистити кеш systemd-resolved на хості (коли це він)

cr0x@server:~$ sudo resolvectl flush-caches
cr0x@server:~$ resolvectl statistics | sed -n '1,25p'
DNSSEC supported by current servers: no
Transactions: 1482
Cache Hits: 312
Cache Misses: 1170

Що це означає: Кеш очищено; наступні запити мають бути cache misses, поки кеш не прогріється.

Рішення: Повторіть запити з хоста/контейнера. Якщо контейнер усе ще бачить старі відповіді, systemd-resolved не був вузьким місцем.

Завдання 9: Перевірити, чи контейнер взагалі може дістатися до host-ового stub-резолвера

cr0x@server:~$ docker exec -it web-1 sh -lc 'grep -E "^nameserver" /etc/resolv.conf; nc -zu -w1 127.0.0.53 53; echo $?'
nameserver 127.0.0.11
1

Що це означає: Контейнер не налаштований використовувати 127.0.0.53, і навіть якби був, ця адреса була б локальною для неймспейсу контейнера.

Рішення: Перестаньте намагатися «очистити host stub» як фікс для контейнера з 127.0.0.11. Працюйте з Docker DNS/upstream.

Завдання 10: Визначити, чи додаток прив’язує з’єднання до старих IP

cr0x@server:~$ docker exec -it web-1 sh -lc 'ss -tnp | head -n 10'
State  Recv-Q Send-Q Local Address:Port   Peer Address:Port  Process
ESTAB  0      0      172.20.0.10:49218   10.40.12.34:443    users:(("app",pid=1,fd=73))
ESTAB  0      0      172.20.0.10:49222   10.40.12.34:443    users:(("app",pid=1,fd=74))

Що це означає: Додаток наразі підключений до старої IP. Навіть якщо DNS тепер резолвиться в нову IP, активні сокети продовжують працювати.

Рішення: Змусьте оновлення з’єднань (reload, restart, зменшити keepalive) або виправте поведінку пулу. Очищення DNS не розірве встановлені TCP-сесії.

Завдання 11: Перевірити /etc/hosts всередині контейнера на несподіванки «DIY DNS»

cr0x@server:~$ docker exec -it web-1 cat /etc/hosts
127.0.0.1	localhost
172.20.0.10	web-1
10.40.12.34	api.internal

Що це означає: Хтось закріпив api.internal у /etc/hosts. Це не кешування. Це постійне перебивання.

Рішення: Видаліть запис (перебудуйте образ, виправте entrypoint або припиніть інжектити hosts-записи). Потім повторно задеплойте. Очищення будь-якого DNS-кешу не має сенсу, поки це не усунуто.

Завдання 12: Перевірити порядок NSS (files vs dns) всередині контейнера

cr0x@server:~$ docker exec -it web-1 sh -lc 'cat /etc/nsswitch.conf | sed -n "1,25p"'
passwd:         files
group:          files
hosts:          files dns
networks:       files

Що це означає: files перевіряється перед DNS. Якщо /etc/hosts має запис, він перемагає.

Рішення: Якщо ви змушені використовувати /etc/hosts для тимчасової міграції, ставтеся до нього як до керованої конфігурації, а не як до забутого хака.

Завдання 13: Спостерігати негативне кешування (NXDOMAIN)

cr0x@server:~$ docker exec -it web-1 sh -lc 'dig +noall +authority does-not-exist.corp.internal'
corp.internal.        300     IN      SOA     ns1.corp.internal. hostmaster.corp.internal. 2026010301 3600 600 604800 300

Що це означає: SOA negative TTL — 300 секунд. NXDOMAIN може кешуватися резолверами 5 хвилин.

Рішення: Якщо ви щойно створили запис, чекати може бути правильним рішенням. Або опитати авторитетні сервери безпосередньо, якщо це можливо.

Завдання 14: Перевірити конфіг Docker демона на наявність фіксованих DNS-настроєнь

cr0x@server:~$ sudo cat /etc/docker/daemon.json
{
  "dns": ["10.0.0.2", "10.0.0.3"],
  "log-driver": "json-file"
}

Що це означає: Upstream-резолвери Docker прив’язані. Якщо корпоративний DNS змінився, Docker не буде автоматично слідувати за хостом.

Рішення: Оновіть цей файл (через конфігураційне управління), потім перезапустіть Docker у вікні обслуговування і, за потреби, пересоздайте контейнери.

Завдання 15: Підтвердити, яким насправді є /etc/resolv.conf хоста

cr0x@server:~$ ls -l /etc/resolv.conf
lrwxrwxrwx 1 root root 39 Jan  3 09:12 /etc/resolv.conf -> ../run/systemd/resolve/stub-resolv.conf

Що це означає: Хост вказує на stub resolv.conf systemd. Якщо Docker читає це і копіює в контейнери, це може призвести до катастрофи, якщо в контейнерах опиниться nameserver 127.0.0.53.

Рішення: Віддавайте перевагу явним DNS-настройкам Docker (daemon.json) або переконайтеся, що Docker використовує «реальний» resolv.conf, який надає ваш дистро (/run/systemd/resolve/resolv.conf) через налаштування демона.

Завдання 16: Протестувати шлях від контейнера до резолвера та виміряти затримку

cr0x@server:~$ docker exec -it web-1 sh -lc 'time dig +tries=1 +timeout=2 api.internal A +noall +answer'
api.internal.         30      IN      A       10.40.12.34

real	0m0.018s
user	0m0.009s
sys	0m0.004s

Що це означає: Lookup швидкий. Якщо ваш додаток повідомляє «DNS timeout», він може виконувати кілька запитів (через search-домени) або використовувати інший шлях резолвера, ніж ваш інструмент тестування.

Рішення: Перевірте search-домени і ndots; інспектуйте поведінку резолвера додатка; при потребі зафіксуйте трафік.

Три міні-історії з корпоративного життя (анонімізовані)

Інцидент: неправильне припущення («ми очистили DNS, тож все має бути гаразд»)

Середня SaaS-компанія перемістила внутрішнє API за новий VIP під час впорядкування дата-центру. Вони заздалегідь знизили TTL, уважно стежили за метриками і зробили cutover у вівторок вранці, бо ніхто не любить п’ятницю.
Половина флету перейшла без проблем. Декілька application pod-ів — ні. Помилки зросли, але лише для частини користувачів.

Канал інцидентів наповнився типовим DNS-фольклором. Хтось очистив systemd-resolved на кількох вузлах. Хтось перезапустив Docker на одному хості. Третій запустив dig на ноуті, отримав нову IP і оголосив перемогу.
Тим часом набір довгоживучих воркер-контейнерів продовжував спілкуватися зі старою IP. Вони вже навіть не виконували DNS; у них були пулі з’єднань, що тримали TLS-сесії годинами.

Ключ прийшов зсередини контейнера: ss -tnp показав встановлені з’єднання з старою адресою. DNS був «правильним» і одночасно не мав значення.
Виправлення не було в очищенні кешу. Це був контрольований перезапуск воркерів (rolling з дренажем черг) плюс тонування часу життя з’єднань клієнта.

Постмортемні дії були нудними: задокументувати, що «DNS змінився» не означає «трафік перемістився», додати канарку, яка перевіряє фактичні peer-IP, і винести час життя з’єднань в конфігурацію, а не в код.

Оптимізація, яка відбилася боком: «давайте прискоримо DNS, додавши кеш»

Інша команда мала реальну проблему: їхні хости виконували багато DNS-запитів, і зрідка в стектрейсах з’являлися спайки затримок.
Очевидне рішення — розгорнути локальний кешуючий резолвер на кожному вузлі. Вони деплойнули dnsmasq і направили хост на нього. Запити стали швидшими. Усі святкували.

Потім стався інцидент, який не виглядав пов’язаним: blue/green деплой переключив внутрішній сервіс на нову IP, але сегмент контейнерів довго продовжував хітити старий бекенд.
Кешуючий резолвер був налаштований з агресивними мінімальними TTL «щоб зменшити навантаження». Він не був злим; він був «ефективним».
На жаль, ефективність — це спосіб створити застарілі відповіді в масштабі.

Команда сперлася спочатку на Docker, бо контейнери були видимою зміною. Але справжня причина — політика кешування dnsmasq разом з негативним кешуванням для запису, що тимчасово зник під час cutover.
Вони виправили це, узгодивши політику кешування з процесом змін: перестали перевизначати TTL, якщо вони не готові нести відповідальність, і додали крок у ранубук для перевірки upstream відповідей під час міграцій.

Урок: кешування — це фіча продуктивності, яка стає фічею надійності тільки тоді, коли її налаштовано так, що вам доведеться відповідати на інциденти. Інакше це часова бомба з регульованою запалкою.

Нудно, але правильно: практика, яка врятувала день

Компанія, що працює з фінансами, тримала Docker-хости в двох мережах: корпоративному LAN і обмеженому PCI-сегменті.
DNS був split-horizon: те ж ім’я могло резолвитися по-різному залежно від вашого місцезнаходження. Це було свідомо, задокументовано й дратувало.

Їхня SRE команда мала звичку, що виглядала як бюрократія: кожен образ контейнера включав невеликий набір діагностичних інструментів, і кожен on-call ранубук починався з «надрукуй resolv.conf і опитай точно вказаний резолвер».
Люди закочували очі — доки не сталася вечірня аварія в четвер.

Деплой у PCI-сегменті не зміг досягти внутрішнього сервісного імені. На ноутбуках працювало. На деяких хостах працювало. На інших — ні.
Ранубук швидко відокремив проблему: контейнери використовували вбудований DNS Docker, який форвардив до корпоративного резолвера, що не мав PCI-перегляду.
Хтось «уніфікував» DNS-сервери в daemon.json по середовищах на початку тижня.

Оскільки шлях резолвера був задокументований і діагностичні кроки були послідовними, вони відкотили зміну в daemon.json за кілька хвилин, перезапустили Docker на невеликій підмножині, перевірили — і потім безпечно розгорнули виправлення.
Ніяких героїчних дій. Жодних пізніх захоплень пакетів. Лише дисципліноване, повторюване спостереження.

Жарт №2: Додавати кеш DNS, щоб виправити надійність, — це як додати другий холодильник, щоб позбутися голоду: ви просто збережете більше поганих рішень на потім.

Типові помилки: симптом → корінна причина → вирішення

1) Симптом: «Контейнер резолвить стару IP, хост — нову»

Корінна причина: Контейнер використовує вбудований DNS Docker, який форвардить до інших upstream-резолверів (daemon.json зафіксовано, або Docker обрав сервери відмінні від хоста).

Вирішення: Порівняйте DNS у docker info з резолвером хоста; опитайте upstream-резолвери Docker безпосередньо; оновіть конфігурацію демона Docker і перезапустіть Docker у контрольований час.

2) Симптом: «Очищення DNS на хості нічого не змінило»

Корінна причина: Додаток кешує DNS або тримає постійні з’єднання; він ніколи більше не перепитує.

Вирішення: Перезапустіть або перезавантажте додаток; налаштуйте максимальний час життя з’єднань; зменшіть keepalive там, де це безпечно; перевірте за допомогою ss, чи змінилася peer IP.

3) Симптом: «На хості DNS працює, а в контейнері — таймаути»

Корінна причина: Контейнер отримав nameserver 127.0.0.53 скопійований з хоста, але ця адреса недоступна в неймспейсі контейнера.

Вирішення: Налаштуйте Docker на використання реальних upstream-резолверів (не host stub), або забезпечте, щоб Docker використовував не-stub resolv.conf; пересоздайте контейнери.

4) Симптом: «Одне конкретне ім’я завжди резолвиться неправильно всередині контейнера»

Корінна причина: Запис у /etc/hosts в образі або інжектований під час запуску; NSS перевіряє files перед DNS.

Вирішення: Видаліть запис у hosts; виправте збірку/entrypoint; перевірте порядок hosts: files dns і вміст.

5) Симптом: «Переривчастий NXDOMAIN після створення запису»

Корінна причина: Негативне кешування (SOA minimum/negative TTL) у upstream-резолверах або локальних кешах.

Вирішення: Перевірте SOA negative TTL; дочекайтеся; або опитайте авторитетні сервери; уникайте патерну delete-and-recreate під час міграцій.

6) Симптом: «DNS повільний тільки в контейнерах»

Корінна причина: Search-домени і ndots викликають кілька запитів; або upstream-резолвер доступний з хоста, але недоступний з контейнера через фаєрвол.

Вирішення: Перевірте опції /etc/resolv.conf; обріжте search-домени; забезпечте доступність UDP/TCP 53 з неймспейсу контейнера; виміряйте за допомогою timed dig.

7) Симптом: «Імена сервісів Docker Compose резолвяться непослідовно»

Корінна причина: Кілька мереж, псевдоніми або застарілі контейнери; вбудований DNS відповідає по-різному залежно від приєднання до мережі.

Вирішення: Перевірте приєднання до мереж; переконайтеся, що сервіси поділяють потрібну мережу; видаліть orphan контейнери і пересоздайте мережу проекту.

8) Симптом: «Після зміни корпоративного DNS деякі хости ніколи не підхопили його»

Корінна причина: Демон Docker має зафіксовані DNS-сервери; контейнери продовжують використовувати вбудований DNS, який форвардить до старих серверів.

Вирішення: Оновіть daemon.json через конфігураційне управління; заплануйте перезапуск Docker у вікні обслуговування; пересоздайте контейнери, створені з старою конфігурацією.

Чеклісти / покроковий план (навмисно нудно)

Покроково: ізолювати «брехуна» за 15 хвилин

  1. Виберіть один невдалий контейнер і один здоровий (якщо є здоровий). Краще — той же образ.
  2. Занотуйте конфіг резолвера контейнера: cat /etc/resolv.conf, cat /etc/nsswitch.conf, та cat /etc/hosts.
  3. Опитайте ім’я інструментом, що показує TTL (dig) зсередини контейнера.
  4. Опитайте те саме ім’я з хоста і проти конкретних upstream-резолверів, які використовує Docker.
  5. Якщо відповіді відрізняються: визначіть перший шар, де вони розходяться (Docker upstream vs host upstream vs split DNS).
  6. Якщо відповіді співпадають, але додаток все ще використовує стару IP: перевірте існуючі TCP-з’єднання, налаштування пулів і кеш додатка.
  7. Зробіть найменшу зміну, яка змусить новий шлях резолюції: очистіть правильний кеш або перезапустіть потрібний процес.
  8. Підтвердіть з доказами: покажіть нову відповідь DNS і нову peer-IP у ss або логи запитів.

Чекліст під час деплойменту: не створюйте DNS-сюрпризів

  • Не відправляйте хаки в /etc/hosts в образах, якщо ви не маєте плану їх видалення.
  • Тримайте DNS-настройки демона Docker явними для кожного середовища; не вважайте, що DNS хоста = DNS контейнера.
  • Для сервісів за балансуванням на основі DNS налаштуйте час життя клієнтських з’єднань так, щоб трафік дійсно міг переміщатися.
  • Під час міграцій уникайте транзисторних NXDOMAIN подій; негативне кешування затримає їх.
  • Майте один канонічний діагностичний контейнер (toolbox) на кожному хості/кластері.

План змін: безпечно оновлюємо DNS Docker на флоті хостів

  1. Визначте, які хости активно використовують вбудований DNS Docker (user-defined мережі, Compose стеки).
  2. Оновіть /etc/docker/daemon.json з правильними DNS-серверами і (за потреби) опціями.
  3. Заплануйте перезапуск демона Docker; оцініть вплив (контейнери можуть перезапуститися залежно від політик).
  4. Перезапустіть Docker на canary-хості спочатку; підтвердіть /etc/resolv.conf в контейнерах і результати dig.
  5. Проходьте по флоту; пересоздайте контейнери, які були створені з старою конфігурацією, якщо поведінка не зникла.

Поширені запитання

1) Чому docker exec показує nameserver 127.0.0.11?

Це вбудований DNS Docker для користувацьких мереж. Він забезпечує резолюцію імен контейнерів/сервісів і форвардить інші запити upstream.
Це нормально, і саме через це «очищення кешу хоста» часто не робить того, що ви очікували.

2) Чи можна безпосередньо очистити кеш вбудованого DNS Docker?

Ні в чистому вигляді «однією командою», як у systemd-resolved. Операційно ви впливаєте на нього, змінюючи upstream-резолвери, пересоздаючи контейнери/мережі або перезапускаючи Docker.
Перш ніж це робити, доведіть, що вбудований шар — джерело розбіжностей, порівнявши відповіді по шарах.

3) Чому перезапуск контейнера іноді вирішує DNS?

Це може змінити кілька речей одразу: контейнер отримає свіжий /etc/resolv.conf, додаток перезапуститься і скине внутрішні кеші/пули, і Docker може перебудувати частини стану мережі.
Це грубий інструмент. Корисний, але він ховає корінну причину, якщо ви робите це першим.

4) Мій TTL 30 секунд. Чому застарілі відповіді трималися хвилинами?

Тому що TTL застосовується до кешів DNS, а не до існуючих з’єднань вашого додатка, і не обов’язково до кешів на рівні додатка.
Також upstream-резолвери можуть обмежувати TTL або встановлювати мінімальні TTL, а негативне кешування має власні таймери.

5) Чому хост резолвить правильно, а контейнери — ні після увімкнення systemd-resolved?

Якщо контейнери отримали nameserver 127.0.0.53, це вказує на самі себе, а не на host stub.
Налаштуйте Docker на використання реальних upstream-резолверів або доступного IP кешуючого резолвера, а не loopback stub хоста.

6) Чи варто запускати DNS-кеш в кожному контейнері?

Ні, якщо вам не подобається відлагоджувати кілька шарів кешування під час інцидентів.
Якщо вам потрібне кешування, робіть його на рівні ноди (керовано й спостережувано) або через виділений рівень резолверів, і тримайте шлях резолюції в контейнерах простим.

7) Який найшвидший спосіб довести, що це не DNS взагалі?

Покажіть, що dig повертає нову IP, але ss -tnp показує встановлені з’єднання зі старою IP, або логи запитів показують старого peer.
Це територія пулів з’єднань / keepalive / кешування додатка.

8) Чи змінює Kubernetes цю історію?

Так, але форма схожа: контейнери часто опитують CoreDNS, який кешує і форвардить. У вас все ще є кешування додатка, поведінка libc і upstream.
Головна різниця в тому, що «вбудований DNS» зазвичай CoreDNS плюс kube-dns конвенції, а не Docker-овий 127.0.0.11.

9) Чому BusyBox nslookup не погоджується з dig?

Різні інструменти мають різну поведінку резолвера, вивід і іноді різні налаштування за замовчуванням (TCP vs UDP, поведінка search, повтори).
Віддавайте перевагу dig для ясності (TTL, authority/additional секції) і використовуйте вимірювані запуски, щоб помітити повтори.

10) Коли очищення кешів — неправильний крок?

Коли ви не довели, що проблема — кешування. Таймаути через фаєрвол, MTU або недоступний резолвер не будуть виправлені очищенням.
Також очищення upstream-кешів може створити «тріскучі» хвилі запитів, якщо багато вузлів почнуть перепитувати одночасно.

Висновок: наступні кроки, які ви насправді можете зробити

Проблеми з DNS у Docker рідко містять містичний компонент. Вони багатошарові. Брехня зазвичай ховається в одному з трьох місць:
додаток, який ніколи не пере-resolve-ить, шлях резолвера, який відрізняється між хостом і контейнером, або upstream-кеш, який робить саме те, для чого його налаштували.

Наступного разу, коли побачите «застарілий DNS»:

  • Почніть з друку /etc/resolv.conf всередині контейнера і припиніть гадати.
  • Виконайте паралельні dig з контейнера й хоста, а потім безпосередньо проти upstream-резолверів Docker.
  • Якщо DNS правильний, але трафік — ні, перевірте встановлені з’єднання і перезапустіть потрібний процес, а не всесвіт.
  • Стандартизуйте невеликий набір діагностичних інструментів і ранубук, що вимагає збору доказів перед очищенням чогось.

Якщо ви послідовно виконуватимете ці чотири кроки, «брехня кешування DNS» перетвориться з повторюваної теми інцидентів на керовану неприємність.

← Попередня
Затримка реплікації MySQL та PostgreSQL: чому виникає і як її зменшити
Наступна →
Лендінг на чистому HTML/CSS: хедер, можливості, тарифи, FAQ (у стилі документації)

Залишити коментар