1
0
Fork 0

Add passive health checks

This commit is contained in:
Nelson Isioma 2025-08-21 10:40:06 +01:00 committed by GitHub
parent c20802b07e
commit fc0fac8543
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
20 changed files with 696 additions and 6 deletions

View file

@ -0,0 +1,31 @@
[global]
checkNewVersion = false
sendAnonymousUsage = false
[log]
level = "DEBUG"
noColor = true
[entryPoints]
[entryPoints.web]
address = ":8000"
[api]
insecure = true
[providers.file]
filename = "{{ .SelfFilename }}"
## dynamic configuration ##
[http.routers]
[http.routers.router1]
service = "service1"
rule = "Host(`test.localhost`)"
[http.services]
[http.services.service1.loadBalancer]
[http.services.service1.loadBalancer.passiveHealthCheck]
failureWindow = "2s"
[[http.services.service1.loadBalancer.servers]]
url = "http://{{.Server1}}:80"

View file

@ -227,6 +227,25 @@ spec:
PassHostHeader defines whether the client Host header is forwarded to the upstream Kubernetes Service.
By default, passHostHeader is true.
type: boolean
passiveHealthCheck:
description: PassiveHealthCheck defines passive health
checks for ExternalName services.
properties:
failureWindow:
anyOf:
- type: integer
- type: string
description: FailureWindow defines the time window
during which the failed attempts must occur for
the server to be marked as unhealthy. It also defines
for how long the server will be considered unhealthy.
x-kubernetes-int-or-string: true
maxFailedAttempts:
description: MaxFailedAttempts is the number of consecutive
failed attempts allowed within the failure window
before marking the server as unhealthy.
type: integer
type: object
port:
anyOf:
- type: integer
@ -1169,6 +1188,25 @@ spec:
PassHostHeader defines whether the client Host header is forwarded to the upstream Kubernetes Service.
By default, passHostHeader is true.
type: boolean
passiveHealthCheck:
description: PassiveHealthCheck defines passive health checks
for ExternalName services.
properties:
failureWindow:
anyOf:
- type: integer
- type: string
description: FailureWindow defines the time window during
which the failed attempts must occur for the server
to be marked as unhealthy. It also defines for how long
the server will be considered unhealthy.
x-kubernetes-int-or-string: true
maxFailedAttempts:
description: MaxFailedAttempts is the number of consecutive
failed attempts allowed within the failure window before
marking the server as unhealthy.
type: integer
type: object
port:
anyOf:
- type: integer
@ -2944,6 +2982,25 @@ spec:
PassHostHeader defines whether the client Host header is forwarded to the upstream Kubernetes Service.
By default, passHostHeader is true.
type: boolean
passiveHealthCheck:
description: PassiveHealthCheck defines passive health checks
for ExternalName services.
properties:
failureWindow:
anyOf:
- type: integer
- type: string
description: FailureWindow defines the time window during
which the failed attempts must occur for the server
to be marked as unhealthy. It also defines for how
long the server will be considered unhealthy.
x-kubernetes-int-or-string: true
maxFailedAttempts:
description: MaxFailedAttempts is the number of consecutive
failed attempts allowed within the failure window
before marking the server as unhealthy.
type: integer
type: object
percent:
description: |-
Percent defines the part of the traffic to mirror.
@ -3078,6 +3135,25 @@ spec:
PassHostHeader defines whether the client Host header is forwarded to the upstream Kubernetes Service.
By default, passHostHeader is true.
type: boolean
passiveHealthCheck:
description: PassiveHealthCheck defines passive health checks
for ExternalName services.
properties:
failureWindow:
anyOf:
- type: integer
- type: string
description: FailureWindow defines the time window during
which the failed attempts must occur for the server to be
marked as unhealthy. It also defines for how long the server
will be considered unhealthy.
x-kubernetes-int-or-string: true
maxFailedAttempts:
description: MaxFailedAttempts is the number of consecutive
failed attempts allowed within the failure window before
marking the server as unhealthy.
type: integer
type: object
port:
anyOf:
- type: integer
@ -3290,6 +3366,25 @@ spec:
PassHostHeader defines whether the client Host header is forwarded to the upstream Kubernetes Service.
By default, passHostHeader is true.
type: boolean
passiveHealthCheck:
description: PassiveHealthCheck defines passive health checks
for ExternalName services.
properties:
failureWindow:
anyOf:
- type: integer
- type: string
description: FailureWindow defines the time window during
which the failed attempts must occur for the server
to be marked as unhealthy. It also defines for how
long the server will be considered unhealthy.
x-kubernetes-int-or-string: true
maxFailedAttempts:
description: MaxFailedAttempts is the number of consecutive
failed attempts allowed within the failure window
before marking the server as unhealthy.
type: integer
type: object
port:
anyOf:
- type: integer

View file

@ -108,6 +108,53 @@ func (s *HealthCheckSuite) TestSimpleConfiguration() {
assert.Equal(s.T(), http.StatusNotFound, resp.StatusCode)
}
func (s *HealthCheckSuite) TestSimpleConfiguration_Passive() {
file := s.adaptFile("fixtures/healthcheck/simple_passive.toml", struct {
Server1 string
}{s.whoami1IP})
s.traefikCmd(withConfigFile(file))
// wait for traefik
err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", 60*time.Second, try.BodyContains("Host(`test.localhost`)"))
require.NoError(s.T(), err)
frontendHealthReq, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/health", nil)
require.NoError(s.T(), err)
frontendHealthReq.Host = "test.localhost"
err = try.Request(frontendHealthReq, 500*time.Millisecond, try.StatusCodeIs(http.StatusOK))
require.NoError(s.T(), err)
// Fix all whoami health to 500
client := &http.Client{}
whoamiHosts := []string{s.whoami1IP, s.whoami2IP}
for _, whoami := range whoamiHosts {
statusInternalServerErrorReq, err := http.NewRequest(http.MethodPost, "http://"+whoami+"/health", bytes.NewBufferString("500"))
require.NoError(s.T(), err)
_, err = client.Do(statusInternalServerErrorReq)
require.NoError(s.T(), err)
}
// First call, the passive health check is not yet triggered, so we expect a 500.
err = try.Request(frontendHealthReq, 3*time.Second, try.StatusCodeIs(http.StatusInternalServerError))
require.NoError(s.T(), err)
// Verify no backend service is available due to failing health checks
err = try.Request(frontendHealthReq, 3*time.Second, try.StatusCodeIs(http.StatusServiceUnavailable))
require.NoError(s.T(), err)
// Change one whoami health to 200
statusOKReq1, err := http.NewRequest(http.MethodPost, "http://"+s.whoami1IP+"/health", bytes.NewBufferString("200"))
require.NoError(s.T(), err)
_, err = client.Do(statusOKReq1)
require.NoError(s.T(), err)
// Verify frontend health : after
err = try.Request(frontendHealthReq, 3*time.Second, try.StatusCodeIs(http.StatusOK))
require.NoError(s.T(), err)
}
func (s *HealthCheckSuite) TestMultipleEntrypoints() {
file := s.adaptFile("fixtures/healthcheck/multiple-entrypoints.toml", struct {
Server1 string