Tous les produits
Search
Centre de documentation

Alibaba Cloud Service Mesh:Customize request and response handling with the Envoy ext_proc filter

Dernière mise à jour :Aug 11, 2026

Le filtre External Processing (ext_proc) d'Envoy délègue le traitement des requêtes et des réponses HTTP à un service gRPC externe. Plutôt que de développer un plug-in Wasm ou d'intégrer la logique directement dans Envoy, exécutez un serveur gRPC autonome qui inspecte et modifie les en-têtes, les corps et les trailers au fur et à mesure que le trafic traverse le maillage.

Le filtre ext_proc est recommandé lorsque vous devez :

  • Ajouter, supprimer ou réécrire des en-têtes HTTP selon une logique métier spécifique

  • Mettre en œuvre des vérifications personnalisées d'authentification ou d'autorisation en dehors du sidecar

  • Enrichir les requêtes avec des données provenant de systèmes externes avant qu'elles n'atteignent le service amont

  • Utiliser un langage ou un framework non pris en charge par Wasm

Fonctionnement

Le filtre ext_proc intercepte le trafic au niveau du sidecar et le transfère vers votre service gRPC externe pour traitement. Ce filtre prend en charge six étapes de traitement indépendantes, chacune contrôlée par un mode de traitement dédié :

Étape Direction Description
En-têtes de requête Entrant Inspecter ou modifier les en-têtes avant que la requête n'atteigne le service amont
Corps de requête Entrant Inspecter ou modifier le corps de la requête
Trailers de requête Entrant Inspecter ou modifier les trailers de requête HTTP/2
En-têtes de réponse Sortant Inspecter ou modifier les en-têtes avant que la réponse n'atteigne le service aval
Corps de réponse Sortant Inspecter ou modifier le corps de la réponse
Trailers de réponse Sortant Inspecter ou modifier les trailers de réponse HTTP/2

Par défaut, aucune de ces étapes n'est envoyée au processeur externe. Activez explicitement chaque étape en définissant son mode de traitement sur SEND dans la configuration EnvoyFilter.

Le schéma suivant illustre le flux complet requête-réponse :

Envoy external processing flow

  1. Un service aval envoie une requête. Envoy l'intercepte et la transmet au service de traitement externe.

  2. Le service de traitement externe inspecte ou modifie la requête, puis renvoie le résultat à Envoy.

  3. Envoy applique les modifications et transfère la requête au service amont.

  4. Le service amont renvoie une réponse. Envoy l'intercepte et la transmet au service de traitement externe.

  5. Le service de traitement externe inspecte ou modifie la réponse, puis renvoie le résultat à Envoy.

  6. Envoy applique les modifications et transfère la réponse au service aval.

Prérequis

Avant de commencer, assurez-vous d'avoir :

Étape 1 : Développer le service de traitement externe

Créez un serveur gRPC implémentant l'interface de service ExternalProcessor d'Envoy. Le serveur reçoit un flux de messages ProcessingRequest depuis Envoy et doit renvoyer un message ProcessingResponse correspondant pour chacun d'eux.

L'extrait Go ci-dessous présente la logique principale. Pour obtenir le projet exécutable complet, consultez le dépôt ext-proc-demo sur GitHub. Pour la spécification complète de l'API, reportez-vous à la référence proto ext_proc d'Envoy.

Afficher la logique de traitement principale (Go)

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

// Server implements the Envoy external processing server interface
// See 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("Processing")
	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 has closed the stream. Don't return anything and close this stream entirely
			return nil
		}
		if err != nil {
			return status.Errorf(codes.Unknown, "cannot receive stream request: %v", err)
		}

		resp := &extProcPb.ProcessingResponse{}
		switch v := req.Request.(type) {
        // Requests that require processing of request headers
		case *extProcPb.ProcessingRequest_RequestHeaders:
			klog.Infof("Got RequestHeaders")
			h := req.Request.(*extProcPb.ProcessingRequest_RequestHeaders)
			resp = handleRequestHeaders(h)
        // Requests that require processing of response headers
        case *extProcPb.ProcessingRequest_ResponseHeaders:
			klog.Infof("Got ResponseHeaders")
			h := req.Request.(*extProcPb.ProcessingRequest_ResponseHeaders)
			resp = handleResponseHeaders(h)
        // Requests that require processing of request headers, not currently implemented
		case *extProcPb.ProcessingRequest_RequestBody:
			klog.Infof("Got RequestBody (not currently handled)")
        // Requests that require processing of request trailers, not currently implemented
		case *extProcPb.ProcessingRequest_RequestTrailers:
			klog.Infof("Got RequestTrailers (not currently handled)")
        // Requests that require processing of response body, not currently implemented
		case *extProcPb.ProcessingRequest_ResponseBody:
			klog.Infof("Got ResponseBody (not currently handled)")
        // Requests that require processing of response trailers, not currently implemented
		case *extProcPb.ProcessingRequest_ResponseTrailers:
			klog.Infof("Got ResponseTrailers (not currently handled)")

		default:
			klog.Infof("Unknown Request type %v", v)
		}
        // Return the processing required for the request
		klog.Infof("Sending ProcessingResponse: %+v", resp)
		if err := srv.Send(resp); err != nil {
			klog.Infof("send error %v", err)
			return err
		}
	}
}

// Add x-ext-proc-header=hello-to-asm to request headers
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
}

// Add x-ext-proc-header=hello-from-asm to response headers
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
}

Cet exemple gère deux étapes de traitement :

  • En-têtes de requête : ajoute x-ext-proc-header: hello-to-asm à chaque requête entrante.

  • En-têtes de réponse : ajoute x-ext-proc-header: hello-from-asm à chaque réponse sortante.

Remarque

Empaquetez votre serveur gRPC sous forme d'image de conteneur à l'aide d'un Dockerfile et publiez-le dans un registre de conteneurs avant le déploiement.

Étape 2 : Déployer le service de traitement externe

Cette étape utilise l'image ext_proc fournie à titre d'exemple par ASM. Le service d'exemple ajoute l'en-tête x-ext-proc-header: hello-to-asm aux requêtes et x-ext-proc-header: hello-from-asm aux réponses.

  1. Créez un fichier nommé ext.yaml avec le contenu suivant :

        apiVersion: v1
        kind: Service
        metadata:
          name: ext-proc
          labels:
            app: ext-proc
            service: ext-proc
        spec:
          ports:
          # gRPC listener port for the external processing service
          - 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. Appliquez le manifeste et vérifiez que le service est en cours d'exécution :

        kubectl apply -f ext.yaml
  3. Consultez les journaux du pod pour confirmer le démarrage du serveur gRPC. Résultat attendu : ce journal confirme que le service de traitement externe est en cours d'exécution et écoute sur le port 9002.

        kubectl logs deploy/ext-proc
        I1126 06:41:25.467033       1 main.go:52] Starting gRPC server on port :9002

Étape 3 : Configurer le EnvoyFilter

Créez un EnvoyFilter pour insérer le filtre ext_proc dans la chaîne de filtres HTTP du sidecar. Cela permet d'acheminer les en-têtes de requête et de réponse du sidecar HTTPBin vers votre service de traitement externe.

  1. Connectez-vous à la console ASM. Dans le volet de navigation de gauche, sélectionnez Service Mesh > Mesh Management.

  2. Sur la page Mesh Management, cliquez sur le nom de l'instance ASM. Dans le volet de navigation de gauche, sélectionnez Plugin Extension Center > EnvoyFilter Template.

  3. Créez un EnvoyFilter nommé httpbin-ext-proc avec le contenu suivant :

    Champs clés :

    Champ Description
    context: SIDECAR_INBOUND Applique le filtre au trafic entrant sur le proxy sidecar
    portNumber: 80 Cible l'écouteur HTTP sur le port 80
    operation: INSERT_BEFORE Insère le filtre ext_proc avant le filtre routeur dans la chaîne
    cluster_name Endpoint du cluster Envoy pour le service ext-proc, au format `outbound `
    request_header_mode: SEND Envoie les en-têtes de requête au processeur externe. La valeur par défaut est SKIP, ce qui signifie que les en-têtes ne sont pas envoyés
    response_header_mode: SEND Envoie les en-têtes de réponse au processeur externe. La valeur par défaut est SKIP, ce qui signifie que les en-têtes ne sont pas envoyés
        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

Vérifier la configuration

Envoyez une requête via le maillage et confirmez que le processeur externe a ajouté les en-têtes attendus.

Depuis le pod sleep, exécutez :

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

Recherchez les deux en-têtes suivants dans la sortie :

  • En-tête de réponse x-ext-proc-header: hello-from-asm — ajouté par le processeur externe avant que la réponse n'atteigne le service aval

  • En-tête de requête X-Ext-Proc-Header: hello-to-asm (visible dans le corps JSON) — ajouté par le processeur externe avant que la requête n'atteigne le service amont

Résultat attendu :

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"
  }
}

Les deux en-têtes sont présents, ce qui confirme que le service de traitement externe intercepte et modifie correctement le trafic via le sidecar Envoy.