Create a gist now

Instantly share code, notes, and snippets.

@itkq /wsa1.md
Created Dec 20, 2017

Embed
WSA研 #1: サービスのパフォーマンス数値と依存関係を用いたサービス同士の協調スケール構想 / @itkq

WSA研 #1: サービスのパフォーマンス数値と依存関係を用いたサービス同士の協調スケール構想 / @itkq

本稿は WebSystemArchitecture 研究会 #1 の予稿です。

はじめに

近年 Docker をはじめとするアプリケーションコンテナが広く使われており、Kubernetes などのオーケストレーションツールを用いて、オンプレミスだけでなく、マネージドサービス (GKE, ECS, EKS) で使用する例が増えている。 クラウドを意識してアプリケーションを構築する現在、もはや「マイクロサービスアーキテクチャ」というまでもないが、service-to-service で通信するモデルが一般的になりつつある。

そこで Zipkin, OpenTracing などの分散トレーシングや、Envoy, Linkerd, Istio などサービスメッシュが登場し、リクエストの追跡・サービス間メトリクスの取得・サービスディスカバリ・障害分離・カナリアデプロイ・サービス間認証などが現実的に行えるようになっている。しかし、これらは依然として開発基盤よりの目的・技術だといえる。サービスメッシュで構築された基盤の上で動作するサービスを安定して運用するために、サービスメッシュを利用して、サービスをどのようにスケールさせるか、といった運用上の応用、実例はほとんど公開されていない。

そこで本稿では、コンテナベースのマイクロサービスにおいて、1つ1つのサービスのパフォーマンスを定量的に定義し、依存サービスのコンテナキャパシティを自動で決定する方法について、限られた想定状況における、プロトタイプを作成し、その上で議論を行う。現時点での分散トレーシングやサービスメッシュの立ち位置を明確にし、運用上の問題意識と本提案について述べた後、今後必要になると思われる技術や、検討すべき問題について考察する。

背景技術

ここでは、本稿で紹介する技術について簡単に述べる。

分散トレーシング

マイクロサービスによる分散型アーキテクチャにおいて、それぞれのサービスのロギング、パフォーマンス測定だけでは、実際のエンドツーエンドの通信を追跡することは困難である。そこで、実際のリクエストを追跡するために、それぞれのサービスでのロギングフォーマットを統一し、ユニークな ID を付与してストレージに保存し、次のサービスに伝播させることを行う。ストレージを参照することで、実際のリクエストがどのサービスを利用するか、すなわちサービスの依存関係を視覚化でき、またリクエスト単位でのパフォーマンス測定が可能となる。Google 社の Dagger [Ben10] を皮切りに、Zipkin や OpenTracing などの OSS が公開されている。AWS X-Ray などのマネージドサービスもある。

分散トレーシングは、サービスメッシュを用いて実現することもできる。

サービスメッシュ

マイクロサービス同士の通信、すなわち service-to-service の通信を考えると、どのように相手のサービスを見つけ、どのようにリトライし、どのようにヘルスチェックするかなどの問題が出てくる。アプローチとしては2つあり、fat client モデルと fat middleware モデルがある。fat client モデルは、リクエストを送信するクライアント側でそれらの処理を行う。しかしこれはライブラリや実装によって処理が異なることや、実装が複雑でトリッキーになってしまうことなどの問題がある。より現実的なアプローチは fat middleware モデルである。ミドルウェアでこれらの処理を担当することにより、アプリケーションの実装に依存せず、通信プロトコルだけを意識すれば良い。すなわち、サービスメッシュとは、賢いサービスプロキシ同士の通信によって作成される、ネットワークの抽象レイヤーといえる。

最近では Cloud Native Computing Foundation (CNCF) に Beoyant 社の Linkerd, Lyft 社の Envoy が仲間入りした。さらに Kubernetes 上にサービスメッシュを構築するプラットフォーム Istio も OSS 化され、Kubecon'17 では、2018 年 Kubernetes はサービスメッシュ方向に大きく進化するだろうとの発表もされた。

問題意識

ここでは、コンテナベースでのマイクロサービスを構築する際、主にインフラ視点での問題を述べる。

コンテナリソースの設定法

アプリケーションがコンテナ化したことで、コンテナ数を増やすスケールアウトが容易になった。しかし、実際にはコンテナを動作させるホストキャパシティが必要であり、インフラのコストを抑えるためにも、平常時はその時の負荷に耐えうるだけの最低限のコンテナを用意しておきたい。現在、k8s などのオーケストレーションでは、コンテナ数のデフォルト値を "desired (reprica) count" として、設定ファイルに記述することができる。しかし、SRE 本でも述べられているように、"desired count" はヒューマンフレンドリーではなく、この値は実際には測定によって、実験的または経験的に決定する。Cookpad では、アプリケーション開発者が ECS をバックエンドとした Hako ベースのデプロイをすることが可能であるが、desired count を決めるのは実際には難しく、運用でカバーする。

サービス同士が連携したスケール

マイクロサービスアーキテクチャでは、コンポーネントのような単位でサービスを切り出し、サービス同士が通信することで、アプリケーションを構築する。しかし、マイクロサービス化によって、サービス同士の依存関係は複雑になる一方である。サービスメッシュによって、サービスの依存関係、障害分離などは解決の道筋が見えているが、ここではマイクロサービスに依存したサービスのスケール計画を考える。例えば事前に予測したスパイクに対して、フロントのサービスのキャパシティだけを用意し、依存する他のサービスも同様に考慮していない場合、それらのサービスがキャパシティ不足になり、サービス全体としては停止してしまう。すなわち、マイクロサービスで構築したサービスを安定してスケールさせるには、依存したサービスとの協調が不可欠である。

解決アプローチ

ここでは、問題意識を解決するアプローチを述べる。

コンテナリソースの設定法

コンテナ数は最低の 1 で用意しておき、いかにトラフィックに合わせてスケールしていくかについて述べる。

マネージドサービスの利用

リソースの管理をマネージドサービスに任せるという手が考えられる。スケールアウトが極めて早い GAE を用いれば、コンテナ数は設定最小数だけ用意しておき、予測不可能な負荷に対しても柔軟なスケールが可能であると考えられる。しかし、これは本質的な解決策とはいえない。

トラフィックを予測する

例えば地震の予測では、地震の予兆と考えられる事柄を観測し続ける。Web においては、例えば Twitter などの SNS で「炎上」を観測することで、リクエスト数ベースのオートスケールよりもより予測的で柔軟にスケールできると考えられる。これについては本稿では述べず、今後の課題とする。

サービス同士が連携したスケール

分散トレーシングやサービスメッシュによって、サービスの依存関係は解決できる。また、サービスメッシュによって、サービス間メトリクスを取得できる。

しかし、依存したサービスのキャパシティを具体的にどれだけ引き上げればいいのかは、サービス間メトリクスだけでは分からない。キャパシティを決めるためには、個々のサービスのパフォーマンスを定量的に定義しておく必要がある。サービスのパフォーマンス数値と、サービスの依存関係、サービスの依存具合が分かれば、スケールする際にキャパシティをある程度自動で決定できるのではないかと考えた。

既存事例

ここでは、現状のサービスメッシュがどのような目的で、何に用いられているかについて調査したことをまとめる。

Envoy

サービスメッシュを構築するためのサービスプロキシ。Lyft 社製で C++13 で書かれている。 SoA アーキテクチャで問題となる、fat client を解決すること、またネットワークがアプリケーションに対して透過的にするために開発された。

The network should be transparent to applications. When networks and application problems do occur it should be easy to determine the source of the problem.

Envoy の特徴は以下のように述べられている^

  • Observability
    • Statistics for every hop
    • Consistent logging
    • Distributed tracing
  • Advanced load balancing
    • Service discovery
    • Load balancing (based on least req)
    • Dynamic stats
    • Circuit breaking
    • Rate limiting
    • Shadowing
    • Retires
    • Timeouts
    • Outlier detection
    • Deploy control (Blue/green, canary)
  • High performance

新規追加予定の機能としては、認証認可や k8s インテグレーションなどを挙げている。Ingress/Egress のパフォーマンス視覚化、サービスメッシュ全体でのパフォーマンス視覚化などの文献は見つかるが、その先の運用に向けた応用事例は見つからなかった。

Linkerd

ドキュメントの特徴^ からするに Envoy との大きな差異は確認できなかった。

Istio

Istio は マイクロサービス、サービスメッシュを構築するためのコンポーネントを提供するプラットフォームである。インフラを問わず、サービスメッシュ構築を標準的に行い、またセキュリティ要求が高い用途にも使用できる利点がある。 Kubernetes と密に関係しており、Envoy が解決しようとしている課題を、より共通化して Kubernetes 上で行うものであると理解している。そのため解決領域は Envoy と大きく異ならない。

ドキュメントでは次の特徴を挙げている。

  • Traffic management
  • Observability
  • Policy Enforcement
  • Service identify and Security
  • Platform support
  • Integration and Customization

Istio のコンポーネントは次の4つがある。

Envoy

Istio 用に手を加えられた Envoy。sidercar proxy として Pod 上で同居して動作し、ルーティングや障害分離を担当する。

Mixer

サービスメッシュ上でアクセス制御、ポリシー強制を行い、Envoy や他のサービスからデータを収集する、プラットフォームに依存しない、抽象レイヤーである。

Pilot

Envoy のためのサービスディスカバリや、より賢いルーティングのためのトラフィック制御 (A/B テスト、カナリアデプロイ)、またレジリエンシー (タイムアウト、リトライ、サーキットブレイク) を提供する。

Auth

どのサービスが通信してよいのか、またエンドユーザの TLS 認証、識別や秘匿値の管理を行う。

提案

解決アプローチの章で最後に述べた、サービスのパフォーマンス定量化と、依存関係解決によってスケールする際のキャパシティ見積もりについて述べる。

想定状況

  • サービスはコンテナベースでアプリケーションで実装されている
  • 複数のサービスが存在し、それぞれのサービスに依存関係を持つ
  • アプリケーションのデプロイによって、サービスのパフォーマンスは変化する

要件

  • サービスの信頼性を保つ (ここでは具体的な数値は設定しない)
  • インフラコスト (= キャパシティ) を必要最低限にする

手法

  • コンテナベースのアプリケーションの負荷試験を継続的に行い、あるリビジョンでの、割当リソースに対するパフォーマンスを記録する
    • 目標性能がある場合、その性能を達成するコンテナ数とリソースを依存度から逆算する
  • サービスの依存関係と、依存割合 (以降「依存度」) を数値化する
  • あるサービスをスケールさせる際、依存度とパフォーマンスを元に、desired count を算出してスケールする

プロトタイプ実装 (WIP)

k6 + k8s + Envoy で構築したコンテナベースのマイクロサービスとサービスメッシュ上で、desired count を決定するプロトタイプ実装について書く。

考察 (WIP)

現実のプロダクション環境で動作させるために必要なこと、今不足していることなどを書く。

付録

本稿のアイディアのヒントとなった、SRE 本中の内容である。

SRE 本 18.2: Auxon のケーススタディ

キャパシティプランニングは、需要予測、リソース割り当て計画、計画のレビュー、リソースデプロイのサイクルを繰り返すものであり、様々な要因が影響するため、本質的に不正確、不安定だと述べられている。また、リソースには料金、物理リソース不足といった制約があり、最適化は NP 困難である。 本当に必要だったのは、インテントベースのキャパシティプランニング、と述べられている。

  1. Foo というサービス用にクラスタX, Y, Zに 50 コア欲しい
  2. Foo というサービス用に YYY リージョンのどれか3つのクラスタに 50 コア欲しい
  3. Foo というサービスの需要を各地理的リージョン内で満たし、N+2 の冗長性を確保したい
  4. Foo というサービスを 5 ナインの信頼性で動作させたい

というように、抽象度の高い、人間に理解できる要求を機械が理解できるとよいとされている。 インテントを把握するためには、依存関係・パフォーマンスメトリクス・優先順位付けが必要である。しかし、まだ現実には Google 以外でこれらを行っている事例は見当たらなかった。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment