Skip to content

Commit 771201f

Browse files
committed
fix: derive the protocol of active health checks from the backend
Envoy sends HTTP health check requests with a codec that is fixed when the health checker is created, and health check connections are created outside of the connection pool, so they don't inherit the ALPN protocols that the pool derives from the backend protocol settings. Envoy Gateway configures neither, so health checks are sent as HTTP/1.1 and fail against a backend that speaks HTTP/2 on its health check endpoint, marking all of its endpoints unhealthy. Derive both from the backend protocol: `codec_client_type` is set to HTTP2 for backends that use HTTP/2 and to HTTP1 otherwise, and for backends that use TLS, `tls_options.alpn_protocols` is pinned to the protocol matching the codec, so the protocol negotiated during the handshake is the one Envoy uses to send the request. The ALPN isn't pinned when the backend TLS settings ask for a protocol that the codec can't speak, such as `istio`, since overriding it would break the handshake. gRPC health checks are always sent over HTTP/2, so TLS backends get `h2`. Add `healthCheck.active.http.version` to select the version explicitly, for backends whose health check endpoint uses a different protocol than their application endpoint. Signed-off-by: Huabing (Robin) Zhao <zhaohuabing@gmail.com> Signed-off-by: Huabing (Robin) Zhao <huabing@tetrate.io>
1 parent 2b40195 commit 771201f

29 files changed

Lines changed: 2589 additions & 4 deletions

api/v1alpha1/healthcheck_types.go

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,39 @@ type HTTPActiveHealthChecker struct {
197197
// +kubebuilder:validation:MinLength=1
198198
// +kubebuilder:validation:MaxLength=1024
199199
Path string `json:"path" yaml:"path"`
200+
// Version defines the HTTP protocol version used to send active health check
201+
// requests to the backend.
202+
//
203+
// Envoy sends health check requests over a dedicated connection, using a fixed
204+
// protocol version that is not negotiated per request. If that version does not
205+
// match the protocol the backend speaks, every health check fails and all
206+
// endpoints of the backend are marked unhealthy.
207+
//
208+
// Defaults to Auto, which resolves the version from the effective upstream protocol
209+
// of the backend: HTTP2 if the backend is configured to use HTTP/2, through a
210+
// `kubernetes.io/h2c`, `gateway.envoyproxy.io/h2c` or `grpc` appProtocol, or by being
211+
// the backend of a GRPCRoute, and HTTP1 otherwise.
212+
//
213+
// How the resolved version is applied depends on the backend:
214+
//
215+
// - For plaintext backends, the resolved version is the version used, since no
216+
// protocol is negotiated on the connection.
217+
// - For backends that use TLS, the resolved version is also the version used, and
218+
// the ALPN offered on health check connections is constrained to the matching
219+
// protocol so that the handshake can't settle on a different one. The ALPN is
220+
// left as configured if the backend TLS settings ask for protocols that can't
221+
// carry the resolved version, since constraining it would break the handshake.
222+
// This may change to the protocol negotiated during the handshake once
223+
// https://github.com/envoyproxy/envoy/issues/46848 lands in Envoy.
224+
//
225+
// Set this field explicitly when the health check endpoint and the application
226+
// endpoint of the backend use different protocols, for example when the backend
227+
// serves HTTP/1.1 traffic but only accepts HTTP/2 health check requests. An
228+
// explicitly configured version is always the version used.
229+
//
230+
// +kubebuilder:default=Auto
231+
// +optional
232+
Version *HTTPHealthCheckVersion `json:"version,omitempty" yaml:"version,omitempty"`
200233
// Method defines the HTTP method used for health checking.
201234
// Defaults to GET
202235
// +kubebuilder:validation:MaxLength=16
@@ -220,6 +253,21 @@ type HTTPActiveHealthChecker struct {
220253
RequestBody *ActiveHealthCheckPayload `json:"requestBody,omitempty" yaml:"requestBody,omitempty"`
221254
}
222255

256+
// HTTPHealthCheckVersion specifies the HTTP protocol version used to send active
257+
// HTTP health check requests to the backend.
258+
// +kubebuilder:validation:Enum=Auto;HTTP1;HTTP2
259+
type HTTPHealthCheckVersion string
260+
261+
const (
262+
// HTTPHealthCheckVersionAuto derives the health check protocol version from the
263+
// effective upstream protocol of the backend.
264+
HTTPHealthCheckVersionAuto HTTPHealthCheckVersion = "Auto"
265+
// HTTPHealthCheckVersionHTTP1 sends health check requests using HTTP/1.1.
266+
HTTPHealthCheckVersionHTTP1 HTTPHealthCheckVersion = "HTTP1"
267+
// HTTPHealthCheckVersionHTTP2 sends health check requests using HTTP/2.
268+
HTTPHealthCheckVersionHTTP2 HTTPHealthCheckVersion = "HTTP2"
269+
)
270+
223271
// TCPActiveHealthChecker defines the settings of tcp health check.
224272
type TCPActiveHealthChecker struct {
225273
// Send defines the request payload.

api/v1alpha1/zz_generated.deepcopy.go

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -801,6 +801,43 @@ spec:
801801
minimum: 100
802802
type: integer
803803
type: array
804+
version:
805+
default: Auto
806+
description: |-
807+
Version defines the HTTP protocol version used to send active health check
808+
requests to the backend.
809+
810+
Envoy sends health check requests over a dedicated connection, using a fixed
811+
protocol version that is not negotiated per request. If that version does not
812+
match the protocol the backend speaks, every health check fails and all
813+
endpoints of the backend are marked unhealthy.
814+
815+
Defaults to Auto, which resolves the version from the effective upstream protocol
816+
of the backend: HTTP2 if the backend is configured to use HTTP/2, through a
817+
`kubernetes.io/h2c`, `gateway.envoyproxy.io/h2c` or `grpc` appProtocol, or by being
818+
the backend of a GRPCRoute, and HTTP1 otherwise.
819+
820+
How the resolved version is applied depends on the backend:
821+
822+
- For plaintext backends, the resolved version is the version used, since no
823+
protocol is negotiated on the connection.
824+
- For backends that use TLS, the resolved version is also the version used, and
825+
the ALPN offered on health check connections is constrained to the matching
826+
protocol so that the handshake can't settle on a different one. The ALPN is
827+
left as configured if the backend TLS settings ask for protocols that can't
828+
carry the resolved version, since constraining it would break the handshake.
829+
This may change to the protocol negotiated during the handshake once
830+
https://github.com/envoyproxy/envoy/issues/46848 lands in Envoy.
831+
832+
Set this field explicitly when the health check endpoint and the application
833+
endpoint of the backend use different protocols, for example when the backend
834+
serves HTTP/1.1 traffic but only accepts HTTP/2 health check requests. An
835+
explicitly configured version is always the version used.
836+
enum:
837+
- Auto
838+
- HTTP1
839+
- HTTP2
840+
type: string
804841
required:
805842
- path
806843
type: object

charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_envoyextensionpolicies.yaml

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -704,6 +704,43 @@ spec:
704704
minimum: 100
705705
type: integer
706706
type: array
707+
version:
708+
default: Auto
709+
description: |-
710+
Version defines the HTTP protocol version used to send active health check
711+
requests to the backend.
712+
713+
Envoy sends health check requests over a dedicated connection, using a fixed
714+
protocol version that is not negotiated per request. If that version does not
715+
match the protocol the backend speaks, every health check fails and all
716+
endpoints of the backend are marked unhealthy.
717+
718+
Defaults to Auto, which resolves the version from the effective upstream protocol
719+
of the backend: HTTP2 if the backend is configured to use HTTP/2, through a
720+
`kubernetes.io/h2c`, `gateway.envoyproxy.io/h2c` or `grpc` appProtocol, or by being
721+
the backend of a GRPCRoute, and HTTP1 otherwise.
722+
723+
How the resolved version is applied depends on the backend:
724+
725+
- For plaintext backends, the resolved version is the version used, since no
726+
protocol is negotiated on the connection.
727+
- For backends that use TLS, the resolved version is also the version used, and
728+
the ALPN offered on health check connections is constrained to the matching
729+
protocol so that the handshake can't settle on a different one. The ALPN is
730+
left as configured if the backend TLS settings ask for protocols that can't
731+
carry the resolved version, since constraining it would break the handshake.
732+
This may change to the protocol negotiated during the handshake once
733+
https://github.com/envoyproxy/envoy/issues/46848 lands in Envoy.
734+
735+
Set this field explicitly when the health check endpoint and the application
736+
endpoint of the backend use different protocols, for example when the backend
737+
serves HTTP/1.1 traffic but only accepts HTTP/2 health check requests. An
738+
explicitly configured version is always the version used.
739+
enum:
740+
- Auto
741+
- HTTP1
742+
- HTTP2
743+
type: string
707744
required:
708745
- path
709746
type: object

0 commit comments

Comments
 (0)