Чому відстає час на VPS і як його синхронізувати
Перевірте помилку часу на VPS: порівняйте chronyc і timedatectl, знайдіть зламану синхронізацію та поверніть доступ до 2FA без вгадування причини.
Чому годинник VPS відстає
Годинник VPS відстає, оскільки його ніхто не коригує. Ядро відлічує час за апаратним лічильником, який працює трохи швидше або повільніше. Якщо клієнт синхронізації часу не запущений, ця невелика похибка зростає щогодини. У віртуальній машині є ще одна причина: гостьова система використовує фізичний CPU спільно з іншими гостьовими системами, тому в моменти, коли для неї не виділено процесорний час, вона не може відлічувати час.
У сучасному KVM guest сам лічильник рідко є справжньою проблемою. Паравіртуалізоване джерело kvm-clock зчитує значення, яке підтримує хост, тому справний guest точно відстежує час хоста. Помітно неправильний час зазвичай має простішу причину. Демон синхронізації не запущений, два демони працюють одночасно й конфліктують або вихідний UDP port 123 не виходить за межі мережі вашого провайдера. Guest синхронізує час із хостом або через NTP (network time protocol), а не за власним генератором тактових імпульсів.
Що насправді ламає неправильний час
- Коди TOTP (одноразові паролі на основі часу) перестають збігатися, тому ви втрачаєте доступ до сервера, хоча пароль і ключ правильні.
- Сертифікат, виданий хвилину тому, відхиляється, а
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 January 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.
Джерело тактового сигналу — це механізм, за допомогою якого ядро веде відлік між цими зчитуваннями. Перевірте, яке джерело вибрало ядро:
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 — це історія відповідей на останні 8 опитувань, виведена у вісімковій системі: 377 означає, що на всі 8 опитувань отримано відповіді, а 0 — що не отримано жодної.
Якщо синхронізацією керує systemd-timesyncd:
timedatectl timesync-status
timedatectl show-timesync --alltimesync-status виводить сервер, з яким встановлено зв’язок, інтервал опитування та значення Offset. Якщо команда повертає помилку про сервіс замість стану, timesyncd не є активним демоном на цій машині. Це і є відповідь на ваше запитання.
Для приблизної перевірки за зовнішнім джерелом без додаткових інструментів порівняйте годинник із заголовком HTTP Date. Він передається в GMT з точністю до 1 секунди:
date -u
curl -sI https://www.cloudflare.com | grep -i '^date:'Різниця в 1–2 секунди тут є нормальною і нічого не означає. Різниця в 1 хвилину означає проблему у вашій системі.
chrony чи systemd-timesyncd на VPS
Ubuntu і Debian типово постачаються із systemd-timesyncd. Це клієнт SNTP (simple network time protocol): він опитує один сервер за раз і поступово наближає до нього системний час. Для машини, яка постійно підключена до мережі та спочатку має приблизно правильний час, цього достатньо. Затрати ресурсів майже нульові.
chrony — це повна реалізація NTP і кращий типовий вибір для віртуальної машини. Причини можна побачити у виводі самої програми. chrony одночасно опитує кілька джерел і відкидає ті, що розходяться з іншими. Він вимірює похибку ходу системного годинника та записує її у drift file, тому виправляє властивий годиннику зсув, а не намагається реагувати на кожен окремий результат вимірювання. Також chrony швидко відновлює коректний час після двох подій, характерних для VM, але не для фізичного сервера: гіпервізор може призупинити VM, а VM може бути перенесена на інший host під час роботи. Якщо host надає 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. Це правильна й очікувана поведінка. Не запускайте обидва демони, оскільки два демони, які змінюють один системний годинник, конфліктуватимуть. Поки вони працюють одночасно, не можна покладатися на значення offset, яке повідомляє будь-який із них. У Rocky та AlmaLinux встановлення виконується за допомогою sudo dnf install -y chrony. Там unit називається 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, крім UDP 123, має бути відкритий вихідний TCP-порт 4460:
server time.cloudflare.com iburst ntsПерезапустіть службу та перевірте її роботу, перш ніж покладатися на неї. Якщо конфігурацію не вдасться розібрати, у системі не залишиться жодного демона синхронізації часу, а системний годинник не повідомить вас про цю проблему.
sudo systemctl restart chrony
chronyc sources -v
chronyc trackingЧому годинник, який відстає на кілька хвилин, продовжує відставати
Демон синхронізації часу має два способи виправити зміщення. Плавне коригування прискорює або сповільнює годинник, доки похибка не зникне. Час при цьому продовжує рухатися вперед, а мітки часу не повторюються й не пропускаються. Примусове встановлення часу одразу змінює його на правильне значення. Це швидко, але може перевести годинник назад. Зворотний хід небезпечний для всього, що вимірює тривалість за системним часом. Тому обидва демони надають перевагу плавному коригуванню.
Саме тому годинник із великою похибкою може залишатися неточним протягом тривалого часу. 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 у середовищі контейнера, і проблема зникне. Вибраний runtime нічого тут не змінює, а в матеріалі порівняння rootless Podman і Docker описано, що саме він змінює.
Часові пояси: UTC на сервері, місцевий час для користувачів
Налаштуйте машину на UTC і не змінюйте це налаштування.
timedatectl list-timezones | grep -i utc
sudo timedatectl set-timezone UTC
timedatectlUTC не використовує літній час, і цього достатньо. Щоденне завдання о 02:30 у часовому поясі, де діє літній час, запускається двічі в день переведення годинника назад і не запускається взагалі в день переведення годинника вперед. man 8 cron описує спеціальну обробку переходів тривалістю менше трьох годин: завдання, пропущені через перехід уперед, запускаються невдовзі після зміни, а завдання, що припали на повторену годину через перехід назад, не запускаються вдруге. Така поведінка є прийнятною, але вам не доведеться враховувати її о 03:00. У UTC завдання запускається раз на день, кожного дня року. Якщо ваше завдання взагалі не запускається, а не запускається в незвичний час, імовірніша причина описана в матеріалі чому cron job непомітно не запускається.
Це саме стосується читання журналів. 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. Це інша проблема, ніж безпосереднє відхилення ключа, для якого виводиться окреме повідомлення. Її описано в посібнику про помилки автентифікації за допомогою publickey.
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 залишається на нулі, це означає, що пакети залишають ваш сервер, але відповіді не надходять. Причиною, імовірно, є firewall між вашим сервером і джерелом.
Годинник показував правильний час, а потім різко змінив його. Таке трапляється через події на хості. Відновлення snapshot, призупинення гостьової системи або live migration на інший хост можуть призвести до того, що час у гостьовій системі відставатиме від фактичного. chrony помітить це під час наступного опитування та скоригує час. systemd-timesyncd може спочатку чекати завершення тривалого інтервалу опитування. Переконайтеся, що daemon запускається під час завантаження, за допомогою systemctl is-enabled chrony, оскільки daemon, запущений вручну, не працюватиме після наступного перезавантаження.
Зміщення невелике, але час не стабілізується. Перевірте CPU steal. Якщо гостьовій системі не виділяється CPU у момент переривання таймера, її вибірки надходять із затримкою. Через це зміщення змінюється, а не наближається до нуля. top показує це значення як показник st у рядку CPU. У матеріалі Як читати CPU steal time на спільному хості пояснено значення цього показника та способи усунення проблеми.
Щойно виданий сертифікат відхиляється як ще недійсний. curl виводить SSL certificate problem: certificate is not yet valid, а браузери показують подібне повідомлення. Сертифікат справний. Відстає годинник системи, яка його перевіряє. Проблема може бути на будь-якій із двох машин, тому перевірте і клієнт, і сервер. Якщо неправильний час встановлено на сервері, який видав сертифікат, у посібнику з сертифікатів certbot і nginx описано поновлення сертифіката для цієї конфігурації.
Додайте це до вже наявних перевірок
Синхронізація часу налаштовується під час завантаження системи, але через кілька місяців може непомітно перестати працювати. Саме такі проблеми виявляє регулярна перевірка, тоді як на пам’ять покладатися не можна. timedatectl і chronyc tracking можна перевірити разом за дві секунди. Виконуйте їх у межах перших десяти хвилин після розгортання нового VPS, а також під час регулярної перевірки Linux-сервера за контрольним списком. Якщо потрібно, щоб перевірка виконувалася автоматично та повідомляла про надмірне відхилення, у матеріалі створення service і timer для systemd описано шаблон невеликого unit, який формує звіт за розкладом.
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 хоста, оскільки простори імен часу 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. Це не змінює системний годинник.