なぜ分散システムにおいて時刻同期が不可欠なのか?
午前2時、PostgreSQLレプリケーションやKafkaクラスタのトラブルシューティング対応をしている場面を想像してみてください。GrafanaやJaegerを開くと、不可解な現象に直面します。ダウンストリームのAPIレスポンスが、アップストリームがリクエストを送信する「前」に返ってきているのです。原因は極めてシンプルでした。2台のサーバー間で時計が3秒ズレていたのです。
ログを調査する際の数秒のズレも厄介ですが、データストレージ層においてはこのズレが致命的な問題へと発展します。Last-Write-Winsを採用しているデータベース(CassandraやCouchbaseなど)は、どのレコードが最新かを判定するためにタイムスタンプのみに依存しています。
仮にノードAの時計がノードBより500ms進んでいた場合、ノードBで新しく書き込まれたデータが、ノードAの古いレコードによって上書きされてしまいます。さらに、KerberosプロトコルやJWT(JSON Web Token)は、クロックドリフト(時計のズレ)が許容値(通常は300秒、または厳格な設定では数秒以内)を超えると即座にリクエストを拒否します。
Ubuntuには標準でsystemd-timesyncdがインストールされています。個人のノートPCや静的ウェブサイトを動かすVPSであれば十分なサービスですが、データベースクラスタやKubernetes環境においては、以下の3つの致命的な弱点が露呈します。
- 本質的に軽量なSNTPクライアントに過ぎず、ハードウェア水晶振動子の周波数ドリフト率(ドリフトレート)の計測や補正ができない。
- 間隔を空けて一度に大きく同期するため、突然の時刻ジャンプ(ステップ同期)が発生しやすく、MySQLやPostgreSQLのコネクションがストールしたりクラッシュしたりする原因になる。
- 同じサブネット内の内部マシン向けにローカルNTPサーバーとして動作する機能を備えていない。
Chronyはこれらの課題を完全に解決します。従来のNTPdよりも迅速に基準時刻を捕捉し、誤差をマイクロ秒単位で安定して維持します。何より重要なのは、Chronyが時間を滑らかに伸縮させる「スルーイング(Slewing)」によって時刻を調整するため、時計が過去へ巻き戻るリスクを完全に排除できる点です。
timesyncdの無効化とUbuntuへのChronyのインストール
2つの同期プロセスが同時に稼働すると、UDPポート123で競合が発生し、カーネルクロックへの干渉を引き起こします。そのため、最初に行うべきはsystemd-timesyncdの完全な無効化です。
sudo timedatectl set-ntp false
sudo systemctl stop systemd-timesyncd
sudo systemctl disable systemd-timesyncd
続いて、Ubuntuの公式リポジトリからchronyパッケージをインストールします。
sudo apt update && sudo apt install -y chrony
バックグラウンドプロセスが正常に起動しているか確認します。
sudo systemctl status chrony
ステータスが緑色でactive (running)と表示されていれば、設定ファイルの調整に進む準備は完了です。
サーバーおよび内部クライアント向けのChrony設定
メインの設定ファイルは/etc/chrony/chrony.confにあります。30台のKubernetesノードとUbuntu 22.04 LTSで稼働するPostgreSQLクラスタの実運用環境において、以下の設定により時刻のズレを常に1.5ms未満に抑えることができています。
使い慣れたエディタで設定ファイルを開きます。
sudo nano /etc/chrony/chrony.conf
1. クライアントとしての時刻同期設定
デフォルトのpoolディレクティブを探し、データセンターの地理的ロケーションに近いNTPプール、またはクラウドプロバイダが提供する内部NTPサーバー(AWS Time Sync 169.254.169.123やGoogle NTP metadata.google.internalなど)に置き換えます。
# 最寄りのサーバープールを使用(日本 / アジア)
pool jp.pool.ntp.org iburst maxsources 4
# 強力なAnycastネットワークを持つ国際サーバーを追加
server time.cloudflare.com iburst
server time.google.com iburst
# マシン起動時の最初の3回の更新に限り、1秒以上のズレがあればステップ同期(ジャンプ)を許可
makestep 1.0 3
# 正確な時刻をマザーボードのリアルタイムクロック(RTC)へ定期的に同期
rtcsync
これらのオプションには以下のような意味があります。
iburst: サービス起動直後に4〜8個のプローブパケットを連続送信します。これにより、数分待つことなくわずか2〜3秒で基準時刻にロックできます。makestep 1.0 3: 最初の3回のクロック更新において1秒以上のズレを検出した場合のみ、即座のステップ同期(ジャンプ)を許可します。それ以降は徐々に時刻を合わせるスルーイング(Slew)に移行し、時刻変動に敏感なサービスのクラッシュを防ぎます。rtcsync: マザーボードのRTCチップに正確な時刻を書き込みます。再起動時にもBIOS側で可能な限り正確な時刻を保持できるようになります。
2. 内部ネットワーク(LAN / プライベートVPC)向けタイムマスターの構築
100台のサーバーが一斉にインターネット上のNTPサーバーへリクエストを送ると、レートリミットに達したり、NATの帯域を無駄に消費したりします。本番環境でのベストプラクティスは、2台をChronyマスターとしてパブリックプールと同期させ、残り98台のノードはその2台のプライベートIPを参照させる構成です。
2台のマスターサーバーの/etc/chrony/chrony.confの末尾に、以下の2行を追加します。
# 内部サブネットからのUDP 123ポート経由の時刻問い合わせを許可
allow 192.168.1.0/24
# インターネット回線切断時でも、マスターが内部クライアントへ時刻を提供し続ける設定
local stratum 10
デーモンを再起動して設定を反映します。
sudo systemctl restart chrony
マスターサーバーでUFWファイアウォールを有効にしている場合は、忘れずにポートを開放してください。
sudo ufw allow 123/udp comment 'Chrony NTP Service'
時刻同期状態の監視と精度の検証
Chronyには同期状況を確認するためのCLIツールchronycが付属しています。
同期ソース一覧の確認(Sources)
chronyc sources -v
実行すると、以下のような結果が出力されます。
.-- Source mode '^' = server, '=' = peer, '#' = local clock.
/ .- Source state '*' = current best, '+' = combined, '-' = not combined,
| / 'x' = may be in error, '~' = too variable, '?' = unusable.
|| .- xxxx [ -xx ] +/- dddd
|| Reachability register (octal) -. | xxxx = adjusted offset
|| Log2(interval) -+ | | xx = measured offset
|| \ | | dddd = error bound
|| \ | |
MS Name/IP address Stratum Poll Reach LastRx Last sample
===============================================================================
^* time.cloudflare.com 3 6 377 25 -120us[ -140us] +/- 12ms
^+ time.google.com 1 6 377 24 +310us[ +290us] +/- 18ms
^+ 118.107.240.230 2 6 377 26 -45us[ -65us] +/- 22ms
注目すべき4つの要素は以下の通りです。
- 記号
^*: Chronyが現在同期先として選択しているメインソース(システムピア)。 - 記号
^+: メインソースの接続が切れた場合に即座に代替可能な適格ソース(バックアップ)。 Reach列: 8進数の377は、直近8回のポーリングすべてで正常に応答を受信できたこと(アップタイム100%に相当)を示します。Last sample列: 実際のオフセット量。-120usは、国際標準時とのズレがわずか0.12ミリ秒であることを示しています。
カーネルのクロックドリフト状況の監視(Tracking)
OSのクロックの全体的な健全性を把握するには、次のコマンドを実行します。
chronyc tracking
Reference ID : A29FC801 (time.cloudflare.com)
Stratum : 4
Ref time (UTC) : Mon Oct 12 10:45:00 2026
System time : 0.000045123 seconds slow of NTP time
Last offset : -0.000012450 seconds
RMS offset : 0.000065201 seconds
Frequency : 12.450 ppm fast
Residual freq : -0.002 ppm
Skew : 0.045 ppm
Update interval : 64.2 seconds
Leap status : Normal
特に重要な2つの指標は次の通りです。
- System time: OSクロックとNTP基準時刻との瞬時誤差。本番環境では、この値が0.005秒(5ms)を超えた場合にPrometheus/Alertmanagerなどでアラートを発報する運用が推奨されます。
- RMS offset: 稼働時間全体における二乗平均平方根オフセット。この値が小さいほど、クロック周波数の揺らぎが少なく安定していることを示します。
わずか15分の適切なChrony設定で、データ不整合に繋がる潜在的なトラブルを未然に防ぎ、ログトレースや分散サーバークラスタの運用を格段に安定させることができます。

