Cloudflare で gRPC を通す (4. ロードバランシング)


## はじめに
Cloudflare で gRPC を通すテスト、第4回です。

1. [プロキシ + Full TLS 化](https://qiita.com/KeioCF/items/fb300ff8bd059ca8ca72)
2. [ファイアウォール](https://qiita.com/KeioCF/items/77b54c50f39318a473fc)
3. [レートリミット](https://qiita.com/KeioCF/items/cad50f07d3af293b4e0f)
4. ロードバランシング <<====


## ロードバランシング
gRPC リクエストのオリジンへの誘導をプログラムしようと思います。
[ロードバランシング](https://developers.cloudflare.com/load-balancing/)を使います。
Cloudflare のトラフィックシーケンスでは最後に出てきます。
![Screenshot 2023-02-12 at 18.25.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2218045/68ac7116-d14d-e0cc-6e4d-b5b53c45dfe2.png)

### テスト構成
gRPC クライアントとサーバの位置関係は下記のとおりです。

|gRPC|国|
|----|--|
|クライアント|:flag_us:|
|サーバー|:flag_jp: :flag_au: :flag_br:|
|URL|https:\//grpc.example.com:443/helloworld.Greeter/SayHello|

### 設定フロー
![Screenshot 2023-02-11 at 18.10.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2218045/2052fadf-b65c-d02e-8ad1-0e24fe708e42.png)
公式の[設定ガイド](https://developers.cloudflare.com/load-balancing/get-started/)に従います。
`負荷分散`ダッシュボードでは下記の項目が選択可能となっています。

`ロードバランサーを作成する` [API](https://developers.cloudflare.com/load-balancing/understand-basics/load-balancers/#api-commands)
モニターやプールを関連つけたロードバランサーの設定

`モニターを管理する` [API](https://developers.cloudflare.com/load-balancing/understand-basics/monitors/#api-commands) 
サービスの死活監視を行う、ヘルスモニターの設定 
(今回は TCP Port 443 に対する TCP コネクションでサービスの生死を確認)

`プールを管理する` [API](https://developers.cloudflare.com/load-balancing/understand-basics/pools/#api-commands)
gRPC オリジンサーバの集まりである、プールの設定  
(今回は国ごとにプールを作成)


下記のようなウィザードに従い、作成します。


#### 1\. ホスト名
![Screenshot 2023-02-11 at 18.03.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2218045/b3f29b33-a803-e006-a995-85af9c7b1e64.png)
ホスト名を入力し、<font color="orange">オレンジ雲</font>に倒して HTTP Proxyとします。
<font color="gray">グレー雲</font>の場合は HTTP Proxy はせず、[DNS によるロードバランシング](https://qiita.com/KeioCF/items/bc8c63ac8883bb7e3abb)となります。

#### 2\. オリジンプール
![Screenshot 2023-02-11 at 18.04.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2218045/bba2238c-768f-1038-0633-87d2b5bfc11c.png)
プール (オリジンサーバーのグループ) を指定します。
今回はプールが 3 つ、各プールに属するオリジンは 1 つ。
|プール|オリジン台数|オリジン IP/Host|
|-|-|-|
|gcp_australia|1|35.xxx.xxx.xxx|
|gcp_brazil|1|34.xxx.xxx.xxx|
|gcp_japan|1|34.xxx.xxx.xxx|

:::note
`編集` をクリックして設定されている内容を見てみます。

![Screenshot 2023-02-11 at 18.05.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2218045/b1abaee5-9659-6253-a40b-29bb3a090fbf.png)

`オリジンステアリング`
[プールの中に複数のオリジンがいる場合の分散方法]((https://developers.cloudflare.com/load-balancing/understand-basics/traffic-steering/origin-level-steering/))になります。今回はプール内のオリジンは 1 なので動作には影響しません。
`オリジン名`
識別子です。
`オリジンアドレス`
オリジンのホスト名あるいは IP アドレスを指定します。

![Screenshot 2023-02-11 at 18.05.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2218045/8e542d85-b427-7ffe-70b3-d8cef8c6b79e.png)

`緯度、経度`
プールの位置。
[プロキシミティ ステアリング](https://developers.cloudflare.com/load-balancing/understand-basics/traffic-steering/steering-policies/proximity-steering/) を試すので、こちらも指定しておきます。

`監視`
[ヘルスモニター](https://developers.cloudflare.com/load-balancing/understand-basics/monitors/)を紐つけます。
TCP の 443 ポートを監視する設定に関連ついています。
ダウンしていたら、そのプールには振らない、などでサービスの可用性を高めることができます。

`ヘルスチェックリージョン`
監視をするクラウドフレアの[地域](https://developers.cloudflare.com/load-balancing/understand-basics/monitors/#health-check-regions)。
[ダイナミックステアリング](https://developers.cloudflare.com/load-balancing/understand-basics/traffic-steering/steering-policies/dynamic-steering/) を試すので、gRPC クライアントのいる Western North America を指定。
Western North America から各プールにヘルスチェックを行った際の応答 RTT がステアリングの判断材料として利用されます。
:::

#### 3\. モニター
![Screenshot 2023-02-11 at 18.06.04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2218045/0d526dfe-a042-9078-2fc4-7ffb6a9f1374.png)

プールにモニターが紐ついていることがわかります。

:::note
`負荷分散 > モニター` では下記の設定となっています。
![Screenshot 2023-02-11 at 18.43.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2218045/c3b9cf31-4ce4-ff31-2ae2-8b1458b5c45b.png)

![Screenshot 2023-02-11 at 18.41.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2218045/2f4df8ee-adb3-323c-521d-0cf9ef6e9dc5.png)
:::

#### 4\. トラフィックステアリング
![Screenshot 2023-02-11 at 18.06.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2218045/3cc0641e-b04c-2396-d4c7-5c2e758cbd6f.png)

プールの選択方法を選択します。
まずは [オフ](https://developers.cloudflare.com/load-balancing/understand-basics/traffic-steering/steering-policies/standard-options/#off---failover) でフェイルオーバ順とします。

#### 5\. カスタムルール
![Screenshot 2023-02-11 at 18.06.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2218045/33af4812-e8ec-217c-9884-a48e2ed9e8a8.png)

柔軟な [ルールの上書き](https://developers.cloudflare.com/load-balancing/additional-options/load-balancing-rules/) が可能です。今回は使用しません。

#### 6\. レビュー

![Screenshot 2023-02-11 at 18.06.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2218045/e018607f-7bfc-ae1f-a6b2-e5f4bbc9bce4.png)

ウィザードで実施した設定をレビューします。
問題なければ保存します。

#### 7\. サマリー画面

![Screenshot 2023-02-11 at 18.06.47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2218045/05bb5eb6-da3c-9291-ec28-27dc259b3a8e.png)

3 プールとも `正常`に稼働していることがわかります。

### ステアリング別の動作確認

gRPC クライアントからは 5 秒に 1 つリクエストを送っています。
ロードバランシングの状況は`負荷分散分析`画面で確認できます。
![Screenshot 2023-02-11 at 18.54.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2218045/d316a341-7e03-f21c-3aff-df78e5d8795a.png)
オリジンのステータスとその履歴も参照可能です。
![Screenshot 2023-02-11 at 18.54.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2218045/0345f0a7-c202-9141-fa5f-5fa85da2885d.png)
ステアリングの方法ごとに、リクエストの分散状況を見ていきます。

#### オフ
[オフ](https://developers.cloudflare.com/load-balancing/understand-basics/traffic-steering/steering-policies/standard-options/#off---failover) では、フェイルオーバー順に利用されます。
 (上の `2. オリジンプール` に記載の通り)

リストの先頭である :flag_au: のみに誘導されています。
![Screenshot 2023-02-11 at 19.00.04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2218045/bb99fbf4-8e00-588e-f231-37dab2d41101.png)
この状態で :flag_au: が応答不能になると、
![Screenshot 2023-02-11 at 19.10.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2218045/7eea5138-8596-d7aa-5981-34f97795be48.png)
リストの次に位置する :flag_br: が選択されます。
![Screenshot 2023-02-11 at 19.15.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2218045/1e897589-4c19-fd3d-6a00-a251910c78d6.png)
:flag_au: が生き返ると、再度選択されます。

#### ランダム
[ランダム](https://developers.cloudflare.com/load-balancing/understand-basics/traffic-steering/steering-policies/standard-options/#random-steering)を選択します。重み付けは均等にしています。
![Screenshot 2023-02-11 at 19.20.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2218045/df089fe3-44c3-c186-f8ab-38df120bdbad.png)
重み通りに均等に分散されています。
![Screenshot 2023-02-10 at 9.49.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2218045/7798215c-73fb-c193-5e87-c0b9efd05aed.png)

#### ダイナミック
[ダイナミック](https://developers.cloudflare.com/load-balancing/understand-basics/traffic-steering/steering-policies/dynamic-steering/)を選択します。
![Screenshot 2023-02-10 at 9.50.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2218045/9075882f-943b-959c-0213-cdb29e38ec8a.png)
gRPC クライアントが接続するデータセンター ( :flag_us: ) からのヘルスチェックの応答 (RTT) が速いプールが選択されます。
:flag_jp: :flag_au: が選択され、 :flag_br: は選択されていません。
![Screenshot 2023-02-10 at 13.22.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2218045/ee1cd8b4-fef1-cacb-a2a4-0e08da243f95.png)

:::note
RTT は Exponentially Weighted Moving Average (EWMA) というのを利用しているとのこと。
```
Dynamic Steering creates Round Trip Time (RTT) profiles based on an exponential weighted moving average (EWMA) of RTT to determine the fastest pool. If there is no current RTT data for your pool in a region or colocation center, Cloudflare directs traffic to the pools in failover order.
```
:::

:::note
RTT は API で確認可能です。
```
$ for POOL_ID in `curl -s -X GET -H "Authorization: Bearer $TOKEN"  "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/load_balancers" | jq -r ".result[]|select(.name |contains(\"$HOSTNAME\"))|.default_pools[]"`; do curl -s -X GET -H "Authorization: Bearer $TOKEN"  "https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/load_balancers/pools/$POOL_ID/health" |jq '.result.pop_health'; done
{
  "WNAM": {
    "healthy": true,
    "origins": [
      {
        "35.xxx.xxx.xxx": {    <<== JP
          "healthy": true,
          "rtt": "158.2ms",
          "failure_reason": "No failures"
        }
      }
    ]
  }
}
{
  "WNAM": {
    "healthy": true,
    "origins": [
      {
        "34.xxx.xxx.xxx": {    <<== BR
          "healthy": true,
          "rtt": "172.7ms",
          "failure_reason": "No failures"
        }
      }
    ]
  }
}
{
  "WNAM": {
    "healthy": true,
    "origins": [
      {
        "34.xxx.xxx.xxx": {    <<== AU
          "healthy": true,
          "rtt": "169.6ms",
          "failure_reason": "No failures"
        }
      }
    ]
  }
}
```
```
ロードバランサーのプール (POOL ID) を探す
$ curl -s -X GET -H "Authorization: Bearer $TOKEN"  "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/load_balancers"

各プールのヘルスモニタ状況を得る
$ curl -s -X GET -H "Authorization: Bearer $TOKEN"  "https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/load_balancers/pools/$POOL_ID/health"
```
:::

#### ジオ
[ジオ](https://developers.cloudflare.com/load-balancing/understand-basics/traffic-steering/steering-policies/geo-steering/)を選択します。
:flag_us: のクライアントを :flag_br: のプール (一番遠い) に誘導してみます。
![Screenshot 2023-02-11 at 16.18.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2218045/62b1dc00-bf73-72c4-fde3-010d0cca573e.png)
クライアントの国を選択します。
![Screenshot 2023-02-11 at 16.18.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2218045/b42ca6d4-d03b-2eca-f2c7-b35fb60c85ba.png)
誘導したいプールを選択します。
:flag_br: を選びます。
![Screenshot 2023-02-11 at 16.18.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2218045/0bdf5c31-9a65-720e-cae5-162e5ee0d3c2.png)
:flag_br: にスタティックに誘導されています。
![Screenshot 2023-02-11 at 17.29.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2218045/3adca4a9-d90c-fb6f-bd74-121dabcdab59.png)

#### プロキシミティ
[プロキシミティ](https://developers.cloudflare.com/load-balancing/understand-basics/traffic-steering/steering-policies/proximity-steering/)を選択します。
![Screenshot 2023-02-11 at 10.43.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2218045/de838a0f-0574-0461-52d1-49c7aa40526b.png)
あらかじめプールの場所 (緯度・経度) は設定しておきます。
ダッシュボードでの地図で選ぶことも可能です (上記 `2. オリジンプール`)。

:flag_jp: のみに誘導されています。
![Screenshot 2023-02-11 at 12.41.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2218045/9db04947-e381-275b-b1de-795044bb6a5d.png)

### レスポンス比較

|ステアリング|応答時間 95 パーセンタイル|50 パーセンタイル|プール|
|-|-|-|-|
|ランダム|0.634|0.279| :flag_au: :flag_br: :flag_jp: | 
|ダイナミック|0.344|0.271| :flag_au: :flag_jp: |  
|プロキシミティ|0.262|0.213| :flag_jp: |

今回の場合は :flag_jp: だけを選択したプロキシミティの応答速度が良かったようです。

:::note
応答時間は gRPC クライアントで [tshark](https://www.wireshark.org/docs/man-pages/tshark.html) パケットキャプチャ分析 (Duration) を利用
```
$ sudo tshark -r /tmp/grpc.proximity.pcap  -q -z conv,tcp 2>/dev/null| head
================================================================================
TCP Conversations
Filter:<No Filter>
                                                           |       <-      | |       ->      | |     Total     |    Relative    |   Duration   |
                                                           | Frames  Bytes | | Frames  Bytes | | Frames  Bytes |      Start     |              |
10.138.0.2:35496           <-> 104.xxx.xxx.xxx:443               15      7058      20      1968      35      9026   763.815464586         0.2134
10.138.0.2:51948           <-> 104.xxx.xxx.xxx:443               15      7060      20      1968      35      9028  1897.589741747         0.2520
:
```
Duration のみ抽出し、小さい順にソート
```
sudo tshark -r /tmp/grpc.dynamic.pcap  -q -z conv,tcp 2>/dev/null| awk '/^1/ {print $11}'|sort > grpc.perf.dynamic.txt
```
パーセンタイル算出は [R](https://www.r-tutor.com/elementary-statistics/numerical-measures/percentile) を利用
```
$ R
> x <- scan("grpc.perf.dynamic.txt")
Read 681 items
> x
  [1] 0.1903 0.1942 0.1957 0.1975 0.1992 0.2021 0.2027 0.2044 0.2052 0.2053
:
[681] 1.1017
>
> summary(x)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max.
 0.1903  0.2629  0.2717  0.2822  0.2821  1.1017
> quantile(x, c(0,0.5,0.95))
    0%    50%    95%
0.1903 0.2717 0.3449
>
```
:::

## まとめ

以上で gRPC クライアントを複数拠点の gRPC サーバに誘導し、パフォーマンスと可用性を高めることができました。サービス展開時の参考になればと思います。

なお、[ロードバランシング](https://developers.cloudflare.com/load-balancing/)については、他にも様々な機能がありますが、そちらは別の機会にでも。

こちらで Cloudflare で gRPC を通すテスト は一旦終了とします。