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 リクエストをコピーすると確認できます。
/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」を使用してサービスを変更し、意味のないラベルなどを追加して保存します。これにより、リソース バージョンも更新されます。原理は以前の回避策と同じです。 間違った方向
サービス、構成マップ、シークレットなど、作成後に基本的に変更されない Kubernetes 内のすべてのリソースがこれによって影響を受けます。バージョン番号が非常に古いため、いずれか 1 つを削除するとこのバグが発生します。たとえば、configmaps は繰り返し更新される可能性があり、コンテナーにマップされた config ファイルも継続的に更新されます。構成ホットリロードをサポートしていないサービスには影響はありません。構成ホット リロードをサポートするサービスの場合、構成ファイルが頻繁にリロードおよび再初期化されるため、予期しない問題が発生する可能性があります。 バグの修正は非常に簡単です。イベント リストを走査して resourceVersion を選択するときは、常に max(event.resourceVersion, currentResourceVersion) を取得します。 Kubernetes 担当者にプルリクエストを送信しました。私の大まかな修正によって新たな問題が起こらないことを願っています。 |
<<: クラウドコンピューティングのコスト見積もりで無視できないいくつかの要素
>>: 主流の分散ストレージ Ceph がプライベートクラウドのリーダー VMware とどのように衝突するかをご覧ください
死亡者数と感染者数は依然として増加しています。この突然の新型コロナウイルス感染症の流行により、常に重...
MoeCloudはダブルイレブンのプロモーションを逃しましたが、遅くてもやらないよりはましです。公式...
onevps は Think Huge Ltd. の VPS サブブランドです。主に KVM 仮想化...
編集者注: この記事の著者は、E-House China の副社長兼 Leju Internet G...
Baidu による新規サイトの検査がますます厳しくなるにつれ、ウェブサイトのコンテンツが毎日更新され...
私は半年以上自分のウェブサイトを構築しており、初心者レベルのウェブサイトビルダーと言えるでしょう。現...
今日、私のウェブサイトのログにこのようなIPが見つかりました。その時はかなり緊張しました。以前、Ba...
ウェブサイトの外部リンクはウェブサイトのランキングに影響を与える鍵です。これはあまり説明しなくても誰...
v6node は今年設立された VPS 会社で ()、IPv6 ベースの VPS に注力しています。...
7月29日〜30日、2020 Trusted Cloud Conferenceがオンラインで開催され...
本誌記者 竇睿星Weibo APPマーケティングが一部の広告主に好まれる理由の1つは、その正確性です...
マネージド クラウド サービス プロバイダー (MCSP) は通常、顧客のクラウド プラットフォーム...
初期の開発中に Web サイトを引き継いだ Web マスターであれば、おめでとうございます。これは良...
Apache Kafka は、大手インターネット企業で広く使用されている分散型オープンソース ストリ...
[51CTO.com からのオリジナル記事] 2017 年は中国におけるクラウド コンピューティング...