🔧

シャーディングによるArgo CDパフォーマンス改善

2024/08/10に公開

シャーディングによるArgo CDパフォーマンス改善

はじめに

本記事はCYBOZU SUMMER BLOG FES’24(クラウド基盤本部Stage)DAY7の記事です。

https://cybozu.github.io/summer-blog-fes-2024/

大規模なKubernetesクラスタ環境でArgo CDを使っていると、いつの間にかパフォーマンスが低下していて同期処理に時間がかかると感じたことはありませんか?
サイボウズでも同様の課題に直面し、その解決のために様々な手段を試みました。
本記事では、特に効果があったApplication Controllerのシャーディングによるパフォーマンス改善の方法を紹介します。

背景

Argo CDは、Kubernetes上で動作するGitOpsを実現するための継続的デリバリーツールです。
GitリポジトリにマニフェストをPushすると、Argo CDがそれを検知してKubernetesクラスタにリソースをデプロイしてくれるという仕組みになっています。

サイボウズでは、Kubernetesクラスタを複数のチームで共有するマルチテナント方式を採用しており、Argo CDも複数のチームで共有しています。
サイボウズでのKubernetes基盤の利用は日に日に拡大しており、それに伴いArgo CDが管理するApplicationやリソースの数も増加し、その結果パフォーマンスの問題が発生することがあります。

パラメータチューニングをおこなうことでArgo CDのパフォーマンスをある程度改善できるのですが、それにも限界があります。
そこで本記事では、Argo CD v2.5で導入された「Applications in Any Namespace」の機能を利用することでApplication Controllerをシャーディングして性能を改善する方法を紹介します。

Argo CDのボトルネック

Argo CDのパフォーマンス低下はさまざまな要因によって発生します。
ここでは、Argo CDの同期処理のボトルネックの調べ方と、その解消方法について紹介します。

まず、Argo CDの仕組みを簡単に説明します。

Argo CDの仕組み

ApplicationリソースとはArgo CDが提供するカスタムリソースで、どのGitリポジトリからマニフェストを取得しどのKubernetesクラスタにデプロイするかを定義することができます。

Argo CDはおおよそ以下の3つのコンポーネントから構成されています。

  • ArgoCD Server: Argo CDのWeb UIやAPIを提供するコンポーネント
  • Repo Server: Gitリポジトリからマニフェストを取得するコンポーネント
  • Application Controller: Gitリポジトリから取得したマニフェストをKubernetesクラスタにデプロイするコンポーネント

Argo CDの同期処理はさまざまなトリガーをきっかけに実行されます。
例えば、開発者がGitにマニフェストをPushした場合、以下のような流れで同期処理が実行されます。

  1. Application ControllerはRepo Server経由で定期的(デフォルト3分)にGitリポジトリをチェックし、変更が見つかるとApplicationリソースの状態を更新します。
  2. Application Controllerは、Applicationリソースの変更を検知すると同期処理をリクエストします。同期処理のリクエストはいったんキューに蓄えられ、複数のgoroutineによって並列に処理されます。
  3. Repo ServerはGitリポジトリからマニフェストファイルを取得し、kustomizeやjsonnetなどのツールを使ってYAMLをレンダリングしキャッシュに蓄えます。
  4. Application Controllerは、レンダリングされたYAMLとKubernetesクラスタ上のリソースとの差分をチェックし、差分がある場合はKubernetesクラスタにデプロイします。

上記の同期処理の中では、以下のように様々な待ち時間が発生します。

  • Gitリポジトリの変更検出: GitリポジトリにマニフェストをPushしてからArgo CDがそれを検出するまでの時間。
  • キュー待ち: 同期処理リクエストがキューに蓄えられ、Application Controllerがそれを処理するまでの時間。
  • マニフェスト取得&レンダリング: Gitリポジトリからマニフェストを取得し、KustomizeやJsonnetなどのツールを使ってYAMLをレンダリングする時間。
  • Reconcile時間: Gitリポジトリから取得したマニフェストとKubernetesクラスタ上のリソースとの差分をチェックし、Kubernetesへの適用が必要かどうかを判断するのにかかる時間。
  • Kubernetesへの適用: Kubernetesクラスタにリソースをデプロイしてから、すべてのリソースがHealthyになるまでの時間。

Argo CDは、処理にかかった時間やキューの滞留時間などさまざまな情報をメトリクスやログとして提供しています。
性能問題が発生した場合、まずはメトリクスやログを確認して、どの部分がボトルネックになっているのかを特定します。
例えば、workqueue_depth, workqueue_queue_duration_seconds というメトリクスを見ると、キューの待ち状況を確認できます。
また、argocd_app_reconcile_bucket というメトリクスによって、Reconcileに要している時間を分析することができます。

ボトルネックが特定できたら、それに対する対策を検討します。
Gitリポジトリのチェック待ちが長い場合は、ポーリング間隔を短くしたり、Git Webhookを導入することで改善できます。
キュー待ちが発生している場合は、同期処理のリクエスト数が適切か確認し、必要に応じて並列実行数を増やすなどApplication Controllerの処理性能を上げることで改善できる場合があります。
Reconcile時間が長い場合は、同期処理のリクエスト数を減らしたり、Application Controllerをシャーディングすることで改善できる場合があります。
Kubernetesへの適用時間が長い場合は、コンテナイメージの取得にかかっている時間や、Podが起動するまでにかかっている時間を確認し、それに対する対策を検討します。また、Kubernetesクライアントのレートリミット引き上げることで解消することもあります。

サイボウズ環境でArgo CDのボトルネックを確認したところ、Reconcileの時間が顕著に長くなっていました。
さらにプロファイラを利用して詳細に分析したところ、Application Controller内のgoroutine間で競合が発生していたのです。
この競合問題は扱うApplicationとリソースの数が多い場合に発生しやすく、goroutineの並列数を増やしても解決しないことが分かっています。
そこで、Application Controllerのシャーディングが有効ではないかと考えました。

Application Controllerのシャーディング

Argo CDでは、複数のApplication Controllerを立ち上げてシャーディング処理することでパフォーマンスを改善する方法を提供しています。
しかし、Argo CDのドキュメントには、デプロイ先のKubernetesクラスタごとにApplication Controllerをシャーディングする方法しか記載されていません。

クラスタごとのシャーディング

ところが、Argo CD 2.5において「Applications in Any Namespace」という機能が追加されました。

https://argo-cd.readthedocs.io/en/stable/operator-manual/app-any-namespace/

これまでApplicationリソースは必ずArgo CDのデプロイされたNamespace上に置く必要がありましたが、「Applications in Any Namespace」機能により、任意のNamespaceにApplicationリソースを配置できるようになりました。
さらに、Application Controllerの引数に、どのNamespace上のApplicationリソースをWatchするのかを指定できるようになりました。

この機能を応用し、チームごとにApplicationリソースを配置するNamespaceを変え、それぞれのNamespace用にApplication Controllerを立ち上げれば、処理をシャーディングすることが可能になったのです。

Namespaceごとのシャーディング

※ ただし、argocd Namespaceに配置したApplicationリソースはすべてのApplication Controllerに処理されるため、同期処理が重複して実行される可能性があります。したがって、argocd namespaceにはApplicationを配置しないように注意が必要です。

実践

サイボウズが運用しているArgo CDは、利用チーム数が17、Application数が192、監視対象となっているオブジェクトは10万を超えています。
チームごとにApplicationのReconcile処理にかかっている時間を調べたところ、特に上位の3チームの負荷が大きいことがわかりました。
また、上位のチームの同期処理が遅くなることにより、他のチームのApplicationも影響を受けていることがわかりました。

そこで、今回は上位の3チームA, B, Cにそれぞれ専用のApplication Controllerを用意し、残りのD~Qチームは1つのApplication Controllerを共有することにしました。

以下のグラフは、シャーディング前後の各チームのReconcile時間を比較したものです。
このように、Application Controllerをシャーディングすることで、全体のパフォーマンスが大幅に改善されました。

改善後のReconcile時間

まとめ

本記事では、Argo CDの同期処理の仕組みを解説し、パフォーマンスのボトルネックの特定と解消方法について紹介しました。
また、Argo CD 2.5で導入された「Applications in Any Namespace」機能を活用し、Application Controllerをシャーディングする手法についても取り上げました。
さらに、サイボウズでの実例をもとに、シャーディングによるパフォーマンス改善の効果を示しました。

パフォーマンスの最適化は一度きりの作業ではありません。Argo CDのバージョンやチームの利用状況に応じて、定期的な見直しと調整が必要になります。
今回ご紹介したシャーディングの手法に加え、パラメータチューニングやKubernetesクラスタの最適化など、さまざまなアプローチを組み合わせることでさらに高いパフォーマンスを実現できるでしょう。

本記事が、Argo CDのパフォーマンス改善に取り組む皆様にとって、少しでもお役に立てれば幸いです。

サイボウズ Necoチーム 😺

Discussion