開発者向けの debuginfod サーバーのデプロイ

先の記事では、Aaron Mereyが新しい elfutils debuginfo-server デーモンを紹介しました。このソフトウェアが elfutils 0.178 に統合され、かつ distro に含まれようとしています。あなた自身およびチームのために、なぜこのようなサービスを設定するべきか、どうやって設定するかを検討する時期になっています。

debuginfodが存在するのは、バイナリー群に関連するELF または DWARF デバッグ情報と、ソースコードを配布するためであることを思い出してください。gdbなどのデバッガー、perf 、systemtapなどのトレーサまたはプローブツール、binutilspahole のようなバイナリ解析ツール、dyninstのようなバイナリー書き換えライブラリーを実行する必要がある場合、最終的にバイナリーに一致する debuginfo が必要です。これらのツールの debuginfod クライアントサポートにより、透過的にすぐにそれらのデータを入手することができます。プログラムを停止し、rootになり、適切な yum debuginfo-install コマンドをすべて実行してから再度試す必要がなくなります。debuginfo が利用できることで、いつでもどこでもデバッグを行えます。

ここでは、「なぜ」について説明しました。 次は、「どうやって」を見ていきましょう。

基本的なサーバー操作

クライアントがコンテンツをダウンロードできるようにするには  、それぞれで必要になる可能性があるdebuginfoを提供する1台以上の debuginfod サーバーが必要になり ます。理想的には、これらの ビルドアーティファクト のコピーを保持しているマシンからできるだけ近くで debuginfod サーバーを実行するべきです。

独自のソフトウェアを構築している場合には、ビルドおよびソースツリーがどこかに存在しているはずです。ビルドマシンで debuginfodを実行するには、以下を実行します。

$ debuginfod -F /path/to/build/tree1 /path/to/build/tree2

こうすると、debuginfodは 、これらのツリーをすべて定期的に再スキャンし、実行可能ファイルおよびデバッグデータすべてと、そこから参照されるソースファイルをすべて利用可能にします。コードをリビルドすると、インデックスはすぐに更新されます( -t パラメーターを参照)。

独自のソフトウェアを RPM に全て構築する場合は、RPM ファイルが含まれる親 ディレクトリーで debuginfod のコピーを実行します。

$ debuginfod -R /path/to/rpm/tree1 /path/to/rpm/tree2

次に、debuginfod は 、 これらのツリーをすべて定期的に再スキャンし、RPM内のすべての実行可能ファイルとデバッグファイルを利用可能にします。このツールは、-debuginfo および -debugsource ファイルを自動的に対応づけします。

これらの引数を一緒に追加することで、1つの debuginfod プロセスで自然に実行できます。

Linux ディストリビューションの一部となるソフトウェアをデバッグする必要がある場合は、もうすこし話が難しくなります。ディストリビューションが公開 debuginfod サーバーを設定するようになるまでは、私達は自前でやる必要があります。幸いなことに、これはそれほど難しくありません。結局のところ必要なのは、ディストリビューションの関連パッケージがインストールされているマシン、または単にダウンロードされたマシンだけです。

$ mkdir distro-rpms ; cd distro-rpms
$ debuginfod -R .

必要に応じて繰り返します。

$ yumdownloader PACKAGE-N-V-R
$ yumdownloader --debuginfo PACKAGE-N-V-R  

ディスクに納まる限りでワイルドカードを使用します。

Red Hat Satellite サーバーを社内で実行している場合、または distro パッケージの非公式に管理されたミラーを実行している場合は、そのシステムのパッケージアーカイブ に対して debuginfodを実行できます。インストール(rpm -i)、フィルタリング、何かしらの方法での再構成は不要です。debuginfod のコピーにディレクトリーをスキャンさせるだけです。

クライアント設定

これで1つまたは複数のサーバーを実行していて、debuginfoの同じツリーまたは異なるツリーをスキャンできる状態になりました。クライアントと通信させる方法は?シンプルで明白な解決法は、知っているすべてのサーバーを列挙することです。

$ export DEBUGINFOD_URLS="http://host1:8002/ http://host2:8002/ ...."                                   
$ gdb ... etc.

ルックアップのたびに、クライアントはすべてのサーバーにクエリを一度に送信し、要求された情報を報告する最初のサーバーが「勝ちます」。

この戦術は機能しますが、いくつかの欠点があります。まず、このURLのリストをすべてのクライアントに伝達する必要があります。第二に、コンテンツを集中的にキャッシュする機会がないため、各クライアントは(HTTPの用語で)オリジンサーバーからコンテンツを個別にダウンロードする必要があります。この問題には単純な対策があります:フェデレーションです。

debuginfod サーバーはクライアントとしても機能します。サーバーがローカルインデックスではクエリーに応答できず、アップストリームの $DEBUGINFO_URLSのリストが設定されている場合は、要求はアップストリームサーバーに転送されます。その後、正しい応答の結果をキャッシュして、リレーします。同じオブジェクトへの次の要求は、キャッシュ(キャッシュ保持制約の対象)から提供されます。

この動作により、 debuginfod サーバーのフェデレーション階層を設定できます。 そうすることで、設定ファイルの集中化とキャッシュの局所化が可能になります。次に、ビルドシステムごとの debuginfod のそれぞれは、その上位ピアのリストで構成できます。ローカルディレクトリーをまったくスキャンしない よう にして、純粋にアップストリームのリレーとして機能するように debuginfod サーバーを実行することも可能になります。フェデレーションがツリーであるか、または有向非巡回グラフであることを確認します。サイクルはまずいです。

サーバー管理

これで1台以上のサーバーが実行され、それらに依存するクライアントができました。それらをうまく実行させ続けるにはどうしたらいいですか?現実的な問題がいくつかあります。

これは、インデックス中およびインデックス後におけるリソースの使用です。最初の debuginfod インデックス作成 はCPU およびストレージに集中しています。現在は、すべての ELF または DWARF ファイルを解析する必要があります。インデックスデータベースは密にフォーマットされた SQLite ファイルですが、通常の圧縮 RPM のサイズの約 1%まで拡大する可能性があります。この側面が問題でない場合は、この次の段落について心配する必要はありません。

アーカイブのセットが非常に大きい場合にインデックスの時間とスペースが過大な場合は、ファイルフィルターつきで debuginfodを実行すると便利です。-I オプションおよび -X オプションを使用すると、ファイル名に正規表現を指定して、含めるか除外を指定できます。たとえば、アーカイブには複数の異なる様々なアーキテクチャーが混在している、またはファイルの異なる主要な distro バージョンがあり、サブセットのみを追跡したいとします。これらのオプションを使用すると、debuginfod が 、名前がパターンに一致しないファイルをスキップできます。

$ debuginfod -I '\.el[78]\.x86_64' -X 'python' -R /path 

サーバーに多数のコアがある場合、debuginfodはコマンドラインで指定されたパスごとに1つまたは2つのスレッドを開始するため、スキャンパスを多数のサブパスに分割することを検討してください。実際の同時実行数は慎重に管理されるため、パスリストの数を多く指定する際に心配はいりません。したがって、

次のようにする代わりに

$ debuginfod -R /path

以下のようにします。

$ debuginfod -R /path/*

ELF、DWARF、またはRPMアーカイブが非常に大きい場合は、各ストレージサーバーの近くで実行されるdebuginfodの複数のコピー間でスキャンタスクを分割することを検討できます。ワイルドカードに加えて包含パスと除外パスを使用して、各debuginfodプロセスにデータのサブセットのみを与えることができます。debuginfodサーバーがどのように連携できるかについては、上記で説明しました。この機能を使用して、何もスキャンせず、安定したシャード全体にクエリを委任する単一のフロントエンドdebuginfodを作成します。

手作業でネットワークサーバーをシェルで実行することは、昔ながらの方法で遊ぶのには適しています。ただし、深刻な展開の場合は、debuginfodサーバーをスーパーバイザシステムで管理する必要があります。debuginfodはプレーンシェルで非常にうまく動作するため、systemdサービスまたはコンテナ内で動作します。elfutilsにはサンプルの systemd 設定が含まれていて、さらにRed Hat OpenShift内で debuginfod を実行したり、別のオーケストレーションサービス上で実行したりすることができるように dockerfile またはコンテナーイメージを公開する計画があります。

サーバーが実行されたら、それを監視して実行し続けることをお勧めします。テキストログは標準出力とエラーストリームに送られ、systemdジャーナルやOpenShiftなどのツールが出力を収集できます。さらに詳細なトレースを生成するには、-v冗長オプションを追加します。このテキストデータに加えて、debuginfod/metrics Web API URLを提供します。これは、Prometheusエクスポート形式の定量的データソースです。この URL は、サーバースレッドの現在の状態かについての内部統計を提供します。 さまざまな種類の異常を検出するためにアラートシステムまたは他のプログラムを接続するのは難しくありません。

debuginfodサービスがインターネットや公衆などの信頼境界を越えて提供されるとすぐに、セキュリティが問題になります。「man」ページには、このようなサービスがユーザーとサービスオペレーターの両方にとって安全であるために必要な対策について、非常に多くの注意が示されています。非常に難しいわけではありませんが、HAProxyを使用するなどの、TLS暗号化や負荷制御などの通常のHTTPフロントエンド保護は必須です。また、debuginfodのインデックス作成を信頼できる(敵意のない)バイナリに制限することも重要です。

今後の展望

将来はどうなりますか?Debianフォーマットパッケージをすぐにサポートし、そのエコシステムの人々も最大限に活用できるようにしたいです。Linuxディストリビューションがユーザー向けの公開debuginfodサービスを運用するのを喜んで支援し、すでにこのサービスをFedora kojiでプロトタイプ化しています。また、より管理しやすくする機能や、おそらくソースバージョン管理システムとの統合も想定しています。 アーリーアダプター(あなたです!)からの提案も歓迎します。

この記事が、あなたのモチベーションを高め、独自のdebuginfodサービスをセットアップするのに役立つことを願っています。elfutils-devel@sourceware.orgメーリングリストでご連絡ください。

* 各記事は著者の見解によるものでありその所属組織を代表する公式なものではありません。その内容については非公式見解を含みます。