Envoy Kubernetes Network gRPC Envoy Gateway を使った gRPC のリトライ Envoy Gateway v1.3.0 のBackendTrafficPolicy を使って、gRPC 通信のリトライを試してみました。検証に使う fake-service には、一定の確率で意図的にエラーを返す fault-injection を設定し、エラー発生時に Envoy Gateway が再試行するかを確認します。
構成 本検証における通信フローを、Envoy Gateway の有無による 2 つのパターンで比較します。
Envoy Gateway を使わない場合 まず、Envoy Gateway を用いずにリトライする場合の構成を確認します。 この構成ではクライアントがサービスにリクエストを送信し、サービスがエラーを返すとクライアントがリトライします。
sequenceDiagram
participant C as Client
participant S as Service
C->>S: Request
S->>C: Response 500
C->>S: Request
S->>C: Response 500
C->>S: Request
S->>C: Response 200
Envoy Gateway を使った場合 次に、Envoy Gateway を経由するリトライ構成を確認します。 この構成ではクライアントからのリクエストを Envoy Gateway がサービスへ転送し、サービスがエラーを返すと Envoy Gateway がリトライします。クライアント側がリトライを意識しなくてよい点が、Envoy Gateway を使わない構成との違いです。
sequenceDiagram
participant C as Client
participant G as Gateway
participant S as Service
C->>G: Request
G->>S: Request
S->>G: Response 500
G->>S: Request
S->>G: Response 500
G->>S: Request
S->>G: Response 200
G->>C: Response 200
以降の手順では、Envoy Gateway を使ったリトライの構成を検証します。
セットアップ クラスターに Envoy Gateway をインストールします。envoyproxies.gateway の CRD がインストールされていることを確認します。
kubectl get crd | grep envoyproxies.gateway
インストールされていない場合は、次のコマンドでインストールします。
helm install eg oci://docker.io/envoyproxy/gateway-helm --version v1.3.0 -n envoy-gateway-system --create-namespace
サンプルの gRPC アプリケーションをデプロイします。
kubectl apply -f https://raw.githubusercontent.com/Nishikoh/envoy-sandbox/refs/heads/main/grpc-retry/grpc-routing.yaml
コードの簡単な解説 apiVersion : gateway.envoyproxy.io/v1alpha1
kind : EnvoyProxy
metadata :
name : custom-envoy-proxy
namespace : envoy-gateway-system
spec :
provider :
type : Kubernetes
kubernetes :
envoyService : # (2)
name : grpc-gateway # (1)
---
apiVersion : gateway.networking.k8s.io/v1
kind : GatewayClass
metadata :
name : eg-example
labels :
example : grpc-routing
spec :
controllerName : gateway.envoyproxy.io/gatewayclass-controller
parametersRef :
group : gateway.envoyproxy.io
kind : EnvoyProxy # (3)
name : custom-envoy-proxy
namespace : envoy-gateway-system
---
apiVersion : gateway.networking.k8s.io/v1
kind : Gateway
metadata :
name : eg-example
labels :
example : grpc-routing
spec :
gatewayClassName : eg-example
listeners : # (4)
- name : http
protocol : HTTP
port : 80
---
apiVersion : apps/v1
kind : Deployment
metadata :
labels :
app : fake
example : grpc-routing
name : fake
spec :
selector :
matchLabels :
app : fake
replicas : 1
template :
metadata :
labels :
app : fake
spec :
containers :
- name : grpcsrv
image : nicholasjackson/fake-service:v0.26.2
env :
- name : ERROR_RATE
value : "0.5" # (5)
- name : ERROR_TYPE
value : "http_error"
- name : ERROR_CODE
value : "14"
- name : SERVICE_TYPE
value : "grpc"
- name : LISTEN_ADDR
value : "0.0.0.0:9000"
ports :
- containerPort : 9000
protocol : TCP
---
apiVersion : v1
kind : Service
metadata :
labels :
app : fake
example : grpc-routing
name : fake
spec :
type : ClusterIP
ports :
- name : http
port : 9000
protocol : TCP
targetPort : 9000
selector :
app : fake
---
apiVersion : gateway.networking.k8s.io/v1
kind : GRPCRoute # (6)
metadata :
name : fake
labels :
example : grpc-routing
spec :
parentRefs :
- name : eg-example # (7)
hostnames :
- "grpc-example.com"
rules :
- backendRefs : # (8)
- group : ""
kind : Service
name : fake
port : 9000
weight : 1
---
apiVersion : gateway.envoyproxy.io/v1alpha1
kind : BackendTrafficPolicy
metadata :
name : retry-policy
namespace : default
spec :
# faultInjection:
# abort:
# grpcStatus: 14
# percentage: 50
retry : # (9)
numRetries : 5
perRetry :
backOff :
baseInterval : 100ms
maxInterval : 10s
timeout : 250ms
retryOn :
triggers :
- unavailable
- retriable-status-codes
targetRefs : # (10)
- group : gateway.networking.k8s.io
kind : GRPCRoute
name : fake
この指定がないと Gateway を新しく作る際にランダムな文字列が付与されて識別しにくいため、指定を強く推奨 Service の設定以外に、HPA や Deployment などの設定も可能 GatewayClass に EnvoyProxy を紐付け port 80 の HTTP リクエストを受け付ける設定(他の port や TCP などのプロトコルも設定可能) リトライ検証のため、fake-service のエラー率を 50% に設定 GRPCRoute の設定 Gateway との紐付け Service との紐付け リトライの設定 リトライの対象の指定 各リソースの依存関係 graph TD
subgraph Gateways
envoyProxy[EnvoyProxy: custom-envoy-proxy]
gatewayClass[GatewayClass: eg-example]
gateway[Gateway: eg-example]
end
subgraph Deployments & Services
fake[Deployment: fake]
fakeService[Service: fake]
end
subgraph Routing & Policies
grpcRoute[GRPCRoute: fake]
faultInjection[BackendTrafficPolicy: retry-policy]
end
gatewayClass --> envoyProxy
gateway --> gatewayClass
faultInjection --> grpcRoute
grpcRoute --> gateway
grpcRoute --> fakeService リトライの動作検証 Envoy Gateway の gRPC リトライを検証します。
export GATEWAY_HOST = $( kubectl get gateway/eg-example -o jsonpath = '{.status.addresses[0].value}' )
grpcurl -plaintext -authority= grpc-example.com ${ GATEWAY_HOST } :80 FakeService.Handle
結果は次のようになります。
{
"Message" : "{\n \"name\": \"Service\",\n \"type\": \"gRPC\",\n \"ip_addresses\": [\n \"x.y.z.32\"\n ],\n \"start_time\": \"2025-02-19T06:06:12.655413\",\n \"end_time\": \"2025-02-19T06:06:12.655454\",\n \"duration\": \"40.876µs\",\n \"body\": \"Hello World\",\n \"code\": 0\n}\n"
}
上記のコマンドでログを見ると、error_injector によってエラーが発生しています。x-request-id valueが同じであることから、同じリクエストに対して Envoy Gateway がリトライし、この例では最終的に成功したことがわかります。
2025-02-19T06:06:12.653Z [INFO] Handling request gRPC request:
context=
| key: :authority value: [grpc-example.com]
| key: content-type value: [application/grpc]
| key: grpc-accept-encoding value: [gzip]
| key: x-forwarded-for value: [xx.yy.zz.1]
| key: user-agent value: [grpcurl/1.9.2 grpc-go/1.61.0]
| key: x-forwarded-proto value: [http]
| key: x-envoy-external-address value: [xx.yy.zz.1]
| key: x-request-id value: [b93fa578-5291-4023-9f2a-63feca3d07ea]
2025-02-19T06:06:12.653Z [INFO] error_injector: Injecting error: request_count=2 error_percentage=0.5 error_type=http_error
2025-02-19T06:06:12.653Z [ERROR] Error handling request: error="Service error automatically injected"
2025-02-19T06:06:12.653Z [INFO] Finished handling request: duration="125.503µs"
2025-02-19T06:06:12.655Z [INFO] Handling request gRPC request:
context=
| key: user-agent value: [grpcurl/1.9.2 grpc-go/1.61.0]
| key: grpc-accept-encoding value: [gzip]
| key: x-request-id value: [b93fa578-5291-4023-9f2a-63feca3d07ea]
| key: :authority value: [grpc-example.com]
| key: x-forwarded-for value: [xx.yy.zz.1]
| key: x-forwarded-proto value: [http]
| key: x-envoy-external-address value: [xx.yy.zz.1]
| key: content-type value: [application/grpc]
2025-02-19T06:06:12.655Z [INFO] Finished handling request: duration="52.877µs"
同じ namespace からのアクセス確認 default の namespace にある fake へ、同じ名前空間から Envoy Gateway を通してアクセスする場合は次のようになります。 grpcurl を使って Envoy Gateway のサービスにリクエストを送信します。
kubectl run grpcurl --image= fullstorydev/grpcurl:latest --namespace default -it --rm -- -plaintext -authority= grpc-example.com grpc-gateway.envoy-gateway-system.svc.cluster.local:80 FakeService.Handle
クライアントのログを確認すると通信が成功していることがわかります。
{
"Message" : "{\n \"name\": \"Service\",\n \"type\": \"gRPC\",\n \"ip_addresses\": [\n \"xxx.yyy.zzz.34\"\n ],\n \"start_time\": \"2025-02-20T07:55:33.765312\",\n \"end_time\": \"2025-02-20T07:55:33.765457\",\n \"duration\": \"145.169µs\",\n \"body\": \"Hello World\",\n \"code\": 0\n}\n"
}
サーバーのログを確認すると、先ほどと同様に失敗した後、同じx-request-idで Envoy Gateway がリトライしていることがわかります。
fake-74cb56bd56-kfx9h grpcsrv 2025-02-20T07:58:17.985Z [INFO] Handling request gRPC request:
fake-74cb56bd56-kfx9h grpcsrv context=
fake-74cb56bd56-kfx9h grpcsrv | key: x-forwarded-for value: [xxx.yyy.zzz.42]
fake-74cb56bd56-kfx9h grpcsrv | key: x-forwarded-proto value: [http]
fake-74cb56bd56-kfx9h grpcsrv | key: :authority value: [grpc-example.com]
fake-74cb56bd56-kfx9h grpcsrv | key: content-type value: [application/grpc]
fake-74cb56bd56-kfx9h grpcsrv | key: x-envoy-external-address value: [xxx.yyy.zzz.42]
fake-74cb56bd56-kfx9h grpcsrv | key: x-request-id value: [6e8c6e70-0133-475c-a070-6aeb3638de5c]
fake-74cb56bd56-kfx9h grpcsrv | key: user-agent value: [grpcurl/v1.9.2 grpc-go/1.61.0]
fake-74cb56bd56-kfx9h grpcsrv | key: grpc-accept-encoding value: [gzip]
fake-74cb56bd56-kfx9h grpcsrv
fake-74cb56bd56-kfx9h grpcsrv 2025-02-20T07:58:17.985Z [INFO] error_injector: Injecting error: request_count=36 error_percentage=0.5 error_type=http_error
fake-74cb56bd56-kfx9h grpcsrv 2025-02-20T07:58:17.986Z [ERROR] Error handling request: error="Service error automatically injected"
fake-74cb56bd56-kfx9h grpcsrv 2025-02-20T07:58:17.986Z [INFO] Finished handling request: duration=1.097642ms
fake-74cb56bd56-kfx9h grpcsrv 2025-02-20T07:58:18.052Z [INFO] Handling request gRPC request:
fake-74cb56bd56-kfx9h grpcsrv context=
fake-74cb56bd56-kfx9h grpcsrv | key: content-type value: [application/grpc]
fake-74cb56bd56-kfx9h grpcsrv | key: grpc-accept-encoding value: [gzip]
fake-74cb56bd56-kfx9h grpcsrv | key: x-request-id value: [6e8c6e70-0133-475c-a070-6aeb3638de5c]
fake-74cb56bd56-kfx9h grpcsrv | key: user-agent value: [grpcurl/v1.9.2 grpc-go/1.61.0]
fake-74cb56bd56-kfx9h grpcsrv | key: x-forwarded-for value: [xxx.yyy.zzz.42]
fake-74cb56bd56-kfx9h grpcsrv | key: x-forwarded-proto value: [http]
fake-74cb56bd56-kfx9h grpcsrv | key: x-envoy-external-address value: [xxx.yyy.zzz.42]
fake-74cb56bd56-kfx9h grpcsrv | key: :authority value: [grpc-example.com]
fake-74cb56bd56-kfx9h grpcsrv
fake-74cb56bd56-kfx9h grpcsrv 2025-02-20T07:58:18.052Z [INFO] Finished handling request: duration="210.211µs"
Gateway の Fault Injection と Retry の併用 本検証では、バックエンドの fake-service 側で Fault Injection を設定しました。Envoy Gateway 自身にも Fault Injection の機能がありますが、Gateway 上でリトライと同時に設定したところ、期待した再試行は行われませんでした。
Gateway 上で Fault Injection を有効化したとき、想定していた動作は次のシーケンスです。
sequenceDiagram
participant C as Client
participant G as Gateway
participant S as Service
C->>G: Request
G->>S: Request
S->>G: Response 500
G->>S: Request
S->>G: Response 500
G->>S: Request
S->>G: Response 200
G->>C: Response 200 しかし実際に試すと、次のように Gateway 自体がエラーを生成してクライアントへ応答し、バックエンドへの再試行は行われませんでした。
sequenceDiagram
participant C as Client
participant G as Gateway
participant S as Service
C->>G: Request
G->>C: Response 500 このため、Gateway のリトライ挙動を検証したい場合、疑似障害は Gateway 自身ではなくバックエンド側で注入します。
まとめ Envoy Gateway を導入したことで、gRPC クライアント側で再試行ロジックを実装しなくても、インフラ層で透過的にリトライを制御できました。
GRPCRoute を対象にした BackendTrafficPolicy のretryOn.triggers(本記事ではunavailableとretriable-status-codes)で、再試行の条件を宣言的に定義できる バックエンドが一時的にエラーを返しても、numRetriesで指定した回数(本記事では 5 回)以内の再試行で成功すれば、クライアントには成功レスポンスが返る Gateway の Fault Injection と Retry を併用するとリトライが行われなかったため、障害注入はバックエンドサービス側で行う クライアントのコードに再試行ロジックを持ち込まずに通信の信頼性を高めたい場合、Envoy Gateway によるリトライ制御は有効な選択肢です。
2026-10-16 10:04:54 2025-02-20 17:54:47