はじめに: まったく未知の問題や未知のシステム コンポーネントのトラブルシューティングは、多くのエンジニアにとって仕事の最大の喜びですが、もちろん大きな課題でもあります。今日は、Alibaba のアフターセールス技術専門家 Shengdong が Kubernetes クラスターの問題の例を紹介します。この問題は広範囲に影響を及ぼし、いつか遭遇する可能性があります。さらに重要なのは、トラブルシューティングのプロセスにおける著者のアイデアと方法も、あなたにインスピレーションを与えるだろうということです。 問題について まだ準備ができていません Alibaba Cloud には独自の Kubernetes コンテナ クラスター製品があります。 Kubernetes クラスターの出荷量が劇的に増加するにつれて、オンライン ユーザーは、クラスターでノードの NotReady ステータスが発生する可能性が非常に低いことに散発的に気付きました。私たちの観察によれば、ほぼ毎月 1 人か 2 人のユーザーがこの問題に遭遇しています。ノードが NotReady になると、クラスター マスターは、新しい Pod の発行や、ノードで実行されている Pod に関するリアルタイム情報の取得など、ノードに対する制御を実行できなくなります。 Kubernetesについて知っておくべきことここで、Kubernetes クラスターに関する基本的な知識をいくつか追加したいと思います。 Kubernetes クラスターの「ハードウェア基盤」は、スタンドアロン マシンの形で存在するクラスター ノードです。これらのノードは物理マシンまたは仮想マシンにすることができます。クラスター ノードは、マスター ノードとワーカー ノードに分かれています。マスター ノードは主に、スケジューラやコントローラなどのクラスター管理コンポーネントをホストするために使用されます。ワーカーノードは主にビジネスを実行するために使用されます。 Kubelet は各ノード上で実行されるエージェントです。制御コンポーネントと通信し、制御コンポーネントの指示に従ってワーカーノードを直接管理する役割を担います。 クラスターノードが NotReady 状態になった場合、最初に行う必要があるのは、ノード上で実行されている kubelet が正常かどうかを確認することです。この問題が発生した場合は、systemctl コマンドを使用して kubelet (kubelet は systemd によって管理されるデーモンです) の状態を確認し、正常に動作していることを確認します。 journalctl を使用して kubelet ログを表示すると、次のエラーが見つかります。 PLEGとは何ですか?このエラーは、コンテナ ランタイムが動作しておらず、PLEG が正常ではないことを明確に示しています。ここでのコンテナ ランタイムは、docker デーモンを指します。 Kubelet は、docker デーモンを操作してコンテナのライフサイクルを制御します。ここでの PLEG は、ポッド ライフサイクル イベント ジェネレーターを指します。 PLEG は、kubelet がランタイムをチェックするために使用するヘルスチェック メカニズムです。これは、ポーリングを使用して kubelet によって実行できた可能性があります。しかし、投票にはコストが高いという欠点があるため、PLEGが誕生しました。 PLEG は、コンテナ ランタイムのヘルス チェックを「割り込み」の形で実装しようとしますが、実際にはポーリングと「割り込み」の両方の妥協的なソリューションを使用します。 基本的に、上記のエラーに基づいて、コンテナのランタイムに問題があることを確認できます。問題のあるノードで、docker コマンドを使用して新しいコンテナを実行しようとすると、コマンドが応答しないので、上記のエラーが正確であることを意味します。 Docker スタック Dockerデーモンのコールスタック分析 Alibaba Cloud Kubernetes クラスターで使用されるコンテナ ランタイムである Docker は、OCI 標準に適応するためにバージョン 1.11 以降、複数のコンポーネントに分割されました。分割後、docker デーモン、containerd、containerd-shim、runC が含まれます。コンポーネント containerd は、クラスター ノード上のコンテナーのライフサイクル管理を担当し、docker デーモンに gRPC インターフェイスを提供します。 この問題では、PLEG はコンテナ ランタイムに問題があると判断しているため、docker デーモン プロセスから開始する必要があります。 kill -USR1 コマンドを使用して、USR1 シグナルを docker デーモンに送信できます。シグナルを受信すると、docker デーモンはすべてのスレッド呼び出しスタックを /var/run/docker フォルダーに出力します。 Docker デーモン プロセスのコール スタックは比較的簡単に分析できます。よく注意してみると、ほとんどのコールスタックは次の図のようになっていることがわかります。スタック上の各関数の名前と、関数が配置されているファイル (モジュール) の名前を観察すると、コール スタックの下半分は http 要求を受信して要求をルーティングするプロセスであることがわかります。上半分は具体的な処理機能です。最後に、処理関数は待機状態に入り、ミューテックス インスタンスを待機します。 この時点で、ContainerInspectCurrent 関数の実装を確認する必要があります。実装から、この関数の最初のパラメータは、このスレッドが操作しているコンテナ名へのポインタであることがわかります。このポインターを使用してコールスタック ファイル全体を検索すると、このコンテナーで待機しているすべてのスレッドが見つかります。同時に、以下のスレッドも見ることができます。 このスレッド呼び出しスタック上の関数 ContainerExecStart も同じコンテナを処理しています。ただし、違いは、ContainerExecStart はコンテナを待機しているのではなく、すでにコンテナの操作権限 (ミューテックス) を取得しており、実行ロジックを containerd 呼び出しに転送していることです。コードを使用してこれを検証することもできます。前述したように、containerd は gRPC を介して docker デーモンへのインターフェースを提供します。このコールスタックの上部にある内容は、gRPC リクエストを通じて containerd を呼び出す docker デーモンです。 Containerd コールスタック分析 docker デーモンと同様に、kill -SIGUSR1 コマンドを使用して containerd の呼び出しスタックを出力できます。違いは、今回はコールスタックがメッセージ ログに直接出力されることです。 gRPC サーバーとして、Containerd は docker デーモンからリモート呼び出しを受信した後、リクエストを処理するための新しいスレッドを作成します。ここでは gRPC の詳細にあまり注意を払う必要はありません。この要求のクライアント呼び出しスタックを見ると、この呼び出しのコア機能がプロセスの開始であることがわかります。 containerd コールスタックで Start、Process、process.go などのフィールドを検索すると、次のスレッドが簡単に見つかります。 このスレッドの中心的なタスクは、runC を利用してコンテナ プロセスを作成することです。コンテナが起動すると、runC プロセスは終了します。したがって、次のステップは、runC がタスクを正常に完了したかどうかを確認することです。プロセス リストを見ると、一部の runC プロセスがシステム内でまだ実行されていることがわかりますが、これは予期された動作ではありません。コンテナの起動にかかる時間は、プロセスの起動にかかる時間とほぼ同じです。システム内で runC プロセスが実行されている場合、runC はコンテナを正常に起動できないことを意味します。 D-Bus とは何ですか? RunCはD-Busを要求する コンテナ ランタイムの runC コマンドは、libcontainer の単純なラッパーです。このツールを使用すると、コンテナの作成や削除など、個々のコンテナを管理できます。前のセクションでは、runC がコンテナの作成タスクを完了できなかったことがわかりました。対応するプロセスを強制終了し、コマンドラインで同じコマンドを使用してコンテナを起動し、strace を使用してプロセス全体を追跡できます。 分析により、runC は org.free フィールドを使用して dbus ソケットにデータを書き込む時点で停止することが示されています。では、dbus とは何でしょうか? Linux では、dbus はプロセス間のメッセージ通信のメカニズムです。 理由はD-Busではない busctl コマンドを使用して、システム内の既存のバスをすべて一覧表示できます。下の図に示すように、問題が発生したとき、問題のあるノードのバス名番号が非常に大きいことがわかりました。したがって、名前などの dbus 関連のデータ構造の一部が使い果たされて、この問題が発生すると考えられます。 Dbus メカニズムの実装は、dbus デーモンと呼ばれるコンポーネントに依存します。 dbus 関連のデータ構造が本当に使い果たされている場合は、デーモンを再起動すると問題が解決するはずです。しかし残念なことに、問題はそれほど単純ではありません。 dbus デーモンを再起動しても、問題は依然として存在します。 上記の runC の strace スクリーンショットでは、runC は org.free フィールドを使用してバスにデータを書き込む時点で停止します。 busctl によって出力されるバス リストでは、このフィールドを持つバスがすべて systemd によって使用されていることは明らかです。この時点で、systemctl daemon-reexec を使用して systemd を再起動すると、問題は解消されました。したがって、基本的には、問題は systemd に関連している可能性があると判断できます。 Systemdは難しい問題だ Systemd は、特に私のように関連する開発作業を一切行ったことがない人にとっては、非常に複雑なコンポーネントです。基本的に、systemd の問題のトラブルシューティングには、(デバッグ レベル) ログ、コア ダンプ、コード分析、ライブ デバッグの 4 つの方法を使用しました。 1 番目、3 番目、4 番目の組み合わせにより、数日間の懸命な作業の末、問題の原因を見つけることができました。しかし、ここでは「役に立たない」コアダンプから始まります。 「役に立たない」コアダンプ systemd を再起動すると問題は解決しましたが、問題自体は、dbus を使用して systemd と通信しているときに runC が応答しなかったため、最初に確認する必要があるのは、systemd にロックされている重要なスレッドがあるかどうかです。コア ダンプ内のすべてのスレッドを確認します。下にはスレッドが 1 つだけあります。このスレッドはロックされておらず、応答するために dbus イベントを待機しています。 散らばった情報 他にできることがないので、いろいろなテストや試行をすることしかできません。 busctl tree コマンドを使用して、バス上の公開されているすべてのインターフェースを出力します。出力結果から、org.freedesktop.systemd1 バスはインターフェース クエリ要求に応答できないようです。 org.freedesktop.systemd1 で受信したすべてのリクエストを監視するには、次のコマンドを使用します。通常のシステムでは、ユニットの作成と削除のメッセージが多数ありますが、問題のあるシステムでは、このバスにメッセージがまったく存在しないことがわかります。 gdbus モニター --system --dest org.freedesktop.systemd1 --object-path /org/freedesktop/systemd1 問題発生前後のシステム ログを分析すると、runC は libcontainer_%d_systemd_test_default.slice テストを繰り返し実行していました。このテストは非常に頻繁に行われていましたが、問題が発生すると、このテストは停止しました。したがって、私の直感では、この問題はこのテストと大きく関係している可能性があると思います。 さらに、systemd-analyze コマンドを使用して systemd のデバッグ レベル ログを開いたところ、systemd に「操作はサポートされていません」というエラー メッセージが表示されていました。 上記の散在した知識に基づいて、大まかな結論を下すことができます。大量のユニットが作成および削除された後、バス org.freedesktop.systemd1 は応答しなくなります。これらの頻繁なユニット作成および削除テストは、runC の変更によって導入されました。この変更により、UseSystemd 関数はユニットを作成して systemd 機能をテストするようになります。 UseSystemd は、コンテナの作成やコンテナのパフォーマンスの確認など、さまざまな場所で呼び出されます。 コード分析 この問題は、すべてのオンライン Kubernetes クラスターで月に 2 回程度発生します。この問題は引き続き発生しており、発生後に systemd を再起動することによってのみ修正できますが、これは非常に危険です。 我々はそれぞれ systemd と runC のコミュニティにバグレポートを提出しましたが、非常に現実的な問題として、Alibaba Cloud のようなオンライン環境がないため、この問題を再現できる可能性はほぼゼロであり、コミュニティがこの問題を解決することは期待できません。固い骨は自分で噛まなければなりません。 前のセクションでは、問題が発生すると、systemd が Operation not supported エラーを出力することを説明しました。このエラーは問題自体とは無関係のようですが、私の直感ではこれが問題に最も近い場所かもしれないので、まずはこのエラーの原因を突き止めることにしました。 Systemd には大量のコードが含まれており、このエラーは多くの場所で報告されています。多くのコード分析 (ここでは千語は省略) を通じて、疑わしい箇所をいくつか見つけました。こうした怪しい場所では、次にすべきことは待つことです。 3 週間待った後、ようやくオンライン クラスターで問題が再現されました。 ライブデバッグ ユーザーの同意を得た後、systemd デバッグ シンボルをダウンロードし、systemd に gdb をマウントし、疑わしい関数にブレークポイントを設定して実行を続行します。複数回の検証の結果、systemd が最終的に sd_bus_message_seal 関数で EOPNOTSUPP エラーに遭遇したことが判明しました。 このエラーの原因は、systemd が変数 Cookie を使用して、処理する dbus メッセージを追跡することです。新しいメッセージが追加されるたびに、systemd はまず Cookie の値を 1 増やし、次にこの値を新しいメッセージにコピーします。 gdb を使用して dbus->cookie の値を印刷すると、この値が 0xffffffff を超えていることが明確にわかります。したがって、問題は、systemd が大量のメッセージをシールした後に、Cookie の 32 ビット値がオーバーフローし、新しいメッセージがシールされず、systemd が runC に応答しなくなることであると思われます。 さらに、通常のシステムでは、gdb を使用して bus->cookie の値を 0xffffffff に近くなるように変更し、cookie がオーバーフローするとすぐに問題が発生することを観察すると、結論が証明されます。 クラスターノードの NotReady がこの問題によって発生したかどうかを判断する方法 まず、問題のノードに gdb と systemd debuginfo をインストールし、コマンド gdb /usr/lib/systemd/systemd1 を使用して gdb を systemd に接続し、関数 sd_bus_send にブレークポイントを設定して、実行を続行する必要があります。 systemd がブレークポイントに到達したら、 p /x bus->cookie を使用して対応する Cookie 値を表示します。この値が 0xffffffff を超えると、Cookie がオーバーフローし、必然的にノード NotReady 問題が発生します。確認後、quit を使用してデバッガーをデタッチできます。 バグ修正 この問題の解決はそれほど簡単ではありません。理由の 1 つは、systemd が dbus1 および dbus2 との互換性を保つために同じ Cookie 変数を使用していることです。 dbus1 の場合、Cookie は 32 ビットであり、systemd が 3 ~ 5 か月間にユニットを頻繁に作成および削除すると、この値は確実にオーバーフローします。一方、dbus2 のクッキーは 64 ビットであり、時間の終わりでもオーバーフローしない可能性があります。 もう 1 つの理由は、オーバーフローの問題を解決するために Cookie を単純にラップすることはできないことです。これにより、systemd が同じ Cookie を使用して異なるメッセージを封印することになり、悲惨な結果を招く可能性があります。 最終的な修正は、32 ビットの Cookie を使用して dbus1 と dbus2 の両方のケースを平等に処理することでした。同時に、クッキーが 0xffffffff に達した後、次のクッキーは 0x80000000 になります。つまり、*** ビットはクッキーがオーバーフロー状態にあることを示すために使用されます。クッキーがこの状態にあることが検出された場合、クッキーの競合を避けるために、次のクッキーが他のメッセージによって使用されているかどうかを確認する必要があります。 追記 この問題の根本的な原因は systemd にあるはずですが、runC 関数 UseSystemd は systemd の機能をテストするためにあまり美しくない方法を使用しています。この関数はコンテナのライフサイクル管理プロセス全体を通じて頻繁に呼び出されるため、この低確率の問題が発生する可能性があります。 systemd の修正は Red Hat によって承認されており、近い将来、systemd をアップグレードすることでこの問題を根本的に解決できると予想されます。 |
<<: キングゴールドグループとオラクルがデジタルトランスフォーメーション2.0の推進で提携
>>: クラウドネイティブストレージに密結合コンテナとマイクロサービスが必要な3つの理由
lcayun/Leicaクラウドサーバーメーカーは、国内認定のエンタープライズサーバーマーチャントで...
国内のソーシャルメディア広告には参考にできる成功モデルが不足しているのに対し、海外のソーシャルメディ...
クラウドはリソースの速度と規模の点で利点を提供しますが、銀行にとってセキュリティは依然として問題点で...
最近、Taobao Mallが正式にTmallに改名され、ネット上で大きな騒動を巻き起こした。 Tm...
ハイブリッド クラウド モデルは、企業がパブリック クラウドとプライベート クラウドの両方の利点を活...
この記事は主に、産業用不動産ウェブサイトの運営において避けるべきいくつかの問題について説明しています...
「私にとって最も辛いのは、廃棄される在庫1000万相当の契約を自らの手で締結したことだ」と、すでに破...
今月18日にウェブサイト(WordPressブログシステム)の外観テーマを変更しました。 6月28日...
SEO は非常に複雑な業界です。一方では、検索エンジンによって奨励されており、優れた Web サイト...
本日、マイクロソフトは Inspire 2021 カンファレンスで Windows 365 Clou...
Qihoo 360 が検索エンジン市場に参入するという話は以前から聞いていましたが、具体的な詳細はま...
[[403388]]このガイドでは、Kubernetes と Istio を使用したプログレッシブ配...
今日、プログラマーの一団がいつものように西二旗地下鉄に乗っていたが、違いは、彼らが向かった先が、有名...
一見合理的と思われる言葉が、他の合理的な言葉と矛盾することがよくあります。 「成功とは粘り強さだ。」...
端午節を記念して、shuhost は中小企業と個人のお客様向けに特別プロモーションを準備しており、香...