В логах сервера однажды появляется тревожная строка от службы chronyd с формулировкой Can't synchronise: no majority of sources. Сначала кажется, что это что-то страшное. На самом деле сообщение довольно болтливое и означает буквально следующее. Демон опросил настроенные NTP-серверы, получил от них ответы, но не смог найти большинства, которое согласилось бы между собой о текущем времени. Звучит как древняя задача о византийских генералах, и по сути это она и есть. Разбираем, как читать вывод диагностических команд, что обычно стоит за этой ошибкой и какими шагами её устраняют.
Почему синхронизация времени устроена сложнее чем кажется обычному пользователю
Начнём с фундаментального момента, который объясняет половину всех проблем. NTP-клиент не доверяет ни одному отдельно взятому серверу. Стандарт прямо предполагает, что любой источник может оказаться "лжецом" - сервером с поплывшими часами или вообще скомпрометированным узлом. Поэтому chronyd опрашивает несколько источников и применяет алгоритм, отдалённо напоминающий голосование. Если большинство серверов сходится во мнении о текущем времени с разумной погрешностью, оно принимается за истину. Если согласия нет, демон отказывается переводить часы и честно пишет в журнал о невозможности выбрать большинство.
Минимальное число источников для надёжного выявления ложных показаний равно четырём. На двух серверах алгоритм просто не способен решить, кто из них прав, если они расходятся. На трёх он работает, но без запаса прочности. Именно отсюда растёт первое и самое частое объяснение знаменитого no majority. У человека настроены два сервера, один из которых периодически плавает или временно недоступен, и chronyd впадает в уныние ровно в эти моменты.
Посмотреть текущий список источников и их состояние можно одной командой:
chronyc sources -v
Вывод выглядит примерно так:
210 Number of sources = 4
.-- Source mode '^' = server, '=' = peer, '#' = local clock.
/ .- Source state '*' = current best, '+' = combined, '-' = not combined,
| / 'x' = may be in error, '~' = too variable, '?' = unusable.
|| .- xxxx [ yyyy ] +/- zzzz
|| Reachability register (octal) -. | xxxx = adjusted offset,
|| Log2(Polling interval) --. \ | yyyy = measured offset,
|| \ | | zzzz = estimated error.
|| | | \
MS Name/IP address Stratum Poll Reach LastRx Last sample
===============================================================================
^* time.cloudflare.com 3 6 377 34 +123us[ +456us] +/- 12ms
^+ ntp.ubuntu.com 2 6 377 33 -234us[ -567us] +/- 15ms
^+ time.google.com 1 6 377 35 +345us[ +678us] +/- 10ms
^? 0.pool.ntp.org 0 7 0 - +0ns [ +0ns] +/- 0ns
Расшифровка магических символов в выводе chronyc sources которая экономит часы отладки
Каждая колонка несёт конкретный смысл, и понимание этих символов превращает диагностику из шаманства в осмысленный процесс. Первый символ в строке показывает режим источника. Крышечка означает обычный сервер, знак равенства был бы пиром. Следующий символ - состояние выбора. Звёздочка отмечает текущий лучший источник, плюс показывает дополнительные источники, чьи измерения комбинируются с основным. Минус означает источник, измерения которого не используются. Знак вопроса говорит о недоступности, тильда о слишком сильных колебаниях, икс о том, что время этого сервера может быть некорректным.
Колонка Stratum показывает, насколько далеко источник находится от эталонных часов. Stratum 1 это сервер, напрямую подключённый к атомным часам или GPS-приёмнику. Stratum 2 синхронизируется с stratum 1, и так далее. Чем меньше число, тем выше точность.
Колонка Reach содержит самое полезное число для диагностики. Это восьмибитный регистр в восьмеричном представлении, где каждый бит соответствует последнему опросу. Значение 377 означает, что все восемь последних опросов прошли успешно. Ноль означает, что не пришёл ни один ответ. Промежуточные значения вроде 17 или 7 указывают на нестабильность связи. Документация прямо говорит, что нулевое значение Reach означает невозможность клиента подключиться к NTP-серверу на порту 123.
Колонка Poll отображает интервал между запросами как степень двойки. Значение 6 это два в шестой, то есть 64 секунды. Значение 10 это уже 1024 секунды, около 17 минут. Чем стабильнее связь, тем больший интервал выбирает chronyd сам.
Если в колонке Reach стоит ноль, то независимо от красоты остальных параметров источник для синхронизации не годится. Это первая и самая частая причина ошибки no majority.
Команда tracking как главный инструмент проверки общего состояния синхронизации
Параллельно с проверкой источников полезно посмотреть, что демон думает о собственном состоянии:
chronyc tracking
Типичный здоровый вывод выглядит примерно так:
Reference ID : C0A80033 (192.168.0.51)
Stratum : 4
Ref time (UTC) : Sat Nov 17 01:58:51
System time : 0.001598277 seconds fast of NTP time
Last offset : +0.001791533 seconds
RMS offset : 0.001791533 seconds
Frequency : 0.546 ppm slow
Residual freq : -0.175 ppm
Skew : 0.168 ppm
Root delay : 0.094823152 seconds
Root dispersion : 0.021242738 seconds
Update interval : 65.0 seconds
Leap status : Normal
Самое важное здесь - первая строка. Reference ID показывает, к какому источнику привязан клиент. Если там написано 7F7F0101 без имени, значит демон работает в режиме local и не синхронизируется ни с кем извне. Поле Stratum показывает позицию в иерархии. Поле System time говорит, насколько системные часы убегают вперёд или отстают от эталона.
Когда возникает та самая ошибка no majority, в этом выводе обычно присутствуют характерные признаки. Reference ID может быть нулевым или содержать строку 00000000, Stratum равен нулю, Leap status выводит Not synchronised. Это означает, что демон не выбрал ни одного источника как достоверный.
Главные подозреваемые в проблеме no majority и порядок их проверки
Когда сообщение появляется в логах, причин обычно ищут в трёх местах. Первое - источников действительно мало или они недоступны. Второе - сетевой фильтр режет UDP на порту 123. Третье - часы одного из серверов ушли в сторону настолько сильно, что chronyd не может объединить его показания с остальными.
Начинать диагностику логично с проверки доступности портов:
# Проверка, что UDP 123 открыт наружу для исходящих запросов
sudo ss -uanp | grep ':123'
# Проверка ответа от конкретного NTP-сервера через ntpdate в режиме отладки
ntpdate -q time.cloudflare.com
# Альтернативная проверка через chronyd напрямую
chronyd -Q 'server time.cloudflare.com iburst'
Команда chronyd с ключом -Q работает в одноразовом режиме без установки времени и просто показывает, удалось ли получить ответ от указанного сервера. Это удобный способ проверить отдельный источник, не трогая работающую конфигурацию.
Если ответа нет вовсе, значит соединение с сервером не устанавливается. Тут на сцену выходят файрволы. Проверить локальный фильтр можно стандартными средствами в зависимости от дистрибутива:
# Для систем с firewalld (CentOS, RHEL, Fedora, Rocky)
sudo firewall-cmd --list-all
sudo firewall-cmd --list-services
# Для систем с ufw (Ubuntu, Debian)
sudo ufw status verbose
# Для систем с прямыми iptables
sudo iptables -L -n -v | grep 123
sudo nft list ruleset | grep 123
NTP использует именно UDP-порт 123 для собственно синхронизации, и если фильтр режет исходящий трафик на этом порту, никакая настройка серверов не поможет. Стоит отметить, что командный порт chronyd, через который работает chronyc, это другой порт - 323/UDP, и его обычно не нужно открывать наружу.
Открыть порт 123 для клиентского NTP в основных системах:
# firewalld
sudo firewall-cmd --permanent --add-service=ntp
sudo firewall-cmd --reload
# ufw
sudo ufw allow 123/udp
# iptables
sudo iptables -A OUTPUT -p udp --dport 123 -j ACCEPT
sudo iptables -A INPUT -p udp --sport 123 -j ACCEPT
Стоит обратить внимание, что для клиентского режима достаточно разрешить исходящий трафик на 123/udp и входящие ответы с того же порта. Открывать 123 на вход извне нужно только если машина сама работает как NTP-сервер для других.
Проверка сетевой связности через сниффинг трафика когда обычные команды не помогают
Если порты вроде бы открыты, но Reach всё равно ноль, остаётся посмотреть на то, что реально происходит в сети. Утилита tcpdump показывает, уходят ли пакеты и приходят ли ответы:
# Слушаем NTP-трафик на интерфейсе
sudo tcpdump -n -i any 'udp port 123'
# С большей детализацией
sudo tcpdump -nvv -i any 'udp port 123'
# Сохранение в файл для последующего разбора
sudo tcpdump -w /tmp/ntp.pcap 'udp port 123'
Параллельно в другом терминале запускается принудительный опрос источников, чтобы спровоцировать обмен:
# Заставить chronyd опросить источники прямо сейчас
sudo chronyc -a burst 4/4
sudo chronyc -a sources
В выводе tcpdump должны быть видны исходящие запросы вида ваш_IP.случайный_порт > сервер.ntp и ответные пакеты в обратном направлении. Если запросы уходят, но ответов нет, проблема за пределами вашей машины - где-то по пути сидит фильтр, который режет либо исходящие запросы, либо входящие ответы. Это часто встречается на корпоративных сетях, где администраторы перенаправляют весь NTP-трафик на внутренний сервер.
Редактирование конфигурации chronyd для добавления и замены источников времени
Главный конфигурационный файл лежит по одному из путей в зависимости от дистрибутива:
# Debian, Ubuntu и производные
sudo nano /etc/chrony/chrony.conf
# RHEL, CentOS, Fedora, Rocky, Arch
sudo nano /etc/chrony.conf
Минимально рабочая конфигурация для домашней или серверной машины выглядит понятно даже для тех, кто никогда не видел chrony:
# /etc/chrony.conf
# Используем пул общедоступных серверов с iburst для быстрого старта
pool 2.pool.ntp.org iburst
# Дополнительно несколько надёжных публичных серверов
server time.cloudflare.com iburst
server time.google.com iburst
server time.nist.gov iburst
# Файл для запоминания дрейфа кварца
driftfile /var/lib/chrony/drift
# Разрешить большую коррекцию при первых трёх обновлениях после старта
makestep 1.0 3
# Синхронизировать аппаратные часы с системными
rtcsync
# Включить логирование измерений и слежения
logdir /var/log/chrony
log measurements statistics tracking
# Минимальное число источников для применения коррекции
minsources 2
Директива pool отличается от server тем, что разрешает имя сразу в несколько IP-адресов и автоматически использует их все. Это удобный способ получить четыре или больше источников, написав одну строку. Параметр iburst заставляет демон при старте отправить пачку из четырёх запросов подряд, что позволяет быстро получить первую оценку времени, а не ждать минуту до первого опроса.
Параметр minsources задаёт, сколько источников должны быть доступны и согласованы, чтобы chronyd начал корректировать часы. По умолчанию это единица, что для надёжных систем мало. Значение два разумно для большинства конфигураций. Значение три и выше требует наличия как минимум четырёх настроенных серверов, иначе при потере связи с одним из них синхронизация остановится.
После правки конфигурации демон нужно перезапустить:
# systemd-системы
sudo systemctl restart chronyd
# или в некоторых дистрибутивах
sudo systemctl restart chrony
# Проверить, что демон запустился без ошибок
sudo systemctl status chronyd
# Посмотреть свежие логи
sudo journalctl -u chronyd -n 50 --no-pager
Через минуту-две стоит посмотреть новое состояние источников. Если всё настроено правильно, столбец Reach начнёт заполняться единицами, постепенно достигая значения 377.
Принудительное обновление времени через chronyc когда нужен быстрый результат
Иногда возникает ситуация, когда часы машины ушли в сторону настолько сильно, что chronyd считает разницу слишком большой для плавной коррекции и просто отказывается делать что-то самостоятельно. По умолчанию демон не делает резких скачков времени, потому что это может сбить с толку запущенные приложения. Но можно заставить его выполнить шаг вручную:
# Принудительно перевести часы на корректное время
sudo chronyc makestep
# Заставить опросить все источники прямо сейчас
sudo chronyc burst 4/4
# Перепроверить результат
chronyc tracking
chronyc sources -v
Команда makestep полезна сразу после установки или после долгого простоя машины. В нормальном режиме её используют редко, потому что директива makestep 1.0 3 в конфиге уже разрешает три первых больших коррекции при старте.
Если всё это не помогает, иногда самый надёжный способ - перезапустить демон с нуля и удалить накопленные данные о дрейфе:
sudo systemctl stop chronyd
sudo rm /var/lib/chrony/drift
sudo systemctl start chronyd
# Подождать пару минут и проверить
sleep 120 && chronyc sources -v
Файл drift содержит накопленную статистику о том, насколько кварцевый генератор машины отклоняется от идеального ритма. После долгого хранения некорректных данных он может мешать новой синхронизации, и его удаление приводит chronyd в исходное состояние.
Особенный случай когда серверов всего два и они расходятся между собой
Существует подкласс проблем, который возникает именно из-за дизайна алгоритма. Если в конфигурации указаны ровно два сервера и их показания расходятся даже на доли секунды, chronyd окажется в положении классической задачи о двух часах, у которой нет правильного решения. Часы расходятся - какие из двух правильные? Без третьего голоса определить нельзя.
Поведение демона в такой ситуации описано прямо в исходном коде. Сообщение Can't synchronise: no majority выводится, когда не удаётся получить даже половину достижимых источников, согласных между собой. На двух серверах это означает простое расхождение показаний.
Решение очевидно. Добавить третий и четвёртый источник:
# Плохо: всего два сервера
server time.windows.com iburst
server router.local iburst
# Хорошо: четыре независимых источника плюс пул
server time.cloudflare.com iburst
server time.google.com iburst
server time.nist.gov iburst
pool 2.pool.ntp.org iburst maxsources 4
Параметр maxsources у директивы pool ограничивает число серверов, выбираемых из пула, чтобы клиент не оказался завален десятками источников.
Альтернативный путь, если по каким-то причинам нельзя добавить серверы - явно пометить один из имеющихся как доверенный:
server primary-ntp.example.com iburst trust
server backup-ntp.example.com iburst
Ключевое слово trust говорит chronyd, что этот источник нужно использовать даже без подтверждения от других. Это противоречит идеологии безопасной синхронизации, потому что эффективно отключает защиту от лжецов, но иногда становится единственным выходом в изолированных или специальных сетях.
Тонкости связанные с виртуализацией и облачными окружениями где время ведёт себя странно
Виртуальные машины и контейнеры добавляют отдельный слой проблем. Когда гипервизор приостанавливает гостя на секунду, чтобы перенести на другой узел, или когда хост сам испытывает нагрузку, гостевые часы могут резко уйти в сторону. На форумах не раз обсуждалась ситуация, когда после миграции виртуальной машины через vMotion демон chronyd теряет связь с серверами и записывает Can't synchronise: no majority, после чего через несколько секунд снова находит источник и возвращается к нормальной работе.
В таких окружениях часто помогает специальная настройка для виртуальных машин:
# Учёт особенностей виртуальной среды
# Разрешить демону делать большие шаги при необходимости
maxchange 1000 1 -1
# Разрешить начальную коррекцию даже при больших расхождениях
makestep 1.0 -1
# Включить более частые проверки при нестабильных часах
maxupdateskew 100.0
# Использовать ядерную синхронизацию RTC
rtcsync
Для контейнеров вообще принято время не синхронизировать внутри, а получать его от хоста. Большинство современных контейнерных рантаймов прокидывают системное время автоматически, и попытка запустить chronyd внутри контейнера приводит либо к ошибкам прав доступа, либо к лишней нагрузке на впустую.
В облачных окружениях вроде AWS и GCP провайдеры часто предоставляют локальные NTP-серверы, доступные по специальным внутренним адресам, которые работают через прямое подключение к гипервизору и не зависят от внешней сети:
# AWS: специальный адрес link-local
server 169.254.169.123 prefer iburst minpoll 4 maxpoll 4
# Google Cloud: внутренний metadata-сервер
server metadata.google.internal iburst
# Azure: можно использовать Windows Time
server time.windows.com iburst
Использование таких серверов обычно даёт точность на порядок выше, чем общедоступные пулы, потому что соединение идёт напрямую внутри инфраструктуры провайдера без публичного интернета.
Полный список диагностических команд который полезно держать под рукой
Набор команд, который помогает быстро локализовать проблему, в одном месте:
# Общее состояние синхронизации
chronyc tracking
# Список источников с подробной таблицей
chronyc sources -v
# Статистика по каждому источнику за последнее время
chronyc sourcestats -v
# Активность демона - сколько источников в каком состоянии
chronyc activity
# Подробный обмен с конкретным источником
chronyc ntpdata time.cloudflare.com
# Список последних клиентов, обращавшихся к локальному серверу
sudo chronyc clients
# Принудительное обновление и большой шаг
sudo chronyc makestep
sudo chronyc burst 4/4
# Заставить пересмотреть DNS-имена серверов
sudo chronyc reload sources
# Системный статус через timedatectl
timedatectl status
timedatectl show-timesync --all
Команда timedatectl полезна как высокоуровневая проверка. Если она показывает System clock synchronized как yes, это надёжный признак работы chronyd, даже если в детальном выводе chronyc видны какие-то промежуточные предупреждения.
Что хочется запомнить про этот класс ошибок и как обезопасить систему на будущее
Ошибка Can't synchronise: no majority of sources почти всегда сводится к одному из четырёх корней. Слишком мало источников настроено, и среди них нет согласия. Файрвол режет порт 123/UDP в одну или обе стороны. Один из серверов даёт ложное время, а защитный алгоритм не позволяет его принять. Виртуальная машина или контейнер приостановились в неподходящий момент.
Профилактика проста. Настраивать минимум четыре независимых источника времени из разных географических зон или хотя бы разных операторов. Проверять открытость UDP-порта 123 в обе стороны. Использовать пулы вместо одиночных серверов, потому что пулы автоматически переключаются на резервные узлы при сбое любого из них. Включать iburst для быстрого старта и rtcsync для удержания аппаратных часов в актуальном состоянии. Раз в неделю заглядывать в chronyc tracking, особенно если на машине крутится что-то критичное к времени, например базы данных, лог-серверы или системы аутентификации с временными метками.
Точное время в системе кажется бесплатной услугой, которая просто работает где-то в фоне. На самом деле за этим стоит хитрый алгоритм статистической обработки нескольких независимых источников с защитой от лжецов и аккуратной коррекцией кварцевого дрейфа. Понимание принципов работы превращает строку в логе из источника тревоги в обычный диагностический сигнал, на который реакция занимает пять минут вместо целого вечера паники.