こんにちは。サイオステクノロジーの塙です。
前回は、Red Hat OpenShift Service Mesh(以下、OSM)、Prometheus、Grafana, Kialiを使用したメトリクスの収集とサービス観察までを行いました。
今回は、耐障害性に着目してOSMではどんなことが出来るのかといった内容について記載をしていきたいと思います。
目次
サービスの耐障害性について
サービスの提供において、サービスを停止せずに提供を続けていくためには、なんらかの障害が発生した場合でも、安定的にサービス継続が出来ることが求められます。
この時の指標として耐障害性が用いられます。
この耐障害性を高める仕組みを取り入れることで、サービスへのパフォーマンスにも繋がっていきます。
例えば、Kubernetesでは、コンテナオーケストレーションの機能が備わっており、レプリカを増やすことで、負荷分散であったりとサービスの継続性への対応を行うことが出来ます。
OSMでも、耐障害性に関わる設定を行うことができ、主にIstioの設定で実現出来るようになっています。その一部をご紹介できればと思います。
耐障害性のテスト
ネットワークの不安定により、サービスに影響を及ぼす可能性があります。この問題にも対応していくためにネットワークの不安定性をシミュレートするカオステストというものを検証してみたいと思います。
カオステストとは、サービスまたはシステムに意図的に障害を発生させて、復旧までの動作をテストする手法です。
計画的にテストを行うことで、性能を判定しその後の耐障害性の向上へと役立てることが出来ます。
それでは、意図的にアプリケーションのエラーを発生させてみましょう。
Developユーザーとしてログインします。ユーザーは事前に作成しています。
$ oc login -u testuser -p xxx
$ oc new-project chaostest
プロジェクトを、ServiceMeshMemberRoll 内のメンバーに追加します。
$ oc patch servicemeshmemberroll/default -n istio-system --type=merge -p '{"spec": {"members": ["chaostest"]}}'
アプリケーションのデプロイをします。
アプリケーションはあるエンドポイントにアクセスが来た際に、応答メッセージとその回数を返却するアプリケーションです。詳細は割愛します。
$ oc create -f testa.yaml ..(snip).. annotations: sidecar.istio.io/inject: "true" # Envoyプロキシの追加 ..(snip).. $ oc create -f gateway.yaml # 前回までの記事を参考 $ oc create -f virtual-service.yaml # 前回までの記事を参考
ingress gatewayのURLを取得します。
$ oc get route -n istio-system istio-ingressgateway -o jsonpath='{.spec.host}'
取得したingress gatewayのURLにエンドポイントを付加してアプリケーションが応答することを確認します。
$ for i in {1..4};do curl <ingress gateway>/chaos; done Hello application!: 1 Hello application!: 2 Hello application!: 3 Hello application!: 4
ここでvirtual service を編集し、50%でHTTP エラー 500 をスローするように設定してみます。
以下のようにHTTPFaultInjection設定を追加することで実現できます。
$ oc edit vs <virtual service> --- kind: VirtualService ..(snip).. spec: ..(snip).. http: - route: - destination: host: testa fault: abort: httpStatus: 500 percentage: value: 50.0
再度エンドポイント充てにアクセスして経過を確認します。
エラーがスローされていることが確認出来るようになります。
$ for i in {1..4};do curl <ingress gateway>/chaos; done Hello application!: 1 Error: 500 fault filter abort Hello application!: 2 Error: 500 fault filter abort
また、virtual serviceの定義として、delayパラメータを利用し遅延を発生させることも出来たりします。 詳細は以下を参考にしていただければと思います。
https://istio.io/latest/docs/tasks/traffic-management/fault-injection/
OSMでの耐障害性の戦略
前項であるように、マイクロサービスアーキテクチャでは、ネットワーク不安定やレイテンシの増大などの影響を受ける可能性があります。
OSMでは、こうした課題のために耐障害性における機能をいくつか備えています。
- 負荷分散
- タイムアウト
- リトライ
- サーキットブレーカー
■ 負荷分散
複数のレプリカを用意し負荷を分散させることで、サービス自体の過負荷を防止していきます。ある一つのレプリカがDownしても、前段のロードバランサーによって再分散されるような仕組みになっています。
そのためサービスレベルで耐障害性を実現していくには、少なくとも1つ以上のレプリカが必要になってきます。
DestinatioinRule リソースに負荷分散を設定する例を以下に示します。
下記のようにtrafficPolicyとしてアルゴリズムを設定します。また、サブセットレベルで定義し、同じサービスの異なるバージョンに適用出来ます。
kind: DestinationRule ..(snip).. spec: ..(snip).. trafficPolicy: loadBalancer: simple: RANDOM subsets: - name: v1 labels: version: v1 - name: v2 labels: version: v2 trafficPolicy: loadBalancer: simple: LEAST_CONN
DestinatioinRuleに関する詳細は以下が参考になります。
https://istio.io/latest/docs/reference/config/networking/destination-rule/#LoadBalancerSettings
■ タイムアウト
サービスまたはネットワークで障害などのエラーが発生することがあります。
エラーが発生したときに無期限に待機するとリソースが解放されずスループット低下の恐れがあるため、タイムアウト時間を設定します。
OSMを使用してタイムアウトを管理することで、アプリケーションのビジネスロジックとネットワーク管理を別々に保守することができます。
また、コードを変更することなく、Virtual Serviceまたは HTTP ヘッダーを使用してタイムアウトを設定できる点があります。
Virtual Serviceにタイムアウトを設定する例を以下に示します。
kind: VirtualService
..(snip)..
spec:
..(snip)..
http:
- route:
- destination:
host: xxx
timeout: 1s
また、HTTP ヘッダーを使用してタイムアウトを設定する詳細については以下が参考になります。
https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_conn_man/header_sanitizing
■ リトライ
ネットワークの停止や瞬間的な断線などによって、サービスが一時的に利用出来なくなることがあります。この状況に対応するには、失敗した要求を所定の回数再試行するように設定できます。
Virtual Serviceにタイムアウトを設定する例を以下に示します。
kind: VirtualService
..(snip)..
spec:
..(snip)..
http:
- route:
- destination:
host: xxx
retries:
attempts: 3
perTryTimeout: 2s
retryOn: 5xx,gateway-error
- attempts:再要求を行う回数。デフォルトで2に設定されているため無効にするには明示的に0を設定します
- perTryTimeout:各要求のタイムアウト値
- retryOn:
- リトライ条件を指定するポリシー。カンマ区切りで複数指定できます。
- 上記では、5xx 応答コードを含む応答と一致する’5xx’と、502、503、504 を含む応答と一致する例を示しています。
耐障害性を確保するために
リトライの設定では、レイテンシの要件、アプリケーションの構成など、環境に応じて検討する必要があります。なのでリトライ設定の標準値はありません。
リトライ設定を誤ると、システムの耐障害性が低下してパフォーマンスの問題を加速させる場合があります。サーキットブレーカーなど、他の耐障害性パターンは、最悪のリトライシナリオを緩和することができます。
リトライ設定のパラメータやリトライに関する詳細については以下が参考になります。
https://istio.io/latest/docs/reference/config/networking/virtual-service/#HTTPRetry
https://www.envoyproxy.io/docs/envoy/latest/faq/load_balancing/transient_failures.html#retries
■ サーキットブレーカー
サービスがスループットの限界値に近づくと、追加の要求が失敗し始める可能性があります。これは過負荷になりトラフィックを受信出来ない状態が起こり始めるためです。
サーキットブレーカーは、要求の上限を定義して、サービスの負荷が高くならないように保護出来る仕組みです。Istioでは、サーキットブレーカーを静的または動的に設定できます。
DestinatioinRule リソースにサーキットブレーカーを設定する例を以下に示します。
OSMでは、サーキットブレーカーを個々のサービスレベルやサブセットレベルではなく全体のサービスレベルで定義します。
例では、`host: testa` というサービスレベルでサーキットブレーカーを定義しています。
kind: DestinationRule ..(snip).. spec: host: testa trafficPolicy: outlierDetection: consecutiveGatewayErrors: 1 interval: 1s connectionPool: tcp: maxConnections: 2 connectTimeout: 15ms http: http1MaxPendingRequests: 1 maxRequestsPerConnection: 1
trafficPolicyにoutlierDetection, connectionPoolを定義しています。
outlierDetection:負荷分散の構成から異常なサービスを制御する設定
- consecutiveGatewayErrors:接続の構成からエビクトされるまでのGateway(502,503,504)のエラー数
- interval:エラー数を確認する間隔
connectionPool:通信量などを制御する設定
- tcp
- maxConnections:最大同時接続数
- connectTimeout:接続を確立するための時間
- http
- http1MaxPendingRequests:Pendingされる要求の最大数
- maxRequestsPerConnection:1つの接続の内、許可される要求の最大数
サーキットブレーカーに関する詳細と、設定については以下が参考になります。
https://istio.io/latest/docs/tasks/traffic-management/circuit-breaking/
https://istio.io/latest/docs/reference/config/networking/destination-rule/
https://www.envoyproxy.io/docs/envoy/latest/intro/arch_overview/upstream/circuit_breaking
まとめ
今回は、耐障害性に着目してOSMではどんなことが出来るのかといった内容について記載を行いました。
カオステストで、障害のテストをしてみたり、耐障害性の設定があったりと耐障害性を高める取り組みが色々とあった印象です。
アプリケーションの外側で制御出来るのは、コードを修正する手間やアプリケーションの再デプロイなどを省けて良い点だなと思います。
本書の記載が読者のお役に立てれば幸いです。