Carpe Diem

備忘録。https://github.com/jun06t

EnvoyのFrontProxyを用意してgRPCの負荷分散をする

概要

gRPCはHTTP/2プロトコルをベースとしたRPCです。
なのでコネクションが永続化され、その中で複数のストリームがリクエスト・レスポンスを取り扱うため負荷分散に注意する必要があります。
今回はEnvoyというgRPCに向いたproxyを使うことでそれを実現します。

環境

  • envoy 1.7.0

成果物

今回のソースはこちらにあります。

github.com

AWSでの問題点

AWSロードバランサーは主にCLB、ALB、NLBがあります。
しかしながらそれぞれ

  • NLBはL4のロードバランサであるため、コネクションを分散することはできてもリクエストレベル(ストリーム)で負荷分散ができない
    • CLBのL4モードも同様
  • ALBはL7だが、HTTP/2の転送がそもそもできない

という問題を持つため、gRPCでの負荷分散に向いていません。
そこでgRPCで通信する場合はELBを使わずにEnvoyを用いて分散する方針が良いです。

Envoy

Envoyはマイクロサービス化を進める上で複雑になるネットワークレイヤの問題点を一手に担ってくれるL7 Proxyです。
今回はgRPCの負荷分散について述べますが、それ以外にも

  • Service DIscovery
  • Circuit Breaker
  • Tracing
  • Metrics
  • 自動リトライ
  • 通信の暗号化

など様々なマイクロサービスの問題を扱えるため、アプリケーション側の実装がシンプルになります。

FrontProxyによる負荷分散

ドキュメントにあるイメージ図は以下です。

f:id:quoll00:20180626161214p:plain

ちなみにこの構成が簡単に試せるdocker-composeも公式のリポジトリで提供されてます。

github.com

gRPC通信のサーバの用意

こちらで紹介したgatewayサーバを用いて検証してみます。

christina04.hatenablog.com

通信経路としては

gateway-server <-> FrontProxy(envoy) <-> grpc-server

となります。

docker-compose.yml

docker-composeを用意します。

gist.github.com

ポイントは

    networks:
      front-proxy:
        aliases:
          - backend

の部分です。
aliasesをつけることで、コンテナをスケールアウトしてもそのalias名でアクセスすれば内部DNSがスケールアウトした分のコンテナのIPも返してくれるので、envoyの設定を弄らずにスケールアウトに対応できます。

front-envoy.yaml

envoyのconfigを用意します。

gist.github.com

大まかなポイントとしては

  • HTTP/2通信なのでhttp2_protocol_optionsを付ける
  • hostsは先程のalias名に
  • 調査しやすいようaccess_logを付ける
  • health_checksを付ける

です。

動作検証

起動します。

$ docker-compose up -d

管理画面

起動するとenvoyの管理画面が見れます。

http://localhost:8001

こんな感じの簡素なやつです。

f:id:quoll00:20180627095913p:plain

/clusters

このようにbackendのgrpcサーバが1台登録されていることが分かります。
IPは172.18.0.4のようですね

f:id:quoll00:20180627100020p:plain

疎通確認

$ curl localhost:3000/alive
{"status":true

ちゃんと疎通できています。

docker-composeのenvoyのログも

front-proxy    | [2018-06-27T01:01:19.585Z] "POST /gateway.AliveService/GetStatus HTTP/2" 200 - 5 7 11 8 "172.18.0.1" "grpc-go/1.13.0" "1af794e4-2cd4-4c92-b575-205d3060c057" "front-proxy:8000" "172.18.0.4:8080"

このようにリクエストをハンドリングできていることが分かります。

スケールアウトして分散されるか検証

次にbackendのgrpcサーバをスケールアウトしてみます。

docker-compose up --scale grpc=3 -d

/clusters

最初の172.18.0.4に加えて、

  • 172.18.0.5
  • 172.18.0.6

が自動で追加されました。aliasによるアクセスのおかげです。

f:id:quoll00:20180627100455p:plain

負荷分散されるか検証

abで1000リクエストほど投げてみます。

$ ab -n 1000 http://localhost:3000/alive

すると/clustersのstats情報が更新され、以下のようにリクエストがバランシングされていることが分かります。

f:id:quoll00:20180627100808p:plain

まとめ

Envoyを使うことでgRPCの負荷分散を実現することができました。

ソース