Enabling GZIP Response Compression with EnvoyFilter
How to enable GZIP compression of responses using EnvoyFilter
We recently decided to centralise all gzip compression for our 500 or so microservices into an EnvoyFilter. Why? You ask. Because:
- Some people added compression response filters to their apps, others didn't.
- Those who did implement compression response filters configured them differently (for example; min-compression-bytes, compression levels etc).
- We have some internal
content-type
responses that are not compressed by default, but we want them to be, globally. - Different languages, and frameworks, have different performance profiles when it comes to compression.
- We have other EnvoyFilters that inspect the response body from a service, that would not work if the service returned a compressed response.
So we strived for consistency. We run Envoy/Istio sidecars next to all of our application containers, so we decided to move this responsibility to there.
Implementation
We opt'd to create an EnvoyFilter per-service, so that it was tied to their release pipeline for a more controlled rollout. However here i'm demonstrating applying it globally, without a workload selector and in the istio-system
namespace.
We're using type.googleapis.com/envoy.extensions.filters.http.compressor.v3.Compressor type in order to do this, with the type.googleapis.com/envoy.extensions.compression.gzip.compressor.v3.Gzip compressor library.
The applyTo
section effetively injects this into the SIDECAR_INBOUND
filterChain for every sidecar on our mesh. It feels a bit backwards; because the compression actually happens on the response
of an inbound request. The configuration of that response can be seen under the response_direction_config
block.
The other keys to draw attentiont to here are:
min_content_length
. You'll need to experiment to find the optimal value for your configuration, and network. Remember that gzipping is a trade, less network for more CPU and potentially more latency.remove_accept_encoding_header
. This will ensure theAccept-Encoding
header from the incoming request is removed. Otherwise your app's compression filter will handle it, rather than Envoy.
apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
metadata:
name: inject-gzip
namespace: istio-system
spec:
priority: -10
configPatches:
- applyTo: HTTP_FILTER
match:
context: SIDECAR_INBOUND
listener:
filterChain:
filter:
name: envoy.filters.network.http_connection_manager
subFilter:
name: envoy.filters.http.router
patch:
operation: INSERT_BEFORE
value:
name: envoy.filters.http.compressor
typed_config:
'@type': type.googleapis.com/envoy.extensions.filters.http.compressor.v3.Compressor
compressor_library:
name: text_optimized
typed_config:
'@type': type.googleapis.com/envoy.extensions.compression.gzip.compressor.v3.Gzip
compression_level: BEST_SPEED
compression_strategy: DEFAULT_STRATEGY
memory_level: 8
window_bits: 15
chunk_size: 4096
response_direction_config:
remove_accept_encoding_header: true
common_config:
enabled:
default_value: true
runtime_key: response_direction_config_enabled
content_type:
- application/hal+json
- application/atom+xml
- application/javascript
- application/x-javascript
- application/json
- application/rss+xml
- application/vnd.ms-fontobject
- application/x-font-ttf
- application/x-web-app-manifest+json
- application/xhtml+xml
- application/xml
- font/opentype
- image/svg+xml
- image/x-icon
- text/css
- text/html
- text/plain
- text/xml
- text/x-component
min_content_length: 860
request_direction_config:
common_config:
enabled:
default_value: false
runtime_key: request_direction_config_enabled
Conclusion
Not much to say, enjoy consistent compression implementation across your mesh! Any client that sends Accept-Encoding: gzip
will get a gzip response.