製品で使用されている NTP クライアントを知らなくても、システム時刻が NTP サーバーに同期されているかどうかを確認するにはどうすればよいですか?

製品で使用されている NTP クライアントを知らなくても、システム時刻が NTP サーバーに同期されているかどうかを確認するにはどうすればよいですか?

製品で使用されている NTP クライアントを知らなくても、システム時刻が NTP サーバーに同期されているかどうかを確認する方法を教えてください。コンテナーとスタンドアロン システムの両方で実行されることが想定されているアプリケーションを開発しています。アプリケーションには、特定の操作を実行する前にシステム時刻が同期されていることを確認する必要があります。ただし、ホスト OS でいずれかの NTP クライアントが使用されている場合でも、コンテナーで NTP/chrony パッケージが使用できるかどうかは保証できません。

システム時刻が同期されているかどうかを確認するための統一された方法を探していますか?

答え1

汎用コンピューティング上のアプリケーションは、実行されているホストで時間同期がどのように機能するかを常に把握できるわけではありません。コンテナーでは、ホストで実行されている chronyd または ntpd が表示されず、接続もできませんが、それでも時間は正常に維持されます。または、ホストの時間同期に依存する VM ゲストも表示されません。さらに一般的な回答を難しくしているのは、想像以上に多くの NTP 実装 (chrony、ntp、ntpsec、openntpd、w32tm) があることです。

多くの場合、正確な時間の重要性を文書化するだけで十分です。

一部のプラットフォームでは、ntpdの起動に依存するのは比較的簡単です。RHELでは、時刻同期を待つ systemctl enable chrony-waitそしてsystemdユニットに追加します

After=time-sync.target
Requires=time-sync.target

しかし、厳しい時間要件を持つアプリケーションもあります。私が思いつく最も厳しいのは、タイムスタンプの発行機関です。そのうちの1つは、1秒未満のオフセットが標準であると主張しており、そうでなければ何も発行できない。この積極的な応答は、アプリケーションが独自の時間チェックを実行することを意味します。

おそらく、設定可能な NTP サーバーに対して、アプリケーション内の NTP オフセットをチェックする SNTP クライアントをバンドルします。 ntpd が適切に実行されているかどうかはチェックできませんが、ホストへの時間同期がどのように機能するかに関係なく、オフセットの健全性をチェックできます。

答え2

これを行うには 2 つの方法があります。

実行中のコンテナに完全な systemd 実装がある場合、timedatectlプログラムはホストが同期されているかどうかを通知できます。

これを内部的に管理する方法は、systemd-timedatedデーモンと通信する dbus 経由です。デーモンが行っているのはシステム コールの実行です。adjtimexこれにより、カーネル内で実行されている調整 (ある場合) の現在の状態を示すデータを取得できます。

したがって、完全な実装なしでこれを自分で行う 2 番目の方法は、adjtimex()システム コールを使用することです。

カーネルは、時間の報告で時間のジャンプ (または、さらに悪いことに、時間の逆戻り) が発生することを望んでいません。そのため、カーネルは、数時間かけてシステム時間を修正する時間のずれを実装します (調整が完了するまで、1 秒あたり数ミリ秒を追加または遅延することで行われます)。

システムadjtimexコールは通常、NTPシステムによって、クロックが直面している現在のスキューを変更して、実際のクロックソースと正しく同期させるために使用されます。しかしまた、クロック ソースのスキューの現在の状態を取得するためにも使用できます。これにより、どのような同期が実行されているか (実行されている場合) に関するカーネルの考えを覗くことができます。

のマニュアル ページには、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.

そのため、完全なコンテナがなくても、このデータを取得することは可能です。私はadjtimexCでカーネルのスキューの状態を取得する簡単なプログラムを書きました。例えば、次のようにコンパイルできます。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 サーバーに同期されているかどうかを確認するにはどうすればよいですか?

ありません。

私のアプリケーションでは、特定の操作を実行する前にシステム時刻が同期されていることを確認する必要があります。

実行するための正しい環境を設定するのはアプリケーションの責任ではなく、システムとその管理者の責任です。

アプリケーションはシステムから返される日付/時刻に依存します。その時刻が"正しい"または"間違っている"; 通常、アプリケーションはそれを知る方法がありません。単にシステムの日付/時刻を使用します。

クライアント サーバー モデルを使用している場合は、(極端な) 日付/時刻オフセットが原因でトランザクションが拒否されるたびに、役立つエラー メッセージを提供すると便利です。
このようなオフセットの存在は、クライアントのクロックが間違っているのか、サーバーのクロックが間違っているのか、あるいはその両方なのかを知らせるものではないことに注意してください。

関連情報