Почему на VPS сбивается время и как его синхронизировать
Узнайте, почему системные часы на VPS спешат или отстают из-за особенностей виртуализации. Используйте команды chronyc и timedatectl для настройки точной синхронизации.
Почему на VPS сбиваются часы
Часы на VPS сбиваются, потому что их никто не корректирует. Ядро отсчитывает время по аппаратному счетчику, который работает чуть быстрее или медленнее нормы. Если клиент синхронизации времени не запущен, эта небольшая погрешность накапливается каждый час. Внутри виртуальной машины есть вторая причина: гостевая система делит физический процессор с другими гостями, поэтому моменты, когда она не получает процессорное время, — это моменты, когда она не может вести отсчет.
На современном KVM-госте сам счетчик редко является реальной проблемой. Паравиртуальный источник kvm-clock считывает значение, которое поддерживает хост, поэтому исправная гостевая система точно следует за хостом. Если время заметно отличается, причина обычно более прозаична. Либо не запущен демон синхронизации, либо запущены два и конфликтуют между собой, либо исходящий UDP-порт 123 заблокирован в сети вашего провайдера. Гостевая система получает настройки времени от хоста или через NTP (network time protocol), а не от собственного осциллятора.
К чему приводит неверное время на системных часах
- Коды двухфакторной аутентификации TOTP (time-based one-time password) перестают совпадать, из-за чего вы теряете доступ к серверу, даже если пароль и ключ верны.
- Сертификат, выпущенный минуту назад, отклоняется, а
curlвыводитSSL certificate problem: certificate is not yet valid. apt updateотказывается работать с репозиторием, выдаваяE: Release file for ... is not valid yet (invalid for another 1d 2h 3min 4s).- Запланированные задачи запускаются в неподходящее время, а скачок времени может привести к тому, что одна задача выполнится дважды, а другая будет пропущена.
- Журналы с двух разных серверов невозможно сопоставить, поэтому хронологию инцидента приходится восстанавливать методом догадок.
Допустимое отклонение меньше, чем принято считать. Приведенные ниже цифры являются задокументированными значениями по умолчанию, а не результатами измерений.
The data behind this chart
[
{
"label": "TLS certificate boundary",
"tolerance_seconds": 0,
"documented_in": "RFC 5280"
},
{
"label": "chrony steps instead of slewing",
"tolerance_seconds": 1,
"documented_in": "Debian and Ubuntu chrony.conf"
},
{
"label": "TOTP code, one time step",
"tolerance_seconds": 30,
"documented_in": "RFC 6238"
},
{
"label": "Kerberos clock skew",
"tolerance_seconds": 300,
"documented_in": "MIT krb5 default"
}
]Код TOTP вычисляется на основе счетчика, который обновляется каждые 30 секунды, при этом большинство систем проверки допускают погрешность в один шаг в любую сторону. Весь бюджет ошибки составляет полминуты в каждом направлении. Протокол Kerberos гораздо более терпим: значение по умолчанию для допустимого отклонения составляет 300 секунд. Сертификаты не допускают никакой погрешности: они проверяются по фиксированным моментам времени с льготным периодом в 0 секунд, поэтому часы, спешащие всего на одну секунду, приведут к отклонению абсолютно валидного сертификата.
Три типа часов и важность каждого из них
Системные часы — это то, что имеет значение. Это CLOCK_REALTIME ядра: количество секунд, прошедших с 1 января 1970 года UTC. Значение хранится в оперативной памяти и используется всеми процессами, которым нужна отметка времени. Строки логов, проверка сертификатов, коды TOTP и время изменения файлов — всё это опирается на данные часы. Когда говорят, что время на сервере неверное, имеют в виду именно их.
Аппаратные часы, также называемые RTC (real time clock), — это отдельный счётчик, который продолжает работать при выключенном питании. На физическом сервере это микросхема с питанием от батарейки. Внутри виртуальной машины они эмулируются гипервизором, поэтому по большей части являются артефактом хоста. Linux считывает их один раз при загрузке для получения начального значения, а затем ведёт собственный отсчёт. Команда timedatectl выводит их значение в строке RTC time. Не используйте эту строку для отладки на VPS, так как она показывает представление о времени хоста, а не состояние синхронизации ваших системных часов. В контейнере /dev/rtc обычно отсутствует, поэтому hwclock --show завершается с ошибкой hwclock: Cannot access the Hardware Clock via any known method..
Источник времени (clocksource) — это механизм, с помощью которого ядро ведёт отсчёт между считываниями. Узнайте у своего ядра, какой источник оно выбрало:
cat /sys/devices/system/clocksource/clocksource0/current_clocksource
cat /sys/devices/system/clocksource/clocksource0/available_clocksourceВ KVM вы обычно увидите kvm-clock. Он считывает значение, поддерживаемое хостом, поэтому гостевая система KVM без NTP-клиента некоторое время сохраняет относительную точность. tsc — это собственный счётчик процессора. Гостевые системы Xen сообщают о xen, а гостевые системы Hyper-V — об источнике hyperv. Не меняйте эту настройку без веских причин, подтверждённых измерениями, так как ядро уже выбрало наиболее подходящий и надёжный источник для данного оборудования.
Некоторые хосты также предоставляют гостевой системе устройство PTP (precision time protocol), которое позволяет chrony считывать время хоста напрямую, а не по сети. Это стоит проверить, хотя на обычном VPS такая возможность часто недоступна:
sudo modprobe ptp_kvm
ls /dev/ptp*
cat /sys/class/ptp/ptp0/clock_nameЕсли modprobe завершается ошибкой или устройство не найдено, значит, ваш хост его не предоставляет и единственным решением остаётся сетевой NTP. Если clock_name указывает на виртуальные часы KVM, chrony может использовать их при наличии строки refclock PHC /dev/ptp0 poll 2 в конфигурационном файле.
Проверка состояния времени на вашей машине
Начните с одной команды. Она отвечает на вопрос «поддерживает ли что-то точность этих часов» на одном экране.
timedatectlИзучите эти строки, вместо того чтобы доверять числу, которое вы помните:
Local timeиUniversal time— это один и тот же момент времени, выведенный в вашем часовом поясе и в UTC. Если они идентичны, машина уже работает в UTC.RTC time— это аппаратные часы, описанные выше. На VPS игнорируйте их.Time zone— это то, что система использует для форматирования локального времени.System clock synchronized— это собственный флаг ядра. Демон времени устанавливает его, как только начинает доверять своим источникам, поэтомуnoозначает, что с момента загрузки никто не корректировал эти часы.NTP serviceсообщает о состоянии systemd-timesyncd.n/a— это норма для машины, на которой запущен chrony, так как timesyncd там не установлен.System clock synchronized: yesвместе сNTP service: n/aозначает, что chrony выполняет свою работу, а ядро подтверждает её корректность.
Затем узнайте, насколько велико отклонение. Не сравнивайте время «на глаз» с телефоном. Если запущен chrony:
chronyc tracking
chronyc sources -vchronyc tracking выводит числа, которые отвечают на этот вопрос. System time — это текущее смещение относительно времени NTP, за которым следует слово fast или slow. Last offset — это размер последней коррекции. Frequency — это ошибка частоты, которую chrony измерил в ваших часах и уже компенсирует. Leap status должно содержать Normal. Если там написано Not synchronised, а Reference ID равно 00000000 (), значит, chrony ещё не выбрал источник синхронизации.
chronyc sources -v выводит легенду над списком, чтобы вам не приходилось запоминать символы. Два столбца несут основную смысловую нагрузку. Символ состояния в начале каждой строки показывает, как chrony оценивает этот источник: * отмечает источник, который используется в данный момент, а ? в каждой строке означает, что ответа нет. Reach — это история ответов последних восьми опросов в восьмеричном виде: 377 означает, что все восемь были получены, 0 — что не было получено ни одного.
Если за синхронизацию отвечает systemd-timesyncd:
timedatectl timesync-status
timedatectl show-timesync --alltimesync-status выводит сервер, с которым идет обмен данными, интервал опроса и значение Offset. Если команда возвращает ошибку службы вместо вывода статуса, значит, timesyncd не является управляющим демоном на этой машине, что само по себе является ответом на ваш вопрос.
Для грубой проверки относительно внешнего мира без дополнительных инструментов сравните свои часы с публичным HTTP-заголовком Date, который передается в GMT с точностью до одной секунды:
date -u
curl -sI https://www.cloudflare.com | grep -i '^date:'Разница в секунду или две здесь нормальна и ничего не значит. Разница в минуту — это ваша ошибка.
chrony или systemd-timesyncd на VPS
В Ubuntu и Debian по умолчанию используется systemd-timesyncd. Это клиент SNTP (simple network time protocol): он опрашивает по одному серверу за раз и корректирует системные часы. Для машины, которая постоянно находится в сети и имеет относительно точное время при старте, этого достаточно, а потребление ресурсов минимально.
chrony — это полноценная реализация NTP и более предпочтительный вариант для виртуальной машины, что видно по результатам его работы. Он опрашивает несколько источников одновременно и отбрасывает те, чьи данные расходятся с остальными. Он измеряет погрешность хода ваших часов и записывает её в drift file, поэтому он корректирует тенденцию отклонения, а не просто подгоняет время по каждому отдельному пакету. Он также быстро восстанавливается после двух ситуаций, характерных для виртуальных машин, но не для физических серверов: приостановка работы хостом и миграция на другой хост во время работы. Если хост предоставляет PTP-устройство, именно chrony считывает с него данные.
sudo apt update && sudo apt install -y chrony
systemctl status chrony --no-pager
chronyc sources -v
chronyc trackingСледите за выводом apt во время установки. В Debian и Ubuntu пакеты chrony и systemd-timesyncd предоставляют time-daemon, поэтому apt удаляет timesyncd при установке chrony. Это корректное и ожидаемое поведение. Никогда не запускайте их одновременно: два демона, пытающиеся управлять одними часами, будут конфликтовать, и их данным о смещении времени нельзя будет доверять. В Rocky и AlmaLinux устанавливайте через sudo dnf install -y chrony, где юнит называется chronyd, а не chrony.
Файл конфигурации находится по пути /etc/chrony/chrony.conf в Debian и Ubuntu, и /etc/chrony.conf в Rocky и Alma. Настройки по умолчанию в дистрибутивах уже подходят для VPS, поэтому меняйте их только при необходимости. Стоит понимать две директивы:
- Строки
poolиserverзадают источники времени. Добавлениеiburstуказывает chrony отправлять быстрый пакет при запуске, чтобы первая синхронизация произошла за секунды, а не за минуты. makestepопределяет, когда chrony должен скачкообразно изменить время, а когда — плавно подвести его. Проверьте текущее значение с помощьюgrep -n makestep /etc/chrony/chrony.conf. Значение по умолчанию в Debian и Ubuntu,makestep 1 3, означает следующее: в течение первых трёх обновлений после запуска chronyd время будет скорректировано скачком, если отклонение превышает одну секунду, а в дальнейшем — только плавным замедлением или ускорением хода часов.
Если вы хотите защитить трафик синхронизации времени от подмены на пути следования, chrony версии 4 и выше поддерживает NTS (network time security). Сначала подтвердите свою версию командой chronyd -v и учтите, что для NTS требуется открытый исходящий TCP-порт 4460 в дополнение к UDP 123:
server time.cloudflare.com iburst ntsПерезапустите службу и проверьте её состояние, прежде чем доверять ей. Если конфигурация содержит ошибки синтаксиса, вы останетесь без работающего демона времени, а системные часы не сообщат вам об этом.
sudo systemctl restart chrony
chronyc sources -v
chronyc trackingПочему часы, спешащие или отстающие на минуты, остаются неточными
У демона времени есть два способа исправить смещение. Плавная корректировка (slewing) ускоряет или замедляет ход часов до устранения ошибки; при этом время движется только вперед, а метки времени не повторяются и не пропускаются. Скачкообразная корректировка (stepping) мгновенно устанавливает верное значение; это происходит быстро, но может привести к переводу часов назад. Перевод назад опасен для любого ПО, измеряющего прошедшее время по системным часам, поэтому оба демона предпочитают плавную корректировку.
Именно из-за этого предпочтения сильно сбитые часы могут оставаться неточными долгое время. chrony выполняет скачок только в пределах окна, заданного makestep, которое по умолчанию открыто лишь в течение нескольких обновлений после запуска демона. Если chronyd проработал неделю, а затем обнаружил ошибку в сорок секунд, он будет исправлять её плавно, а плавная корректировка сорока секунд занимает гораздо больше времени, чем вы готовы ждать. Выполните принудительную корректировку один раз, осознанно, в спокойный момент:
sudo chronyc makestep
chronyc trackingТеперь chronyc tracking должен показывать System time, близкое к нулю, а Last offset отобразит величину только что исправленного смещения. Подумайте перед выполнением этой команды на нагруженном сервере баз данных, так как скачок часов назад может привести к сбоям в работе ПО, которое предполагает, что время движется только вперед. Перезапуск демона — это более мягкий вариант того же исправления, так как окно makestep снова открывается при старте.
Контейнеры используют системные часы хоста
У контейнера нет собственных часов, поэтому внутри него нечего синхронизировать. Пространства имен времени в Linux виртуализируют только монотонные часы и время с момента загрузки. CLOCK_REALTIME не виртуализируется, а значит, контейнер считывает те же системные часы, что и хост, на котором он запущен. Исправьте время на хосте, и оно автоматически станет верным для всех контейнеров на этом хосте.
Из этого следует несколько выводов. Не устанавливайте chrony или ntpd в образ, так как в лучшем случае это не даст никакого эффекта. Попытка изменить дату внутри непривилегированного контейнера завершится ошибкой date: cannot set date: Operation not permitted, поскольку ядро требует наличия CAP_SYS_TIME для выполнения этого вызова. Предоставление CAP_SYS_TIME не дает контейнеру собственных часов, а позволяет изменять время на хосте, а значит, и на всех остальных контейнерах.
Разница в часовых поясах внутри контейнера не является проблемой часов. Образ, содержащий собственный файл /etc/localtime, выводит тот же момент времени, отформатированный для другого пояса, поэтому вывод date выглядит неверным, хотя сами часы идут правильно. Установите TZ=UTC в переменные окружения контейнера, и проблема исчезнет. Выбранная среда выполнения здесь ничего не меняет, а сравнение rootless Podman и Docker описывает то, что она действительно меняет.
Часовые пояса: UTC на сервере, местное время для пользователей
Установите на сервере время UTC и не меняйте его.
timedatectl list-timezones | grep -i utc
sudo timedatectl set-timezone UTC
timedatectlВ UTC нет перехода на летнее время, и в этом заключается главный аргумент. Ежедневная задача, запланированная на 02:30 в часовом поясе с переходом на летнее время, в день перевода часов назад выполнится дважды, а в день перевода вперед — не выполнится вовсе. В man 8 cron описана специальная обработка для сдвигов менее чем на три часа: задачи, пропущенные из-за перевода часов вперед, запускаются сразу после изменения, а задачи, попавшие в повторный час при переводе назад, второй раз не запускаются. Такое поведение логично, но это именно то, о чем вам не следует беспокоиться в 03:00. В UTC задача выполняется один раз в день, каждый день в году. Если ваша задача не выполняется вовсе, а не просто запускается в странное время, более вероятным объяснением являются причины, по которым cron-задача молча не запускается.
Тот же аргумент применим к чтению логов. journalctl форматирует метки времени в системном часовом поясе, а journalctl --utc принудительно использует UTC. Два сервера в разных часовых поясах превращают каждый инцидент в упражнение по конвертации времени, а конвертация в условиях спешки — это способ неверно интерпретировать хронологию событий. Держите системы в UTC, храните метки времени в UTC и выполняйте конвертацию только в момент, когда их читает человек. Любой, кому нужно увидеть местное время для одной команды, может получить его без изменения настроек сервера:
TZ=Europe/Berlin dateК этому разделу относится еще одна строка в выводе timedatectl. Параметр RTC in local TZ должен иметь значение no. Установка его в yes — это обходное решение для систем с двойной загрузкой Windows на ноутбуках, а на сервере это лишь добавляет смещение, из-за которого кто-то может совершить ошибку в будущем. Если этот параметр установлен, timedatectl выводит предупреждение о том, что система настроена на чтение времени RTC в местном часовом поясе.
Устранение неполадок по симптомам
Ваш код двухфакторной аутентификации отклоняется на одном из серверов. Прежде всего проверьте системное время. Код генерируется на основе счетчика, который обновляется каждые 30 секунд. Если часы сервера отстают на 90 секунд, он вычисляет код для шага, который ваш телефон уже прошел. timedatectl покажет System clock synchronized: no, или chronyc tracking сообщит о значительном смещении System time. Это не то же самое, что отклонение ключа, которое сопровождается собственным сообщением об ошибке и описано в руководстве по ошибкам аутентификации с использованием открытого ключа.
apt update сообщает, что файл Release еще не действителен. Полное сообщение содержит название репозитория и время, в течение которого он будет оставаться недействительным, например is not valid yet (invalid for another 1d 2h 3min 4s). Ваши часы отстают от даты, указанной в файле Release репозитория, а это время — прямое измерение величины отставания. Исправьте время. Не отключайте проверку даты в apt, чтобы обойти ошибку: эта проверка предотвращает получение устаревшего индекса пакетов.
Каждая строка источника показывает состояние недоступности, а Reach равно 0. Ни один источник не отвечает, поэтому проверяйте исходящий трафик, а не конфигурацию. NTP использует исходящий UDP-порт 123, который в некоторых сетях фильтруется или перенаправляется. sudo chronyc ntpdata выводит счетчики для каждого источника, включая Total TX и Total RX. Если счетчик отправленных пакетов (TX) растет, а полученных (RX) остается на нуле, значит, ваши пакеты уходят, но ответы не приходят. Это указывает на наличие межсетевого экрана между вами и источником.
Время было верным, но произошел скачок. Это случается при событиях на хосте. Восстановление из снимка (snapshot), приостановка гостевой системы или «живая» миграция на другой хост могут привести к отставанию времени в гостевой системе. chrony замечает это при следующем опросе и корректирует время; systemd-timesyncd может сначала дождаться завершения длительного интервала опроса. Убедитесь, что демон запускается при загрузке с помощью systemctl is-enabled chrony, так как демон, запущенный вручную, не будет работать после следующей перезагрузки.
Смещение небольшое, но не стабилизируется. Проверьте показатель CPU steal. Если гостевая система не получает процессорное время в момент прерывания таймера, замеры задерживаются, и смещение «блуждает», вместо того чтобы прийти к стабильному значению. top показывает это как значение st в строке CPU. Чтение показателя CPU steal на общем хосте объясняет значение этого числа и возможные действия.
Только что выпущенный сертификат отклоняется как еще не действительный. curl выводит SSL certificate problem: certificate is not yet valid, браузеры сообщают о похожей проблеме. С сертификатом всё в порядке, просто часы, проверяющие его, отстают. Ошибка может быть на любой из сторон, поэтому проверьте и клиент, и сервер. Если неверное время установлено на сервере, который выпустил сертификат, руководство по certbot и сертификатам nginx описывает процесс обновления для такой конфигурации.
Добавьте это в список выполняемых проверок
Синхронизация времени — это настройка, выполняемая при загрузке, которая может незаметно выйти из строя спустя месяцы. Именно такие проблемы выявляются регулярными проверками, а не памятью администратора. timedatectl и chronyc tracking требуют всего двух секунд на ознакомление. Выполняйте их в рамках первых десяти минут работы с новым VPS, а также при прохождении регулярного списка обслуживания Linux-сервера. Если вы хотите, чтобы проверка выполнялась автоматически и уведомляла вас при увеличении смещения, в руководстве создание systemd-сервиса и таймера описан шаблон для небольшого юнита, который отчитывается по расписанию.
FAQ
Как проверить, синхронизированы ли часы на VPS?
Выполните timedatectl и изучите строку System clock synchronized. Это собственный флаг ядра, устанавливаемый демоном, который управляет часами, поэтому yes вместе с NTP service: n/a — это нормальное и здоровое состояние для системы с chrony. Чтобы узнать величину ошибки, выполните chronyc tracking и посмотрите System time, либо выполните timedatectl timesync-status и посмотрите Offset, если используется systemd-timesyncd. Для проверки относительно внешнего источника сравните date -u с заголовком Date, который возвращает любой HTTPS-сайт.
Что использовать на VPS: chrony или systemd-timesyncd?
Используйте chrony для любых важных задач. systemd-timesyncd — это SNTP-клиент, который работает с одним сервером; он подходит для машин, которые постоянно находятся в сети и изначально имеют близкое к точному время. chrony опрашивает несколько источников, отбрасывает недостоверные, вычисляет погрешность хода ваших часов и быстро восстанавливается после паузы хоста или «живой» миграции (live migration). Установка chrony в Debian или Ubuntu автоматически удаляет systemd-timesyncd, так как оба пакета предоставляют time-daemon. Никогда не запускайте два демона времени одновременно.
Почему TOTP-коды не работают на одном сервере, но работают везде?
Потому что TOTP-код зависит от текущего времени. Код генерируется на основе счетчика, который меняется каждые 30 секунд, поэтому сервер и ваш телефон должны быть синхронизированы. Большинство систем проверки принимают код с отклонением в один шаг, что дает примерно полминуты запаса в каждую сторону. Проверьте timedatectl на этом сервере. Если System clock synchronized показывает no, исправьте синхронизацию, и коды снова начнут подходить без изменения общего секретного ключа.
Можно ли установить время внутри Docker-контейнера?
Нет, и в этом нет необходимости. Контейнер использует CLOCK_REALTIME хоста, так как пространства имен времени (time namespaces) в Linux виртуализируют только монотонные часы и часы с момента загрузки. Непривилегированный контейнер получает date: cannot set date: Operation not permitted, а добавление CAP_SYS_TIME позволяет ему изменять часы хоста, а не дает собственные. Синхронизируйте хост. Разное локальное время внутри контейнера — это вопрос настройки часового пояса, поэтому установите TZ в переменные окружения контейнера.
Должен ли сервер использовать UTC или локальное время?
UTC, а локальное время следует применять только при выводе данных для пользователя. UTC не меняется при переходе на летнее время, поэтому ежедневные задачи выполняются один раз в сутки круглый год, а временные метки с разных серверов выстраиваются в ряд без конвертации. Установите его с помощью sudo timedatectl set-timezone UTC. Если кому-то нужно увидеть локальное время, можно добавить префикс к команде, например TZ=America/New_York date, что никак не повлияет на системные часы.