世界と神々を震撼させたKubernetesの大きなバグ

世界と神々を震撼させたKubernetesの大きなバグ

Kubernetes は 3 月に本番環境と非本番環境で完全にリリースされたため、デプロイメントの問題については一安心できると思いました。しかし、本番環境と非本番環境で断続的に奇妙な問題が見つかりました。今週3日間デバッグして、諦めかけた時に原因を見つけました。この欠陥は、GitHub の現在のマスター ブランチを含む、現在 (2018-5-23) までのすべての Kubernetes バージョンに影響します (以下の誇張された更新を参照)。その結果、特定の状況下では Kubernetes サービスがサービスを提供できなくなり、非常に悪い影響が生じます。

この問題の現象は、1 つ以上の Kubernetes サービスに対応する Kubernetes エンドポイントが数分から数十分の間消え、その後再び現れ、その後再び消えるというケースがあるというものです。 「kubectl get ep --all-namespaces」で継続的にクエリを実行すると、AGE が分単位の一部のエンドポイントが突然消えたり、AGE が約 1 分から突然減少したりすることが確認できます。エンドポイントが不安定になると、必然的に、対応する Kubernetes サービスが安定したサービスを提供できなくなります。誰かが Github で問題を報告しましたが、開発者はそれに気付かず、解決もしていません。

まず、Kubernetes の一般的な実装メカニズムについて説明します。これにより、次のケース解決プロセスを理解するのに役立ちます。

Kubernetes の実装原理は、CFengine、Ansible、Salt などの構成管理ツールと非常によく似ています。つまり、構成の収束です。つまり、予想される構成と実際の構成を常に比較し、実際の構成を修正して予想される構成に収束させます。

Kubernetes の主要なシステム サービスは、api-server、scheduler、controller-manager です。 api-server は、Kubernetes システムのアクセス ポイントとして、また etcd ストレージのプロキシ サーバーとして機能します。 Service、Deployment、ReplicaSet、DaemonSet、Pod、Endpoint、ConfigMap、Secret など、Kubernetes 内のすべてのリソース オブジェクトは、protobuf 形式でシリアル化され、api-server によって形式がチェックされた後、etcd に保存されます。これは「期待される構成」を記述するプロセスです。

コントローラー マネージャー サービスには、さまざまなリソース タイプに対応する多数のコントローラー インスタンスが含まれています。


v1.12.0-alpha.0/cmd/kube-controller-manager/app/controllermanager.go#L317

これらのコントローラーが行うことは収束です。api-server の watch API を介して間接的に etcd を監視し、etcd 内のデータの変更ログを収集し、変更された (ADD、DELETE、UPDATE を含む) リソース (つまり、予想される「構成」) を kubelet + Docker によって収集された情報 (実際の「構成」) と 1 つずつ比較して、差異を修正します。

たとえば、Pod リソースが etcd に追加されると、コントローラーはスケジューラーにスケジュールを要求し、次に kubelet に Pod の作成を要求します。サービス リソースが etcd から削除されると、対応するエンドポイント リソースも削除され、このエンドポイント削除操作は kube-proxy によって監視され、実際の iptables コマンドがトリガーされます。

コントローラが watch を介して変更ログを取得するのは、単なる実装の最適化であることに注意してください。すべてのリソース オブジェクトを定期的に取り出して、1 つずつ判断するわけではありません。クラスターの規模が大きい場合、このような完全な収束は非常に遅くなり、クラスターのスケジューリングと自己修復も非常に遅くなります。

全体像を理解していれば、エンドポイントが誤って削除された場所を簡単に見つけることができます。


v1.12.0-alpha.0/pkg/コントローラ/エンドポイント/endpoints_controller.go#L396

では、どのような状況で Services(namespace).Get(name) がエラーを返すのかという疑問が生じます。コメントから、サービスが削除されると、397 行目に移動して不要なエンドポイントが削除されることがわかります。エンドポイントはサービス用であり、サービスが存在しない場合はエンドポイントが存在する意味がないためです。

そこで疑問が生じます。 「kubectl get svc」を実行すると、問題発生期間中、サービス リソースは常に存在しており、再構築も新規展開も行われていないことがわかります。実際、数か月間にわたって多くのサービス リソースが作成されています。対応するエンドポイントが誤って削除され、再構築されるのはなぜですか?この問題は2か月間私を悩ませてきました。かなりの時間と脳細胞を費やしました。今日、諦めかけていた時に、突然大きな発見がありました。

自社で構築したKubernetesクラスタではX509認証とRBAC権限制御が有効になっているため、簡単に確認できるようkube-apiserverの監査ログを有効にしました。問題が発生すると、監査ログに特別なパターンが記録されます。


jq コマンドを使用して抽出された監査ログ スニペット

監査ログでは、各エンドポイントの削除と作成が発生する前に、「/api/v1/services...watch=true」の監視呼び出しが行われます。前述のように、コントローラー マネージャーは etcd 内のリソースの変更ログを継続的にクロールする必要があります。ここでの奇妙な問題は、この呼び出しの「resourceVersion=xxx」のバージョン値が変更されないままであることです。 resourceVersion が増加し続けているため、変更ログの最後からクロールし続けるべきではないでしょうか?

苦労して推測した後、ついに「変更ログをクロールする」コードを見つけました。


v1.12.0-alpha.0/ステージング/src/k8s.io/client-go/tools/cache/reflector.go#L394

上記のコードにおける resourceVersion の処理ロジックは、イベント リストをトラバースし、現在のイベントの「resourceVersion」フィールドをクロールする新しい「開始 resourceVersion」として取得することです。そのため、トラバースが完了した後、「開始 resourceVersion」は最後のイベントの「resourceVersion」であることは明らかです。

さて、イベント リストがどのようになっているか見てみましょう。上記の api-server リクエストをコピーすると確認できます。

  1. kubectl プロキシ
  2. curl -s localhost:8001/api/v1/services?resourceVersion=xxxx&timeoutSeconds=yyy&watch=t


/api/v1/services の出力は参考用です。上記の監査ログと同じ期間ではないことに注意してください。

resourceVersion が増加していないことは明らかです。

この非増分的な問題は、実は非常に簡単に理解できます。 ResourceVersion はイベントのシリアル番号ではなく、Kubernetes リソース自体のバージョン番号です。 etcd を Subversion と見なすと、どちらもグローバルに増加するバージョン番号を持ち、Kubernetes リソースは Subversion に保存されたファイルと見なします。ファイル A を svn リビジョン = 100 で保存すると、A のバージョンは 100 になります。その後、他のファイルへの変更を svn に送信し続け、特定のバージョンで A を削除します。この時点では、svn log で確認できる関連ファイルの「自身の最終変更バージョン」は増分ではなく、最初のエントリの「自身の最終変更バージョン」は 100 です。

つまり真実が明らかになったのです。 etcd の「changelog」をトラバースする reflector.go のコードは、イベント シーケンスの resourceVersion が増加していると誤って認識しますが、ほとんどの場合はこれが当てはまります。このコードは普遍的であり、すべてのリソースが影響を受けるため、非常に深刻なバグです。多くの人が報告しているのを見てきました (私自身も遭遇しました)。controller-manager がエラー「reflector.go:341] k8s.io/kubernetes/pkg/controller/bootstrap/bootstrapsigner.go:152: watch of *v1.ConfigMapended with: too old resource version:」を報告しています。これはおそらく、非常に古いリソース バージョンから etcd の「変更ログ」を誤ってクロールしているが、etcd は古すぎる変更ログをすでに「圧縮」しているために発生します。

***このバグをトリガーして回避する方法をまとめると次のようになります。

トリガー

Kubernetes サービス オブジェクトが次々に作成、削除、作成され、その後、「kubectl delete svc xxx」を使用して、作成時刻が古いサービスが削除されます。つまり、より小さい resourceVersion を持つレコードがサービス イベント リストの最後に挿入され、これにより、コントローラー マネージャーは、すでにクロールされているサービス イベント リストからクロールして再生し、その後、サービスの ADDED イベントと DELETED イベントを再生します。その結果、コントローラー マネージャー メモリにキャッシュされたサービス オブジェクトが削除され、EndpointController は「存在しない」サービスに対応するエンドポイントを削除します。

バイパス

docker restart を使用して kube-controller-manager コンテナを再起動し、イベント リスト *** の位置から強制的にクロールを開始します。

resourceVersion が etcd の最新のグローバル バージョン番号となるサービスを作成します。この ADDED イベントはイベント リストの最後に表示されるため、コントローラー マネージャーはこの最新のリソース バージョンからクロールを開始します。

「kubectl edit」を使用してサービスを変更し、意味のないラベルなどを追加して保存します。これにより、リソース バージョンも更新されます。原理は以前の回避策と同じです。

間違った方向

  • 「kubectl delete pod/xxx」: サービスイベントリストには影響しません
  • 「kubectl delete deploy/xxxx」: サービスイベントリストには影響しません

サービス、構成マップ、シークレットなど、作成後に基本的に変更されない Kubernetes 内のすべてのリソースがこれによって影響を受けます。バージョン番号が非常に古いため、いずれか 1 つを削除するとこのバグが発生します。たとえば、configmaps は繰り返し更新される可能性があり、コンテナーにマップされた config ファイルも継続的に更新されます。構成ホットリロードをサポートしていないサービスには影響はありません。構成ホット リロードをサポートするサービスの場合、構成ファイルが頻繁にリロードおよび再初期化されるため、予期しない問題が発生する可能性があります。

バグの修正は非常に簡単です。イベント リストを走査して resourceVersion を選択するときは、常に max(event.resourceVersion, currentResourceVersion) を取得します。 Kubernetes 担当者にプルリクエストを送信しました。私の大まかな修正によって新たな問題が起こらないことを願っています。

<<:  クラウドコンピューティングのコスト見積もりで無視できないいくつかの要素

>>:  主流の分散ストレージ Ceph がプライベートクラウドのリーダー VMware とどのように衝突するかをご覧ください

推薦する

BMW グループと AWS が協力し、自動車業界のデータ主導型イノベーションを推進

12月9日、Amazon Web Services (AWS) とBMWグループは、データとデータ分...

マルチクラウドインフラストラクチャでは制御が重要

[[208125]]開発者や DevOps マネージャーにパブリック クラウドの経験について尋ねると...

手を携えて疫病と闘う:青騰はSaaSホストセキュリティ製品を全国に無料で提供すると発表した

COVID-19の流行状況下で、全国の人々が困難を乗り越えるために協力しています。各方面の共同の努力...

SEOVIP が降格され、Baidu はそれが偶発的な傷害だと言った。草の根に対する偶発的な傷害だと言ったのは誰ですか?

SEOVIP は、今では誰もが知っている SEO 業界の有名なウェブサイトです。その理由は、このウェ...

ウェブサイト運営: ユーザーに「7年目の痒み」を起こさせない

ほとんどのウェブマスターはKaixin.comをよく知っているはずです。一日懸命に働いた後、農場から...

どのようなコンテンツが共有され、広まる可能性が高いでしょうか?

インターネットを通じてコン​​テンツが拡散する力はすごいですが、もちろん一部の**ビデオを除いて、イ...

推奨: turnkeyinternet-格安ハイエンド KVM VPS/1000M 無制限トラフィック/ロサンゼルス

Turnkeyinternet は、1999 年からホスティング事業を運営しているアメリカの老舗ブラ...

DIY SEOの4つのステップは一度きりの作業ではありません

簡単に言えば、SEO は 4 つのステップに分かれています。1 つ目は基本的な基準を確立すること、2...

gcorelabs: 香港 VPS、50~200M 帯域幅、月額 4.99 ドル、KVM/512M メモリ/1 コア/20g SSD/500g トラフィック

ほとんどの人は gcorelabs を知っているはずです。世界中に数十のデータセンターを持ち、CDN...

中国のエッジクラウド市場規模は2022年後半も成長を続け、前年比50%を超える見込み

最近、International Data Corporation(IDC)が発表した最新の「中国エ...

ウェブサイトデザイン分析: モジュール化 - 効率的なリファクタリング

モジュール化と言えば、おそらく最初に思い浮かぶのはプログラミングにおけるモジュール設計でしょう。モジ...

ウェブサイト構築の鍵はロングテールキーワードにある

ロングテールキーワードの役割は疑う余地がなく、ウェブサイトのコンバージョン率を高めたい場合は、主にロ...

Kubernetes で Minecraft を実行する

休暇中に、ずっと前から話題になっていたMinecraftを子供たちにインストールしてみました。それを...