kakakakakku blog

Weekly Tech Blog: Keep on Learning!

Envoy の「サーキットブレーカー」を試す検証環境を構築する

マイクロサービスを実現するときに Envoy「サーキットブレーカー (Circuit Breaking)」はよく話題に挙がる.Try Envoy には「サーキットブレーカー」を試すコンテンツがなく,今回ドキュメントを読みながら検証環境を構築し,動作確認をした.もし Try Envoy に興味があったら以下の記事を読んでもらうと良いかなと!

Learn Envoy : Circuit Breaking を読む

試す前に概要を整理するため,今回は Learn Envoy のドキュメントを読んだ.まず,マイクロサービスを運用するときに「連鎖的な障害」を防ぐために「サーキットブレーカー」を使うべきと書いてある.そして,単純な「閾値」によってリクエストを制御するのではなく,プライオリティ機能を使って,例えば「購入などの重要な POST 処理」を優先するなど,実践的なアドバイスも載っている.

www.envoyproxy.io

さらに,Envoy の Route 側に設定する「自動リトライ機能」を使って,例えばレスポンスコードが 5xx のときにリトライすることもできるし,サーキットブレーカーに設定する max_retries によって,Cluster 側でリトライすることもできる.ただし,リトライをするとリクエスト数も増えるため,必要性は適切に検討する必要がある.

www.envoyproxy.io

circuit_breakers で使えるパラメータ

「サーキットブレーカー」を使う場合,Envoy の設定ファイル envoy.yaml の Cluster 設定に circuit_breakers を追加する.使えるパラメータの概要を以下に載せる.詳細はドキュメントを見てもらえればと.retry_budgettrack_remaining など,ドキュメントを読んでもよく理解できなかったパラメータもある.

  • priority : DEFAULTHIGH のプライオリティを設定する(デフォルト DEFAULT
  • max_connections : 最大コネクション数を設定する (HTTP/1.1 用)
  • max_pending_requests : 最大保留リクエスト数を設定する(デフォルト 1024
  • max_requests : 最大リクエスト数を設定する (HTTP/2 用)
  • max_retries : 最大リトライ回数を設定する(デフォルト 3
  • retry_budget : 最大リトライ回数を上書きしてリトライ予算を設定する(?)
  • track_remaining : サーキットブレーカーのリソース数をメトリクスとして取得できるようにする(?)
  • max_connection_pools : 最大コネクション数を設定する(デフォルト 上限なし)

www.envoyproxy.io

検証環境

実際に検証環境を構築し,動作確認をした.今回は Docker Compose で「Envoy コンテナ(フロントエンド)」「Sinatra コンテナ(バックエンド)」を起動し,Sinatra では単純に文字列を返すプロトタイプ実装にした.

get '/' do
  'Hello!'
end

構成図は以下のようになる.

f:id:kakku22:20200229233653p:plain

負荷テスト : Vegeta

今回は Vegeta を使って負荷テストを行う.-rate オプションに秒間のリクエスト数を指定し,-duration オプションに継続する時間を指定する.レポートを生成できたり,グラフをプロットできたり,便利な機能が多くあるため,個人的によく使っている.

github.com

検証結果

簡単に「サーキットブレーカー」を試す場合,envoy.yaml を以下のように書く.今回は負荷を低く 5 にした.なお,パラメータの概要に載せた通り HTTP/1.1HTTP/2 は仕組みが異なるため,対象とするパラメータも異なる.HTTP/1.1 の場合は max_connections を使って,HTTP/2 の場合は max_requests を使う.今回は HTTP/1.1 を使うため,基本的には max_connections の値に依存する.そして,保留リクエストをプールする max_pending_requests の値はデフォルト 1024 で検証用途だと大きいため,今回は 128 にした.

(中略)

  clusters:
  - name: backend
    connect_timeout: 0.25s
    type: STRICT_DNS
    dns_lookup_family: V4_ONLY
    lb_policy: ROUND_ROBIN
    circuit_breakers:
      thresholds:
        max_connections: 5
        max_requests: 5
        max_pending_requests: 128

(中略)

検証結果 : -rate=5-duration=1m

まず,Vegeta を使って -rate=5-duration=1m で負荷をかけると,問題なく実行できた.Status Codes の部分を見ると 200:300 となり,300件全てのレスポンスコードが 200 だった.そして vegeta report の結果だと時系列データが確認できないため,今回は jaggrjplot を組み合わせてグラフも生成した.

$ echo 'GET http://localhost:8080' | vegeta attack -rate=5 -duration=1m | tee result.bin | vegeta encode | \
    jaggr hist\[200,500\]:code | \
    jplot code.hist.200+code.hist.500

$ cat result.bin | vegeta report
Requests      [total, rate, throughput]  300, 5.02, 5.02
Duration      [total, attack, wait]      59.804635434s, 59.797234263s, 7.401171ms
Latencies     [mean, 50, 95, 99, max]    12.469781ms, 9.218021ms, 28.394007ms, 82.423598ms, 116.723594ms
Bytes In      [total, mean]              1800, 6.00
Bytes Out     [total, mean]              0, 0.00
Success       [ratio]                    100.00%
Status Codes  [code:count]               200:300
Error Set:

f:id:kakku22:20200302134115p:plain

検証結果 : -rate=20-duration=1m

次に,Vegeta を使って -rate=20-duration=1m で負荷を上げてみても,問題なく実行できた.Status Codes の部分を見ると 200:1200 となり,1200件全てのレスポンスコードが 200 だった.

$ $ echo 'GET http://localhost:8080' | vegeta attack -rate=20 -duration=1m | tee result.bin | vegeta encode | \
    jaggr hist\[200,500\]:code | \
    jplot code.hist.200+code.hist.500

$ cat result.bin | vegeta report
Requests      [total, rate, throughput]  1200, 20.02, 20.01
Duration      [total, attack, wait]      59.965268845s, 59.951868329s, 13.400516ms
Latencies     [mean, 50, 95, 99, max]    53.635778ms, 15.593157ms, 228.294851ms, 572.832074ms, 714.658048ms
Bytes In      [total, mean]              7200, 6.00
Bytes Out     [total, mean]              0, 0.00
Success       [ratio]                    100.00%
Status Codes  [code:count]               200:1200
Error Set:

f:id:kakku22:20200302134128p:plain

検証結果 : -rate=50-duration=1m

最後に,Vegeta を使って -rate=50-duration=1m で負荷をかけると,サーキットブレーカーの機能によって,一部のレスポンスコードが 503 になった.そしてグラフを見ると,途中から 503 が増えている.これはリトライと保留リクエストの結果だと考えられる.

$ echo 'GET http://localhost:8080' | vegeta attack -rate=50 -duration=1m | tee result.bin | vegeta encode | \
    jaggr hist\[200,500\]:code | \
    jplot code.hist.200+code.hist.500

$ cat result.bin | vegeta report
Requests      [total, rate, throughput]  3000, 50.02, 42.52
Duration      [total, attack, wait]      1m3.498684051s, 59.979999578s, 3.518684473s
Latencies     [mean, 50, 95, 99, max]    2.355218631s, 2.643233064s, 3.520133351s, 3.722747469s, 3.787535824s
Bytes In      [total, mean]              40500, 13.50
Bytes Out     [total, mean]              0, 0.00
Success       [ratio]                    90.00%
Status Codes  [code:count]               200:2700  503:300
Error Set:
503 Service Unavailable

f:id:kakku22:20200302134137p:plain

まとめ

今回は Envoy の「サーキットブレーカー (Circuit Breaking)」を検証するため,Mac に Docker Compose で検証環境を構築し,さらに Vegeta も Mac で実行した.リクエスト数も少なく,実用的な負荷テスト環境とは言えないけど,お手軽に試せたのは良かった.とは言え,パラメータチューニングは難易度が高そうに感じた.「サーキットブレーカー」に求めるシナリオなどを検証し,繰り返し検証をするのが良さそう.検証環境の設定などは全て GitHub に置いておいた.

github.com