すべてのプロダクト
Search
ドキュメントセンター

Alibaba Cloud Service Mesh:リクエストのカスタム処理に Envoy 外部処理サービスを使用する

最終更新日:Jan 13, 2025

Envoy 外部処理は、外部処理サービスを Envoy フィルターチェーンに接続できる機能です。この外部サービスを使用して、Envoy 用の Wasm プラグインまたは追加の後処理スクリプトを作成するのではなく、HTTP リクエストとレスポンスを処理できます。これにより、柔軟性とスケーラビリティが確保されます。このトピックでは、Envoy プロキシで外部処理を構成および使用する方法について説明します。

前提条件

メカニズム

次の図は、Envoy 外部処理サービスがダウンストリームサービスからのリクエストとアップストリームサービスからのレスポンスをどのように管理するかを示しています。

  1. ダウンストリームサービスは、アップストリームサービスに ①リクエストを送信します。Envoy はリクエストをブロックし、処理のために ②外部処理サービスに送信します。

  2. 外部処理サービスはリクエストを処理し、Envoy に ③結果を返します。

  3. Envoy は結果に基づいてリクエストを処理し、アップストリームサービスに ④リクエストを転送します。

  4. アップストリームサービスはリクエストを処理し、ダウンストリームサービスに ⑤レスポンスを返します。Envoy はレスポンスをブロックし、外部処理サーバーに ⑥転送します。

  5. 外部処理サーバーはレスポンスを ⑦処理し、Envoy に ⑧返します。

  6. Envoy は結果に基づいてレスポンスを処理し、処理されたレスポンスをダウンストリームサービスに ⑨転送します。

手順 1:外部処理サービスの処理ロジックを作成する

以下は、コアロジックコードのスニペットです。完全なサンプルコードについては、ext-proc-demo を参照してください。詳細については、「Envoy のドキュメント」をご参照ください。

詳細を表示するには展開します

func NewServer() *Server {
	return &Server{}
}

// Server は Envoy 外部処理サーバーインターフェースを実装します
// https://www.envoyproxy.io/docs/envoy/latest/api-v3/service/ext_proc/v3/external_processor.proto を参照してください
type Server struct{}

func (s *Server) Process(srv extProcPb.ExternalProcessor_ProcessServer) error {
	klog.Infof("処理中") // 処理中
	ctx := srv.Context()
	for {
		select {
		case <-ctx.Done():
			klog.Infof("context done") // コンテキスト完了
			return ctx.Err()
		default:
		}

		req, err := srv.Recv()
		if err == io.EOF {
			// envoy がストリームを閉じました。何も返さずに、このストリームを完全に閉じます
			return nil
		}
		if err != nil {
			return status.Errorf(codes.Unknown, "cannot receive stream request: %v", err)
		}

		resp := &extProcPb.ProcessingResponse{}
		switch v := req.Request.(type) {
        // リクエストヘッダーの処理が必要なリクエスト
		case *extProcPb.ProcessingRequest_RequestHeaders:
			klog.Infof("Got RequestHeaders") // RequestHeaders を取得しました
			h := req.Request.(*extProcPb.ProcessingRequest_RequestHeaders)
			resp = handleRequestHeaders(h)
        // レスポンスヘッダーの処理が必要なリクエスト
        case *extProcPb.ProcessingRequest_ResponseHeaders:
			klog.Infof("Got ResponseHeaders") // ResponseHeaders を取得しました
			h := req.Request.(*extProcPb.ProcessingRequest_ResponseHeaders)
			resp = handleResponseHeaders(h)
        // リクエストボディの処理が必要なリクエスト(現在実装されていません)
		case *extProcPb.ProcessingRequest_RequestBody:
			klog.Infof("Got RequestBody (not currently handled)") // RequestBody を取得しました(現在処理されていません)
        // リクエストトレーラーの処理が必要なリクエスト(現在実装されていません)
		case *extProcPb.ProcessingRequest_RequestTrailers:
			klog.Infof("Got RequestTrailers (not currently handled)") // RequestTrailers を取得しました(現在処理されていません)
        // レスポンスボディの処理が必要なリクエスト(現在実装されていません)
		case *extProcPb.ProcessingRequest_ResponseBody:
			klog.Infof("Got ResponseBody (not currently handled)") // ResponseBody を取得しました(現在処理されていません)
        // レスポンストレーラーの処理が必要なリクエスト(現在実装されていません)
		case *extProcPb.ProcessingRequest_ResponseTrailers:
			klog.Infof("Got ResponseTrailers (not currently handled)") // ResponseTrailers を取得しました(現在処理されていません)

		default:
			klog.Infof("Unknown Request type %v", v)
		}
        // リクエストに必要な処理を返します
		klog.Infof("Sending ProcessingResponse: %+v", resp) // ProcessingResponse を送信しています
		if err := srv.Send(resp); err != nil {
			klog.Infof("send error %v", err) // 送信エラー
			return err
		}
	}
}

// リクエストヘッダーに x-ext-proc-header=hello-to-asm を追加します
func handleRequestHeaders(req *extProcPb.ProcessingRequest_RequestHeaders) *extProcPb.ProcessingResponse {
	klog.Infof("handle request headers: %+v\n", req) // リクエストヘッダーを処理する

	resp := &extProcPb.ProcessingResponse{
		Response: &extProcPb.ProcessingResponse_RequestHeaders{
			RequestHeaders: &extProcPb.HeadersResponse{
				Response: &extProcPb.CommonResponse{
					HeaderMutation: &extProcPb.HeaderMutation{
						SetHeaders: []*configPb.HeaderValueOption{
							{
								Header: &configPb.HeaderValue{
									Key:      "x-ext-proc-header",
									RawValue: []byte("hello-to-asm"),
								},
							},
						},
					},
				},
			},
		},
	}
    
	return resp
}

// レスポンスヘッダーに x-ext-proc-header=hello-from-asm を追加します
func handleResponseHeaders(req *extProcPb.ProcessingRequest_ResponseHeaders) *extProcPb.ProcessingResponse {
	klog.Infof("handle response headers: %+v\n", req) // レスポンスヘッダーを処理する

	resp := &extProcPb.ProcessingResponse{
		Response: &extProcPb.ProcessingResponse_ResponseHeaders{
			ResponseHeaders: &extProcPb.HeadersResponse{
				Response: &extProcPb.CommonResponse{
					HeaderMutation: &extProcPb.HeaderMutation{
						SetHeaders: []*configPb.HeaderValueOption{
							{
								Header: &configPb.HeaderValue{
									Key:      "x-ext-proc-header",
									RawValue: []byte("hello-from-asm"),
								},
							},
						},
					},
				},
			},
		},
	}

	return resp
}
説明

デプロイ前に、Dockerfile を記述して、外部処理サービスのコードをイメージにパッケージ化し、イメージをイメージリポジトリにアップロードする必要があります。

手順 2:外部処理サービスをデプロイする

この例では、ASM によって提供される外部処理サービスのサンプルイメージが使用されます。このサービスは、受信したリクエストにリクエストヘッダー x-ext-proc-header: hello-to-asm を追加し、受信したレスポンスにレスポンスヘッダー x-ext-proc-header: hello-from-asm を追加します。

  1. ext.yaml という名前のファイルを作成し、次の内容を記述します。

    apiVersion: v1
    kind: Service
    metadata:
      name: ext-proc
      labels:
        app: ext-proc
        service: ext-proc
    spec:
      ports:
      # 外部処理サービスのリスナーポート。
      - name: grpc
        port: 9002
        targetPort: 9002
      selector:
        app: ext-proc
    ---
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: ext-proc
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: ext-proc
          version: v1
      template:
        metadata:
          labels:
            app: ext-proc
            version: v1
        spec:
          containers:
          - image: registry-cn-hangzhou.ack.aliyuncs.com/ack-demo/ext-proc:v0.2
            imagePullPolicy: IfNotPresent
            name: ext-proc
            ports:
            - containerPort: 9002
  2. 次のコマンドを実行して、Pod のログを表示し、外部処理サービスが動作しているかどうかを確認します。

    kubectl logs ext-proc-64c8xxxxx-xxxxx

    期待される出力:

    I1126 06:41:25.467033       1 main.go:52] Starting gRPC server on port :9002

    上記のようにログが表示されれば、外部処理サービスは想定どおりに実行されています。

手順 3:Envoy フィルターを構成する

  1. ASM コンソール にログインします。左側のナビゲーションペインで、[サービスメッシュ] > [メッシュ管理] を選択します。

  2. [メッシュ管理] ページで、ASM インスタンスの名前をクリックします。左側のナビゲーションペインで、[プラグイン拡張センター] > [envoyfilter テンプレート] を選択します。

  3. 次の内容で、httpbin-ext-proc という名前の Envoy フィルターを作成します。

    apiVersion: networking.istio.io/v1alpha3
    kind: EnvoyFilter
    spec:
      configPatches:
        - applyTo: HTTP_FILTER
          match:
            context: SIDECAR_INBOUND
            listener:
              portNumber: 80
              filterChain:
                filter:
                  name: envoy.filters.network.http_connection_manager
            proxy:
              proxyVersion: ^MIN_VERSION-MAX_VERSION.*
          patch:
            operation: INSERT_BEFORE
            value:
              name: envoy.filters.http.ext_proc
              typed_config:
                '@type': >-
                  type.googleapis.com/envoy.extensions.filters.http.ext_proc.v3.ExternalProcessor
                grpc_service:
                  envoy_grpc:
                    cluster_name: outbound|9002||ext-proc.default.svc.cluster.local
                    authority: ext-proc.default.svc.cluster.local
                processing_mode:
                  request_header_mode: SEND
                  response_header_mode: SEND

手順 4:HTTPBin アプリケーションにアクセスして検証する

次のコマンドを実行して HTTPBin アプリケーションにアクセスし、レスポンスヘッダーを確認します。

kubectl exec -it deploy/sleep -- curl httpbin:8000/headers -i

期待される出力:

HTTP/1.1 200 OK
server: envoy
date: Wed, 11 Dec 2024 06:47:59 GMT
content-type: application/json
content-length: 564
access-control-allow-origin: *
access-control-allow-credentials: true
x-envoy-upstream-service-time: 3
x-ext-proc-header: hello-from-asm

{
  "headers": {
    "Accept": "*/*",
    "Host": "httpbin:8000",
    "User-Agent": "curl/8.1.2",
    "X-B3-Parentspanid": "5c6dd2cc9312d6bb",
    "X-B3-Sampled": "1",
    "X-B3-Spanid": "1153a2737cee4434",
    "X-B3-Traceid": "baba86b696edc75a5c6dd2cc9312d6bb",
    "X-Envoy-Attempt-Count": "1",
    "X-Ext-Proc-Header": "hello-to-asm",
    "X-Forwarded-Client-Cert": "By=spiffe://cluster.local/ns/default/sa/httpbin;Hash=69d8f267c3c00b4396a83e12d14520acc9dadb1492d660e10f77e94dcad7cb06;Subject=\"\";URI=spiffe://cluster.local/ns/default/sa/sleep"
  }
}

出力は、リクエストが転送される前にリクエストヘッダー x-ext-proc-header: hello-to-asm が追加され、レスポンスが返される前にレスポンスヘッダー x-ext-proc-header: hello-from-asm が追加されたことを示しています。