1つのバグの物語

昨日、非常に微妙で特定のバグに対処する必要がありました。 このバグは、別のバグにつまずいた機能であることが判明しました。 問題を研究している間、私はDebianのいくつかの機能を学び、4時間を捨てて多くの経験を積むことを余儀なくされました。

少し洗練された外観で、イベントの年表を提供します。システム管理者がどのように機能するかを誰かが興味を持ってくれることを願っています。

背景

複数の同一サーバーから実験台を展開する際に、多数のホストのコードを手動で更新することなく、必要なバージョンのアプリケーションを実行できるようにしたかったのです。 NFSボールを使用して必要なプログラムを実行することが決定されました。 アプリケーションは内部使用のみで、使い捨てで、特定のタスク用に作成されました。 ボールは起動時に/ optディレクトリにマウントされ、そこからのアプリケーションはrc.localスクリプトを使用して起動されました。 頻繁にコードを変更する実験的なスタンドについて話していたため、正直な開発者(パッケージ、リポジトリ、更新、init.dスクリプト)をプレイするのは面倒でした。 Debian Squeezeの下ですべてが起こりました。

ボールは/ etc / fstabに登録され、rc.localで必要なテストが開始されました。 すべてが完了したように思えます。

...そして、私はミスティックに出会いました。 アプリケーションは5回起動し、「曲がったアプリケーション」のバージョンはほとんどすぐにマークされました-同様に、他の実行可能ファイルは時々起動しませんでした。 また、/ optで。 他のディレクトリから正常に動作しました。 同時に、rc.localを手で起動します-100%すべてが問題ありません。 起動時に、5回のうち、正常に起動できます。

最初は、この問題を深刻な問題とは思わず、簡単に解決しようとしました。 問題は/ optにのみ現れたため、コマンドls -a1 / opt> / var / log / lsをrc.localに追加しました。 予想どおり、rc.localの実行時に/ optには2つのファイル(ポイントと2つのポイント)しかありませんでした。 つまり、NFSボールはマウントされていません。 時々。 そして時々マウントされました。


明らかに、「ログインした後、時々ボールがマウントされているのを見たという事実にもかかわらず」「マウントされた、時にはマウントされていない」という状況は、ボールをマウントしてからrc.localを開始する間の競合状態を意味します。

決め始める


仮説:Debian Squeeze(およびそれはすべてスクイーズ)がinitスクリプトの新しい依存システムに切り替わりました。 /etc/init.d/rc.local(/etc/rc.localを呼び出すinitスクリプト)に$ mountnfsに直接依存することを書きました(つまり、mountnfsの後にスクリプトを実行する必要がありました)。

助けた。 ばかげた偶然により、2回連続して再起動することさえあります。 その後、すべてがそのまま続けられました-時には成功し、より頻繁にはそうではありませんでした。

nfs-ballsがSランレベルにマウントされ、rc.localが2番目のrunlelvel(つまり、後のS)で起動されるため、驚きました。

秘跡行ls -a1 / opt >> / var / log / lsを/etc/init.d/rcスクリプトに追加しました-このスクリプトは/etc/init.dの他のすべてのスクリプトを実行します。 正確には、ディレクトリ/etc/rcS.d、/etc/rc2.dなどを読み取り、最初の文字(KまたはS)に応じて、stopまたはstartパラメーターを使用してそこからスクリプトを開始します。 これらのディレクトリ自体には、S10serviceと呼ばれる/etc/init.d内のスクリプトへのシンボリックリンクが含まれています。 S(またはK)は開始(強制終了)を示し、数字を使用すると、スクリプトの起動順序を合理化できます。

そのため、/ etc / init.d / rcの最後に、次のlsを登録しました。 再起動しました。 ログに3セットのls出力が見つかりました(1つはシャットダウン時に実行され、2つは開始時に実行されました-これらはレベル「S」および「2」です)。 驚いたことに、最後の2つは空でした。 つまり、ランレベル2に切り替えるためのスクリプトが終了した時点では、ボールはまだマウントされていません! つまり、スクリプトの依存関係システム、起動順序など。 は何の役割も果たしません-ロードの終了時にボールはありません。

/etc/init.d/rcのフーリガンを削除し、stop-bootlogdと呼ばれる最後のブートスクリプトに同様の行を追加しました(/etc/init.d/rc2.dにS99stop-bootlogdという名前で表示されます。つまり、最後に起動します。 )

もちろん、ダウンロードした後、私はきれいなディレクトリを得た。 カタログがマウントされることがあることを念頭に置いて、これはおそらく競合状態であるため、lsの前にクレイジースリープ360を追加しました。

6分のお茶を飲んだ後、「非常に遅い」ダウンロードの最後にカタログがまだマウントされていることがわかりました。 その後、時間を短縮し始めました:60、45、30、20、10、5秒-ファイルは安定していました。 さらに、ファイルはスリープ1でさえもありましたが、削除されたときはほとんどありませんでした。 (この時点で20回再起動しましたが、信頼できる結果が得られました-10分の10のスリープ1がマウントされたディレクトリを与え、10分の8のスリープなしのリブートが空のディレクトリを与えました。つまり、競合状態は非常に薄かったです。

いくつかのフォーラム、ubunts(はい、主にnfs、rc.local、bootの分野でさまざまなキーワードを検索するのに15分費やしました)で、この問題が議論されました。 rc.localファイル自体)。

ますます深く

このような不明瞭さをプラグインするシャーマニックな方法による内部不満がなければ、これで落ち着くことができます。

$ mountnfs依存関係を登録したときに、/ etc / init.d / mountnfs.shファイルを開いたことを思い出しました(Provides行の内容をコピーするため)。 そして説明がありました:
 #簡単な説明:ネットワークファイルシステムがマウントされるのを待ちます
 #説明:ネットワークファイルシステムは
 #バックグラウンドで/etc/network/if-up.d/mountnfs
 #インターフェイスが起動されたとき。 このスクリプトは待つ
 #続行する前にそれらをマウントします。



この短い説明で私にとって多くの重要な発見がありました:
1)NFSはバックグラウンドで、非同期に、つまりメインロードと並行してマウントされます。 つまり、NFSマウントプロセスとブートプロセスの間で競合状態が発生します。
2)このスクリプトは、実行時にすべてのNFSボールをマウントする必要があります(「S」ランレベルの終わり近くに呼び出されました)。 そして彼はしません。

次に、mount.nfsマニュアルに目を向けると、次のように書かれています-bgオプションはバックグラウンドでマウントします(私が知る限り、この機能はsqueezeに登場しました。lennyマニュアルには何も記載されていないため)。同期モード。

明らかな解決策は、fgを/ etc / fstabのNFSマウントオプションのリストに追加することです。 ビンゴ? 再起動...いいえ。 うまくいきませんでした。

この瞬間、私はすべてを送り、睡眠5を守り、入浴しないようにしました。時間が真夜中になり、ささいな問題が突然、ほぼ科学的な研究になりました。 しかし、スポーツへの関心は依然として根強く残っています。

マインドマップを見て(この時点ですでに問題を「問題」として認識し、混oticとしたメモからマインドマップに切り替えていました)、mountnfs.shの説明に目を向け、「mount by /etc/network/if-up.d/mountnfs」というフレーズに注目しました「。

このスクリプトに入りました。

わずかなコメントがありました:
 #「yes =」の代わりに「no!=」を使用して、非同期nfsのマウントが確実に行われるようにする
 #/ etc / default / rcSに値がなくてもデフォルト


ファイル/ etc / default / rcSがありますか? (debianでは、init.dのスクリプト設定は/ etc / defaultに保存されますが、ランレベルの設定がそこに保存されないことはいくぶん予想外でした)。

このファイルにはいくつかの行がありましたが、1行を除いて興味深いものはありません。
これらの変数については、rcS(5)のマニュアルページを参照してください。


マニュアルがあります!

そしてそこにオプションの説明があります:

非同期
 ネットワークの非同期マウントを無効にするには、これを「no」に設定します
 ネットワークインターフェイスがマウントされているときにドライブし、代わりに
 マシンの起動時に一度だけ。 デフォルトは「yes」です。 それ
 ルートファイルシステムを持つマシンでこれを無効にするのに便利です
 そのようなセットアップでifupdownからのifupが適切に機能するまでNFSで。


ビンゴ ビンゴ! ビンゴ! バンザイ / etc / default / rcSファイルに「ASYNCMOUNTNFS = no」という行を喜んで書き込み、再起動します...

...まったく同じ問題を繰り返します。

/etc/network/if-up.d/mountnfsファイルに戻り、ASYNCMOUNTNFSオプションが「no」の場合に実行される関数をすぐに見つけました。
 exit_unless_last_interface(){
     grep "^ [:space:] * auto" / etc / network / interfaces |  \
         sed -e 's / [\ t] * auto [\ t] * //; s / [\ t] * $ //; s / [\ t] / \ n / g' |  \
        読みながら する
         if [`grep -c $ i / etc / network / run / ifstate` -eq" 0 "]; それから
             msg = "if-up.d / mountnfs [$ IFACE]:NFSマウントを行う前にインターフェイス$ iを待機しています"
             log_warning_msg "$ msg"
             #whileサブシェルのため、これを変数として渡すことはできません
             mkdir / var / run / network / mountnfs_earlyexit 2> / dev / null
         fi
    やった
     if [-d / var / run / network / mountnfs_earlyexit]; それから
         rmdir / var / run / network / mountnfs_earlyexit 2> / dev / null
        出口0
     fi
 }

2行目に注意してください。 グレップ。 そして、私たちは何を食べますか? 右、「auto」の行。

停止しますが、debianインストーラー...はい、そうです、自動オプション(/ etc / network / interfacesファイル内)の代わりに、ホットプラグオプションを使用して、外出先でインターフェースを有効/無効にすることができます...

解決策


言い換えると、eth0インターフェースはデフォルトのインストールではautoに書き込まれず、ホットプラグに書き込まれるため、スクリプトはこのインターフェースがマウントの準備ができるのを待たず、nfsとrc.localマウントプロセスの間ではなく、ネットワークの準備状態の間で競合状態を取得しますインターフェースと取り付けプロセス。 永続的なNFSが最初に失敗した場合、パケットを再送信することは明らかです。 しかし、彼はすぐにはそれを行いません...それは光学に関するものであると考えて-モジュールがSFP +を検出し、初期化され、そこに物理リンクが表示されるまで...実際には、目で見ることができます-プロセスは非常に速くなく、状況によってはスリープ1そして十分ではありません...

そのため、インターフェイスを/ etc / network / interfaceにautoに登録すると、過負荷になり、歓声が上がり、ボールは時間通りに表示されます。 もう一回、もう一回、もう一回。 12のうち12。問題は解決されました。

rc.localを修復しますが、すべてが機能します。 午前1時半、警備員が門を閉め、私を車に乗せるために約10分間ノックしなければなりませんでした。

しかし、それは価値がありました。

道徳


道徳1:魔法と神は存在しません。 奇妙な振る舞いに出会った場合、彼は常に合理的な説明を持ち、おそらく不合理な動機(バグ)があります。

道徳2:問題の真の原因を発見した後、科学的な突刺しのモデルに基づいて構築するよりも信頼性の高い回避策を考え出すことができます(うまくいくようです)。

道徳3:問題の調査中に、システムの次のコンポーネントがどのように機能するかを、どういうわけか詳細に研究します。 このような知識から、職業における真に深い知識が形成されます。 私自身は、NFSボールが/ etc / fstabから正確にマウントされる方法を調べ、/ etc / default / rcSファイルが存在することを知り、debianで使用されているランレベルの正確なリストを明確にしました(それらが何であるか、どのように使用しますが、どのレベルレベルが続くかを正確に汚すような方法で-いいえ-今-はい)。

教訓4: バグレポート送信し、最も正しい方法でこの問題を解決して、問題を解決することもできます 。 さらに、問題をより適切に(そして実際に)記述すればするほど、解決される可能性が高くなります。 特定の関数のエラーステートメントは、「rc.localの実行時にNFSがマウントされていない」というフレーズよりも、なぜ、どのような状況での説明がはるかに優れているかに同意します。

Source: https://habr.com/ru/post/J113482/


All Articles