次のCRIUリリースの前に

今日は、 CRIUプロジェクト(主にユーザー空間でのチェックポイント/復元)に関する一連の記事を続けたいと思います。 このプロジェクトは1年を少し過ぎており、機能面では、すでにOpenVZの同様の機能に近づいています。

記事の最初の部分では、過去数か月間にCRIUに登場した新しい機能について説明します。 第2部では、開発プロセスを改善するための新しいテクノロジーの導入における経験について説明します。



新機能



メモリスナップショットと反復移行



次のリリースのキラー機能は、プロセスの状態の反復的なスナップショットであり、その結果、反復的な移行です。 どちらの場合も、後続の各反復で、前回から変更されたメモリ部分のみが保存されます。 前者の場合、これによりディスク上のデータの時間と量が削減されます。 移行の場合、メモリコピーの最初の繰り返しでプロセスがフリーズしないため、システムのダウンタイムが大幅に削減されます。



主な問題は、前回から変更されたメモリ領域を追跡するメカニズムの実装でした。 とても簡単に機能します。 プロセスのすべての既存ページを「クリーン」としてマークすることができます。 この場合、カーネルはそれらへの書き込みを禁止し、誰かがそのようなページに書き込もうとすると、例外(pagefault)が発生します。 その結果、カーネルはページの書き込み許可を返し、ダーティ(PME_SOFT_DIRTY)としてマークします。 すべてのページプロパティは、ファイル/ proc / PID / pagemap pagemap2から読み取ることができます。 はい、ファイルの2番目のバージョンを作成する必要がありました。最初のフォーマットのビットがなくなったためです。実際、それらの一部は常にゼロです。 2番目のバージョンでリリースされたのは彼らでした。



ディスクレス移行



文字通り、CRIUの存在の最初の数ヶ月で、ドイツの研究所からの友人が、ディスクを使用せずに移行を行うためのリクエストで私たちに近づきました。 多くの場合、マシン間のデータ転送速度は、データをディスクに書き込む速度よりも数倍速いため、彼の要求は妥当であるように思われました。 1か月未満前に、この機能が追加されました。 次のように機能します。 リモートエンドでは、サーバーが起動し、そこにすべてのデータが転送され、回復手順も開始します。



ここでの特徴は、メモリの不必要なコピーを最大限に回避しようとしたことです。 カーネルは、これらの目的のための多くのシステムコール(vmsplic、spliceなど)を表します。 たとえば、単一のコピーを作成せずにプロセスメモリをパイプ経由で送信できます。



信号



信号の保存と復元は大きなタスクではないように思えますが、1か月以上にわたって行われています。 オブジェクトを保存または復元するための対応するインターフェイスがない場合は、CRIUだけでなく有用な新しいインターフェイスを考案しようとします。 このアプローチには、支持者と反対者がいます。



今回も同じでした。 別のコマンドをptrace(デバッガーが使用するインターフェイス)に追加することもできますが、そのアプリケーションは狭すぎるように見えましたが、Linuxでは、非常に似たようなことを行うsignalfdシステムコールが既にありました。 実装が開始されると、問題が発生し始めました。 通常の生活では、プロセスはどのキューからシグナルを受信するかを知りません(2つのキュー、1つはプロセス全体用、もう1つはスレッドごと)。しかし、CRIUの場合、これは重要です。 あるスレッドから別のスレッドにシグナルを復元することはできません。 ダンププロセスは破壊的ではないので、シグナルをスパイしたいのですが、キューからそれらを取り出したくありませんでした。 これらの問題はどちらも深刻に見えませんでした。他のオブジェクトについても同様のことを既に行いました。 3番目の問題は、ユーザー空間のsiginfoプレゼンテーション形式のいずれも、回復に十分な完全な情報を提供しなかったことです。 以前のカーネルには2つの形式がありました。 最初のものはシグナルハンドラで取得するものであり、カーネルに保存されているものに最も近いものですが、例外が1つあります-オブジェクトタイプがありません。 カーネルは、ユーザー空間に渡す前に型を切り捨てます。 2番目の形式は、signalfdが返すものです。 その中で、間接的な兆候によって、メッセージのタイプを判別できますが、場合によっては、情報の一部が単に欠落しています。 この問題の解決策は明白です-カーネルが保存するのと同じ形式でsiginfoを返す3番目の形式が必要です。



4番目の重要な問題は、signalfd記述子が特定のプロセスにバインドされていないことです。一方、ファイル記述子のセマンティクスでは、fork()の後にfork前と同じオブジェクトを指すと想定されます。 この矛盾はsignalfdの最初の開発者によって追加されたものであり、後方互換性を維持するために変更することはできません。 現在のインターフェースでは、この矛盾は何にも影響しませんが、signalfdの機能を拡張することに決めたとき、問題が始まりました。 誰もができる限り他の記述子と同様にsignalfd記述子を使用したいと考えましたが、上記の矛盾のために、合理的な解決策を見つけることはできませんでした。 約5〜7回試行した結果、ptraceで初期バージョンを作成する必要がありました。



OpenVZイメージからのコンバーター



以前の記事で、CRIUはOpenVZの既存のチェックポイント設定メカニズムを置き換える必要があり、もちろん下位互換性を維持する必要があると既に述べました。 OpenVZチームが現在、カーネルの新しい安定バージョンに取り組んでいるという秘密をお伝えします。 通常どおり、RHELの次のバージョン(7)のカーネルに基づいています。 このプロセスとともに、OpenVZイメージからCRIUイメージへのコンバーターが開発されています。 このタスクは、時間がかかるほど難しくありません。



Netlinkソケット、TCP接続の移行、vdso変換



また、vDSOライブラリの復元作業も現在進行中です。 主な難点は、ライブラリがカーネルによって提供され、カーネルとの相互作用のメカニズムが固定されていないことです。 このライブラリは動的にロードされるため、関数のアドレスはカーネルごとに変更できます。 最も簡単な方法は、古いライブラリのように見えるが、新しいライブラリから関数を呼び出すプロキシを作成することだと判断しました。 このパスに沿っても、すべてがスムーズであるとは限りません。 たとえば、プロセスがライブラリコードに入り、信号処理によって中断される可能性があります。 シグナルハンドラーでは、プロセスは何でも実行でき、一般的な場合にプロセスがどのように到達したかを理解することは不可能です。 LKMLでは、コードが変更されない「安定した」vdsoライブラリを作成することさえ考えていました。 より良いソリューションを思い付くまで、プロキシを使用し、vdsoコードでプロセスが中断されないようにします。



最近まで、すべてのTCP接続はグローバルカウンターを使用して、パケットのタイムスタンプ(TCPタイムスタンプ)を設定していました。 このカウンターは、リブートするたびにリセットされます。 このスキームにより、あるマシンから別のマシンへのTCP接続の移行が防止されました。 次のLinuxカーネルは、各ソケットのオフセットを個別に設定できるため、CRIUは接続を移行できます。

別の大きくない機能は、Netlinkソケットのサポートでした。 / proc / net / netlinkファイルで表される現在のインターフェイスでは十分な情報が提供されないため、netlinkソケットのソケット診断を拡張する必要がありました。



技術



継続的インテグレーション



CRIU開発プロセスは、Linuxカーネル開発のイメージと類似性に基づいています。 私たちが従う基本原則:



最初の原則は、変更を表示してメインリポジトリに配置する人によって制御されます。 2番目の原理は、実験的にのみ検証できます。 通常、このプロセスは「継続的統合」と呼ばれ、自動化にはいくつかのソリューションがあります。 現在人気のあるJenkinsプロジェクトを選択しました。 インストールと構成にそれほど時間はかかりません。 過去2か月の結果によれば、努力が無駄に費やされたわけではないと言うことができます。 いいえ、ビルドを壊すことはほとんどありませんが、多数のユニットテストの実行により、リソースの競合または状況の組み合わせに関連するいくつかのバグを発見しました。



すべての構成(2つのアーキテクチャ、2つのカーネルバージョン)で、すべてのテスト(ユニットだけでなく)、すべてのバリエーション(後方互換性チェック、非破壊ダンプのチェック)の実行を開始すると、テクノロジは最大限に機能します。 これは、開発者が自分で行う作業であり、費用がかかります。



静的コードアナライザー



私は静的コードアナライザーに常に懐疑的でした。何らかの理由で、静的コードアナライザーはあまり利点をもたらさないが、時間を節約するのに役立つという意見がありました。 仕事の途中で、何もしたくなくて気を散らさなければならないときが時々起こります。 そのようなとき、私は何か新しいことに挑戦したいです。 これが、Jenkinsの使用を開始した方法であり、clang-analyzerを介してコードを実行した方法です。 これは超自然的な結果を得たと言うことではありませんが、エラー処理の方法に関するいくつかのバグを指摘しました。



結果に触発されて、別の開発者がscan.coverity.comにプロジェクトを登録しました 。 ここでは、エンジンはclang-analyzerよりもやや強力ですが、「誤検知」の数が多くなっています。 静的アナライザーに関する私の意見はこれです。利点はありますが、優先順位はそれほど高くありません。 プロジェクトがテストで十分にカバーされていて、バグが見つからない場合は、静的アナライザーに時間をかけることができます。



参照資料



lwn.net/Articles/546966

lwn.net/Articles/531939

habrahabr.ru/post/152903

habrahabr.ru/post/148413

jenkins-ci.org

en.wikipedia.org/wiki/CRIU

criu.org



All Articles