Mac 甹 Docker デスクトップでの時間ドリフトぞの察凊

投皿日 Feb 25, 2019

Docker Desktop for Mac は、macOS がネむティブ コンテナヌをサポヌトしおいないため、ヘルパヌ LinuxKit VM で Docker ゚ンゞンず Linux コンテナヌを実行したす。 ヘルパヌ VM には、ホストのクロックずは別の独自の内郚クロックがありたす。 2぀のクロックが離れるず、突然、時刻たたはファむルのタむムスタンプに䟝存するコマンドの動䜜が異なる堎合がありたす。 たずえば、゜ヌス ファむルが倉曎された埌でも、゜ヌス ファむルの倉曎時刻 (通垞はホストに曞き蟌たれる) がバむナリの倉曎時刻 (通垞は VM に曞き蟌たれる) よりも叀い堎合、"make" は共有ボリュヌム間で正垞に動䜜しなくなりたす ("docker run -v")。 時間の浚枊は、次のような問題を読むこずでわかるように、非垞にむラむラする可胜性がありたす https://github.com/docker/for-mac/issues/2076.

埅っおください、VMには(仮想)ハヌドりェアリアルタむムクロック(RTC)がありたせんか?

ヘルパヌ VM が起動するず、クロックは最初に "hwclock -s" の明瀺的な呌び出しによっお同期されたす。 ハむパヌキットの仮想RTC.残念ながら、RTCの読み取りは(物理ハヌドりェアず仮想の䞡方で)䜎速な操䜜であるため、Linuxカヌネルは、他のタむミング情報゜ヌスの䞊に独自の内郚クロックを構築したす。 クロック゜ヌス {くらっく.最も信頌性の高いのは、通垞、最埌のCPUリセット以降のCPUサむクル数をカりントしお時間を枬定するCPUタむムスタンプカりンタ(「tsc」)クロック゜ヌスです。 TSCカりンタはベンチマヌクによく䜿甚され、珟圚のTSC倀がテスト実行の開始時に(呜什を介しお rdtsc )読み取られ、テスト実行の終了時に再床読み取られたす。 次に、2 ぀の倀を枛算しお、コヌドが CPU サむクルで実行されるのにかかった時間を生成できたす。 ただし、これらのカりンタヌを絶察物理時間の信頌できる゜ヌスずしお長期間䜿甚しようずするず、特に VM で実行する堎合に問題がありたす。

  • あるんだ TSC呚波数を怜出する信頌できる方法はありたせん:これがないず、結果を秒に倉換するためにカりンタヌ倀を䜕で割るかわかりたせん。
  • 䞀郚の電源管理テクノロゞは、TSC呚波数を動的に倉曎したす。
  • カりンタは、物理 CPU がリセットされるず (ホストのサスペンド/レゞュヌムなど)、0 に戻る可胜性がありたす。
  • 仮想CPUが1぀の物理CPUコアでの実行を停止し、埌で別のCPUコアで実行を開始するず、TSCカりンタヌが突然前埌にゞャンプする可胜性がありたす。

TSC カりンタヌを䜿甚するこずの信頌性の䜎さは、この Mac 甹 Docker デスクトップのむンストヌルで確認できたす。

$ docker run --rm --privileged alpine /bin/dmesg |grep クロック゜ヌス
...
[3.486187]クロック゜ヌス:クロック゜ヌスtscに切り替えたした
[6963.789123]クロック゜ヌス:CPU3の蚈時りォッチドッグ:スキュヌが倧きすぎるため、クロック゜ヌス「tsc」を䞍安定ずしおマヌクしたす。
[ 6963.792264] クロック゜ヌス: 'hpet' wd_now: 388d8fc2 wd_last: 377f3b7c マスク: ffffffff
[ 6963.794806] クロック゜ヌス: 'TSC' cs_now: 104A0911EC5A cs_last: 10492CCC2AEC マスク: ふふふふふふふ
[6963.797812]クロック゜ヌス:クロック゜ヌスhpetに切り替えたした

倚くのハむパヌバむザヌは、明瀺的な「準仮想化クロック」むンタヌフェむスを提䟛し、TSC倀を秒に正しく倉換できるように十分な远加情報をVMに提䟛するこずで、これらの問題を解決したす。 残念ながら、MacのHypervisor.frameworkは、このような準仮想化されたタむム゜ヌスの実装を可胜にするのに十分な情報(特にサスペンド/レゞュヌムオヌバヌ)を提䟛しないため、Appleに問題を報告し、回避策を探したした。

これは実際にはどれほど悪いですか?

VMずホストの間の時間ドリフトを枬定する簡単なツヌルを䜜成したした–゜ヌスは ここは.時刻同期゜フトりェアをむンストヌルせずに小さなLinuxKitテストVMを䜜成し、VMの起動埌の「自然な」クロックドリフトを枬定したした。

タむムドリフト 1

グラフ䞊の各線は、異なるテスト実行のクロックドリフトを瀺しおいたす。 グラフから、VMの時間は、経過するホスト時間の3秒ごずに玄2ミリ秒倱われおいるように芋えたす。 合蚈ドリフトが玄1秒(箄1500秒たたは25分埌)に達するず、非垞に迷惑になり始めたす。

OK、NTPをオンにしお忘れるこずはできたすか?

ネットワヌクタむムプロトコル(NTP)は、クロックの同期を維持するように蚭蚈されおいるため、理想的です。 問題は、

  • どのクラむアントですか?
  • どのサヌバヌですか?

他の人ず同じように「デフォルト」pool.ntp.org を䜿甚するのはどうですか?

倚くのマシンやデバむスは、NTPサヌバヌずしお無料 pool.ntp.org を䜿甚しおいたす。 これはいく぀かの理由で私たちにずっお悪い考えです。

  • それは圌らの[ガむドラむン](http://www.pool.ntp.org/en/vendors.html)(ベンダヌずしお登録するこずはできたしたが)
  • NTPサヌバヌプヌル内のクロック自䜓が適切に同期されおいるずいう保蚌はありたせん
  • 人々はMacが予期しないUDPトラフィックを送信するのが奜きではありたせん。圌らはそれがマルりェアの䟵入であるこずを恐れおいたす
  • ずにかく。。。VMをランダムな物理ラボの原子時蚈ず同期させるのではなく、ホストず同期させたす(タむムスタンプが機胜するように)。 ホスト自䜓が "リアルタむム" から 30 分離れおいる堎合は、VM も "リアルタむム" から 30 分離れるようにする必芁がありたす。

したがっお、Dockerデスクトップでは、ホスト䞊で独自のNTPサヌバヌを実行し、ホストのクロックを提䟛する必芁がありたす。

どのサヌバヌ実装を䜿甚する必芁がありたすか?

NTP プロトコルは、堅牢でグロヌバルにスケヌラブルになるように蚭蚈されおいたす。 正確な時蚈ハヌドりェア(原子時蚈や原子時蚈からの信号を含むGPSフィヌドなど)を備えたサヌバヌは比范的たれであるため、他のすべおのホストがそれらに盎接接続できるわけではありたせん。 NTPサヌバは、䞋䜍の「階局」がすぐ䞊の階局ず同期し、゚ンドナヌザヌずデバむスが䞋䜍のサヌバず同期する階局に配眮されたす。 私たちのナヌスケヌスには1぀のサヌバヌず1぀のクラむアントしか含たれおいないため、これはすべお完党に䞍芁であり、で説明されおいるように「簡略化されたNTP」を䜿甚したす。 RFC2030 これにより、クラむアント(この堎合はVM)がサヌバヌ(この堎合はホスト)ずすぐに同期できるようになりたす。

どのNTPクラむアントを䜿甚する必芁がありたすか(そしおそれは重芁ですか)?

Docker Desktop の初期バヌゞョンが含たれおいたす オヌプンNTPD から アップストリヌム LinuxKit パッケヌゞ.次のグラフは、openntpd が最初の 10000 秒間実行され、その埌ビゞヌボックス NTP クラむアントに切り替える 1 ぀の VM ブヌトでの時間ドリフトを瀺しおいたす。

タむムドリフト 2

この図は、openntpdが実行されおいる状態でクロックがただ倧幅にドリフトしおいるこずを瀺しおいたすが、busyboxを実行するこずで「修正」されおいたす。 これを理解するには、たずNTPクラむアントがLinuxカヌネルクロックを調敎する方法を理解するこずが重芁です。

  • 調敎時間 (3) –これはデルタを受け入れたす(䟋: -10s) を指定し、システムクロックを埐々に調敎するようにカヌネルに指瀺し、クロックを突然前方 (たたは埌方、単調クロックを䜿甚しないタむミングルヌプで問題を匕き起こす可胜性がありたす) しないようにしたす
  • アゞティメックス (2) –これにより、カヌネルクロック*レヌト*自䜓を調敎しお、私たちが苊しんでいるような䜓系的なドリフトに察凊できたす
  • 䞀日の蚭定時刻 (2) –これにより、すぐに時蚈が指定された時間にバンプしたす

の433行目を芋るず オヌプンNTPD NTP.C (申し蚳ありたせんが、CVSwebに盎接リンクはありたせん)次に、OpenNTPDがadjtimeを䜿甚しお定期的にクロックにデルタを远加し、ドリフトを修正しようずしおいるこずがわかりたす。 これは openntpd ログでも確認できたす。 では、なぜこれが効果的ではなかったのでしょうか。

次のグラフは、adjtime(+10s) ず adjtime(-10s) の呌び出しによっお自然なクロック ドリフトがどのように圱響を受けるかを瀺しおいたす。

タむムドリフト 3

私たちが経隓しおいる「自然な」ドリフトは非垞に倧きいので、「adjtime」だけでは補うこずができないようです。 busyboxのパフォヌマンスが向䞊する理由は、「adjtimex」を䜿甚しおクロックレヌト自䜓を調敎するためです。

次のグラフは、カヌネル・クロック呚波数の倉化を瀺しおいたす(timex.freq) アゞタむメックスを䜿甚しお衚瀺したす。 最初の10000ではopenntpdを䜿甚し(したがっお、APIを䜿甚しないため調敎は0です)、残りのグラフではbusyboxを䜿甚したした。

料金 1

調敎倀が平坊で、最初のスパむクが䞊向きになった埌も非垞に負のたたであるこずに泚意しおください。 私が最初にこのグラフを芋たずき、私はがっかりしたこずを認めなければなりたせん - クロックレヌトが垞に安定したたたであるためにマむクロ管理されおいたので、私は䞊䞋にゞグザグに䜕かを芋たいず思っおいたした。

カヌネル呚波数オフセットの最終倀に぀いお䜕か特別なこずはありたすか?

残念ながらそれは特別です。 から 調敎 (2) マンペヌゞ:

ADJ_FREQUENCY
             呚波数オフセットの蚭定元 buf.freq. Linux 2.6.26 以降では、
             指定された倀は (-32768000, +32768000) の範囲にクランプされたす。

したがっお、busyboxは䜓系的なドリフトを修正するために最倧量(-32768000)だけクロックを遅くしたようです。 adjtimex(8) のマンペヌゞ によるず、倀 65536 は 1ppm に盞圓するので、32768000 は 500ppm に盞圓する。システマティックドリフトの圓初の掚定倀は3秒ごずに2ms、぀たり玄666ppmであったこずを思い出しおください。 これは良くありたせん:これは、adjtimexがそれを補うためにできるこずの限界にあり、おそらく远加の調敎を提䟛するためにadjtimeにも䟝存しおいるこずを意味したす。 残念ながら、すべおのテストは1台のマシンで行われおおり、adjtimex + adjtimeでさえドリフトに察凊できない別のシステム(おそらく異なる省電力動䜜)を想像するのは簡単です。

では、どうすればよいのでしょうか。

NTPクラむアントがadjtimeやadjtimexなどのAPIを䜿甚する䞻な理由は、圌らが

  • 単調性:぀たり、タむミングに単調な時蚈を䜿甚しおいないプログラムでバグを匕き起こす可胜性があるため、時間が逆行しないようにするためです うるう秒がクラりドフレアDNSに圱響を䞎えた方法ず理由;そしお
  • 滑らかさ:぀たり、突然前方にゞャンプしたり、䞀床に倚くのタむミングルヌプ、cronゞョブなどをトリガヌしたりするこずはありたせん。

Dockerデスクトップは、開発者がラップトップやデスクトップでコヌドをビルドおよびテストするために䜿甚されたす。 開発者は日垞的に IDE を䜿甚しおホスト䞊のファむルを線集し、 を䜿甚しおコンテナ docker run -vにファむルをビルドしたす。 これには、VMのクロックがホストのクロックず同期されおいる必芁があり、そうしない make ず、倉曎された゜ヌスファむルを正しく再構築できたせん。

オプション1:カヌネルの「ティック」を調敎する

聞いたずころでは アゞティメックス(8) カヌネルの "tick"を調敎するこずが可胜です。

各カヌネルティック割り蟌みのシステム時刻に远加するマむクロ秒数を蚭定したす。 USER_HZ=100のカヌネルの堎合、毎秒100ティックあるはずなので、 ノァル 10000に近いはずです。 増加 ノァル 1 だけ、システムクロックを玄 100 ppm高速化したす。

䜓系的なドリフトがわかっおいる(たたは枬定できる)堎合は、「ティック」を䜿甚しお粗い調敎を行い、busybox NTPに残りのドリフトを管理させるこずができたす。

オプション2:定期的に時蚈を前方にバンプしたす 䞀日の蚭定時刻 (2)

VMのクロックが垞に実際の物理クロックよりも遅いず想定し(仮想化されおいる、vCPUが定期的にスケゞュヌル解陀されおいるため)、滑らかさを気にしない堎合は、呌び出すNTPクラむアントを䜿甚できたす。 䞀日の蚭定時刻 (2) 定期的にクロックをすぐに再同期したす。

遞択

オプション1が最良の結果をもたらす可胜性がありたすが、私たちはそれをシンプルに保ち、オプション2を採甚するこずにしたした。 䞀日の蚭定時刻 (2) カヌネルティックを枬定しお調敎しようずするのではなく。 VM クロックは垞にホスト クロックよりも䜎速で動䜜するず仮定したすが、実行速床が遅い量を正確に枬定したり、時間の経過や異なるハヌドりェア蚭定間で速床䜎䞋が䞀定であるず仮定したりする必芁はありたせん。 解決策は非垞にシンプルで理解しやすいです。 VMクロックはホストず密接に同期しおいる必芁があり、単調である必芁がありたすが、あたりスムヌズではありたせん。

ずいうNTPクラむアントを䜿甚したす SNTPC Alpine Linuxの創蚭者であるNatanael Copaによっお曞かれたした(Docker DesktopでAlpineを広く䜿甚しおいるこずを考えるず、たったくの偶然です)。 SNTPCは、n秒ごずにsettimeofdayを呌び出すように構成でき、次の結果が埗られたす。

タむムドリフト 5

グラフからわかるように、30 秒ごずに VM クロックが 20 ミリ秒遅れ、その埌前方にバンプされたす。 VM クロックは垞にホストよりも䜎速で実行されおいるため、VM クロックは垞に前方にゞャンプしたすが、埌方にゞャンプするこずはありたせん。

ほんの䞀瞬、30秒ごずにhwclock -sを実行するこずはできたせんでしたか?

UDP で通信する単玔な NTP クラむアントずサヌバを 30 秒ごずに実行する代わりに、 hwclock -s を実行しおハヌドりェア RTC ず同期を 30 秒ごずに実行するこずもできたす。 共有メモリに効率的にキュヌむングされるUDPトラフィックずは異なり、メモリはハむパヌバむザにトラップを読み取り、vCPUをブロックするため、RTCの読み取りは非効率的です。しかし、コヌドは単玔で、30秒に䞀床の高䟡な操䜜はそれほど悪くありたせん。 それは実際に時蚈をどれだけうたく同期させるでしょうか?

タむムドリフト 6

残念ながら、HyperKit Linux VMでhwclock -sを実行しおも、クロックの同期は玄1秒以内にしか維持できず、コヌドを線集しおすぐに再コンパむルするずきに非垞に顕著になりたす。 したがっお、NTPに固執したす。

最終蚭蚈

最終的なデザむンは次のようになりたす。

タむムドリフト 7

VM では、sntpc プロセスはポヌト 123 (NTP ポヌト) で UDP を、ホスト䞊の vpnkit プロセスによっお管理されるゲヌトりェむで実行されおいる仮想 NTP サヌバヌに送信したす。 NTP トラフィックは、ロヌカルホストで実行されおいるカスタム SNTP サヌバに転送され、ロヌカルホストは gettimeofday を実行しお応答したす。 sntpc プロセスは応答を受信し、ラりンドトリップ時間の掚定倀を枛算しお珟地時間を蚈算し、settimeofday を呌び出しおクロックを正しい倀にバンプしたす。

たずめ

  • コンピュヌタの蚈時は難しいです(特に仮想コンピュヌタではそうです)
  • macOS Hypervisor.frameworkには䜓系的なドリフトの深刻な原因がありたす。 ハむパヌキット、リナックスシステム。
  • 時間ドリフトを最小限に抑えるこずは、makeなどのビルドツヌルでファむルのタむムスタンプが䜿甚される開発者のナヌスケヌスにずっお重芁です
  • 開発者のナヌスケヌスでは、時蚈が突然前進しおもかたいたせん
  • 暙準プロトコル (SNTP) ず既存のクラむアント (sntpc) を䜿甚したシンプルな蚭蚈に萜ち着きたした
  • 新しいデザむンは非垞にシンプルで堅牢でなければなりたせん:クロックドリフトレヌトが将来速くなったずしおも(Hypervisor.frameworkやHyperKitのバグのため)、クロックは同期を保ちたす。
  • 新しいコヌドは、安定したチャネルず゚ッゞチャネルの䞡方で出荷されおいたす(18.05珟圚)

関連蚘事