DockerコンテナがフリーズするLinuxカーネル+AUFS起因の問題

2016年3月25日金曜日

.NET Core環境用の操作ツールであるdotnetコマンドをDocker環境上で動作させようと試みたものの、.NET Coreのdotnetコマンドを試そうとしたら試せなかったの通りハングアップしました。一旦issueを立てて置いていたのですが、その後の話を書きます*1
[*1] 少々タイトルの範囲が大きいですね。Docker環境での.NET Core公式イメージ実行時に発生した問題ですが、もっと一般的な問題だとわかったのでこうしました。

現象の概要(おさらい)

  • microsoft/dotnet:latestを実行すると、mkdircdは通るがdotnet newという.NET Core用のプログラムを実行した時点でコンテナがフリーズする
  • フリーズしたコンテナはdocker kill ...で強制終了できない
  • topコマンドでホストマシン状態を見ると、DockerデーモンがひたすらCPUを食いつぶしている
  • 結局Dockerデーモンを再起動するか、ホストマシンを丸ごと再起動するしかなくなる
この現象はMac OS X上に作ったBoot2Docker環境(VMWare Fusionベース)でも、手持ちのDebianサーバ上に作ったDocker環境でも同じように発生しました。
これを受けて前回は、`latest` image freezes immediately after running `dotnet new` #26というissueを立てましたよという話で終えていました。

その後

issueを立てた時点で一番疑っていたのはDocker自体です。ホストとのコミュニケーションを多用するメモリ管理かI/Oのどこかでミスっているのだろうという読みでした。
その後の手元テストにより、この問題はBoot2Docker(b2d)を新しいバージョン(1.10系)へ上げると発生しなくなることがわかっていました。このためひとまずb2d環境でdotnetコマンド群のテストをおこなっておけば良いか、と考えていました。
しかしissueに対してDocker 1.9.1 hanging at build step "Setting up ca-certificates-java" #18180関連の問題では?というコメントをもらいました。

本当の原因(Dockerコミュニティの阿鼻叫喚)

このissueは私が見た時点でクローズ済みでしたが、なかなか壮絶で壮大な戦闘録が残っていました。
結論をまとめると、これはLinuxカーネル内のmm/filemap.cに対する改善(sendfileシステムコールを利用したデータの書き込み処理で、停止シグナルを受けて途中終了できるようにしたもの)と、Dockerが利用するレイヤードファイルシステムのひとつであるAUFSのデータ書き込み部分の一部に従来のカーネル実装へ依存するコードが存在したことで発生した問題でした。2016年2月末時点で主要ディストリビューションでの対応は終わっており、OSアップデートにより解消します。
この問題の解決までを時系列でざっと見てみます。
  • 最初の問題報告が11/24
  • Linuxカーネル4.1.13での変更が関わっているらしいと判明したのが12/4
  • --virtualbox-cpu-countなどによりDockerデーモン稼働VMへ割り当てるCPU数を増やすワークアラウンドが発見されたのが12/5
    • その後、これはあくまでも発生頻度を下げるだけで根本対応とならないこともコメントにて指摘されています
    • この少し前から、Boot2Dockerで利用しているAUFSのバージョン依存の問題では、という推測(ほぼ確信)がいくつか出てきました
      • 「だっから!AUFSなんて!使うのやめろ!!」勢もぽちぽちと出ていました
  • 時間経過と共に影響を受ける人が増えた分か、しばらく再現報告や+1が続き(そう、当時GitHubにはリアクション機能がなかったのです)、12/8にはDockerチームメンバーから「新情報無いのにコメントするのやめーや」というお達しがありつつもしばし事態は硬直
  • 事態は12/21にAUFS内で動作停止に至る具体的な箇所が特定されたのをきっかけに再び動き始め
  • ワークアラウンド版の実装、カーネルバージョンを下げる緊急回避策の確立、AUFSの新版を取り込んだカーネルビルド策での対応を経て
  • b2dを含む各ディストリで徐々に新版AUFSの取り込み作業がおこなわれ
  • Debianが2/29に3.16.7-ckt20-1+deb8u4を出したことで主要ディストリでの問題対応が収束
3ヶ月に及ぶ、長く苦しい戦いだったようです。
まず、https://github.com/docker/docker/issues/18180#issuecomment-161843456にてAUFSとmm/filemap.cへの変更によるものだと突き止めているのすごいなと思いました(小並感)。
そしてこの問題へ取り組み、さっそうとトラブルシュートしつつ更に広い視点からの解決策を模索してAUFSへ潜って本家とやりとりをし、更に主要ディストリ(UbuntuDebian)へfix取り込みの働きかけをおこない、それらの状況をトラッキングしissueへ追記していくAkihiroSuda氏まじぱねぇという感想でした。手練というか、なんかNINJA感がありました。
そういうわけで、私が試した環境2つがたまたま該当してDocker本体の問題と思い込んだのが敗因(?)でしたね。

その後、dotnetコマンドは

普通にうごきました。
立てたissueも先ほどcloseされました。

0 件のコメント:

コメントを投稿