在不知道產品使用的NTP客戶端的情況下,如何檢查系統時間是否同步到NTP伺服器?我正在開發一個預計可以在容器或獨立系統中運行的應用程式。我的應用程式需要確保在嘗試執行某些操作之前同步系統時間。但是,即使在主機作業系統中使用一個或另一個 NTP 用戶端,也無法保證 NTP/chrony 套件的可用性在容器中可用。
所以我正在尋找一種統一的方法來知道系統時間是否同步?
答案1
通用運算上的應用程式無法在所有情況下都知道時間同步在其運行的主機上如何運作。在容器中,您看不到也無法連接到主機上執行的 chronyd 或 ntpd,但這可以保持時間正常。或是依賴主機時間同步的虛擬機器來賓,也是不可見的。進一步使一般答案變得困難的是,NTP 實作比您想像的要多:chrony、ntp、ntpsec、openntpd、w32tm。
通常記錄正確時間的重要性就足夠了。
在某些平台上,建立對 ntpd 啟動的依賴相對簡單。在 RHEL 上,等待時間同步 systemctl enable chrony-wait
並新增至您的 systemd 單元
After=time-sync.target
Requires=time-sync.target
然而,有些應用程式有嚴格的時間要求。我能想到的最苛刻的是時間戳當局,其中一項索賠標準要求抵銷少於一秒,否則無法簽發任何索賠。這種積極的回應意味著應用程式會進行自己的時間檢查。
也許捆綁一個 SNTP 用戶端,該客戶端根據可設定的 NTP 伺服器檢查應用程式中的 NTP 偏移。無法檢查 ntpd 是否正確運行,但無論主機的時間同步如何運作,都可以健全性檢查偏移量。
答案2
有兩種方法可以做到這一點。
如果您正在執行的容器具有完整的 systemd 實現,那麼程式timedatectl
可以通知您主機是否同步。
內部管理的方式是透過 dbus 與systemd-timedated
守護程序通訊。它所做的是執行系統呼叫:adjtimex
從中可以取得指示核心中正在進行的調整(如果有)的當前狀態的資料。
因此,無需完整實作即可自行執行此操作的第二種方法是使用adjtimex()
系統呼叫。
內核不希望在時間報告中出現時間跳躍(或者更糟的是,時間向後移動),因此它會在時間上實現偏差,在幾個小時的過程中會糾正系統時間(它完成了)通過每秒添加或延遲幾毫秒直到調整完成)。
NTP系統adjtimex
通常使用系統呼叫來更改時鐘面臨的當前偏差,使其與真正的時鐘來源正確同步 -但它也可用於取得時鐘來源偏移的目前狀態。因此,它使您能夠了解內核正在執行什麼同步(如果有)。
的手冊頁adjtimex
提供了一些與您所要求的內容相關的有趣部分:
The buf.status field is a bit mask that is used to set and/or retrieve status bits associated with the NTP implementation. Some bits in the mask
are both readable and settable, while others are read-only.
...
STA_UNSYNC (read-write)
Clock unsynchronized.
和
RETURN VALUE
On success, adjtimex() and ntp_adjtime() return the clock state; that is, one of the following values:
...
TIME_ERROR The system clock is not synchronized to a reliable server. This value is returned when any of the following holds true:
* Either STA_UNSYNC or STA_CLOCKERR is set.
* STA_PPSSIGNAL is clear and either STA_PPSFREQ or STA_PPSTIME is set.
* STA_PPSTIME and STA_PPSJITTER are both set.
* STA_PPSFREQ is set and either STA_PPSWANDER or STA_PPSJITTER is set.
The symbolic name TIME_BAD is a synonym for TIME_ERROR, provided for backward compatibility.
因此,如果您沒有成熟的容器,仍然可以獲得這些資料。我編寫了一個簡單的程序,它將通過 C 語言獲取內核傾斜的狀態adjtimex
。gcc -o timex timex.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/timex.h>
/* Written for https://serverfault.com/questions/1077601/how-to-check-whether-the-system-time-is-synchronised-to-ntp-server-without-knowi */
void test_status(
int st)
{
if (st & STA_PLL)
printf("Phase locked loop\n");
if (st & STA_PPSFREQ)
printf("Pulse per second frequency discipline\n");
if (st & STA_FLL)
printf("PPS Time discipline\n");
if (st & STA_INS)
printf("Insert leap second and end-of-day\n");
if (st & STA_DEL)
printf("Delete leap second and end-of-day\n");
if (st & STA_UNSYNC)
printf("Clock is not syncronized\n");
if (st & STA_FREQHOLD)
printf("Hold frequency\n");
if (st & STA_PPSSIGNAL)
printf("Valid PPS signal is present\n");
if (st & STA_PPSJITTER)
printf("PPS signal jitter exceeded\n");
if (st & STA_PPSWANDER)
printf("PPS Signal wander exceeded\n");
if (st & STA_PPSERROR)
printf("PPS signal calibration error\n");
if (st & STA_CLOCKERR)
printf("Clock hardware fault\n");
if (st & STA_NANO)
printf("Nanosecond resolution\n");
else
printf("Microsecond resolution\n");
if (st & STA_MODE)
printf("Frequency locked loop\n");
else
printf("Phase locked loop\n");
}
int main() {
struct timex tx = {};
tx.modes = ADJ_OFFSET_SS_READ;
int err = adjtimex(&tx);
switch(err) {
case -1:
printf("Time error: %s\n", strerror(errno));
break;
case TIME_WAIT:
printf("Leap second insert/delete completed\n");
break;
case TIME_INS:
printf("Leap second to be added next UTC day\n");
break;
case TIME_DEL:
printf("Leap second to be deleted next UTC day\n");
break;
case TIME_OOP:
printf("Leap second insertion in progress\n");
break;
case TIME_ERROR:
printf("Error getting time\n");
break;
case TIME_OK:
printf("Time OK\n");
break;
default:
printf("Time default: %x (%d)\n", err, err);
break;
}
test_status(tx.status);
exit(0);
}
在未同步的系統上運作:
$ ./timex
Error getting time
Clock is not syncronized
Microsecond resolution
Phase locked loop
在不同步的同一主機上的容器中執行:
# podman run -v /tmp/timex/timex:/timex docker.io/gammabytehosting/rockylinux /timex
Error getting time
Clock is not syncronized
Microsecond resolution
Phase locked loop
設定要同步的主機系統中的時間:
# systemctl start chronyd
# chronyc sources
210 Number of sources = 9
MS Name/IP address Stratum Poll Reach LastRx Last sample
===============================================================================
^* _gateway 2 6 7 1 +5568ns[ -720ms] +/- 32ms
# ./timex
Time OK
Microsecond resolution
Phase locked loop
在同一主機上的容器中執行相同的程式檢查:
# podman run -v /tmp/timex/timex:/timex docker.io/gammabytehosting/rockylinux /timex
Time OK
Microsecond resolution
Phase locked loop
時間命名空間可能存在一些問題,我還沒有測試過(儘管它們確實非常新),以查看它們adjtimex
在單獨的上下文中是否不同或一致(請參閱 參考資料man 7 time_namespaces
),但從我讀到的內容來看,它可能仍然可以工作 - - 我會把這個留給你來決定。
答案3
如何檢查系統時間是否與NTP伺服器同步?
沒有。
我的應用程式需要確保在嘗試執行某些操作之前同步系統時間
設定正確的運行環境不是應用程式的責任,而是由系統及其管理員負責。
應用程式依賴系統傳回的日期/時間。無論那個時間是“正確的”或者“錯誤的”;應用程式通常無法知道這一點。它將簡單地使用該系統日期/時間。
如果您有一個客戶端-伺服器模型,那麼每當事務由於(極端)日期/時間偏移而被拒絕時,提供有用的錯誤訊息會很好。
請注意,這種偏移的存在並不能告訴您客戶端或伺服器或兩者的時鐘是否不正確。