UberのKubernetes運用術:ServiceScaleで実現する100万コアの動的スケーリング

AI・テクノロジー
STΛCKHUB ANALYSIS2026.09.28 21:01
📌 30秒でわかるこの記事の要点
⏱ 読了目安: 約6分
  • UberがServiceScaleコントローラーを導入し、複数のオーケストレーターによるKubernetesワークロードの安全な同時スケーリングを実現した。
  • スケーリングの「意図」と「実行」を分離することで、フェイルオーバー時の動的なリソース再配分を可能にし、100万CPUコア以上の削減に成功した。
  • 分散システム特有の「APIの整合性」と「キャッシュの遅延」という課題に対し、read-your-own-writeのガードレールを実装し、運用の安定性を確保した。

なぜUberはスケーリングの「意図」を分離したのか

深夜の障害対応で最も恐ろしいのは、システムが「良かれと思って」行った自動スケーリングが、実は別の制御ロジックと競合し、デッドロックやリソースの枯渇を招くことだ。Uberのエンジニアリングチームが直面していたのは、まさにこの「スケーリングの意図の衝突」だった。彼らは100以上のコンピューティングクラスター、約4,000のサービス、そして300万コアという巨大なインフラを運用している。この規模において、リージョン障害時のフェイルオーバーは単なる冗長化の切り替えではなく、全リソースの再配置を意味する。

従来、Uberはフェイルオーバーに備えて常に一定のアイドル容量を確保していた。しかし、これはコスト効率の観点から見て極めて非効率だ。そこで彼らは、低優先度のワークロードを縮小し、そのリソースを高優先度のワークロードに割り当てるという動的なアプローチを選択した。ここで問題となったのが、既存のUber Deployment Controller(UDC)の複雑化だ。UDCはすでにサービスライフサイクルのホットパスを担っており、ここにフェイルオーバーのロジックを詰め込むことは、システム全体の信頼性を損なうリスクがあった。もしフェイルオーバーのロジックでバグが発生すれば、それは全サービスのデプロイに波及する。この「責務の肥大化」を避けるために彼らが選んだのが、スケーリングの「意図(Intent)」と「実行(Execution)」の分離である。

彼らはServiceScaleという新しいカスタムリソース定義(CRD)を導入し、Service Scale Controller(SSC)を構築した。これにより、複数のオーケストレーターがそれぞれのスケーリング要求をServiceScaleに書き込み、SSCがそれを統合してKubernetesのプリミティブに反映させるという、極めてクリーンなアーキテクチャが完成した。外部データベースや複雑な調整サービスを排除し、KubernetesのCRDを唯一の真実のソース(Single Source of Truth)とした判断は、障害時のデバッグ容易性を劇的に向上させた。これは、複雑な分散システムを構築する我々エンジニアにとって、非常に示唆に富む設計判断である。

分散システムの罠:キャッシュ遅延とAPI整合性

「マルチオーケストレーターシステムが難しいのはAPIのせいではない。書き込みと書き込みの間に何が起きるか、その不確実性が難しいのだ」。Uberのエンジニアが残したこの言葉は、分散システムを扱う全てのエンジニアの心に刺さるはずだ。彼らが直面した最大の技術的課題の一つが、Kubernetesのインフォーマーキャッシュによる「遅延」である。コントローラーがリソースを更新しても、キャッシュが追いつくまでに数秒のラグが生じる。このラグの間に次のワークフローが走れば、システムは古い状態を前提に誤った判断を下すことになる。

Uberはこれを解決するために、read-your-own-write(自分の書き込みを読み取る)という一貫性のガードレールを実装した。具体的には、コントローラーがリソースを更新する際、現在の世代(generation)をアノテーションとして付与し、キャッシュがその世代を反映するまで次のアクションを待機させるという手法だ。これは、Kubernetes v1.36で導入された「staleness mitigation」と本質的に同じアプローチであり、プラットフォームレベルでの標準化が進んでいる領域でもある。しかし、Uberはこれを自前で実装し、運用の中で洗練させてきた。

さらに深刻だったのが、マルチライター環境におけるReplicaSetの不整合だ。UDCとSSCが同時に同じリソースを更新することで、メタデータとスペックが乖離し、ローリングアップデートがスタックする事態が発生した。これに対し、彼らは単なるパッチではなく、フリート全体でのオブザーバビリティを強化し、自動修復機能を組み込むことで対応した。この1年間にわたる段階的なロールアウトは、彼らのエンジニアリング文化の成熟度を物語っている。以下に、彼らが直面した課題と解決策の要点をまとめる。

課題 技術的影響 解決策
インフォーマーキャッシュの遅延 古い状態に基づく誤った更新 read-your-own-writeガードレールの実装
マルチライターによる競合 ReplicaSetのメタデータとスペックの乖離 自動修復機能(Healer)の導入とオブザーバビリティ強化
フェイルオーバー時のリソース確保 アイドル容量の無駄遣い ServiceScaleによる動的リソース再配分

この取り組みにより、Uberは定常時のプロビジョニングを2xから1.3xまで削減し、100万CPUコア以上の余剰を排除した。これは単なるコスト削減ではなく、エンジニアリングの力でインフラの限界を押し広げた成果である。

エンジニアへの問い:複雑性をどう飼い慣らすか

Uberの事例は、Kubernetesという強力なプラットフォームの上で、いかにして「複雑性の爆発」を制御するかという問いに対する一つの回答である。多くのエンジニアは、Kubernetesの標準機能で解決できない課題に直面した際、すぐに外部の複雑な調整レイヤーを追加しようとする。しかし、Uberが示したのは、KubernetesのCRDというプリミティブを最大限に活用し、状態を可視化し、コントローラーの責務を分離するという、極めて堅実なアプローチだ。彼らは「外部データベース」や「別の調整サービス」を拒絶した。これは、障害時に「どこを見ればいいのか」を明確にするための、極めて高度な判断である。

我々が明日から取るべき対策は明確だ。まず、自社のシステムにおいて「スケーリングの意図」がどこで管理されているかを再確認すること。もし複数のコントローラーが同じリソースを操作しているなら、それは将来的な障害の温床である。次に、キャッシュの遅延を前提とした設計になっているかを確認すること。read-your-own-writeの考え方は、Kubernetesに限らず、あらゆる分散システムにおいて必須の教養となりつつある。そして何より、複雑なロジックを実装する前に、「それは本当にコントローラーの責務か?」と自問自答することだ。

最後に、読者であるあなたに問いかけたい。あなたのチームが運用しているシステムにおいて、もし明日、全リージョンのトラフィックが半分になったとしたら、あるいは逆に倍になったとしたら、そのリソースを自動的に、かつ安全に再配分する準備はできているだろうか?「なんとかなる」という楽観的な運用から脱却し、Uberのように「意図」をコードとして定義し、実行を制御するアーキテクチャへと進化させる準備はできているか。技術の進化は止まらない。我々エンジニアに求められているのは、ツールを使いこなすこと以上に、複雑性を飼い慣らすための「設計の規律」を自ら課すことではないだろうか。

🏷 関連トピック・技術タグ:
#Kubernetes#Uber#DevOps#CloudNative#DistributedSystems
Published at 21:01

コメント

タイトルとURLをコピーしました