Container Cloud Platform API サーバーの停止問題のトラブルシューティング

Container Cloud Platform API サーバーの停止問題のトラブルシューティング

58 クラウド コンピューティング プラットフォームは、Kubernetes + Docker テクノロジーをベースに 58 グループ アーキテクチャ ラインがグループ内サービス向けに開発したビジネス インスタンス管理プラットフォームです。シンプルで軽量であり、物理リソースの効率的な使用、より迅速な展開、統一され標準化された運用環境を実現します。クラウド プラットフォームを通じて、サービスが標準化され、オンライン プロセスが標準化され、リソースの使用が合理化されます。しかし、クラウドプラットフォームの構築プロセスは順風満帆というわけではなく、いくつかの問題や課題もあります。この記事では、クラウド プラットフォームの現実で遭遇する問題を紹介します。

1. 問題について

1.1 問題の概要

最近、多くのビジネス関係者から、クラウド プラットフォームの使用時にコンテナの展開が遅く、プラットフォームの応答が遅いという問題があると報告されています。詳細なトラブルシューティングと問題の特定を行った結果、問題はようやく解決しました。

1.2 Kubernetesの基礎知識

プライベート クラウド プラットフォームは、Kubernetes を通じてコン​​テナをオーケストレーションします。 Kubernetes の全体的なアーキテクチャを次の図に示します。

いくつかの主要モジュールの機能について簡単に説明します。

  • etcd: Kubernetes のバックエンド ストレージ。
  • Pod: Kubernetes の最も基本的な動作単位。1 つ以上の密接に関連したコンテナで構成されます。
  • レプリケーション コントローラ: デプロイメントまたは RC 内のレプリカの数を確保するために使用されるレプリケーション コントローラ。
  • スケジューラ: Kubernetes のスケジューラ。スケジューラは API サーバーをリッスンします。新しいポッドを作成する必要がある場合、スケジューラはポッドをバインドするノードを選択する責任があります。
  • Kubelet: 各ノードには、マスターによってノードに送信される特定のタスクを担当し、ノード上の Pod とコンテナを管理する Kubelet があります。
  • API サーバー: Kubernetes クラスター全体に対して、API サーバーは Kubernetes API を内部コンポーネントまたは外部プログラムに公開し、Kubernetes 操作を完了します。さまざまなコンポーネントは、ブリッジとして API サーバーを介して間接的に通信し、コンポーネント間の完全な分離を保証します。

ビジネス担当者が管理プラットフォームを操作してクラスター作成のリクエストを送信してからクラスターが作成されるまでのプロセス全体は、次のようになります。

  1. ビジネス系の学生は管理プラットフォームを操作してアップグレード操作を実行し、管理プラットフォームは HTTP 経由で API サーバーにリクエストを送信します。
  2. API サーバーはリクエストパラメータを処理および解析し、API サーバーを介して etcd に作成される Pod 情報を保存します。
  3. スケジューラは、API サーバーの監視メカニズムを使用して新しいポッドを表示し、ノードをポッドにバインドしようとします。
  4. 事前選択後、不適当なノードが選別され、特定のルールに従って候補ノードから最も適したノードが選択されます。
  5. 選択したノードとポッドに対してバインド操作を実行し、関連する結果を API サーバー経由で etcd に保存します。
  6. 対応するノードの Kubelet プロセスはコンテナ ランタイムを呼び出してコンテナを作成します。

2. ポジショニングの問題

2.1 トラブルシューティング

1.2 からわかるように、API サーバーは、ポッドの作成、外部リクエストの解析、etcd の読み取りと書き込みのプロセスにおける中間ブリッジとして非常に重要な役割を果たします。そのため、まず API サーバー プロセスが配置されているホスト マシンのパフォーマンス インジケーターとログをチェックして、何か問題がないか確認することにしました。

現在、トラフィックの負荷分散を実現するために、オンライン環境で API サーバーを実行しているホストが 3 台あります。異常な時間帯における eth2 ネットワーク カードの受信トラフィックを次の図に示します。

3 つの API サーバー ホストの監視データから、サーバー A のネットワーク カード流入トラフィックが他の 2 つよりもはるかに高く、ほとんどのリクエストがサーバー A に送信されていることがわかりました。

3 台のサーバーの API サーバーの CPU 使用率を比較すると、サーバー A の API サーバー プロセスの CPU 使用率は 2000% (20 コア) 前後で変動しているのに対し、他の 2 台のサーバーの API サーバーの CPU 使用率は 100% (1 コア) を超えていないことがわかりました。これにより、A の API サーバー プロセスがリクエストの大部分を処理していることがさらに確認されます。

サーバーAのAPIサーバーログを確認すると、以下のログが大量に出力されていることがわかりました。

このログは、Pod のステータスを照会するために、API サーバーを介して etcd に大量のリクエストが送信されたことを示しています。

Kubernetes バックエンドのストレージについては、現在 5 つの etcd ノードを使用して etcd クラスターを形成しています。ノードの 1 つ (E1) にログインした後、E1 ノードでコマンド「etcdctl ls /registry/pods/default」などの etcd 操作コマンドを実行すると、コマンドの実行が頻繁にタイムアウトすることが判明しました。より多くの Kubernetes 技術専門家とコミュニケーションを取りたい場合は、私の WeChat liyingjiese を追加し、「グループに参加」と記入してください。当グループは、世界中の大手企業のベストプラクティスや業界のトレンドを毎週レポートしています。

5 つの etcd ノードのトラフィックを比較すると、1 つのノードのネットワーク カードの受信トラフィックが他の 4 つのノードの受信トラフィックよりもはるかに高いことがわかりました。このノード (E1) の etcd プロセスの CPU 使用率は約 100% で、残りの 4 つのノードよりも大幅に高くなっていました。ノード E1 の etcd プロセス ログを確認すると、次のエラーが頻繁に表示されます。

ノード E1 の負荷が非常に高く、ノード間の同期ハートビートがタイムアウトし、外部要求に正常に応答できないことが推測されます。

2.2 問題分析

上記の調査の後、主な焦点は次の 2 つの問題にあります。

2.2.1 負荷分散戦略の失敗

まず、Kubernetes クラスターの操作リクエストのほとんどが特定の API サーバーに集中し、その結果、API サーバーの 1 つに高い負荷がかかっていることがわかります。この場合、負荷分散戦略に何らかの問題がある可能性があります。まず、現在の負荷分散戦略を見てみましょう。

当社は現在、Tencent からデータセンターを借りており、TGW (Tencent Gateway) システムでサポートされている負荷分散戦略を使用しています。 Tencent Cloud に関する関連紹介は次のとおりです。

TGW 負荷分散戦略は、要求の分散転送を保証し、リソース サーバー (RS) で自動的に生存検出を実行します。 TGW に接続されている IP ポートを検出するために、ハートビート パケットが 1 分ごとに送信されます。

TGW 関連の構成は次のとおりです。

  1. ドメイン名の解決: API サーバーにアクセスする必要があるすべての物理マシンに対してローカル DNS を設定し、固定ドメイン名 (D) を特定の VIP (V) (TGW によって提供される仮想 IP) に解決します。
  2. TGW サービスの RS リストを構成します。3 つの API サーバー ノードに対応する物理 IP アドレスを RS リストに追加します。

通常の状況では、API サーバーにアクセスする必要があるすべての要求は、最初にローカル ドメイン名から仮想 IP V に解決され、要求データ パケットが V に送信されます。V は、TGW の外部アクセス ポイントに相当します。次に、要求データ パケットは、TGW の内部負荷分散戦略を通じて宛先ネットワーク アドレス変換 (DNAT) され、異なる RS に分散されます。

調査の結果、TGW の監視および検出モジュールはすべての RS にハートビート パケットを定期的に送信していましたが、TGW の監視および検出モジュールはサーバー A からの応答パケットしか受信できないことが判明しました。そのため、TGW はノード A のみが稼働していると判断し、すべての要求データ パケットは最終的に TGW によってサーバー A に転送されました。これが、負荷分散戦略が失敗した根本的な理由です。

ここでのもう 1 つの現象は、etcd クラスター内の 1 つのノードだけに高い負荷がかかるのはなぜかということです。

5 ノードの etcd クラスターのうち、1 つのノードのみ負荷が高く、他のノードは正常です。サーバーAのAPIサーバーのログを確認すると、同じetcdノードに固定的に大量の読み取りリクエストが送信されていることがわかります。

この現象については、バックエンド ストレージにアクセスする API サーバーのソース コードを確認するとわかります。現在、オンライン Kubernetes は v1.7.12 のソース コードに基づいてコンパイルされ、実行されています。 API サーバーは、内部で etcd クライアントを初期化して etcd にアクセスし、etcd クライアントを介して etcd サーバーにリクエストを送信します。 etcd クライアントには、v2 と v3 の 2 つのバージョンがあります。オンライン API サーバーは、v2 バージョンのクライアントを使用します。メインコードは次のとおりです。

  1. // etcd の作業を初期化する
  2. func New(cfg Config) (クライアント、エラー) {
  3. c := &httpClusterClient{//httpタイプのクライアントを返す
  4. クライアントファクトリー: newHTTPClientFactory(cfg.transport(), cfg.checkRedirect(), cfg.HeaderTimeoutPerRequest)、
  5. rand: rand.New(rand.NewSource(int64( time .Now().Nanosecond()))), // 現在の時刻のランダムシードを渡します
  6. 選択モード: cfg.SelectionMode、
  7. }
  8.  
  9. エラーの場合:= c.SetEndpoints(cfg.Endpoints);エラー != ゼロ {
  10. nil、エラーを返す
  11. }
  12. c, nilを返す
  13. }
  14. //etcdリストをシャッフルする
  15. func (c *httpClusterClient) SetEndpoints(eps []string) エラー {
  16. ...
  17. neps、エラー:= c.parseEndpoints(eps)
  18. クロック()
  19. c.Unlock() を延期する
  20. c.endpoints = shuffleEndpoints(c.rand, neps) // etcdリストをシャッフルする
  21. c.ピン留め = 0
  22. ...
  23. ゼロを返す
  24. }
  25.  
  26. shuffleEndpoints関数(r *rand.Rand, eps []url.URL) []url.URL {
  27. p := r.Perm(len(eps)) //rankライブラリのPermメソッドは、[0, n)の間のランダムな配列を返すことができます。
  28. neps := make([]url.URL, len(eps))
  29. i , k := 範囲 p {
  30. ネップス[i] = eps[k]
  31. }
  32. ネップを返す
  33. }

etcd クライアントを初期化するときに、現在の時刻のランダム シードが渡され、すべてのエンドポイント (etcd ノード) の順序が乱れることがわかります。

etcd 上のすべての操作は、API サーバー内の etcd クライアントを介して etcd サーバーに http リクエストを送信することによって実行されます。主な方法は、次のメソッドを呼び出すことです。

  1. func (c *httpClusterClient) Do(ctx context.Context, act httpAction) (*http.Response, []byte, error) {
  2. ...
  3. i := ピン留めの場合; i < leps+ピン留め;私は++ {
  4. k := i % レップ
  5. hc := c.clientFactory(eps[k])
  6. resp、body、err = hc.Do(ctx、 action )
  7. ...
  8. ステータスコード/100 == 5の場合{
  9. スイッチ resp.StatusCode {
  10. http.StatusInternalServerError、http.StatusServiceUnavailableの場合:
  11. cerr.Errors = ...
  12. デフォルト
  13. cerr.Errors = ...
  14. }
  15. ...
  16. 続く 
  17. }
  18. k != ピン留めの場合 {
  19. クロック()
  20. c.ピン留め = k
  21. c.ロック解除()
  22. }
  23. 応答、本文、nil を返す
  24. }
  25. nil、nil、cerrを返す
  26. }

このメソッドは、リクエストが行われるたびに、固定されたノードからリクエストの送信を試行することを示します。要求が異常な場合は、初期化中に中断された次のノード (pinned++) からデータの送信が試行されます。 API サーバーが特定のエンドポイントを使用してデータを送信する場合、ノードが壊れていない限り、常にこのノード (固定) を使用してデータが送信されるようです。つまり、異常な状況がない限り、API サーバーは固定の etcd にリクエストを送信します。

etcd クラスターの場合、書き込み要求の場合、フォロワー ノードは処理のためにリーダー ノードに要求を転送し、その後、リーダーは同期のためにフォロワーに転送します。そうすれば、5 つのノードの CPU 負荷はそれほど不均衡にはなりません。しかし、2.1によると、API Serverのログを確認すると、読み取りリクエストが大量に発生していることがわかります。書き込み要求と比較して、読み取り要求はすべてのフォロワー ノードによって提供できます。つまり、負荷分散戦略の失敗により、大量のリクエストがサーバー A に転送されます。次に、A はすべてのクエリ要求を固定の etcd ノードに送信し、ノードが etcd クエリ要求の処理でビジー状態になり、負荷が急上昇します。

通常、TGW が負荷分散を実行すると、ハートビート検出モジュールと 2 つのリソース サーバー間の接続がないため、すべてのリクエストが誤って API サーバーの 1 つに転送されます。 etcd クライアントの v2 バージョンを使用する特定の API サーバーは、固定の etcd サーバーにのみリクエストを送信するため、負荷分散戦略全体が無効になります。

2.2.2 etcdデータアクセスが遅い

名前空間は分割されていません:

2.1 の API サーバー ログから、「Get /api/v1/namespaces/default/pods?...」など、Pod オブジェクト情報に対する多くの get 要求が確認できます。これらはすべて、デフォルトの名前空間から Pod 情報を取得するものであり、Pod 名前空間がオンラインで分割されていないことを意味します。

Kubernetes は名前空間を通じてコン​​テナ リソースを分離します。デフォルトでは、名前空間が指定されていない場合、作成されたコンテナはデフォルトの名前空間に分類されます。これにより、後でコンテナのメタデータ情報を etcd に保存する際にも落とし穴が残ります。次の図に示すように、すべての Kuberentes メタデータは etcd の /registry ディレクトリに保存されます。

Kubernetes 内の Pod 情報は、/registry/pods/#{namespace}/#{specific instance name} のディレクトリ構造に保存されます。名前空間が指定されていない場合は、デフォルトの名前空間に保存されます。つまり、すべてのオンライン Pod オブジェクト情報は /registry/pods/default ディレクトリに保存されます。

つまり、名前空間の分割が行われないため、Pod オブジェクト情報に対する大量の get リクエストは、毎回デフォルトのサブディレクトリにアクセスすることになります。各リクエストはグローバル検索に相当します。クラスターの数が増えると、このサブディレクトリにポッドが常に保存され、検索パフォーマンスがどんどん低下します。

クエリ結果はキャッシュされません:

2.1 の API サーバー ログから、多くの Get/List 操作を確認できます。次に、関連するメソッドの実行プロセスを詳しく見ていきます。以下は、List メソッドの実行中に呼び出される中間関数です。

  1.  
  2. unc (c *Cacher) GetToList(ctx context.Context, key string, resourceVersion string, pred SelectionPredicate, listObj runtime.Object) エラー {
  3. リソースバージョン == ""の場合{
  4. return c.storage.GetToList(ctx, key , resourceVersion, pred, listObj) //etcdを直接クエリする
  5. }
  6. listRV、エラー:= ParseListResourceVersion(resourceVersion)
  7. ...
  8. obj, exists, readResourceVersion, err := c.watchCache.WaitUntilFreshAndGet(listRV, key , trace) //キャッシュから取得
  9. ...
  10. ゼロを返す
  11. }

ご覧のとおり、GetToList メソッドに渡される resourceVersion パラメーターがあります。設定されている場合、キャッシュから取得されます。設定されていない場合は、etcd で照会されます。これも重要なポイントです。 resourceVersion の関連する使用法は次のとおりです。

  • 設定されていません: API サーバーを介して etcd から読み取ります。
  • 0 に設定: API サーバーと etcd への負荷を軽減するために、API サーバーのキャッシュから読み取ります。たとえば、Kubelet は Node オブジェクトを取得するためにこのメソッドを頻繁に使用し、List も Kubernetes Infomer が初めて起動されたときにオブジェクトを取得するためにこのメソッドを使用します。
  • 0 より大きい: オブジェクトの指定されたバージョンを読み取ります。

オンライン管理プラットフォームが http インターフェースを介して Pod 情報を照会する場合、resourceVersion は設定されません。したがって、Get/List メソッドを通じてリソースが取得されるたびに、etcd がクエリされます。その結果、etcd への頻繁かつ高頻度のクエリにより、etcd に大きな負荷がかかります。キャッシュ戦略を有効にすると、etcd へのアクセスの負荷が軽減されるだけでなく、クエリの速度も向上します。

上記の 2 つの点をまとめると、すべてのリクエストが固定の API サーバーに送信され、API サーバー ノードに高い負荷がかかります。同時に、API サーバーは固定の etcd ノードにクエリ要求を送信します。ただし、リクエストの結果は API サーバー側ではキャッシュされません。毎回、etcd が直接クエリされます。 etcd から Pod 情報を取得する場合、デフォルトのサブディレクトリからグローバル検索が実行されます。各リクエストには時間がかかるため、固定の etcd が大量の時間のかかるリクエストを常に処理することになり、最終的に etcd リソースが枯渇し、過度の負荷が発生します。その結果、クエリ結果を API サーバーに時間内に返すことができず、Pod 作成時に関連情報を取得できず、Pod 作成作業を実行できなくなります。したがって、最終的な現象は、クラスターの展開が長時間停止することです。

3. 解決策

負荷分散ソリューションを切り替える: 各 API サーバー ノードでトラフィックのバランスをとるために、一時的に DNS ポーリングに切り替えます。同時に、特定のネットワーク セグメントで TGW が RS および TGW サービスのハートビートを検出できない問題とその後の改善についてフォローアップします。

Kubernetes のポッドは複数の名前空間に分割されます。現在、すべてのオンライン Pod はデフォルトの名前空間に分割されています。 Pod 情報が読み取られるたびに、etcd から名前空間全体が取得されるため、etcd のパフォーマンスが低下します。現在、Pod 名前空間が細分化されており、Pod 情報の読み取りが高速化され、etcd のパフォーマンスの低下が軽減されています。

etcd v3 クライアントはエンドポイントを定期的に中断します。同じ API サーバーへのリクエストが常に特定の etcd に届かないように、後でバージョン v3 にアップグレードする予定です。この方法では、負荷分散戦略が失敗した場合でも、etcd リクエストは分散され続けます。

Kubernetes リソース情報を照会するときは、resourceVersion を入力してキャッシュ メカニズムを有効にし、etcd へのアクセス負荷を軽減します。

4. まとめ

API サーバーの遅延問題をトラブルシューティングするプロセスから、潜在的な問題は長期間存在し、それが一定量蓄積されたときにのみ問題の影響が顕著になることがわかります。これには、Kubernetes 関連コンポーネントのパフォーマンス指標とログに常に注意を払い、Kubernetes のさまざまなデフォルト ポリシーとパラメーターに精通し、ソース コード レベルで重要な機能モジュールを理解する必要があります。この方法によってのみ、潜在的なリスクを回避し、問題が発生したときにすぐに特定して、生産環境の安定した健全な運用を確保できます。

<<:  クラウドコンピューティングとエンタープライズソフトウェアが世界のIT支出増加を牽引

>>:  理想的なIaaS契約を策定する方法

推薦する

ソーシャルメディアがSEOに与える4つの直感的な影響を分析する

ソーシャル メディアが SEO に影響を与えることは誰もが知っていますが、ソーシャル メディアは S...

2019年ダブル11の最新ブランドマーケティング事例

ダブル11の先行販売は10月20日に始まります。この期間中に目立つために、大手ブランドはさまざまなマ...

原著論文が掲載されていない場合の救済策の分析

百度は現在、独自のスパークプランを立ち上げており、ウェブマスターは自分のウェブサイトにオリジナルコン...

3月にドメイン名販売業者から提供されたプロモーション オファーの一覧

長い間、ドメイン名には注目していませんでした。一般的なドメイン名販売業者のプロモーション情報をお送り...

戴志康:中小規模のウェブマスターにとってのチャンスは垂直分割産業にある

最近のニュースによると、2012年中国ウェブマスターカンファレンスの重慶大会が4月21日に重慶南平国...

電子商取引ウェブサイトマーケティングを成功させる秘訣の分析例

インターネット マーケティングは、従来のマーケティングに徐々に取って代わってきました。インターネット...

Amazon クラウド: クラウドへの移行のための 6 つの戦略

6 つのアプリケーション移行戦略: 「6 つの R」 私たちがよく目にする 6 つの最も一般的なアプ...

クラウドコンピューティング市場は2028年までに1兆2,664億ドルに達する

人工知能と機械学習はクラウド コンピューティング市場を牽引し、2028 年までに 1 兆 2,664...

imidc台湾データセンター高品質BGPネットワーク独立サーバーの簡単な評価

台湾サーバーは、香港とマカオを除いて、物理的に中国本土に最も近いです。そのため、台湾サーバーの高速性...

テンセントクラウドビッグデータプラットフォームは、毎日のリアルタイムコンピューティング量が40兆を超え、引き続きリードしています。

9月11日、テンセントグローバルデジタルエコシステムカンファレンスで、テンセントクラウド副社長の劉宇...

Dynatrace: AI を活用して自律型エンタープライズ クラウド自動運転を実現

[51CTO.comからのオリジナル記事] 中国の企業ユーザーのクラウドへの移行はますます加速してお...

生態フローレポート

生態交通の状況を全体的にご紹介したいと思います。 QuestMobileのデータによると、数年にわた...

Oracle Cloud の可観測性および管理プラットフォームが利用可能になりました

新しい統合ソリューションにより、企業はOracle Cloud Infrastructure、マルチ...

K8SのPV/PVC/StorageClassをわかりやすく説明する

一言でまとめると、PV と PVC は、K8S がストレージ管理に使用するリソース オブジェクトです...