Volterra では、SRE チームの仕事は、グローバルなSaaS ベースのエッジ プラットフォームを運用することです。 私たちは、さまざまな状態 (オンライン、オフライン、管理ダウンなど) にある多数のアプリケーション クラスターを管理する上でさまざまな課題を解決する必要があり、GitOps を使用した宣言型プルベース モデルで Kubernetes エコシステムとツールを活用することでこれを実現します。
このブログでは、次の内容について説明します。
GitOps を使用して、大規模なインフラストラクチャ (物理またはクラウド ホスト) と K8s クラスターを効果的に管理および監視します。
大規模(3000 エッジ サイト)で学んだ教訓について、さらに詳しく掘り下げていきます。これは、サンディエゴで開催された Cloud Native Rejekts での最近の講演でも取り上げられました。
上記のアーキテクチャ図 (図 1) は、RE と CE 間の論理接続を示しています。各 CE は、最も近い RE への冗長 (IPSec または SSL VPN) 接続を確立します。
約 2 年前にプラットフォームの設計を開始したとき、製品チームから次の課題を解決するように依頼されました。
高度に分散されたシステムを運用するための要件と課題を考慮し、下流の問題を軽減するために SRE チームが従う必要のあるいくつかの原則を設定することにしました。
エッジ サイトのライフサイクル管理の一環として、ホスト OS のプロビジョニング、基本的な構成 (ユーザー管理、証明機関、hugepages など) の実行、K8 の起動、ワークロードのデプロイ、進行中の構成変更の管理の方法を解決する必要がありました。
私たちが検討したが最終的に却下したオプションの 1 つは、KubeSpray + Ansible (OS の管理と K8 のデプロイ) と Helm/Spinnaker (ワークロードのデプロイ) を使用することです。 これを拒否した理由は、エッジ クラスターの自動スケーリング、セキュア TPM モジュールのサポート、差分アップグレードなどの機能を追加するにつれて要件が拡大し続け、2 ~ 3 個のオープン ソース ツールを管理し、要件を満たすために大幅な変更を加える必要があったためです。
私たちの目標はシンプルさを保ち、OS 内 (Kubernetes 外部) で直接実行されるコンポーネントの数を最小限に抑えることだったので、Volterra Platform Manager (VPM) と呼ばれる軽量の Golang デーモンを作成することにしました。 これは OS 内の唯一の systemd Docker コンテナであり、多くの機能を実行するスイスアーミーナイフとして機能します。
VPM は、インストール、アップグレード、パッチ、構成など、ホスト オペレーティング システムのライフサイクルを管理する役割を担います。 設定が必要な側面は多数あります(例:hugepages の割り当て、/etc/hosts など)
Kubernetes マニフェストのライフサイクルを提供する管理。 Helm を使用する代わりに、K8s client-go ライブラリを使用することを決定し、これを VPM に統合して、このライブラリのいくつかの機能を使用しました。
楽観的= リソースを作成し、ステータスを待機しません。 これは、実際のポッドが正常に起動したかどうかがわからないkubernetes applyコマンドと非常によく似ています。
悲観的= Kubernetes リソースの状態を待機します。 たとえば、デプロイメントはすべてのポッドの準備が整うまで待機します。 これは新しいkubectl waitコマンドに似ています。
K8s マニフェストに関連する構成に加えて、API を介してさまざまな Volterra サービスを構成する必要もあります。 一例として、IPsec/SSL VPN 構成が挙げられます。VPM はグローバル コントロール プレーンから構成を受信し、個々のノードにプログラムします。
この機能を使用すると、ボックスをリモートで元の状態にリセットし、インストールと登録のプロセス全体を再度実行できます。 これは、コンソール/物理アクセスを必要とするサイトを回復するための非常に重要な機能です。
K8s ライフサイクル管理は多くの人にとって大きな議論のテーマのように見えるかもしれませんが、私たちのチームにとっては、全体の作業量の 40~50% 程度でしょう。
あらゆる場所 (クラウド、オンプレミス、またはノマディック エッジ) でのエッジ サイトのゼロタッチ プロビジョニングは重要な機能です。個々のサイトにアクセスすることは期待できず、個々のサイトのインストールと管理に多くの Kubernetes エキスパートを配置することも望んでいないためです。 数千にまで拡張できないだけです。
次の図 (図 2) は、新しいサイトの登録プロセスに VPM がどのように関与するかを示しています。
ご覧のとおり、プロセス全体は完全に自動化されており、ユーザーは詳細な構成について何も知る必要はなく、手動の手順を実行する必要もありません。 デバイス全体をオンライン状態にして、顧客のアプリやリクエストに対応できるようになるまでに約 5 分かかります。
アップグレードは、私たちが解決しなければならなかった最も複雑なことの 1 つです。 エッジサイトでアップグレードされるものを定義しましょう。
エッジ サイトに更新を配信するために使用できる既知の方法は 2 つあります。
アップグレードの目標は、標準的な携帯電話のアップグレードと同様に、シンプルさと信頼性を最大限に高めることでした。 さらに、アップグレード戦略が満たさなければならない他の考慮事項もあります。たとえば、アップグレードのコンテキストはサイトの運営者のみにある場合や、接続の問題などによりデバイスがオフラインまたはしばらく使用できない場合があります。 これらの要件はプル方式でより簡単に満たすことができるため、ニーズを満たすためにプル方式を採用することにしました。
さらに、Kubernetes クラスター、ワークフロー、監査の変更を管理するための標準運用モデルを SRE チームに提供しやすくするため、GitOps を選択しました。
何千ものサイトのスケーリング問題を解決するために、図 3 に示す SRE のアーキテクチャを考案しました。
まず、Git は状態やマニフェストを保存するためだけに使用しているのではないことを強調しておきます。 その理由は、当社のプラットフォームでは K8s マニフェストだけでなく、進行中の API 構成、K8s バージョンなども処理する必要があるためです。 私たちの場合、K8s マニフェストは宣言型構成全体の約 60% を占めます。 このため、git に保存される独自の DSL 抽象化をその上に作成する必要がありました。 また、git は API やパラメータのマージ機能を提供していないため、SRE 用に追加の Golang デーモンを開発する必要がありました。 Config API、Executor、VP コントローラー。
当社の SaaS プラットフォームを使用して、顧客エッジで新しいソフトウェア バージョンをリリースするワークフローを見てみましょう。
ワークフロー全体のデモはここでご覧いただけます:
前のセクションでは、ツールを使用してエッジ サイトのライフサイクルを展開および管理する方法について説明しました。 設計を検証するために、3,000の顧客エッジサイトを備えた大規模な環境を構築することにしました(図4を参照)。
私たちは、スケールをシミュレートするために、Terraform を使用して、AWS、Azure、Google、および自社のオンプレミスのベアメタル クラウドにわたって 3000 台の VM をプロビジョニングしました。 これらの VM はすべて独立した CE (顧客エッジ サイト) であり、地域のエッジ サイト (PoP) への冗長トンネルを確立していました。
以下のスクリーンショットは SRE ダッシュボードからのもので、円のサイズで表される場所のエッジ番号を示しています。 スクリーンショットを撮った時点では、正常なエッジ サイトが約 2711 個、異常なエッジ サイトが 356 個ありました。
スケーリングの一環として、構成と運用面でいくつかの問題が見つかり、ソフトウェア デーモンに変更を加える必要がありました。 さらに、クラウド プロバイダーでは、API 応答の遅延、単一リージョンで 500 台を超える VM を取得できないなど、複数のサポート チケットを開くことになる多くの問題が発生しました。
分散システム全体の可観測性は、システムの規模が拡大するにつれて、はるかに大きな課題をもたらしました。
当初、メトリクスについては、Prometheus フェデレーションから始めました。これは、グローバル制御の中央 Prometheus が地域エッジ (RE) の Promethei をフェデレーションし、サービス メトリクスをスクレイピングして、接続された CE からメトリクスをフェデレーションするものです。 最上位レベルの Prometheus はアラートを評価し、さらなる分析のためのメトリック ソースとして機能しました。 このアプローチはすぐに限界に達し(約 1000 CE)、CE 数の増加による影響を最小限に抑えようとしました。 ヒストグラムやその他の高カーディナリティ メトリックの事前計算済みシリーズを生成し始めました。 これにより、1、2 日の節約ができましたが、その後はメトリックのホワイトリストを採用する必要がありました。 最終的に、各 CE サイトの時系列メトリックの数を約 60,000 から 2000 に削減することができました。
最終的に、3,000 を超える CE サイトへの拡張を継続し、本番環境で何日も実行した後、これは拡張可能ではないことが明らかになり、監視インフラストラクチャを再考する必要がありました。 私たちは、トップレベルの Prometheus (グローバル制御) を削除し、各 RE の Prometheus を 2 つの個別のインスタンスに分割することにしました。 1 つはローカル サービス メトリックのスクレイピングを担当し、もう 1 つは CE メトリックのフェデレーションを担当します。 どちらもアラートを生成し、メトリックを Cortex の中央ストレージにプッシュします。 Cortex は分析および視覚化ソースとして使用され、コア監視アラート フローの一部ではありません。 私たちは、Thanos と M3db といういくつかの集中型メトリクス ソリューションをテストし、Cortex が私たちのニーズに最も適していることを発見しました。
次のスクリーンショット (図 7) は、3000 エンドポイントの時点での prometheus-cef のスクレイピングによるメモリ消費量を示しています。 興味深いのは、消費される RAM が 29.7 GB であることです。これは、システムの規模を考えると、実際にはそれほど多くはありません。 スクレイピングを複数に分割したり、Cortex へのリモート書き込みをエッジ自体に直接移動したりすることで、さらに最適化できます。
次のスクリーンショット (図 8) は、この規模で Cortex インジェスター (最大 19 GB RAM) とディストリビューターに必要なメモリと CPU リソースの量を示しています。 Cortex の最大の利点は水平スケーリングです。これにより、垂直スケーリングが必要な Prometheus と比較して、より多くのレプリカを追加できます。
CE および RE のログ記録インフラストラクチャでは、ノードごとに Fluentbit サービスを使用して、サービスおよびシステム ログ イベントを収集し、接続された RE の Fluentd に転送します。 Fluentd は、RE にある ElasticSearch にデータを転送します。 ElasticSearch からのデータは Elastalert によって評価され、Alertmanager アラートを作成するためのルールが設定されます。 私たちは、Elastalert から Alertmananger へのカスタム統合を使用して、Prometheus が生成するものと同じラベルのアラートを生成しています。
監視の旅の重要なポイント:
- 当初はCEあたり約50,000の時系列があり、平均15のラベルがありました。
- 平均してCEあたり2000に最適化しました
メトリック名にはシンプルな while リスト、ラベル名にはブラックリスト
- 集中型PrometheusはすべてのREとCEのPrometheusをスクレイピングしました
- 西暦1000年には、指標の量を管理することが不可能になった。
- 現在、各REにPrometheus(接続されたCEのPrometheiと連携)があり、RWはCortexに接続されています。
- 分散ログアーキテクチャ
- 各ノードのコレクターであるFluentbitは、REのFluentd(アグリゲータ)にログを転送します。
- ElasticSearch は、リモート クラスター検索を使用してすべての RE にデプロイされ、単一の Kibana インスタンスからログを照会します。
このブログが、世界中に展開されている何千ものエッジ サイトとクラスターを管理するために考慮すべきすべての事項についての洞察を与えてくれることを願っています。 当初の設計要件のほとんどを満たし、検証することができましたが、まだ改善すべき点はたくさんあります…