電子ブックのダウンロード|超実用的!AliアフターセールスエキスパートのK8トラブルシューティングケースのコレクション

Book.png

<パブリックアカウントに従い、「トラブルシューティング」に返信してダウンロードリンクを取得します>

「Kubernetesの概要」のオープンダウンロード

著者Luojianロング(ニックネーム音東)、アリクラウド技術の専門家は、オペレーティングシステムとグラフィックスカードドライバのデバッグおよび開発の長年の経験を持っています。現在、クラウドネイティブエリア、コンテナークラスター、サービスグリッドに焦点を当てています。この本は、理論的な記事と実用的な記事、合計12の技術記事、クラスター制御の詳細な分析、クラスタースケーリングの原理、ミラープルなどの理論に分かれています。 、使いやすいKubernetes!

この本には4つのハイライトがあります。

  • 上海での実際の事件の降水量
  • 理論と実践の完璧な一致
  • 理論的な詳細
  • 技術的な詳細を調査する

一度に6つの基本原則を理解し、基本理論を完全に理解し、6つの典型的な問題の豪華な操作を一度に学習するのに役立ちます!

Directory.png
(目次)

無料でダウンロードするには?

「Alibaba Cloud Native」に従って、**トラブルシューティング**と返信して、この本を無料でダウンロードしてください。

序文

次のコンテンツは、「詳細なKubernetes」の本からの抜粋です。

Alibaba Cloudには、独自のKubernetesコンテナクラスタ製品があります。Kubernetesクラスターの出荷数が急激に増加したため、オンラインユーザーはクラスターがNotNotyノードの確率が非常に低いことを散発的に発見しました。

私たちの観察によれば、この問題はほぼ毎月1人または2人の顧客によって発生します。ノードNotReadyの後、クラスターマスターは、新しいポッドを発行したり、ノードで実行中のポッドに関するリアルタイム情報をキャプチャしたりするなど、このノードを制御する方法がありません。

上記の問題のトラブルシューティングパスは、K8sクラスターからコンテナーランタイム、sdbusおよびsystemdまでで、複雑ではありません。この問題はsystemdで修正されたため、基本的にこの問題が発生する可能性は低くなっています。

ただし、クラスターノードの準備にはまだ問題がありますが、理由は異なります。

今日の記事では、クラスターノードNotReadyの別の例を共有することに焦点を当てます。この問題は、上記の問題とは完全に異なります。

問題現象

この問題の現象は、クラスターノードがNotReady状態になることです。ノードを再起動することで一時的に問題を解決できますが、約20日後に問題が再発します。

1.png

問題が発生した後、ノードでkubeletを再起動すると、ノードは準備完了になりますが、この状態は3分間だけ続きます。これは特殊なケースです。

大きな論理

この問題を詳細に分析する前に、クラスターノードの準備完了状態の背後にある大きなロジックを見てみましょう。K8sクラスターには、ノードの準備完了状態に関連する4つの主要コンポーネントがあります。クラスターetcdのコアデータベース、クラスターの入り口APIサーバー、ノードコントローラー、およびクラスターノードに常駐し、ノードを直接管理するkubeletです。

2.png

一方、kubeletはクラスターコントローラの役割を果たします。APIサーバーから定期的にポッドおよびその他の関連リソースに関する情報を取得し、その情報に従ってノードで実行されているポッドの実行を制御します。他方、kubeletはノードステータスモニターとして機能しますノード情報を取得し、これらの条件をクラスタークライアントの役割でAPIサーバーに同期できます。

この問題では、kubeletが2番目の役割を果たします。

Kubeletは、上の図のNodeStatusメカニズムを使用して、クラスターノードのステータスを定期的にチェックし、ノードのステータスをAPIサーバーに同期します。NodeStatusがノードの準備完了ステータスを判断するための主な基礎はPLEGです。

PLEGはポッドライフサイクルイベントジェネレータの略です。基本的に、その実行ロジックはノードのポッド操作を定期的にチェックすることです。対象の変更が見つかった場合、PLEGはこの変更をイベントにラップし、それをKubeletのメイン同期メカニズムsyncLoopに送信します。対処する。ただし、PLEGのポッドチェックメカニズムを定期的に実行できない場合、NodeStatusメカニズムはこのノードのステータスが間違っていると判断し、このステータスをAPIサーバーに同期します。

最終的にkubeletからノードステータスに報告されるノードステータスを実装するのはノードコントロールコンポーネントです。ここでは、kubeletによって報告されたノードのステータスを、ノードの最終的なステータスと意図的に区別しました。前者は実際にはノードを説明するときに見た条件ですが、後者は実際のノードリストのNotReady状態です。

3.png

3分間準備完了

問題が発生した後、kubeletを再起動すると、ノードは3分後にNotReadyになりません。この現象は、問題の主要なエントリポイントです。

4.png

説明する前に、公式のPLEGダイアグラムをご覧ください。この図は主に2つのプロセスを示しています。

  • 一方、kubeletはクラスターコントローラーとして機能し、APIサーバーからポッド仕様の変更を取得し、ワーカースレッドを作成することでポッドを作成または終了します。
  • 一方、PLEGは定期的にコンテナのステータスを確認し、ステータスをイベントの形でkubeletにフィードバックします。

ここで、PLEGには2つの重要な時間パラメーターがあります。1つはチェックの実行間隔で、もう1つはチェックのタイムアウト時間です。デフォルトでは、PLEG検査は1秒間隔で行われます。つまり、各検査プロセスが実行された後、PLEGは1秒間待機してから次の検査を実行します。各検査のタイムアウトは3分です。 PLEGチェック操作は3分以内に完了できません。この状況は、前のセクションで説明したNodeStatusメカニズムによってクラスターノードの資格情報として使用され、APIサーバーに同期されます。

kubeletを再起動してから3分後にノードの準備ができることを確認したのは、kubeletが再起動した後、最初のPLEGチェック操作が正常に終了しなかったためです。ノードは準備完了状態であり、3分のタイムアウトが経過するまでクラスターに同期されません。

下図に示すように、上の線は通常時のPLEGの実行フローを示し、下の線は問題を示します。relistはチェックの主な機能です。

5.png

PLEG

原理を理解して、PLEGのログを見てみましょう。ログは基本的に2つの部分に分けられます。スキップするポッド同期は、kubelet同期関数syncLoopによって出力され、ポッド同期をスキップしたことを示します。残りのPLEGは正常ではありません。最後にアクティブになったレッグが検出されました。しきい値は3分0秒です。前のセクションで説明した再リストタイムアウトの問題が3分であることを明確に示しています。

17:08:22.299597 kubelet skipping pod synchronization - [PLEG is not healthy: pleg was last seen active 3m0.000091019s ago; threshold is 3m0s]
17:08:22.399758 kubelet skipping pod synchronization - [PLEG is not healthy: pleg was last seen active 3m0.100259802s ago; threshold is 3m0s]
17:08:22.599931 kubelet skipping pod synchronization - [PLEG is not healthy: pleg was last seen active 3m0.300436887s ago; threshold is 3m0s]
17:08:23.000087 kubelet skipping pod synchronization - [PLEG is not healthy: pleg was last seen active 3m0.700575691s ago; threshold is 3m0s]
17:08:23.800258 kubelet skipping pod synchronization - [PLEG is not healthy: pleg was last seen active 3m1.500754856s ago; threshold is 3m0s]
17:08:25.400439 kubelet skipping pod synchronization - [PLEG is not healthy: pleg was last seen active 3m3.100936232s ago; threshold is 3m0s]
17:08:28.600599 kubelet skipping pod synchronization - [PLEG is not healthy: pleg was last seen active 3m6.301098811s ago; threshold is 3m0s]
17:08:33.600812 kubelet skipping pod synchronization - [PLEG is not healthy: pleg was last seen active 3m11.30128783s ago; threshold is 3m0s]
17:08:38.600983 kubelet skipping pod synchronization - [PLEG is not healthy: pleg was last seen active 3m16.301473637s ago; threshold is 3m0s]
17:08:43.601157 kubelet skipping pod synchronization - [PLEG is not healthy: pleg was last seen active 3m21.301651575s ago; threshold is 3m0s]
17:08:48.601331 kubelet skipping pod synchronization - [PLEG is not healthy: pleg was last seen active 3m26.301826001s ago; threshold is 3m0s]

relist関数の実行を直接確認できるのは、kubeletの呼び出しスタックです。SIGABRTシグナルをkubeletプロセスに送信する限り、golangランタイムはkubeletプロセスのすべてのコールスタックを出力するのに役立ちます。この操作はkubeletプロセスを強制終了することに注意してください。ただし、この問題のため、kubeletを再起動しても再生環境は破壊されないため、効果はほとんどありません。

次の呼び出しスタックは、PLEG再リスト関数の呼び出しスタックです。下から上に、再リストがgrpcを介してPodSandboxStatusを取得するために待機していることがわかります。

kubelet: k8s.io/kubernetes/vendor/google.golang.org/grpc/transport.(*Stream).Header()
kubelet: k8s.io/kubernetes/vendor/google.golang.org/grpc.recvResponse()
kubelet: k8s.io/kubernetes/vendor/google.golang.org/grpc.invoke()
kubelet: k8s.io/kubernetes/vendor/google.golang.org/grpc.Invoke()
kubelet: k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2.(*runtimeServiceClient).PodSandboxStatus()
kubelet: k8s.io/kubernetes/pkg/kubelet/remote.(*RemoteRuntimeService).PodSandboxStatus()
kubelet: k8s.io/kubernetes/pkg/kubelet/kuberuntime.instrumentedRuntimeService.PodSandboxStatus()
kubelet: k8s.io/kubernetes/pkg/kubelet/kuberuntime.(*kubeGenericRuntimeManager).GetPodStatus()
kubelet: k8s.io/kubernetes/pkg/kubelet/pleg.(*GenericPLEG).updateCache()
kubelet: k8s.io/kubernetes/pkg/kubelet/pleg.(*GenericPLEG).relist()
kubelet: k8s.io/kubernetes/pkg/kubelet/pleg.(*GenericPLEG).(k8s.io/kubernetes/pkg/kubelet/pleg.relist)-fm()
kubelet: k8s.io/kubernetes/vendor/k8s.io/apimachinery/pkg/util/wait.JitterUntil.func1(0xc420309260)
kubelet: k8s.io/kubernetes/vendor/k8s.io/apimachinery/pkg/util/wait.JitterUntil()
kubelet: k8s.io/kubernetes/vendor/k8s.io/apimachinery/pkg/util/wait.Until()

PodSandboxStatusを使用してkubeletコールスタックを検索すると、以下のスレッドを簡単に見つけることができます。このスレッドは、サンドボックスのステータスを実際に照会するスレッドです。下から上に、このスレッドがプラグインマネージャでミューテックスを取得しようとしていることがわかります。

kubelet: sync.runtime_SemacquireMutex()kubelet: sync.(*Mutex).Lock()
kubelet: k8s.io/kubernetes/pkg/kubelet/dockershim/network.(*PluginManager).GetPodNetworkStatus()
kubelet: k8s.io/kubernetes/pkg/kubelet/dockershim.(*dockerService).getIPFromPlugin()
kubelet: k8s.io/kubernetes/pkg/kubelet/dockershim.(*dockerService).getIP()
kubelet: k8s.io/kubernetes/pkg/kubelet/dockershim.(*dockerService).PodSandboxStatus()
kubelet: k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2._RuntimeService_PodSandboxStatus_Handler()
kubelet: k8s.io/kubernetes/vendor/google.golang.org/grpc.(*Server).processUnaryRPC()
kubelet: k8s.io/kubernetes/vendor/google.golang.org/grpc.(*Server).handleStream()
kubelet: k8s.io/kubernetes/vendor/google.golang.org/grpc.(*Server).serveStreams.func1.1()
kubelet: created by k8s.io/kubernetes/vendor/google.golang.org/grpc.(*Server).serveStreams.func1

そして、このミューテックスはプラグインマネージャーでのみ役立つため、プラグインマネージャーに関連するすべてのコールスタックをチェックします。スレッドのいくつかはミューテックスを待っていますが、残りのスレッドはTerway cniプラグインを待っています。

kubelet: syscall.Syscall6()kubelet: os.(*Process).blockUntilWaitable()
kubelet: os.(*Process).wait()kubelet: os.(*Process).Wait()
kubelet: os/exec.(*Cmd).Wait()kubelet: os/exec.(*Cmd).Run()
kubelet: k8s.io/kubernetes/vendor/github.com/containernetworking/cni/pkg/invoke.(*RawExec).ExecPlugin()
kubelet: k8s.io/kubernetes/vendor/github.com/containernetworking/cni/pkg/invoke.(*PluginExec).WithResult()
kubelet: k8s.io/kubernetes/vendor/github.com/containernetworking/cni/pkg/invoke.ExecPluginWithResult()
kubelet: k8s.io/kubernetes/vendor/github.com/containernetworking/cni/libcni.(*CNIConfig).AddNetworkList()
kubelet: k8s.io/kubernetes/pkg/kubelet/dockershim/network/cni.(*cniNetworkPlugin).addToNetwork()
kubelet: k8s.io/kubernetes/pkg/kubelet/dockershim/network/cni.(*cniNetworkPlugin).SetUpPod()
kubelet: k8s.io/kubernetes/pkg/kubelet/dockershim/network.(*PluginManager).SetUpPod()
kubelet: k8s.io/kubernetes/pkg/kubelet/dockershim.(*dockerService).RunPodSandbox()
kubelet: k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2._RuntimeService_RunPodSandbox_Handler()
kubelet: k8s.io/kubernetes/vendor/google.golang.org/grpc.(*Server).processUnaryRPC()
kubelet: k8s.io/kubernetes/vendor/google.golang.org/grpc.(*Server).handleStream()
kubelet: k8s.io/kubernetes/vendor/google.golang.org/grpc.(*Server).serveStreams.func1.1()

応答しないTerwayd

この問題をさらに説明する前に、TerwayとTerwaydを区別する必要があります。基本的に、TerwayとTerwaydはクライアントとサーバーの関係であり、flannelとflanneldの関係と同じです。Terwayは、kubeletの定義に従ってcniインターフェースを実装するプラグインです。

6.png

前のセクションの最後で、私たちが目にした問題は、kubeletがポッドネットワークを構成するためにCNI terwayを呼び出したときに、Terwayが長時間応答しないことでした。通常の状況では、この操作は第2レベルで非常に高速である必要があります。問題が発生したとき、Terwayはタスクを正常に完了しなかったため、クラスターノードに多数のterwayプロセスが蓄積しているのがわかりました。

7.png

同様に、SIGABRTをこれらのterwayプラグインプロセスに送信して、プロセスのコールスタックを出力できます。以下は、terwayのコールスタックの1つです。このスレッドはcmdDel関数を実行しています。その役割は、ポッドネットワーク関連の構成を削除することです。

kubelet: net/rpc.(*Client).Call()
kubelet: main.rpcCall()kubelet: main.cmdDel()
kubelet: github.com/AliyunContainerService/terway/vendor/github.com/containernetworking/cni/pkg/skel.(*dispatcher).checkVersionAndCall()
kubelet: github.com/AliyunContainerService/terway/vendor/github.com/containernetworking/cni/pkg/skel.(*dispatcher).pluginMain()
kubelet: github.com/AliyunContainerService/terway/vendor/github.com/containernetworking/cni/pkg/skel.PluginMainWithError()
kubelet: github.com/AliyunContainerService/terway/vendor/github.com/containernetworking/cni/pkg/skel.PluginMain()

上記のスレッドは、rpcを経由して呼び出しを行い、実際にポッドネットワークを削除します。したがって、この問題をさらに特定するには、terwaydのコールスタックをさらに調査する必要があります。Terwayは、Terwayのサーバー側として、Terwayからのリモート呼び出しを受け入れ、TerwayのcmdAddまたはcmdDelを完了して、ポッドネットワーク構成を作成または削除します。

上記のスクリーンショットでは、クラスターノードに数千のTerwayプロセスがあり、それらすべてがTerwaydを待機しているため、実際には数千のスレッドがTerwaydによるTerwayリクエストの処理を行っています。

次のコマンドを使用して、Terwaydを再起動せずにコールスタックを出力します。

curl  --unix-socket /var/run/eni/eni.socket 'http:/debug/pprof/goroutine?debug=2'

Terwaydの呼び出しスタックは非常に複雑であり、ほとんどすべてのスレッドがロックを待機しているため、ロック待機関係を直接分析することはより複雑です。現時点では、「timeメソッド」を使用できます。つまり、最も早いスレッドが待機状態にあると仮定すると、確率はロックを保持しているスレッドです。

コールスタックとコード分析の結果、次のスレッドは待機時間が最も長く(1595分)、ロックを保持していることがわかりました。このロックは、ポッドネットワークを作成または破棄するすべてのスレッドをブロックします。

goroutine 67570 [syscall, 1595 minutes, locked to thread]:
syscall.Syscall6()
github.com/AliyunContainerService/terway/vendor/golang.org/x/sys/unix.recvfrom()
github.com/AliyunContainerService/terway/vendor/golang.org/x/sys/unix.Recvfrom()
github.com/AliyunContainerService/terway/vendor/github.com/vishvananda/netlink/nl.(*NetlinkSocket).Receive()
github.com/AliyunContainerService/terway/vendor/github.com/vishvananda/netlink/nl.(*NetlinkRequest).Execute()
github.com/AliyunContainerService/terway/vendor/github.com/vishvananda/netlink.(*Handle).LinkSetNsFd()
github.com/AliyunContainerService/terway/vendor/github.com/vishvananda/netlink.LinkSetNsFd()
github.com/AliyunContainerService/terway/daemon.SetupVethPair()github.com/AliyunContainerService/terway/daemon.setupContainerVeth.func1()
github.com/AliyunContainerService/terway/vendor/github.com/containernetworking/plugins/pkg/ns.(*netNS).Do.func1()
github.com/AliyunContainerService/terway/vendor/github.com/containernetworking/plugins/pkg/ns.(*netNS).Do.func2()

前のスレッドのコールスタックを詳細に分析するために、3つのことを決定できます。

  • まず、Terwaydはnetlinkライブラリを使用して、仮想ネットワークカード、IPアドレス、ノード上のルーティングリソースを管理し、netlinkはiproute2と同様の機能を実装します。
  • 次に、netlinkはソケットを使用してカーネルと直接通信します。
  • 3番目に、上記のスレッドはrecvfromシステムコールを待機します。

この場合、このスレッドのカーネルコールスタックを確認して、このスレッドが待機している理由をさらに確認する必要があります。このスレッドのシステムスレッドIDをgoroutineスレッド番号から見つけるのは比較的難しいため、ここでは、システムのコアダンプを取得して、上位スレッドのカーネルコールスタックを見つけます。

カーネルコールスタックで、recvfromを検索し、以下のスレッドを見つけます。基本的には、以下のコールスタックから、このスレッドがrecvfrom関数で待機していることのみを確認できます。

PID: 19246  TASK: ffff880951f70fd0  CPU: 16  COMMAND: "terwayd" 
#0 [ffff880826267a40] __schedule at ffffffff816a8f65 
#1 [ffff880826267aa8] schedule at ffffffff816a94e9 
#2 [ffff880826267ab8] schedule_timeout at ffffffff816a6ff9 
#3 [ffff880826267b68] __skb_wait_for_more_packets at ffffffff81578f80 
#4 [ffff880826267bd0] __skb_recv_datagram at ffffffff8157935f 
#5 [ffff880826267c38] skb_recv_datagram at ffffffff81579403 
#6 [ffff880826267c58] netlink_recvmsg at ffffffff815bb312 
#7 [ffff880826267ce8] sock_recvmsg at ffffffff8156a88f 
#8 [ffff880826267e58] SYSC_recvfrom at ffffffff8156aa08 
#9 [ffff880826267f70] sys_recvfrom at ffffffff8156b2fe
#10 [ffff880826267f80] tracesys at ffffffff816b5212 
(via system_call)

この問題を詳細に調査することはより困難です。これは明らかにカーネルの問題またはカーネル関連の問題です。カーネルコア全体を調べ、すべてのスレッドコールスタックをチェックしましたが、この問題に関連する可能性のある他のスレッドを見つけることができませんでした。

修正

この問題の修正は、netlinkの信頼性が100%ではないという前提に基づいています。Netlinkの応答が非常に遅いか、まったく反応しない場合があります。したがって、netlink操作のタイムアウトを増やすことができます。これにより、特定のnetlink呼び出しを完了できない場合でも、terwaydはブロックされません。

まとめ

ノードの準備が整っているシナリオでは、kubeletは実際にノードのハートビートメカニズムを実装します。Kubeletは、メモリ、PID、ディスクなどのノードに関連するさまざまなステータスを定期的に同期し、もちろん、この記事に関連する準備完了状態をクラスターの管理および制御に同期します。クラスターノードを監視または管理するプロセスでは、kubeletはさまざまなプラグインを使用してノードリソースを直接操作します。これには、ネットワーク、ディスク、コンテナランタイムなどのプラグインが含まれます。これらのプラグインのステータスは、kubeletまたはノードのステータスに直接適用されます。

<公開アカウントに従い、調査に返信して本をダウンロード>

Alibaba Cloud Nativeは、マイクロサービス、サーバーレス、コンテナ、Service Meshなどの技術分野に焦点を当て、クラウドネイティブの人気のあるテクノロジートレンド、クラウドネイティブの大規模なランディングプラクティス、およびクラウドネイティブの開発者を最もよく理解するテクノロジーサークルになることに焦点を当てています。」

おすすめ

転載: www.cnblogs.com/alisystemsoftware/p/12722092.html