diff --git a/docs/basics.md b/docs/basics.md index c183a2c84..7496b0d06 100644 --- a/docs/basics.md +++ b/docs/basics.md @@ -240,16 +240,22 @@ For example: sticky = true ``` -Healthcheck URL can be configured with a relative URL for `healthcheck.URL`. -Interval between healthcheck can be configured by using `healthcheck.interval` -(default: 30s) +A health check can be configured in order to remove a backend from LB rotation +as long as it keeps returning HTTP status codes other than 200 OK to HTTP GET +requests periodically carried out by Traefik. The check is defined by a path +appended to the backend URL and an interval (given in a format understood by [time.ParseDuration](https://golang.org/pkg/time/#ParseDuration)) specifying how +often the health check should be executed (the default being 30 seconds). Each +backend must respond to the health check within 5 seconds. + +A recovering backend returning 200 OK responses again is being returned to the +LB rotation pool. For example: ```toml [backends] [backends.backend1] [backends.backend1.healthcheck] - URL = "/health" + path = "/health" interval = "10s" ``` diff --git a/healthcheck/healthcheck.go b/healthcheck/healthcheck.go index fb2f74eb4..73f6753c1 100644 --- a/healthcheck/healthcheck.go +++ b/healthcheck/healthcheck.go @@ -25,11 +25,11 @@ func GetHealthCheck() *HealthCheck { // BackendHealthCheck HealthCheck configuration for a backend type BackendHealthCheck struct { - URL string - Interval time.Duration - DisabledURLs []*url.URL + Path string + Interval time.Duration + DisabledURLs []*url.URL requestTimeout time.Duration - lb loadBalancer + lb loadBalancer } var launch = false @@ -85,12 +85,12 @@ func (hc *HealthCheck) execute(ctx context.Context, backendID string, backend *B checkBackend(backend) ticker := time.NewTicker(backend.Interval) defer ticker.Stop() - for { - select { - case <-ctx.Done(): - log.Debugf("Stopping all current Healthcheck goroutines") - return - case <-ticker.C: + for { + select { + case <-ctx.Done(): + log.Debugf("Stopping all current Healthcheck goroutines") + return + case <-ticker.C: log.Debugf("Refreshing healthcheck for currentBackend %s ", backendID) checkBackend(backend) } @@ -98,33 +98,33 @@ func (hc *HealthCheck) execute(ctx context.Context, backendID string, backend *B } func checkBackend(currentBackend *BackendHealthCheck) { - enabledURLs := currentBackend.lb.Servers() - var newDisabledURLs []*url.URL - for _, url := range currentBackend.DisabledURLs { + enabledURLs := currentBackend.lb.Servers() + var newDisabledURLs []*url.URL + for _, url := range currentBackend.DisabledURLs { if checkHealth(url, currentBackend) { - log.Debugf("HealthCheck is up [%s]: Upsert in server list", url.String()) - currentBackend.lb.UpsertServer(url, roundrobin.Weight(1)) - } else { + log.Debugf("HealthCheck is up [%s]: Upsert in server list", url.String()) + currentBackend.lb.UpsertServer(url, roundrobin.Weight(1)) + } else { log.Warnf("HealthCheck is still failing [%s]", url.String()) - newDisabledURLs = append(newDisabledURLs, url) - } - } - currentBackend.DisabledURLs = newDisabledURLs + newDisabledURLs = append(newDisabledURLs, url) + } + } + currentBackend.DisabledURLs = newDisabledURLs - for _, url := range enabledURLs { + for _, url := range enabledURLs { if !checkHealth(url, currentBackend) { log.Warnf("HealthCheck has failed [%s]: Remove from server list", url.String()) - currentBackend.lb.RemoveServer(url) - currentBackend.DisabledURLs = append(currentBackend.DisabledURLs, url) - } - } + currentBackend.lb.RemoveServer(url) + currentBackend.DisabledURLs = append(currentBackend.DisabledURLs, url) + } + } } func checkHealth(serverURL *url.URL, backend *BackendHealthCheck) bool { client := http.Client{ Timeout: backend.requestTimeout, } - resp, err := client.Get(serverURL.String() + backend.URL) + resp, err := client.Get(serverURL.String() + backend.Path) if err == nil { defer resp.Body.Close() } diff --git a/server.go b/server.go index a48ad9313..058066d01 100644 --- a/server.go +++ b/server.go @@ -667,7 +667,7 @@ func (server *Server) loadConfig(configurations configs, globalConfiguration Glo interval = time.Second * 30 } } - backendsHealthcheck[frontend.Backend] = healthcheck.NewBackendHealthCheck(configuration.Backends[frontend.Backend].HealthCheck.URL, interval, rebalancer) + backendsHealthcheck[frontend.Backend] = healthcheck.NewBackendHealthCheck(configuration.Backends[frontend.Backend].HealthCheck.Path, interval, rebalancer) } } case types.Wrr: @@ -701,7 +701,7 @@ func (server *Server) loadConfig(configurations configs, globalConfiguration Glo interval = time.Second * 30 } } - backendsHealthcheck[frontend.Backend] = healthcheck.NewBackendHealthCheck(configuration.Backends[frontend.Backend].HealthCheck.URL, interval, rr) + backendsHealthcheck[frontend.Backend] = healthcheck.NewBackendHealthCheck(configuration.Backends[frontend.Backend].HealthCheck.Path, interval, rr) } } maxConns := configuration.Backends[frontend.Backend].MaxConn diff --git a/types/types.go b/types/types.go index 815409ef7..d62d77d92 100644 --- a/types/types.go +++ b/types/types.go @@ -39,7 +39,7 @@ type CircuitBreaker struct { // HealthCheck holds HealthCheck configuration type HealthCheck struct { - URL string `json:"url,omitempty"` + Path string `json:"path,omitempty"` Interval string `json:"interval,omitempty"` }