CyberAgentでの「うるう秒」対策 | サイバーエージェント 公式エンジニアブログ

皆様うるう秒対応お疲れ様でした。

こんにちはこんにちは!!

インフラ基盤サービスグループという名前のアレに所属させてもらって、画像配信基盤とかネイティブアプリ基盤などのインフラの面倒を見させてもらってる、@kakerukaeruと申します。
副業でジャック・ニコル◯ンの偽物をしたり社内chatにAAや画像を貼って場を荒らす和ませる仕事をしております。

はい

というわけで、
ここ最近うるう秒対策チームを私と@nekoruriさんの二人でやっていたのですが、
せっかくだからウチはこういう対策やったよ、ってのを
「you エンジニアブログに書いちゃいなよ」って雑談から始まり、
「me エンジニアブログに書いちゃいなよ(?)」となり、
公式エンジニアブログに初参戦させていただく運びと相成りました。
もううるう秒終わってんじゃねーか、うるう秒の前に公開しろや。とは僕も思いますが、突っ込まないで下さい

うるう秒ってなんだっけ

うるう秒に関する情報は有識者たちが既にたくさん書いてくれているので、ここでは割愛します。
うるう秒チームの@nekoruriさんもLinuxのうるう秒おさらい
を書いてくれてるので、ここをみればだいたい分かります(宣伝)

もっともっと詳しく知りたいという人はNTPメモとかを参照して下さい。
メモどころではない知識が詰まっています。

で、どういう対応をしたの?

弊社では大きく分けて、AWSとオンプレミスの2つのインフラ環境が存在します。
うるう秒を乗り切るに当たり、こちらの2環境での対応を進める事になりました。

基本方針

うるう秒は受け入れない。
うるう秒発生時に日本標準時より全てのサーバが1秒未来を生きる形に統一。
その後、ゆっくりと時間を修正させる。

AWS編

AWSを使用しているサービスの方には、ntpdを最新verにupdateし、slew modeで起動させてうるう秒を乗り切っていただくようにしました。


So Simple :)

オンプレ編

オンプレ編で基本方針を実現するために必要なのはイカの2つ

  • Leap Indicatorを受け取らず、うるう秒を挿入させない。

  • うるう秒経過後に大幅な時間の修正がかからないようにゆっくりと時間を巻き戻す。

  • 結論

    構成的にはこんな感じ。



    実際の対策の手順はこんな感じ。


  • 各DCの内部NTPサーバの経路を変更して、一組のNTPサーバへ終端させる
  • 終端先のNTPサーバをうるう秒フラグ挿入1日前からhwclock提供に切り替える
  • うるう秒経過後、1秒をゆっくりもどす(後ほど詳細を説明)
  • 1秒戻った事を確認し各DCの内部NTPサーバの経路を元の上位NTPサーバへ戻す

  • この構成でうるう秒を乗り切る大前提として、各DCに設置されてるサーバが各々の内部NTPサーバをちゃんと参照してる事が必須となる。

    NTPサーバの基本的な設定は、OS install時にKickstartで内部のNTPサーバに向くように設定されているが、取りこぼしがないようにScriptを組んで各DCのセグメントに対してブロードキャスト的にntpの参照先を確認して回った。

    だいたい16000台ぐらい調べたのだが、なんと全部内部のNTPサーバを参照していた、ほんまかいな。まぁ、数十台ログイン出来ない謎サーバがいたんですが(その後ちゃんと調べました)


    もうひとつ

  • うるう秒経過後に大幅な時間の修正がかからないようにゆっくりと時間を巻き戻す。
  • を実現させなければイケないのだが、実現方法はイカ。



    基本的にオンプレのサーバたちは数が多すぎるので、設定変更をさせない。
    ので、各clientを強制的にslew_modeにさせて放置、はナシ


    なので、NTPサーバ側でScriptを仕込んで、ゆっくりと時間を戻す方針を取る。
    実際に動かした、Scriptはイカ。

    #!/bin/bash

    #!/bin/bash

    UPSTREAMORIG_NTP_CONF=/etc/ntp.conf.upstream
    NO_UPSTREAMORIG_NTP_CONF=/etc/ntp.conf.no_upstream
    NTP_CONF=/etc/ntp.conf


    # check

    if [ ! -e $UPSTREAMORIG_NTP_CONF ]; then
    echo "Not found $UPSTREAMORIG_NTP_CONF"
    exit 1
    fi

    if [ ! -e $NO_UPSTREAMORIG_NTP_CONF ]; then
    echo "Not found $NO_UPSTREAMORIG_NTP_CONF"
    exit 1
    fi

    export PATH=/bin:/usr/bin:/sbin:/usr/sbin

    print_date() {
    N=$1
    echo $1 "$(date +%Y-%m-%dT%H:%M:%S) "
    }

    syslog() {
    logger -t slew_batch -p user.info "$1"
    }

    syslog_and_echo() {
    logger -t slew_batch -p user.info "$1"
    print_date -n
    echo "$1"
    }

    log_ntpdate() {
    msg=$(ntpdate -q 210.173.160.27 | head -1)
    syslog_and_echo "$msg"
    }


    # 1ループで15msぐらいずつ合わせる
    # 無限ループで手で止める。
    n=0
    while true; do
    n=$(($n + 1))
    print_date -n
    syslog_and_echo "[loop:$n]"

    # 上位NTPサーバに向ける
    syslog_and_echo "Connecting to MFEED"

    log_ntpdate

    sudo cp "$UPSTREAMORIG_NTP_CONF" "$NTP_CONF"
    sudo /sbin/service ntpd restart

    # 40秒まつ(0.5ms * 40s = 最大20msだけずらす: 実際はiburst同期で実績15msくらい)
    wait=40
    wait_notify_interval=10
    wait_start=$(date +%s)
    wait_notify=$wait_start
    syslog_and_echo "Waiting $wait secs"
    while [ $(($(date +%s) - $wait_start)) -le $wait ]; do
    if [ $(($(date +%s) - $wait_notify)) -ge $wait_notify_interval ]; then
    # $wait_notify_interval ごとにntpdate表示
    # 時間かかるので先にwait_notify更新
    wait_notify=$(date +%s)
    log_ntpdate
    fi
    sleep 1
    done

    # sysclock => hwclock
    syslog_and_echo "Sync to hwclock"
    log_ntpdate

    sudo /sbin/hwclock --systohc

    # 上位NTPサーバを外しhwclockのみ見るようにする
    syslog_and_echo "Disconnecting from MFEED. Only hwclock"

    sudo cp "$NO_UPSTREAMORIG_NTP_CONF" "$NTP_CONF"
    sudo /sbin/service ntpd restart

    # 1024s(default max poll) + 60s(予備) = 1084s まつ
    wait=1084
    wait_notify_interval=100
    wait_start=$(date +%s)
    wait_notify=$wait_start
    syslog_and_echo "Waiting $wait secs"
    while [ $(($(date +%s) - $wait_start)) -le $wait ]; do
    # こちらは待つだけなのでピリオド表示
    if [ $(($(date +%s) - $wait_notify)) -ge $wait_notify_interval ]; then
    # $wait_notify_interval ごとに経過秒数とntpdateを表示
    echo "[$(($(date +%s) - $wait_start))]"
    log_ntpdate

    wait_notify=$(date +%s)
    fi
    # 長く待っても大丈夫なので10秒でsleep
    sleep 10
    echo -n "."
    done
    echo

    done

    考え方はイカ


    1.2015/07/01 09:00:00 以降に上位外部NTPサーバに戻す。
    2.NTPクライアントがSTEPモードにならないよう、閾値の128msより低い40msだけ時間を修正させる。
    3.修正されたシステム時刻をハードウェアクロックに保存する。
    4.NTPサーバの参照先を上位外部NTPサーバからハードウェアクロックに向ける。
    5.1024秒(NTPクライアントの最大ポーリング間隔) + 60秒 待つ。
    6.NTPサーバの参照先を上位外部NTPサーバに戻す。
    7.2~6を時間が調整されるまで繰り返す

    最初にちょっと1loopで戻す時間を頑張りすぎたせいでoffset監視のalertが飛びまくったがごめんなさい、変更時間を修正してからは特に何事も無くゆっくり修正されました。


    ばっちり✌(´◉౪◉)✌ィェーィ

    まとめ

    冒頭にリンクを記載した、@nekoruriさんのブログでも記載されてましたが、



    非うるう秒三原則の厳守
    - 持たない
    - 作らない
    - 持ち込ませない

    これを徹底した感じです。

    全サーバ調べたんだからその時にntpの設定変更しても良かったんじゃない?とか
    逆に一気に時間戻っても良くない?とか
    うるう秒とかどうでもよくない?とか
    色々考えましたが大きな事故が起こらず過ごせて一旦安心です。

    うるう秒的小ネタ話

    NTP終端サーバでhwclockの提供を開始したらalert大量発生事件

    Leap Indicator配布の前日に、上位NTPサーバへの経路を切ってhwclockの提供を開始した途端にntpのalertが大量に飛んできた。



    offsetを確認してみてもどうやら遅れてる気配はない。。
    調べてみるとどうやらmonからの監視だけがコケているようだ。。。
    使われていた監視スクリプトを読んでみると、イカのような記述が。

    28 =item B<--maxstratum> Maximum stratum number, default is 10. Stratum  
    29 16 indicates that ntp is running on a system, but the clock is not
    30 synchronized. An alarm will be triggered if this value is exceeded.


    どうやら、defaultでStratum 10イカであることの監視が入っている模様。。
    hwclockの提供を開始した際に、そのサーバでStratum 10を設定してしまったために、
    下位のclientの監視が全てコケてしまったというオチ。



    設定変更前のStratumを再設定して、収束。
    ちゃんと確認しましょう(土下座

    上位NTPサーバの時間がずれるとLOCAL(0)を見て帰ってこない問題

    ntp.confとかに、
    server 127.127.1.0

    を記述している場合、
    うるう秒によるSLEWモード調整などで上位NTPサーバの時計がズレる場合、
    LOCAL(0)を信用してしまい上位NTPサーバが候補に乗らなくなってしまう問題がある。



    ので、一気に時間を調整して時間を元に戻す未来は厳しかったのかもしれない。


    http://support.ntp.org/bin/view/Support/UndisciplinedLocalClock
    The Undisciplined Local Clock is not a back-up for leaf-node (i.e. client only) ntpd instance.

    DCのNTPサーバにCentOS4.6がいた事件

    今回を機にリプレイスをして引退してもらいました。合掌。

    [kakerukaeru@ntp02 ~]$ cat /etc/redhat-release 
    CentOS release 4.6 (Final)
    [kakerukaeru@ntp02 ~]$ uptime
    14:50:38 up 2542 days, 22:02, 1 user, load average: 0.00, 0.00, 0.00

    最後に

    なんでこんな謎時間の更新なの?と思われるかもしれませんが、
    ついさっきまでゆっくりと時間調整が行われるのを優しい目で見守っていたからなのでした。

    以上になります。
    みなさま、次回もいいうるう秒に巡りあえますように。

    参考にさせて頂いたURL

    NTPメモ (Internet Archive)
    NTP設定 - とあるSIerの憂鬱
    The Network Time Protocol (NTP) Distribution
    うるう秒挿入後に Leap Second Insertion フラグを削除する - Red Hat Customer Portal