最近、この分野の専門家である Vallery Lancey 氏と Kubernetes について話し合いました。具体的には、既存の Kubernetes との互換性にこだわることなく、新しいオーケストレーション システムをゼロから設計する場合、どのような異なるアプローチを取ることができるでしょうか。会話がとても有意義だったので、頭に浮かんだ多くの考えを記録する必要性を感じ、この投稿をすることにしました。
最後に、いくつかの点を強調したいと思います。
設計原則 私のこれまでの Kubernetes の実践経験は、2 つのまったく異なる分野から来ています。1 つはベアメタル クラスター用の MetalLB を保守することです。もう 1 つは、GKE SRE で大規模なクラスターをサービスとして運用することです。これら 2 つの経験から、Kubernetes は非常に複雑であると感じました。現在市場で宣伝されている効果を達成するには、多くの準備作業が必要になることが多く、試してみたいユーザーのほとんどはこれに十分な準備ができていません。 MetalLB を保守した経験から、Kubernetes と統合する堅牢なソフトウェアを構築するのは非常に難しいことがわかりました。 MetalLB は非常に安定していると思いますが、Kubernetes で誤って設定してしまう可能性は依然として高く、デバッグも非常に困難です。 GKE SRE 運用の経験から、最高の Kubernetes エキスパートであっても、大規模な Kubernetes クラスターをエラーなしで運用することはできないことがわかりました (ただし、GKE SRE はいくつかのツールの助けを借りて優れた機能を発揮します)。 Kubernetes は、オーケストレーション ソフトウェアの C++ に例えることができます。これは強力で機能が豊富で、シンプルに見えるかもしれませんが、その原理をすべて理解するために多くの時間と労力を費やすまでは、何度もつまずくことになるでしょう。それでも、Kubernetes を構成および展開する方法は数多くあり、エコシステムもまだ進化しているため、立ち止まって休憩できると感じるのは難しいです。 この類推に従うと、私にとって理想的なリファレンスは Go です。 Kubernetes が C++ だとしたら、オーケストレーション システムにおける Go はどのようなものになるでしょうか?非常に簡潔で、機能が豊富で、ゆっくりと慎重に新しい機能が追加されており、1 週間以内に習得して仕事に使用できるものです。 次に、上記の原則に従って作業を開始しました。 Kubernetes を再設計する場合、既存のものとの互換性を無視して、別のアプローチを採用することができます。どのような点を考慮すべきでしょうか? 変更可能なポッド Kubernetes では、ほとんどの (ただしすべてではない) Pod は作成後に変更できません。 Pod を直接変更したい場合は、いいえです。新しいものを作成してから、古いものを削除する必要があります。これは、Kubernetes のほとんどのリソース オブジェクトの処理方法とは異なります。Kubernetes では、ほとんどのオブジェクトが変更可能であり、予想される状態が変化すると、実際の状態が予想される状態と一致するように適切に調整されます。 したがって、私は Pod を例外にしたくありません。完全に読み取り/書き込み可能にし、他のリソース オブジェクトと同じチューニング ロジックを持たせる予定です。 最初に思いついた解決策は、その場でやり直すことでした。ポッドのスケジュール制約と必要なリソースが変更されていない場合、それがどのように達成されると思いますか? SIGTERM シグナルを送信して runc を終了し、異なるパラメータで runc を再起動すると、再起動が完了します。このように、Pod は以前の systemd サービスに少し似ており、必要に応じて異なるマシン間を移動できます。 実行時レベルで可変性を操作する必要がないことに注意してください。ユーザーが Pod 定義を変更した場合でも、コンテナを強制終了して新しい構成で再起動することは可能です。 Pod は元のノードで予約されたリソースを引き続き保持するため、概念的には systemctl restart blah.service と同等です。実行時レベルで Pod の更新を実行することもできますが、必須ではありません。これを行わないことの主な利点は、スケジューリング、Pod ライフサイクル、およびランタイム ライフサイクル管理を分離できることです。 バージョン管理 Pod の設計について引き続き議論しましょう。Pod は変更可能になったので、次に検討するのは当然 Pod のロールバックです。これを実行するには、古い Pod バージョンの定義を保持しておき、「特定の古いバージョンにロールバックする」ことが簡単にできるようにします。 現在、Pod の更新プロセスは次のようになります。Pod 定義を更新するファイルを書き込み、期待される定義と一致するように更新します。アップデートが失敗しましたか?以前のバージョンにロールバックすると、プロセスは終了します。 上記のプロセスの利点は、いわゆる GitOps に頼ることなく、クラスター内のアプリケーションのバージョン反復を簡単に把握できることです。 GitOps には多くの利点がありますが、必要がない限り導入しないでください。非常に基本的な「クラスターに何が起こったのか」という質問に答えたいだけの場合、質問ですが、クラスター内のデータを使用するだけで十分です。 より多くのデザインの詳細も関係します。特に、外部変更(ユーザーが送信した Pod の変更)とシステム変更(Kubernetes 内からトリガーされた Pod 定義の変更)を区別したいと思います。これら 2 種類の変更の履歴をエンコードして、オペレーターやその他のシステム コンポーネントが利用できるようにする方法がまだわかりません。おそらく、新しいバージョンを送信するときに「修飾子」が自分自身を識別できるように、完全に汎用的に設計することもできます。ユーザーは、特定の編集者を指定して(または特定の編集者を除外して)、特定の種類の変更を照会することができます(タグ クエリの動作と同様)。ここでも、より多くの設計上の考慮が必要になります。私が確信しているのは、履歴バージョン レコードを照会できるバージョン管理機能を備えた Pod オブジェクトが必要だということです。 最後に、ガベージコレクションを考慮する必要があります。具体的には、各ポッドへの変更はデルタ圧縮でうまく機能するはずです。デフォルト設定では、すべての変更を保持し、一定量のデータが蓄積された後に、これに基づいて圧縮を実行します。すべての変更を保持すると、システムに多少の負担がかかりますが、「変更の頻繁な送信」によりシステムの他の部分への影響を回避できます。ユーザーは、集約の利便性のために、毎回 1 つのフィールドを変更して一連のバージョンを生成するのではなく、より少ない、より意味のある変更を行う必要があることに注意する必要があります。 履歴バージョン機能があれば、他の小さな機能も開発できます。たとえば、ノードはコンテナ イメージの最新バージョンをノード上に保持できるため、ロールバックが高速化されます。元のガベージ コレクションは、一定の時間が経過するとトリガーされました。履歴バージョンレコードを使用すると、必要なバージョン数をより正確に保持できます。一般に、すべてのオーケストレーション ソフトウェアは、ロールバックを高速化するために、さまざまなリソース オブジェクトの GC ルートとして古いバージョンを使用します。ロールバックはサービスの中断を回避するための基本的な方法であり、非常に価値のあるものです。 デプロイメントを PinnedDeployment に置き換える これは主にヴァレリーにインスピレーションを受けた短いセクションです。彼の PinnedDeployment 設計は素晴らしいです。 PinnedDeployment を使用すると、オペレーターは 2 つのバージョンのデプロイメントのステータスを追跡してアプリケーションのリリースを制御できます。これは SRE によって設計されたデプロイメント オブジェクトです。設計者は、デプロイメントにおける SRE の重点を非常によく認識しています。個人的にこのデザインがとても気に入っています。 これは、上記のバージョン管理可能でインプレース更新可能な Pod と非常にうまく組み合わせられており、他に追加するものは思いつきません。複数のポッドのワークフローを非常にわかりやすく説明します。 Kubernetes の制約から抜け出してこの新しいプロセスに適応するには、多少の調整が必要になるかもしれませんが、全体的な設計は非常に優れています。 明示的なオーケストレーションプロセス Kubernetes の「API マシン」メカニズムの最大の問題は、ワークフローを構成する独立した制御ループの緩やかな集合であるオーケストレーションであると私は考えています。表面的には、これは良いアイデアのように思えます。数十個の小さな制御ループがあり、それぞれが小さな機能を担当しているからです。これらがクラスターに統合されると、相互に連携してリソース オブジェクトの状態を調整し、目的の最終状態に収束します。それで何が問題なのですか? 問題は、何か問題が発生した場合、デバッグがほぼ不可能であることです。 Kubernetes の典型的なエラーは、ユーザーがクラスターに変更を送信し、リソース オブジェクトが期待どおりになるのを待つために繰り返し更新することです。短期間で期待に応えられなければ、問題があります。 Kubernetes は、「オブジェクトが期待どおりであった」ことと、「制御ループが壊れて他のものをブロックしている」ことの違いを区別できません。問題のある制御ループが、デバッグに役立つように、管理しているオブジェクトにいくつかのイベントを発行することを期待するかもしれませんが、一般的には、それほど多くのことは行われません。 この時点で、唯一実行可能なオプションは、関係する可能性のあるすべての制御ループのログを収集し、壊れているものを探すことです。すべての制御ループの動作メカニズムを深く理解していれば、エラーをより早く見つけることができます。豊富な経験により、どの制御ループにエラーがあり、リソース オブジェクトの現在の状態から回復しようとしているかを推測できます。 ここで注目すべき重要な点は、複雑性に関する私たちの視点が制御ループ設計者からクラスターオペレーターに移行したことです。単一のタスクを独立して実行できる制御ループを設計するのは簡単です(重要でないとは言いません)。ただし、クラスター内でこのような制御ループを数十個維持するには、オペレーターがこれらの制御ループの操作とそれらの間の相互作用を熟知し、緩く構成されたシステムを理解しようとする必要があります。これは真剣に検討しなければならない問題です。通常、設計者は制御ループのコードを記述してその機能性を検証しますが、これは 1 回限りのタスクです。しかし、運用および保守担当者は 1 日中コードに対処し、制御ループで発生する問題を繰り返し処理しなければならない場合があります。一度だけ実行する必要がある作業を簡素化することは、運用スタッフにとって不公平です。 この問題を解決するために、systemd のアプローチを参照します。これは、同様のライフサイクル管理の問題を解決します。現在の状態 A と目標状態 B がある場合、どのようにして A から B に移行するかという問題です。違いは、systemd では手順とその依存関係が明示されていることです。サービス ユニットが multi-user.target サービス グループの一部であることを systemd に伝えたので、ファイル システムをマウントした後、ネットワークに接続する前にサービス ユニットを起動する必要があります。また、sshd が実行されている限りサービスが実行されている必要があるなど、システムの他の特定のコンポーネントに依存することもできます (サイドカーのように聞こえますか?)。 これの利点は、systemd がシステムのどの部分に障害が発生したか、どの部分がまだ実行中か、またはどの前提条件が満たされていないかをユーザーに正確に伝えることができることです。 「どのサービスの起動に最も時間がかかるか」などの分析や問題箇所の特定のために、システム起動の実行プロセスを印刷することもできます。 これらの設計をクラスター オーケストレーション システムにバッチでコピーしたいと考えています。多少の微調整は必要ですが、大まかに言うと、制御ループは他の制御ループへの依存関係を宣言する必要があり、ユーザーが「Pod X に関連するすべての制御ループの操作ログ」を簡単に検索できるように構造化されたログを生成する必要があり、オーケストレーション システムはライフサイクル イベントを処理し、systemd のようなアプローチを使用して問題のあるサービス グループ ユニットを 1 つずつトラブルシューティングして特定する必要があります。 これは実際どのように機能するのでしょうか?まずは Pod のライフサイクルから始めましょう。おそらく、私たちが達成したい状態である抽象的な「実行中」ターゲットを定義します。つまり、ポッドが起動して実行されており、すべてが正常に動作している状態です。コンテナ ランタイムは、コンテナを起動するために、「実行」の前にタスクを追加します。ただし、ストレージ システムがネットワーク デバイスのマウントを完了するまで実行できない場合があるため、"storage" ターゲットの後で自動的に起動します。同様に、ネットワークの場合、コンテナは「ネットワーク」ターゲットの後に起動されることが予想されます。 これで、Ceph 制御ループは、ストレージの開始を担当するため、「ストレージ」ターゲットの前に実行されるようにスケジュールされます。その他のストレージ制御ループも同じ実行フローに従います (ローカル バインド マウント、NFS など)。ここでの実行フローはすべてストレージの準備が整う前に実行されるように宣言されているため、並行して実行できることに注意してください。ただし、他のストレージ プラグインの制御ループの前に実行されるか、後に実行されるかは関係ありません。例外があるかもしれません!たとえば、優れた機能を備えた優れたストレージ プラグインを作成したとしても、それを実行するには NFS マウントする必要があります。はい、nfs-mounts ステップに依存関係を追加するだけで完了です。これは systemd に似ており、他のコンポーネントが適切に動作するために必要な順序と厳格な要件の両方を指定するため、ユーザーはサービスの起動手順を簡単に定義できます。 (ここでは説明を少し簡略化し、さまざまなステップ間に循環依存関係があまりないことを前提としています。さらに深く掘り下げたい場合は、より複雑なプロセスに拡張できます。詳細については以下を参照してください。ここでは複雑すぎるプロセスについては説明しません。) これらの設計により、オーケストレーション システムは「なぜ Pod が起動しないのか」というユーザーの質問に答えることができます。ユーザーは、Pod の起動フローチャートをダンプして、どのステップが完了したか、どのステップが失敗したか、どのステップがすでに実行中であるかを確認できます。 NFS マウントは 5 分間続いていますか? NFS サーバーがダウンしているが、制御ループがタイムアウトしていない可能性はありますか?サービスのさまざまな構成と可能な状態を合計すると、非常に大きな結果マトリックスが作成されます。私たちが設計したようなデバッグ補助機能があれば、これは大きな問題にはなりません。 Systemd を使用すると、ユーザーは任意の順序と制約でサービス起動プロセスにコンテンツを追加できます。しかし、問題が発生した場合でも、簡単にトラブルシューティングできます。制約に基づき、デバッグ ツールの助けを借りて、まず問題の鍵を見つけることができます。 Systemd がシステムの起動にもたらす利点と同様に、これによりシステムはライフサイクル操作を可能な限り並行して実行できるようになりますが、それだけです。また、ワークフローは明示的であるため、拡張することもできます。クラスターには、特定のライフサイクル ステージで実行する必要がある各ポッドに対するカスタム操作がありますか?正しい事前条件または事後条件に依存する新しい中間ターゲットを定義し、ここで制御ループをフックしてコールバックを受信できます。オーケストレーション システムは、ライフサイクルの各段階で制御ループが機能することを保証します。 これにより、Istio などの奇妙な問題も解決されることに注意してください。 Istio では、動作させるために開発者が提供する追加の定義をいくつか挿入する必要があります。必要なし!ライフサイクル管理に介入し、実際のニーズに応じて調整を行うための対応する制御ループを提供します。ライフサイクルの特定の段階で操作を実行する必要があることをシステムに示すことができれば、操作を実行するために運用および保守担当者に追加のリソース オブジェクトを提供する必要はありません。 このセクションは長いですが、要点は簡潔です。これは Kubernetes のオリジナルの API 機構とは大きく異なるため、実装するには多くの新しい設計作業が必要になります。最も顕著な変更点は、制御ループがクラスター オブジェクトの状態を監視して修正するだけでなく、オーケストレーターが特定のオブジェクトへの呼び出しを完了するまで待機する必要があることです。これらのオブジェクトが期待される状態に達すると、制御ループはさらに応答します。アノテーションと規則を通じて、Kubernetes 上でこれを実装できるようになりました。しかし、仕組みの詳細を理解しなければ、観測可能性やデバッグ可能性の点では役に立ちません。 興味深いことに、Kubernetes にはすでにこれらのアイデアのいくつかのプロトタイプ実装、つまりイニシャライザーとファイナライザーがあります。これらは、ライフサイクルの 2 つの異なる段階で事前操作を実行するフックです。制御ループを 2 つのハードコードされた「ターゲット」に接続できます。制御ループを初期、デフォルト、最終の 3 つの部分に分割します。ハードコードされていますが、これは明示的なワークフロー図の始まりです。私はこの設計をより一般的なケースに一般化するつもりです。 明示的なフィールド所有権 前のセクションの設計を適度に拡張して、リソース オブジェクトの各フィールドを特定の制御ループによって明示的に所有するようにします。このループは、このフィールドに書き込むことができる唯一のループです。所有者が定義されていない場合、フィールドはクラスター オペレーターによって書き込み可能ですが、オペレーターは他に書き込むことはできません。これは、慣例ではなく、API メカニズムによって強制されます。 これが大多数のケースですが、フィールドの所有権が不明な場合もあります。これにより、2 つの問題が発生します。フィールドが間違っている場合、誰が責任を負うのかを判断するのが難しくなります。注意しないと、2 つのコントローラーがフィールドを変更し、ループに陥ってしまう可能性があります。 後者は、他のロードバランサーの実装と競合するため、MetalLB にとって大きな問題点となります。こんなことは起こるはずがない。 LB に関連するフィールドには 2 人の所有者が存在するため、Orchestrator はクラスターへの MetalLB の追加を拒否する必要があります。 フィールドに複数の所有者がいる状況をユーザーが処理できるように、バックドアを残しておく必要がある場合があります。しかし、簡単にするために、ここではそれを考慮せず、デザインが維持されるかどうか確認するつもりはありません。それを裏付ける強力な証拠がない限り、共有所有権は潜在的に危険な設計です。 制御ループで、読み取るフィールドを明示的に登録する (そして、登録されていないフィールドを削除する - 不正行為ではない) ことも要求すると、システムの収束を証明したり (読み取り -> 書き込み -> 読み取りループなし)、システムの応答性を低下させている呼び出しループを特定したりするなど、興味深いことが可能になります。 はい、IPv6のみ 私はKubernetesのネットワーク部分に非常に精通しており、最も完全に再構築したい部分です。ネットワークモジュールが現在の状況に発展した理由は数多くあります。 Kubernetes ネットワーク設計が役に立たないと言っているわけではありません。しかし、私のシステムにはインターネットがありません。このセクションは長いので、しばらくお待ちください。 まず、Kubernetes の既存のネットワークについては完全に忘れましょう。オーバーレイ ネットワーク、サービス、CNI、kube-proxy、ネットワーク プラグイン、これらはすべて不要になりました。 (ところで、ネットワークプラグインがK8sの理想的な設計に登場すべきではない理由。現在、多くの企業がネットワークプラグインを販売し始めています。それらが客観性や中立性を保てるとは信じない方が良いでしょう。反論する理由を挙げてみましょう。自然界であろうとソフトウェアの世界であろうと、すべてのエコシステムの第一の優先事項は、その存続を確保することです。エコシステムを自ら絶滅へと進化させることはできず、外部から絶滅を誘発する必要があります。) 話を元に戻すと、今はすべてゼロに戻っています。コンテナがあり、コンテナは互いに、そして外部の世界と通信する必要があります。それで何をすればいいのでしょうか? 各ポッドに IPv6 アドレスを割り当てましょう。はい、IPv6 アドレスは 1 つだけです。彼らはどこから来たのですか?ここでは、LAN が IPv6 をサポートすることが求められます (このような条件が満たされていると仮定すると、結局のところ、私たちの設計は将来志向です)。IP アドレスはここから取得されます。 IP アドレスの競合検出を行う必要はほとんどありません。 2^64 は十分な大きさであり、ランダムに生成された IP アドレスは基本的にニーズを満たすことができます。各ノードが互いを検出し、他のポッドがどのノードにあるかを見つけられるようにするメカニズムが必要です。これを実行するのは難しくありません。理由は単純です。クラスター ネットワークの残りの部分では、Pod がノードの 1 つで実行されているように見えるからです。 または、すべて一意のローカル アドレスを持つネットワークを作成し、各ノードで手動でルーティングを行うこともできます。これは実装が非常に簡単で、アドレスの割り当ては基本的に「ランダムに番号を選択する」だけです。ノード間のルーティングがより効率的になるようにサブセットを設計する必要がある場合もありますが、これはそれほど複雑なものではありません。 問題は、クラウド プロバイダーがネットワークのインフラストラクチャに関与したがることです。したがって、トラフィックの転送方法を AWS に説明するなどの操作を実行できるように、IPAM モジュールは (上記のワークフロー モデル内で) プラグ可能なままにしておく必要があります。ただし、GCP では IPv6 を使用できない可能性があります。 ただし、設計のこの部分を実行するには、さまざまな代替手段があります。本質的には、ノード間で IPv6 を使用し、基本的な単純なルーティングを構成したいだけです。この方法では、IPv6 には十分に大きなアドレス空間があり、ランダムな数字をいくつか選択するだけでよいため、ほとんど構成なしで Pod 間の接続の問題を解決できます。 より複雑な接続ニーズがある場合は、これらを追加のネットワーク インターフェイスとして接続し、私が設計したシンプルで予測可能な IPv6 ルーティングを使用できます。ノード間の通信のセキュリティを確保する必要がありますか? Wireguard トンネルを導入し、ルーティングを追加し、ノード IP を Wireguard トンネル経由でプッシュすれば完了です。トンネルが確立された後にノードが準備完了状態になるように、ノードのライフサイクル管理に小さな制御ループを追加する場合を除き、オーケストレーション システムはこれらの詳細を知る必要はありません。 さて、Pod 間の相互接続と、Pod と外部ネットワーク間の接続は解決されました。 IPv6 しかない場合、クラスターに流入する IPv4 トラフィックをどのように処理すればよいでしょうか? まず、Pod がインターネットに接続されている場合にのみ IPv4 が適用されることを規定します。クラスター内では IPv6 の使用が必須です。 この制限にはいくつかの方法で対処できます。簡単に言えば、各ノードに IPv4 トラフィックをカプセル化させ、ポッドの RFC 1918 仕様に準拠した小さなアドレス空間を予約させることができます (すべてのノードで予約されているアドレスはこの空間に属します)。これにより、IPv4 インターネットにアクセスできるようになりますが、これはすべてノードごとの静的構成であり、クラスターに表示される必要はまったくありません。 IPv4 関連のものをコントロール プレーン内に完全に隠すこともできます。コントロール プレーンでは、実行時に各マシンが実行する処理の実装の詳細だけが残ります。 NAT64 と CLAT を使って楽しむこともできます。ネットワーク全体を IPv6 のみにして、CLAT を使用してポッドに IPv4 接続があると思わせるのです。その後、Pod 内で IPv4 から IPv6 へのアドレス変換が行われ、トラフィックは NAT64 ゲートウェイに送信されます。各マシン上で NAT64 にすることも、クラスター内に展開することもできます。大量の NAT64 トラフィックを処理する必要がある場合は、クラスターの外部で CGNAT のようなものを使用することもできます。 CLAT と NAT64 はこの時点ですでに確立されており、携帯電話はおそらくこれを使用して IPv4 アドレスを取得し、インターネットにアクセスします。 おそらく、シンプルな IPv4 スプーフィング (オプション 1) から始めると思います。これは、最小限の構成しか必要とせず、各マシンですべてローカルに処理でき、クロストークも発生しないため、簡単に始めることができます。さらに、Pod から見るとすべてが同じように見えるため、後で変更することも簡単です。また、すべてをネットワーク プラグインで処理する必要はありません。 これまで、私たちはアウトバウンド側の処理をしており、インターネットへのデュアル スタック アクセスを実現しています。次はインバウンド側をどうするか?ロードバランサー。これをコアオーケストレーションシステムに組み込むことは検討しないでください。オーケストレーション システムは、パケットの宛先 IP が Pod IP である場合にパケットを Pod に配信するという 1 つの点に重点を置く必要があります。 偶然にも、これは主にパブリック クラウドのシナリオに適しているはずです。ベンダーもロードバランサー製品を販売するためにこのモデルを好みます。はい、あなたの勝ちです。とりあえずこの設計モデルを採用しましょう。しかし、VPC がパケットを Pod IP にルーティングする方法を理解できるように、ロードバランサーを制御し、それを IPAM と統合するための制御ループが必要です。 これは、物理マシンを使用してクラスターを構築するシナリオを無視します。しかし、これは悪いことではありません。なぜなら、万能のロードバランサーは存在しないからです。ロードバランサーを提供しようとしたが、期待通りに動作しなかった場合。これによって気が狂いそうになり、激怒して Istio をインストールしてしまうかもしれません。その場合、複雑さを軽減することについて私が議論したすべてのことは役に立たなくなります。 ロード バランサーが 1 つのことを適切に実行することに集中できるようにします。つまり、パケットを Pod に転送する必要がある場合は、パケットを Pod に転送します。この原則に従うことを前提に、LVS、Nginx、ステートレス、クラウドベンダーのロードバランシングサービス、F5などをベースにロードバランサーを構築し、自由に遊ぶことができます。おそらく、いくつかの「デフォルト」実装を提供すると役立つでしょう。ロードバランサー部分については多くのアイデアがあり、私が設計したソリューションはおそらく非常に適切です。ここで重要なのは、オーケストレーション システムは、データ パケットを Pod に転送できる限り、ロード バランサーがどのように実装されているかを気にしないということです。 IPv4 イングレスの問題については触れませんでしたが、主な理由は、それがロード バランサの役割であり、各ロード バランサが最も適切な方法で問題を解決できると考えているからです。 Nginx のようなプロキシ ロード バランサーは、バックエンドを IPv6 経由で転送するだけで問題ありません。ステートレス ロード バランサは IPv4 アドレスを IPv6 に簡単に変換できますが、その間には変換標準があります。送信元アドレス ::fffff:1.2.3.4 のパケットが Pod に到着すると、Pod はそれを IPv4 に戻すことができます。または、ネットワーク内のすべてのアドレスが IPv6 であると想定して、単純に IPv6 として直接扱います。ステートレス変換方式を使用する場合、ステーションを離れて IPv4 にマップし直すときに、ステートフル追跡メカニズムが必要になります。しかし、これは元々 IPv4 で採用されていたレイヤーごとのカプセル化方式よりも優れています。ノードの観点からは、これは ::fffff:0000:0000/96 の追加ルートによって処理できます。 最後までシンプルに 上記のすべてのネットワーク問題に対する代替案として、ネットワーク問題をまったく発生させないようにしてみましょう。 Borg スタイルのポート割り当てに戻ると、すべてのサービスはホストのネットワーク名前空間で実行され、ポートを要求する必要があります。 :80 でリッスンする代わりに :%port% でリッスンすると、オーケストレーション システムは未使用のポート番号を置き換えます。たとえば、リスニング ホストでは最終的に :53928 になります。 このデザインは本当にとてもシンプルです。とてもシンプルなので、基本的に余分な作業は必要ありません。ポートを割り当てるときは、ポートの競合を避けるためにいくつかの面倒なチェックを行う必要があり、これは面倒です。また、ポート枯渇の問題もあります。非常にアクティブなクライアントが多数存在する場合、65,000 個のポートは実際にはそれほど多くありません。しかし、これは本当に素晴らしいです。私は個人的にシンプルさを信じています。 従来の Docker アプローチを使用して、上記の設計と組み合わせることもできます。コンテナーは一時的なプライベート IP を使用して、独自のネットワーク名前空間で実行されます。任意のポートを使用できますが、他の Pod や外部から見えるポートは、ランタイムに公開するように指示したポートのみです。また、公開するコンテナ ポートのみを宣言でき、ホストにマップされるポートはコンテナ ランタイムによって選択されます。 (特殊なケースに対処するために、ここでバックドアをいくつか残すこともできます。ポート 80 を使用する必要があることをシステムに伝え、スケジューリング制約を使用して、ポート 80 が使用されていないマシンにスケジュールすることができます。この点では、現在の Kubernetes 処理に似ています。) 上で説明した重要な点は、これらの設計によりネットワーク層が大幅に簡素化されることです。ほんの数分で誰かに説明して、その仕組みを理解させることができるほどです。 欠点は、サービス検出に複雑さが加わることです。ほとんどの DNS クライアントは SRV レコードを解析せず、ランダムなポートを動的に検出しないため、「純粋な DNS」を検出メカニズムとして使用することはできません。 面白いことに、サービス メッシュの人気が高まるにつれて、この問題はもはや問題ではなくなりました。なぜなら、今では人々は、サービス検出が実行できるすべてのことを実行できるローカル インテリジェント プロキシが存在し、それを必要とする Pod だけが参照できるネットワーク ビューにそれをマッピングすると想定しているからです。しかし、サービス メッシュは複雑さとメンテナンス コストを大幅に増加させるため、このアプローチを受け入れることには消極的であり、少なくともそれがうまく機能することを示す実用的なソリューションが登場するまでは、採用したくありません。 したがって、サービス メッシュのようなものを、よりシンプルなものにするほうがよいでしょう。ソース ホストで自動 IP ポート変換を実行します...ただし、これは kube-proxy と非常によく似ており、それに伴う複雑さとデバッグの難しさがあります (これは、トラフィックが異なるホップ間で変化するため、異なる場所で tcpdump を実行しても解決できる問題ではありません)。 したがって、このソリューションは、明示的なホスト ポート マッピングもソリューションになる可能性があることを示していますが、まだ多くの隠れた複雑さが残っています (これが、Kubernetes が最初に単一の Pod と単一の IP を採用した理由だと思います)。 Borg は、アプリケーションが独自に設計された依存関係ライブラリとフレームワークを使用することを要求する規制を強制することで、この複雑さを解決します。したがって、ここでは明らかな欠点があり、サービス検出と負荷分散の実装フレームワークを自由に変更することはできません。真のサービス メッシュを採用しない限り、これは実現できません。 このセクションで説明したソリューションは改善の余地がありますが、前のセクションの実装の方が好ましいと思います。設計時に考慮すべき点は他にもありますが、その結果、新しい要件を満たすために機能を無制限に追加する必要がない、構成可能でデバッグ可能で理解しやすいシステムが実現します。 安全性も重要 インターネットについて長々と議論した後で、セキュリティの問題について簡単にお話ししましょう。コンテナはデフォルトで最大限にサンドボックス化され、明示的な二重確認手順が必要になります。 コンテナ セキュリティに関する Jessie Frazelle の優れた研究を直接適用できます。デフォルトの apparmor および seccomp プロファイルを開きます。コンテナ内で root として実行することは許可されていません。分離のためにユーザー名前空間を使用すると、誰かがコンテナ内でルートに昇格できたとしても、その人はシステム ルートにはなりません。デフォルトでは、すべてのデバイスのマウントがブロックされます。ホストバインドマウントは許可されません。これを実現するには、考えられる最も制限の厳しい Pod セキュリティ ポリシーを記述し、それをデフォルトにして、そこから逸脱することを困難にします。 ポッド セキュリティ ポリシーは、二重の確認を強制するという点でこれにかなり近いものです。つまり、クラスター オペレーターは、ユーザーが安全でない操作を実行できることを確認し、ユーザーは安全でない操作を実行するための許可を明示的に要求する必要があります。残念ながら、Kubernetesの現在の設計ではこれを考慮していません。ここでは、後方互換性を気にしません。デフォルト値をできるだけ安全にします。 (温かいヒント:ここから、章の内容が少しワイルドになり始めます。これらは私が望むデザインですが、多くの詳細が明確に考慮されていないことを非常に認識しています。) gvisor?爆竹? デフォルトで最も高いレベルのセキュリティに関しては、サンドボクシングに対してより積極的なアプローチをとる方が良いと思います。 GVISORまたはFireCrackerをデフォルトのコンテナサンドボックスとして使用することを検討し、二重確認メカニズムが最終的に「ホストとカーネルを共有する最も安全なコンテナ環境」の目標を達成できるようにすることができますか? これには、さらに考慮する必要があります。一方で、これらのツールが約束する極端なセキュリティは、私にとって非常に魅力的です。一方、これには必然的に多くの追加コードを実行することが含まれます。これは、潜在的な脆弱性と複雑さももたらします。そして、これらのサンドボックスは、あなたができることについて非常に制限的です。さらに、ストレージと関係するものはすべて、「いいえ、ストレージはありません」に移動します。これはいくつかのシナリオでは問題ありませんが、デフォルトにすることは過度に制限されます。 少なくともストレージ側では、Virtio-FSの成熟度はこれらの問題の多くを解決し、これらのサンドボックスがセキュリティモデルを損なうことなく効率的なバインドおよびマウント操作を実行できるようにします。多分私たちはその時点でこの決定を再訪するべきですか? 分散クラスター 流行語は「エッジコンピューティング」だと思いますが、私が本当に意味するのは、すべてのサーバーを単一のユニットとして操作する1つのオーケストレーションシステムの下にあることを望んでいるということです。これは、自宅のサーバーラックのコンピューター、DigitalOceanのVM、およびインターネット上の他のいくつかのサーバーを意味します。これらはすべて基本的にクラスターの同等の部分であり、実際にそのような機能を持っているはずです。 これは、Kubernetesとのいくつかの違いにつながります。まず、ワーカーノードは、現在よりも独立しており、コントロールノードに依存しないように設計する必要があります。ポッドの再スケジュールを必要とする機械障害がない限り、制御ノードなしで長い間(極端な場合は数週間)実行できます。 主なシフトは、ノード上のより多くのデータを同期し、永続的なストレージに保存することだと思います。このようにして、ノード自体には、通常の動作を再開するために必要なデータがあり、プライマリノードとの接続を失った後、コールドスタートから応答性のある状態に回復できます。理論的には、クラスターオーケストレーションシステムが各ノードにSystemDユニットのセットに入力され、ノードの動作にパッシブ管理の役割を果たしたいと考えています。ローカルで必要なものがすべて揃っており、それらが変更する必要がない限り、ノードは管理ノードに依存しません。 これは、ノードの損失を処理する方法の問題につながります。 「集中化された」クラスターでは、これはワークロードの再スケジュールをトリガーするための重要な信号になりますが、分散型シナリオでは、「心配しないでください。これはおそらく接続の短い損失であり、ノードはすぐに戻ってくるでしょう」と言う可能性が高くなります。したがって、ノードライフサイクルとそれがPODライフサイクルにどのように関連するかは、変更する必要があります。ポッドを「高度に利用可能」にしたいか(つまり、ノードがダウンしたときに積極的に再スケジュールされる)か、「最良の努力」(システムがノードがダウンして回復できないと判断した場合にのみ再スケジュールするかどうかを明示的に宣言する必要がある場合があります。 1つの声明は、私が設計した「分散型」クラスターでは、ポッドは「牧場の羊の群れ」よりも「ユニークなペット」のように機能するということです。ステートレスアプリケーションレベルのスケーリングと同様のメカニズムを設計することを検討しますが、このシナリオでは、Kubernetesとは異なり、アプリケーションのレプリカの数が1に削減されると、アプリケーションが実行されるノードに干渉できます。これはクベルネテスが阻止する慣行なので、ここではクベルネテスのいくつかの慣行から逸脱する必要があります。 別の見解は、クラスターフェデレーションを第1レベルのオブジェクトとして扱うことです。実際、分散型マシンは、それぞれが独自の制御プレーンを備えた別々のクラスターと見なすことができ、その後、使用するために超大規模なクラスターに統合されます。これは確かに、コントロールプレーンからノードを切り離す方法についてのいくつかの質問に答えることができます(私の個人的な答え:これをやろうとしないでください、コントロールプレーンの機能は、多数の作業ノードにできるだけ配置する必要があります)。この場合、コントロールプレーンを非常に合理化したいと考えています。そうでなければ、Kubernetesでこれを行うオーバーヘッドは素晴らしいことであり、個人的にこれを避けたいと思います。 これにより、ネットワーク全体に接続する必要があるため、ネットワークの部分がより困難になります。私のアプローチは、何らかの形でテールスケールと統合することです。これは、私たちのニーズを解決するだけです。また、よりカスタマイズされたコンポーネントとより少ないコンポーネントを必要とするソリューションを選択することもできます(追加のNAT変換を実行しないでください)。 仮想マシンを管理します 注:ここで仮想マシンについて話しているとき、私は「ユーザーがKubernetesで実行するポッド」について言及していません。私は、管理者自身が作成したハイパーバイザーサーバー仮想マシンを意味します。 ProxmoxまたはESXiと同様に作成されていますが、EC2で管理されていません。 オーケストレーションシステムにコンテナと仮想マシンをシームレスに管理したいと思います。そうでなければ、実際には、別のハイパーバイザーが必要になるので、2つの管理システムがあります。 これがどのようなデザインになるかはわかりません。 Kubevirtが提供する機能は、システムに組み込まれ、コンテナのようにシステムの重要な部分になる必要があります。これは非常に大きな問題です。なぜなら、「仮想フロッピーディスクを備えたシステムを実行してください」から「EC2のように見えるハイパーバイザーの実行」まで、2つの非常に異なるものを意味する可能性があるためです。私が確信しているのは、Proxmoxとこのオーケストレーションシステムを同時に実行したくないということですが、仮想マシンとコンテナの両方が必要です。 保管方法は? 私の現在のビジョンでは、ストレージは巨大な未知です。私は十分な経験がなく、多くのユニークな洞察があります。 CSIは複雑すぎて合理化する必要があると思いますが、上記のライフサイクルワークフローの一部を除いて、提案することは良い考えを持っていません。ストレージは、現在のKubernetesプラグインデザインを維持したい唯一のモジュールですが、これを理解したら、さまざまなアイデアがあるかもしれません。 やっと これを書いて、この記事には多くのコンテンツがあり、最初に解決したいという問題や奇妙なアイデアを見逃したかもしれません。ただし、明日Kubernetesの交換を開始しなければならない場合は、上記のポイントは変更しなければならない場所でなければなりません。 この業界の他のプレーヤーについてはあまり言及していません - ハシコープの遊牧民、FacebookのTwine、Google's Borg、Twitterのメソス。ボルグとは別に、私は他の解決策を実践しておらず、それらを深く理解することはできません。真新しいKubernetesの開発を開始したい場合は、これらの競合他社を理解するためにより多くの時間を費やして、本質を取り、ドロスを取り除くことができます。また、Nixについて深く考え、それを私のデザインに組み込む方法について考えます。 正直に言うと、私はそれについて考えて、何も練習しないかもしれません。私はBorgからのクラウドコンピューティングの本質について多くを学び、Kubernetesは私に反映するように促しました。私は今でも、最高のコンテナオーケストレーションシステムはコンテナオーケストレーションシステムがないことであり、この努力により、Kubernetesのあらゆる種類の落とし穴が犠牲になることです。明らかに、このアイデアはコンテナオーケストレーションシステムを構築することで場違いです。 |
<<: 商用車のデジタル変革を可能にする、Foton Motorの産業インターネットブランドFOTON-ICLOUDが発売されました
>>: バックエンド テクノロジーを 5 分で学ぶ: 1 つの記事でクラウド コンピューティングとは何かを説明します。
月収10万元の起業の夢を実現するミニプログラム起業支援プラン概要:この一連の変革に影響を与える主な要...
どのようなタイプのビジネスでも、独自のウェブサイトを構築し、それを最適化するために専門家を雇った場合...
現在、インターネット企業の買収が相次いでおり、自社のエコシステム内の隙間を埋めるものであれ、上場への...
従来のネットワーク アプローチでは、ネットワーク トラフィックの構成、管理、および誘導にハードウェア...
現在、クラウド コンピューティングの導入は、ホスト型データ センター インフラストラクチャと同様の傾...
現在、電子商取引サイトは主に2つの形式があります。1つはタオバオをベースとした独立サイト、もう1つは...
Phase7 はルーマニアに登録された会社で、そのコンピューター ルームもルーマニアにあります。主に...
インターネット企業にとって、ブランドの役割は特に重要です。ブランドは無形資産として、企業のビジネス価...
時代の発展とともに、あらゆる業界のマーケティングは、一定の発展期間を経て、打破できない行き詰まりに陥...
[[431325]] JVM 全体構造HotSpot VM は、市場における代表的な高性能仮想マシン...
ほとんどの業界と同様に、IT 業界でも選択肢が増えることは常に良いことです。ハイブリッド クラウドは...
記者は1月11日、国際データコーポレーション(IDC)が発表した「中国ビデオクラウド市場追跡(202...
最近、米国でひっそりと上場していた滴滴出行が国家安全法に基づくサイバーセキュリティ審査の対象となり、...
2015年、当時Google会長だったエリック・シュミットはこう語った。「インターネットは消え去り、...
人気の王宝宝が新たな消費の渦の中心に。王宝宝は2018年5月に設立されました。双十一ショッピングフェ...