新しいNTPクライアント＆サーバ、chrony

# はじめに

CentOS 7で"最小限のインストール"以外を選んだ場合にインストールされるchronyはntpdに代わり標準となったNTPクライアント兼サーバである。

とはいえ、CentOS 7でも相変わらずntpdは使用できるし、ntpdateの代わりではないのでntpdateコマンドを打ちたければntpdateを使用する。

以下、初期稿ではchronyの、ntpdとの違いを中心に記述する。注目点があればそれも書くが、まともに追っていくと深いので、後で気になる点が増えたら追記する形を取る。
あと、この記事でネタにするのはchronyであってcronieではないので悪しからず。

# chronyのインストールと起動

CentOS 7ではOSインストール時にインストールされていなければ`yum install chrony`でインストールすることができる（CentOS 6でも6.8からOS標準のリポジトリに入ったので同様にインストールできる）。

chronyのデーモンはchronydである。chronyをインストールした時点でchronydはenabled、つまりシステム起動時に自動起動する状態になっているが、インストール直後は起動していない。
手動で起動させる場合は`systemctl start chronyd`を実行する。

なお、chronydとntpdの両方がenabledになっている場合、システム起動後はchronydだけが起動した状態になる。手動で両方を起動した場合は後から起動した方が起動状態になり、先に起動した方は停止させられる。
ntpdを使いたい場合は忘れずにchronyをアンインストールするなり`systemctl disable chronyd`でchronydがシステム起動時に自動起動しないようにする。

ちなみにntpd起動中は-uオプションを付けないとntpdateを実行できなかったが、chronyd起動中は-uオプションがなくてもntpdateを実行できる。

# chronyの設定

chronyの設定ファイル /etc/chrony.conf はntpdの設定ファイル /etc/ntp.conf と同じフォーマットであり設定項目も似通っている。
/etc/chrony.conf はインストール直後何も手を入れない状態だと以下のようになっている。
ぶっちゃけた話、NTPサーバになる気はなく、問い合わせに行くNTPサーバも問わないなら設定を変更せずに起動するだけで構わない。

```/etc/chrony.conf
# Use public servers from the pool.ntp.org project.
# Please consider joining the pool (http://www.pool.ntp.org/join.html).
server 0.centos.pool.ntp.org iburst
server 1.centos.pool.ntp.org iburst
server 2.centos.pool.ntp.org iburst
server 3.centos.pool.ntp.org iburst

# Ignore stratum in source selection.
stratumweight 0

# Record the rate at which the system clock gains/losses time.
driftfile /var/lib/chrony/drift

# Enable kernel RTC synchronization.
rtcsync

# In first three updates step the system clock instead of slew
# if the adjustment is larger than 10 seconds.
makestep 10 3

# Allow NTP client access from local network.
#allow 192.168/16

# Listen for commands only on localhost.
bindcmdaddress 127.0.0.1
bindcmdaddress ::1

# Serve time even if not synchronized to any NTP server.
#local stratum 10

keyfile /etc/chrony.keys

# Specify the key used as password for chronyc.
commandkey 1

# Generate command key if missing.
generatecommandkey

# Disable logging of client accesses.
noclientlog

# Send a message to syslog if a clock adjustment is larger than 0.5 seconds.
logchange 0.5

logdir /var/log/chrony
#log measurements statistics tracking
```

以下で設定項目のいくつかを説明する。

## NTPクライアントとしての設定

### server

```
書式： server <NTPサーバ> [<オプション1> <オプション2> ...]
デフォルト： 指定なし
複数指定可能
```

NTPクライアントとしてどのNTPサーバに時刻問い合わせに行くかを指定する。
ntpdにもオプションは一部異なるが同一の設定項目がある。

オプションの中でiburstオプションは付けておく方がメリットのあるものである。chronydはiburstオプションの付いたNTPサーバに対して、起動直後に短い間隔で4回問い合わせをする（ntpdのiburstは8回）。NTPサーバの妥当性の判断には1回の問い合わせだけでは足りず数回を要するのだが、起動直後に4度問い合わせをすることによって妥当性判断が早く済み、結果として起動から時刻同期が行われるまでの時間が短くなる。

## 時刻調整の設定

### rtcsync

```
書式： rtcsync
デフォルト： 指定なし
```

コンピュータがマザーボード上に保持する時刻をハードウェアクロックと呼ぶが、ピコ秒単位で動作するCPUなど相手に使用するには秒の小数点以下の桁数が足りないため、OSカーネルは起動時に、より高精度のソフトウェアクロックを生成する。これをシステムクロックと呼ぶ。

ntpdはシステムクロックを問い合わせたNTPサーバの時刻に同期するが、ハードウェアクロックは同期しない。ハードウェアクロックなんてそんな正確に時間を刻んでくれるわけではないので、時間経過とともにシステムクロックとのずれが大きくなっていく。
そこでcronで定期的にシステムクロックの時刻をハードウェアクロックに書き出すhwclock -wを実行させるのが定番であった。

一方、chronyでは設定ファイルにrtcsyncと書いておけばシステムクロックを11分置きにハードウェアクロックに書き出してくれる。

ハードウェアクロックはリアルタイムクロックとも呼ばれる。rtcsyncのrtcはリアルタイムクロックの略である。

**2014-07-24追記**: 調べた範囲では、物理マシン上やVirtualBoxの仮想マシン上ではrtcsyncの機能は想定通り働いているが、KVMの仮想マシン上では働かなかった。
働かないコンピュータではntpdと同様、hwclock -wを定期的に実行するのが良いだろう（KVMの仮想マシン上でもhwclock -wは有効だった）。
働くかどうかは`hwclock --set --date '-65min'`で適当にハードウェアクロックの時刻をずらし、`while true; do echo; hwclock; date; sleep 1m; done`で12分ほど待ってハードウェアクロックが時計合わせされているかで確認している。

### makestep

```
書式： makestep <何秒以上> <何回目まで>
デフォルト： 指定なし
```

システムクロックの調整には、NTPサーバから取得した時刻にすぐに修正するstep調整と、他アプリへの影響が少ないようにシステムクロックのずれを徐々に修正するslew調整という2つの方式がある。

chronydは基本的にはslew調整を行うが、起動直後だけstep調整を行うようにすることができる。それがこのmakestepであり、続く1つ目の数字で何秒以上のずれがあればstep調整を行うか、2つ目の数字で起動から何回目の同期までstep調整を行うかを設定する。
2つの数字両方の条件に一致している時はstep調整、そうでなければslew調整となる。

ntpdではntpdサービス有効時にntpdateサービスも有効にしてntpdが動く前にntpdateを実行する、ということをしていたが、この機能があるためchronydサービス有効時にはntpdateサービスを有効にする必要はない（ntpdでもtinkerで調整できる）。

なお、chronydがslew調整中に強制的にstep調整させたい場合は`chronyc -a makestep`を実行する（ntpdateがインストールされているなら`ntpdate <NTPサーバ>`でも良いだろう）。

**2015-02-24追記**: コメントでご指摘頂いた通り、「何秒以上」と「何回目まで」を逆にしていたので修正した。

### maxslewrate

```
書式： maxslewrate <slewレート>
デフォルト： maxslewrate 83333.333
```

slew調整を最大どれだけの速さで行うかを決定する設定項目である。最大と言っても、大抵そのペースでやってくれるが。
slew調整が行われている間は1秒間に、設定したslewレートの100万分の1、NTPサーバから取得した時刻に近づくように調整される。
つまり、デフォルト値の83333.333なら1秒間で83333.333/1000000 = 1/12秒だけ近づいていく。言い換えればシステムクロックが遅れていれば1秒ごとに13/12秒、早ければ1秒ごとに11/12秒、システムクロックが進むようになるというわけだ。

一方、ntpdのslewレートは500で固定である。つまり1秒間で1/2000秒だけNTPサーバから取得した時刻に近づく。
特に一部の仮想環境ではそれでは追いつかないのでchronyでは大胆に大きな数値に設定されている、ということのようだが、物理環境でもslewレート500じゃどうにもならないこともままある。

## NTPサーバとしてのアクセス制御設定

### port

```
書式： port <NTPリクエストの待ち受けポート番号>
デフォルト： port 123
```

chronydはntpdとは違い、設定によりNTPリクエストの待ち受けUDPポート番号を変更することができる。ただし、SELinuxが有効な場合は`semanage port -a -t ntp_port_t -p udp <ポート番号>`を実行する必要がある（実行するにはpolicycoreutils-pythonをインストールすること）。
ここでport 0を指定するとNTPサーバとして待ち受けを行わず、NTPクライアントとしてだけ働くことになる。

この設定項目でポート番号を変更したchronydには、server設定項のオプションとして「port <ポート番号>」を指定することで時刻問い合わせが可能である。

ntpdでは問い合わせに行く時のポートも123だが、chronydは問い合わせには空いている適当なポートを使うためportの設定値には影響されない（問い合わせ時のポート番号を固定するための「acquisitionport <ポート番号>」という設定項目もある）。

### allow / deny

```
書式：
allow [all] <IPアドレスかサブネット>
deny [all] <IPアドレスかサブネット>
デフォルト： 指定なし（全IPアドレスからのアクセスを禁止）
複数指定可能
```

ntpdではrestrictで書いていた、NTPサーバとしてどのコンピュータにアクセスさせるか、の設定である。
ntpdのrestrictではいくつかの項目のアクセス制御ができたが、chronyではシンプルにNTPクライアントからのアクセスを許すかどうかだけである。

allowに設定されたIPアドレスやサブネットはアクセスが許可され、denyに設定されたIPアドレスやサブネットはアクセスが許可されない。どちらも複数書くことができ、上にあるものが優先順位が高い。ただし、allを付けると上にあるものを上書きして指定されたIPアドレスやサブネットを許可/禁止する。

公式ユーザガイドにある例だと、

```
allow 1.2.3.4
deny 1.2.3
allow 1.2
```

は、1.2.3.4は許可、それ以外の1.2.3.0/24は禁止、それ以外の1.2.0.0/16は許可だが、

``` 	
allow 1.2.3.4
deny 1.2.3
allow all 1.2
```

は、1.2.0.0/16はすべて許可になる。

また、この例の通りいくつかの省略記法が使用できる。
x.y.zと書いてあればx.y.z.0/24のことである。
x.y.z/22という書き方もできる。これは当然x.y.z.0/22のことである。
allowとだけ書いてあればallow 0.0.0.0/0と同義である。

### bindaddress

```
書式： bindaddress <IPアドレス>
デフォルト： 指定なし（*と::で待ち受け）
IPv4とIPv6で1つずつ指定可能
```

特定のIPアドレスをバインドするための設定項目である。
ここで指定したIPアドレスへの接続のみと通信するようになる。
確認は`ss -nplu | grep chronyd`で。

## chronycのアクセス制御設定

### cmdport / cmdallow / cmddeny / bindcmdaddress

ntpdにおけるntpqやntpdcに当たる、chronydの状態閲覧や操作用のクライアントがchronycである。
cmd付きのこれらの設定項目では、chronycからの待ち受けもNTPリクエストの待ち受けと同様に設定することができる。
cmdportのデフォルト値は323である。こちらもUDP。SELinuxが有効な場合に変更するなら`semanage port -a -t chronyd_port_t -p udp <ポート番号>`を実行。

## /etc/chrony.confの設定まとめ

現在のところ、私はこう設定する。
（コメント行は省略した）

### NTPサーバ

ネットワーク内に1台以上立てる。

```/etc/chrony.conf
server ntp.nict.jp iburst
server 0.jp.pool.ntp.org iburst
server 1.jp.pool.ntp.org iburst

stratumweight 0

driftfile /var/lib/chrony/drift
rtcsync
makestep 10 3

allow 10.0.2.0/24 # すべてのコンピュータが所属するサブネット
bindcmdaddress 127.0.0.1
bindcmdaddress ::1

keyfile /etc/chrony.keys
commandkey 1
generatecommandkey

noclientlog
logchange 0.5
logdir /var/log/chrony
```

### NTPクライアント

NTPサーバを除く、すべてのコンピュータ。

```/etc/chrony.conf
server ntp1 iburst # 立てたNTPサーバのホスト名

stratumweight 0

driftfile /var/lib/chrony/drift
rtcsync
makestep 10 3

port 0
bindcmdaddress 127.0.0.1
bindcmdaddress ::1

keyfile /etc/chrony.keys
commandkey 1
generatecommandkey

noclientlog
logchange 0.5
logdir /var/log/chrony
```

## 設定自動化

Ansible修行中の身にて、Ansibleで書いてみた。
https://github.com/yunano/ansible-centos7-roles

このリポジトリにはchrony以外も追加していく予定。

# chronydの状態確認

ntpdにおけるntpq -pに当たるのはchronyc sourcesである。

```
# chronyc sources
210 Number of sources = 3
MS Name/IP address         Stratum Poll Reach LastRx Last sample
===============================================================================
^* ntp-b3.nict.go.jp             1  10   377   445   -114us[ -195us] +/- 5038us
^- 122x215x240x75.ap122.ftth     2  10   377   954    +34us[  -39us] +/-   29ms
^- 219x123x70x91.ap219.ftth.     2  10   377   837  -8280us[-8355us] +/-   40ms
```

項目の意味は以下の通り。

<dl>
<dt>M</dt>
<dd>問い合わせ先の種類。"^"はサーバ、"="はピア、"#"はローカルのハードウェアクロック</dd>
<dt>S</dt>
<dd>現在の状態。代表的なものを挙げると、"*"は同期対象として採用中、"+"は同期対象候補、"-"はアルゴリズムにより同期対象から外れているもの</dd>
<dt>Name/IP address</dt>
<dd>ホスト名かIPアドレス</dd>
<dt>Stratum</dt>
<dd>NTPサーバの階層</dd>
<dt>Poll</dt>
<dd>2を底とする問い合わせ間隔（秒）の対数。言い換えれば2をPollの値で累乗した値が問い合わせ間隔</dd>
<dt>Reach</dt>
<dd>最近8回の問い合わせが成功したかどうかを8進数で表したもの</dd>
<dt>LastRx</dt>
<dd>どれくらい前に前回問い合わせが行われたか</dd>
<dt>Last sample</dt>
<dd>前回測定時の時刻のずれ。[]内はslew調整分を除いたもの。+/-以降は測定誤差マージン</dd>
</dl>

# あとで書くかも

* 初期設定ファイルにあってまだ書いていない項目
* offline
* peer
* chronycのもっといろいろ

# 参考資料

chronyの公式ユーザガイド。

* http://chrony.tuxfamily.org/manual.html

この辺りも参考にした。

* http://ja.scribd.com/doc/194048031/Red-Hat-Enterprise-Linux-7-Beta-System-Administrators-Guide-en-US

ntpdについては以下を参考にした。

* http://d.hatena.ne.jp/incarose86/20110505/1312522379
* http://jp.fujitsu.com/platform/server/primequest/products/1000/catalog/manual/linux/pdf/J2UL-1337-07Z0.pdf