CentOS Stream 9へのChronyインストールと設定:NTPサーバーとクライアントで正確な時刻同期を実現

CentOS tutorial - IT technology blog
CentOS tutorial - IT technology blog

午前2時、ログが一致しない、アラートが乱発

ある金曜日の夜のことだった。寝ていたところ、devチームから電話が来た:「DBサーバーとアプリサーバーのトランザクションログが5分近くずれていて、エラーがいつ発生したのかわからなくなっています。」

ノートパソコンを開いて確認すると、本当だった――DBサーバーは正確な時刻で動いているのに、アプリサーバーは4分37秒遅れていた。これだけで十分だ。一部のサービスでSSL証明書のハンドシェイクが失敗し、Kerberos認証がタイムアウトし、そして最も痛かったのは、ノード間でタイムスタンプが同期されていないため分散トレーシングが完全に無意味になっていたことだ。

そのサーバーは古いntpdを使っており、NTPプールは数週間前から到達不能になっていたのに、モニタリングで警告が一切出ていなかった。それ以来、フリート全体をChronydに切り替えた。会社にはまだCentOS 7で動いているサーバーがいくつかあり、AlmaLinuxへの移行は既に対処済みの課題だが――CentOS 7でもCentOS Stream 9でも、Chronydが現時点で最も正しい時刻同期ソリューションであることに変わりはない。

なぜサーバーの時計はずれるのか?

サーバーのハードウェアクロック(RTC)は時間とともにドリフトする――1日あたり数秒から数分ずれることがある。仮想化環境(KVM、VMware)では、VMが頻繁にサスペンド/レジュームされるため状況はさらに悪化し、時計が不規則に跳ぶことがある。

サーバーの時刻がずれたときに実際に経験した問題:

  • TLS/SSLハンドシェイク失敗:サーバーの時刻が間違っているため、証明書が未有効または期限切れと見なされる
  • Kerberos認証タイムアウト:Kerberosは最大5分のクロックスキューしか許容しないため、それを超えると認証が全て失敗する
  • データベースレプリケーションの偽ラグ:マスター/スレーブのタイムスタンプのずれにより、実際には問題がないのにモニタリングがレプリケーション遅延を報告する
  • ログフォレンジックが無意味に:インシデント発生時、タイムスタンプが同期されていなければ複数サーバーのログを相関させることができない
  • cronジョブが間違った時刻に実行:バックアップジョブが設定通りの午前3時ではなくピーク時間帯に実行される

原因分析:古いntpdとChronydが完全に置き換えた理由

CentOS Stream 9(およびRHEL 9)はntpdを完全に廃止し、Chronydのみをデフォルトで搭載している。しかし多くの管理者は慣れ親しんでいるため、外部リポジトリからntpdを無理にインストールしようとする――これは避けるべき間違いだ。

Chronydはntpdより2つの重要な点で優れている:

  • 起動後の同期が速い:Chronydは独自のアルゴリズムを使用しており、徐々にスルーするのではなく即座にステップ調整できる(ntpdはデフォルトで1000秒未満のずれではステップしない)
  • 不安定な環境でも良好に動作:ノートPCのオン/オフ、VMのサスペンド/レジューム、不安定なネットワーク接続――Chronydはリアルタイムでずれを追跡するため、これらをはるかにうまく処理できる

何かを行う前に、どのサービスが動いているか確認する:

systemctl status chronyd ntpd 2>/dev/null
timedatectl status

両方が有効になっている場合は競合が発生している――まずntpdを停止する必要がある。

解決策:Chronydのインストールと設定

ステップ1:Chronydのインストール(CentOS Stream 9では通常プリインストール済み)

dnf install -y chrony
systemctl enable --now chronyd
systemctl status chronyd

サーバーでntpdが動いている場合は、先に停止して削除する:

systemctl stop ntpd
systemctl disable ntpd
systemctl mask ntpd  # 他のサービスが再度有効化するのを防ぐ
dnf remove -y ntp

ステップ2:基本的なNTPクライアント設定

メインの設定ファイルは/etc/chrony.confにある。CentOS Stream 9のデフォルトは2.centos.pool.ntp.orgを使用している。サーバーがベトナムや日本に設置されている場合は、レイテンシを下げるためにアジアのプールに変更する:

cat > /etc/chrony.conf << 'EOF'
# レイテンシを下げるために最も近いNTPプール
pool 0.asia.pool.ntp.org iburst
pool 1.asia.pool.ntp.org iburst
pool 2.asia.pool.ntp.org iburst
pool 3.asia.pool.ntp.org iburst

# 時間経過に伴うクロックドリフトを追跡するためのドリフトファイルを保存
driftfile /var/lib/chrony/drift

# 起動時に1秒以上ずれている場合は即座にステップ調整する(最大3回)
makestep 1.0 3

# ハードウェアクロック(RTC)を定期的に同期する
rtcsync

logdir /var/log/chrony
EOF
systemctl restart chronyd
chronyc sources -v

chronyc sourcesの出力は正常に動作している場合このようになる:

MS Name/IP address         Stratum Poll Reach LastRx Last sample
===============================================================================
^* 103.145.x.x                   2   6   377    51   +123us[ +456us] +/- 5ms
^+ time.cloudflare.com            3   6   377    52   -234us[ -789us] +/- 8ms

^*はメインソースとして選択されているサーバー、^+はバックアップを意味する。全てが^?と表示されている場合は、サーバーがどのプールにも到達できていない――ファイアウォールのポート123/UDPを確認する。

ステップ3:社内NTPサーバーの構築(企業環境で重要)

50台のサーバーが各自インターネットにアクセスしてNTPを同期するのではなく、1〜2台の社内NTPサーバーを構築し、残りのサーバーはそこに同期させるべきだ。メリットは明確:外部トラフィックの削減、インフラ全体が同じタイムソースを使用、インシデント発生時のログ相関が正確になる。

NTPサーバーマシン(例:IP 192.168.1.10)で、allowセクションを追加設定する:

cat > /etc/chrony.conf << 'EOF'
# 外部プールから同期
pool 0.asia.pool.ntp.org iburst
pool 1.asia.pool.ntp.org iburst
pool 2.asia.pool.ntp.org iburst

driftfile /var/lib/chrony/drift
makestep 1.0 3
rtcsync
logdir /var/log/chrony

# このサーバーへの社内サブネットからのNTPクエリを許可
allow 192.168.1.0/24
allow 10.0.0.0/8

# インターネットが切断されても、ローカルクロックを基にクライアントへ時刻を提供
# stratum 10 = "あまり確かではないが、何もないよりはまし"
local stratum 10
EOF
systemctl restart chronyd

# NTPのファイアウォールポートを開く
firewall-cmd --permanent --add-service=ntp
firewall-cmd --reload

# このサーバーに同期しているクライアントを確認
chronyc clients

残りの全サーバー(NTPクライアント)で、社内NTPサーバーを指すようにコンフィグを変更する:

cat > /etc/chrony.conf << 'EOF'
# 社内NTPサーバーを指定
server 192.168.1.10 iburst prefer
server 192.168.1.11 iburst  # バックアップNTPサーバー(存在する場合)

driftfile /var/lib/chrony/drift
makestep 1.0 3
rtcsync
EOF

systemctl restart chronyd
sleep 10
chronyc tracking

よくあるエラーの簡単なトラブルシューティング

# 同期状態の詳細を確認
chronyc tracking

# 即座に同期を強制する(設定変更後に便利)
chronyc makestep

# リアルタイムでログを確認
journalctl -u chronyd -f

「No NTP sources selected」エラー――通常はファイアウォールがポート123/UDPをブロックしている:

firewall-cmd --list-services | grep ntp
# ない場合は開く:
firewall-cmd --permanent --add-service=ntp && firewall-cmd --reload

SELinuxがchronydを拒否する――ログパスやドリフトファイルを変更した後によく発生:

ausearch -m avc -ts recent | grep chrony
restorecon -Rv /var/lib/chrony /etc/chrony.conf

ベストプラクティス:本番環境向けの安定したセットアップ

深夜の緊急コールを何度も経験した後、再発しないようにこのチェックリストをまとめた:

  1. 社内NTPサーバーを最低2台構築する――1台はマスター、1台はバックアップ。マスターがダウンするとChronydクライアントは自動的にフェイルオーバーする
  2. 社内NTPサーバーは少なくとも3つの外部プールから同期する――Chronydは信頼性の低いプールを自動的に除外する
  3. rtcsyncを有効にする――ハードウェアクロックを同期することで、サーバー再起動時に最初からずれが生じないようにする
  4. Prometheusでオフセットをモニタリングする――node_timex_offset_secondsメトリクスは1秒以上ずれる前にアラートを出すため、インシデントになる前に対処する時間が確保できる
  5. Ansibleプレイブックに組み込む――新しくデプロイする全サーバーが初日から正しいChrony設定を持つようにし、手動設定が不要になる

現在使用しているAnsibleタスクはこちら。シンプルだが十分に使える:

- name: chronyをインストール
  dnf:
    name: chrony
    state: present

- name: Chrony設定をデプロイ
  template:
    src: chrony.conf.j2
    dest: /etc/chrony.conf
    owner: root
    mode: '0644'
  notify: restart chronyd

- name: chronydを有効化
  systemd:
    name: chronyd
    state: started
    enabled: yes

- name: NTPポートを開く(NTPサーバーノードのみ)
  firewalld:
    service: ntp
    permanent: yes
    state: enabled
  when: inventory_hostname in groups['ntp_servers']

このセットアップをフリート全体に適用してから――AlmaLinuxへの移行待ちのサーバーも含めて――時刻同期が原因で午前2時に電話が来ることはなくなった。そして本当のインシデントが発生したとき、全サーバーのログが正確に相関付けられるため、不要なデバッグに何時間も費やすことがなくなった。

Share: