2017-12-19

Raspbian Stretch でタイムサーバー

Raspbian stretch の新しいイメージファイル (2017-11-29-raspbian-stretch-lite.zip) が出ていたので、これを使って久しぶりに Raspberry Pi でハードウェアクロック(RTC)付きタイムサーバーを作ってみた。備忘録を残しておく。

以前(wheezyで試したとき)とは、いろいろ、やり方が変わっているようだ。stretchのほうが簡単に思える。

RasPiに接続したRTCを認識させるには device tree overlay 機能を使う。おなじみのconfig.txtファイルに次のような設定を追加してRasPiを再起動すれば /dev/rtc0 と /dev/rtc が生えてくる。

$ sudo -e /boot/config.txt
dtoverlay=i2c-rtc,pcf2127

この設定例はリアルタイムクロックICとしてPCF2127を接続した場合を想定している(I2C接続なのでraspi-configでI2Cを有効にしておくのも忘れずに)。 他にどのようなチップに対応しているか調べるには次のようにする。

$ dtc -I dtb -O dts -@ /boot/overlays/i2c-rtc.dtbo

wheezyではNTPクライアント/サーバーに ntpd を使っていたが、最近は、NTPクライアント機能だけでよければ systemd-timesyncd を、サーバー機能も必要なら Chrony を使うのが一般的らしい(ntpdも使える)。

fake-hwclock をアンインストール(あるいは無効化)して chrony をインストールする。

$ sudo apt-get purge -y fake-hwclock
$ sudo apt-get install -y chrony

chronyの設定は /etc/chrony/chrony.conf で行う。

$ sudo -e /etc/chrony/chrony.conf
$ sudo systemctl restart chrony

デフォルトの設定ファイルの内容に、allowディレクティブで接続を許可するホスト(ネットワークの範囲)を追加記述するだけでタイムサーバーとして機能する(最低限の設定で正常動作を確認してから、参照するNTPサーバーを手近なもの例えばNICTに変更するなど細かい調整を行う)。

RTCへの書き込みもchronyが自動的にやってくれる(デフォルトの設定にrtcsyncと書かれている)。確認してみよう。

$ timedatectl status
      Local time: Tue 2017-12-19 17:09:07 JST
  Universal time: Tue 2017-12-19 08:09:07 UTC
        RTC time: Tue 2017-12-19 08:09:07
       Time zone: Asia/Tokyo (JST, +0900)
 Network time on: yes
NTP synchronized: yes
 RTC in local TZ: no

$ sudo hwclock -r 
2017-12-19 08:09:07.790809+0900

$ chronyc sources
210 Number of sources = 4
MS Name/IP address         Stratum Poll Reach LastRx Last sample               
===============================================================================
^+ ntp-b2.nict.go.jp             1   6    17    13    -38us[  -38us] +/-   17ms
^- ntp-a2.nict.go.jp             1   6    17    12    -88us[  -88us] +/-   18ms
^- ntp-a3.nict.go.jp             1   6    17    12    +67us[  +67us] +/-   17ms
^* ntp-b3.nict.go.jp             1   6    17    14   +737us[+1294us] +/-   18ms

うまくいっているようだ。めでたしめでたし。

と、いきたいところだが、大きな問題が残っている。 実はこれだけだと、インターネットに接続していない(NTPサーバーにアクセスできない)状況で、RasPiを起動したとき、システムクロックが狂ってしまう。 Raspbianは単にRTCを有効にしただけでは、起動時にハードウェアクロックの時刻をロードしない。 これじゃあ、なんのためにRTCを付けたんだか分からない。ただの無意味なお荷物ハードウェアだ。

ネットで解決策を調べてみる(Google先生に聞いてみる)と、 /lib/udev/hwclock-set スクリプトの、systemdで動作しているか判定している部分をコメントアウトしろ、

#if [ -e /run/systemd/system ] ; then
#    exit 0
#fi

みたいな情報がたくさんヒットする。いや、まあ、確かにそれで先には進むんだけど……

不細工じゃない? そこは書き換えるべきところなの? 理由があってそういう(Systemdに任せる)コードになってんじゃないの?

次に多くヒットするのは、古いRaspbianを使ってのRTCの取り付け方として、昔からよくある /etc/rc.local に hwclock -s と書けってやつ。う〜ん、これでも時刻は合うけれど、もっとうまい方法はないのか?

stretchにデフォルトで用意されているsystemdのユニットファイルに hwclock.service という、それっぽいものを発見したが、こいつはマスクされている。

$ systemctl list-unit-files --no-pager | grep hwclock
hwclock.service                        masked   

$ ls -lah /lib/systemd/system/hwclock.service 
lrwxrwxrwx 1 root root 9 Jul  6 05:31 /lib/systemd/system/hwclock.service -> /dev/null

その他に、(stretchにも) /etc/init.d/hwclock.sh があるようだが、こいつはudevまかせで堂々巡りである。

仕方がないので、起動時にハードウェアクロックの時刻をシステムクロックにセットする自前のサービス(Systemdユニット)を追加する方法を採用した。

正しい方法は藪の中、どなたかご存知なら教えてください。

そうそう、もう一つハマった点として、いろいろな設定を試している途中で、RasPiの電源をオフにしてから、再度オンにすると、時刻の読み取りに失敗する謎の怪現象が発生して困った。

$ timedatectl 
Failed to query server: Invalid argument

$ sudo hwclock -r
hwclock: ioctl(RTC_RD_TIME) to /dev/rtc to read the time failed: Invalid argument

と、まあ、こんな調子で、ああでもない、こうでもないと、小一時間悩んだのだが……

そういえばこのRTCモジュールって、ものすごく久しぶりに使うけど、何年前に買ったんだっけ?

ひょっとして、と思ってバックアップ用のボタン電池(リチウム電池)を新品に交換したら一瞬にしてエラー解消!

設定の問題じゃなくて、

電池切れ

だったというオチでした。

0 件のコメント:

コメントを投稿