3 DNS сервера в /etc/resolv.conf

May 23, 2026

Сколько DNS серверов можно вручную указать в ОС?

В Linux, в /etc-resolv.conf можно указать 3 сервера. Остальные будут игнорироваться. Об этом написано в комментариях в /etc/resolv.conf и обычно никакого дискомфорта это не вызывает.

Нo недавно я обнаружил тонну DNSConfigForming warning'ов в событиях k8s-кластера, запущенного в Hetzner Cloud/Robot:

Nameserver limits were exceeded, some nameservers have been omitted, the applied nameserver line is:

Как вообще так вышло: Hetzner, как поставщик инфраструктуры, может твоему серверу по DHCP прислать несколько DNS серверов. А именно, 3 штуки IPv4 резолверов и 3 IPv6. Никакой магии.

В дистрибутивах Linux ограничение на 3 DNS сервера в /etc/resolv.conf существовало всё время, сколько я себя помню. И, спустя много времени, это ограничение не то, чтобы сильно мозолит глаз. Но сейчас этот варнинг порядочно спамит в события k8s, и если тебе что-то срочно надо найти в событиях, придется визуально скипнуть все сообщения про превышение "нормы DNS". А спамятся они постоянно и в большом количестве.

Как это работает

Когда программа в Linux хочет разрезолвить доменное имя в IP-адрес, оно не читает /etc/resolv.conf напрямую. Оно вызывает библиотечные функции getaddrinfo() или gethostbyname() (ранее), которые используют структуры данных и константы из хзаголовочного файла resolv.h (или res_state.h). Всё это описано в glibc.

Вот как это выглядит в mainline glibc (ссылка ведёт на неофицильный форк):


res_state.h
#ifndef __res_state_defined
#define __res_state_defined 1

#include <sys/types.h>
#include <netinet/in.h>

/* res_state: the global state used by the resolver stub.  */
#define MAXNS			3	/* max # name servers we'll track */
#define MAXDFLSRCH		3	/* # default domain levels to try */
#define MAXDNSRCH		6	/* max # domains in search path */
#define MAXRESOLVSORT		10	/* number of net to sort on */

struct __res_state {
	int	retrans;		/* retransmission time interval */
	int	retry;			/* number of times to retransmit */
	unsigned long options;		/* option flags - see below. */
	int	nscount;		/* number of name servers */
	struct sockaddr_in
		nsaddr_list[MAXNS];	/* address of name server */

Историческая справка

3 DNS сервера появились в glibc достаточно давно. Но насколько? Настолько, что история resolv.h тянется ещё с ранних верcий BSD.


git clone https://sourceware.org/git/glibc.git
git log --reverse resolv/resolv.h
git show 28f540f45bbacd939bfd07f213bcad2bf730b1bf

История git-репозитория glibc может показать эту версию и появление MAXNS в:

+ * @(#)resolv.h 8.1 (Berkeley) 6/2/93

Но корни resolv.conf уходят в те времена, когда Линус Торвальдс ещё не написал Git. Я сначала думал искать старые верcии unix, но натыкался на git и версии кода ~1993 года include/resolv.h:


/*
 * Global defines and variables for resolver stub.
 */
#define	MAXNS			3	/* max # name servers we'll track */
#define	MAXDFLSRCH		3	/* # default domain levels to try */
#define	MAXDNSRCH		6	/* max # domains in search path */
#define	LOCALDOMAINPARTS	2	/* min levels in name that is "local" */

Однако, достаточно быстрый поиск даёт прояснение в рассылке internet history, где один из авторов BSD расставляет все точки над i:

Someone pointed out to me that I did the code check-in to the source code repository for UC Berkeley CSRG (BSD) in October 1985. Code change set MAXNS to 3. Now someone over 35 years is questions why. I would say setting it to 3 has stood the test of time.

MAXNS is in the resolver code, designed to be fairly light weight since it lives in libC. If you wanted DNS caching you put a recursive caching only name server on the local system that would do all the real resolution work and cache the results for other DNS resolutions.

Back in 1985 named, name server, would occasionally crash. So by setting MAXNS to three would give the resolver the opportunity to try the localhost name server and two backups. Hopefully, you set the two backup nameservers to ones in you LAN. Setting to nameservers across the internet would not result in speedy name resolution.

За долгое время с 1985 года проблема 3 DNS серверов всплывала не раз.

https://bugs.launchpad.net/ubuntu/+source/glibc/+bug/118930

https://unix.stackexchange.com/questions/28004/how-to-overcome-libc-resolver-limitation-of-maximum-3-nameservers

https://superuser.com/questions/417519/dns-resolver-improvements

https://askubuntu.com/questions/1157265/how-do-i-allow-more-than-3-dns-servers-in-ubuntu-16

https://access.redhat.com/solutions/142493

Workaround решение

В чем проблема просто взять и пересобрать libc/glibc с большим MAXNS? Размер структуры res_state захардкожен в миллионы уже скомпилированных программ. Если в новой версии glibc изменить MAXNS с 3 на 10, размер массива nsaddr_list вырастет. Соответственно, изменится и общий размер всей структуры __res_state. Eсли пересобрать дистрибутив и glibc, но не пересобрать программы, то старый софт будет ожидать __rest_state с неверным количеством памяти под nsaddr_list. Бинарная совместимость (Application Binary Interface) будет нарушена. Несмотря на то, что в glibc, насколько я знаю, нет "срока годности ABI", на практике libc могут выдерживать стабильность с прошлыми версиями на протяжении 20-25 лет.

Что придумали для обхода ограничения?

Если коротко, то вариантов, кроме использования локального dns резолвера, особо и нет. Ранее использовали dnsmasq, сейчас же systemd-resolved за тебя решает эту проблему.

Почему варнинг про DNS неймсерверы всплыл в k8s?

Оказалось, что на нодах свежеразлитого кластера k8s искал dns не в /etc/resolv.conf а в stub-файле, который использовал systemd-resolved. Использование системного /etc/resolv.conf убрало проблему постоянно спамящих алертов про превышение системного лимита в 3 DNS неймсервера.

Можно ли сделать алерт менее назойливым в k8s?

Достаточно долгое время я следил за issue, которая описывала один в один события в events k8s, с которыми я столкнулся. Автор предлагал сделать меньше количество таких варнингов, так как их количество превышало толк

Буквально на днях в issue появилась активность, но увы: на issue не было реакции со стороны контрибьюторов, что значит, конфигурацию DNS на новых нодах придётся периодически подпиливать.

Дополнительные материалы

https://kubernetes.io/docs/tasks/administer-cluster/dns-debugging-resolution/#known-issues

https://jpetazzo.github.io/2024/05/12/understanding-kubernetes-dns-hostnetwork-dnspolicy-dnsconfigforming/