Allow overriding port for backend healthchecks

This commit is contained in:
Brian Akins 2017-05-10 14:28:57 -04:00 committed by Ludovic Fernandez
parent c7281df230
commit 13e8a875cf
3 changed files with 114 additions and 13 deletions

View file

@ -296,8 +296,9 @@ 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 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 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 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 often the health check should be executed (the default being 30 seconds).
backend must respond to the health check within 5 seconds. Each backend must respond to the health check within 5 seconds.
By default, the port of the backend server is used, however, this may be overridden.
A recovering backend returning 200 OK responses again is being returned to the A recovering backend returning 200 OK responses again is being returned to the
LB rotation pool. LB rotation pool.
@ -311,6 +312,16 @@ For example:
interval = "10s" interval = "10s"
``` ```
To use a different port for the healthcheck:
```toml
[backends]
[backends.backend1]
[backends.backend1.healthcheck]
path = "/health"
interval = "10s"
port = 8080
```
## Servers ## Servers
Servers are simply defined using a `URL`. You can also apply a custom `weight` to each server (this will be used by load-balancing). Servers are simply defined using a `URL`. You can also apply a custom `weight` to each server (this will be used by load-balancing).
@ -346,17 +357,17 @@ Here is an example of backends and servers definition:
# Configuration # Configuration
Træfik's configuration has two parts: Træfik's configuration has two parts:
- The [static Træfik configuration](/basics#static-trfk-configuration) which is loaded only at the beginning. - The [static Træfik configuration](/basics#static-trfk-configuration) which is loaded only at the beginning.
- The [dynamic Træfik configuration](/basics#dynamic-trfk-configuration) which can be hot-reloaded (no need to restart the process). - The [dynamic Træfik configuration](/basics#dynamic-trfk-configuration) which can be hot-reloaded (no need to restart the process).
## Static Træfik configuration ## Static Træfik configuration
The static configuration is the global configuration which is setting up connections to configuration backends and entrypoints. The static configuration is the global configuration which is setting up connections to configuration backends and entrypoints.
Træfik can be configured using many configuration sources with the following precedence order. Træfik can be configured using many configuration sources with the following precedence order.
Each item takes precedence over the item below it: Each item takes precedence over the item below it:
- [Key-value Store](/basics/#key-value-stores) - [Key-value Store](/basics/#key-value-stores)
@ -398,18 +409,18 @@ Træfik supports several Key-value stores:
- [Consul](https://consul.io) - [Consul](https://consul.io)
- [etcd](https://coreos.com/etcd/) - [etcd](https://coreos.com/etcd/)
- [ZooKeeper](https://zookeeper.apache.org/) - [ZooKeeper](https://zookeeper.apache.org/)
- [boltdb](https://github.com/boltdb/bolt) - [boltdb](https://github.com/boltdb/bolt)
Please refer to the [User Guide Key-value store configuration](/user-guide/kv-config/) section to get documentation on it. Please refer to the [User Guide Key-value store configuration](/user-guide/kv-config/) section to get documentation on it.
## Dynamic Træfik configuration ## Dynamic Træfik configuration
The dynamic configuration concerns : The dynamic configuration concerns :
- [Frontends](/basics/#frontends) - [Frontends](/basics/#frontends)
- [Backends](/basics/#backends) - [Backends](/basics/#backends)
- [Servers](/basics/#servers) - [Servers](/basics/#servers)
Træfik can hot-reload those rules which could be provided by [multiple configuration backends](/toml/#configuration-backends). Træfik can hot-reload those rules which could be provided by [multiple configuration backends](/toml/#configuration-backends).
@ -427,7 +438,7 @@ List of Træfik available commands with description :             
- `version` : Print version  - `version` : Print version 
- `storeconfig` : Store the static traefik configuration into a Key-value stores. Please refer to the [Store Træfik configuration](/user-guide/kv-config/#store-trfk-configuration) section to get documentation on it. - `storeconfig` : Store the static traefik configuration into a Key-value stores. Please refer to the [Store Træfik configuration](/user-guide/kv-config/#store-trfk-configuration) section to get documentation on it.
Each command may have related flags. Each command may have related flags.
All those related flags will be displayed with : All those related flags will be displayed with :
```bash ```bash
@ -439,4 +450,3 @@ Note that each command is described at the beginning of the help section:
```bash ```bash
$ traefik --help $ traefik --help
``` ```

View file

@ -3,8 +3,10 @@ package healthcheck
import ( import (
"context" "context"
"fmt" "fmt"
"net"
"net/http" "net/http"
"net/url" "net/url"
"strconv"
"sync" "sync"
"time" "time"
@ -27,6 +29,7 @@ func GetHealthCheck() *HealthCheck {
// Options are the public health check options. // Options are the public health check options.
type Options struct { type Options struct {
Path string Path string
Port int
Interval time.Duration Interval time.Duration
LB LoadBalancer LB LoadBalancer
} }
@ -127,11 +130,32 @@ func checkBackend(currentBackend *BackendHealthCheck) {
} }
} }
func (backend *BackendHealthCheck) newRequest(serverURL *url.URL) (*http.Request, error) {
if backend.Options.Port == 0 {
return http.NewRequest("GET", serverURL.String()+backend.Path, nil)
}
// copy the url and add the port to the host
u := &url.URL{}
*u = *serverURL
u.Host = net.JoinHostPort(u.Hostname(), strconv.Itoa(backend.Options.Port))
u.Path = u.Path + backend.Path
return http.NewRequest("GET", u.String(), nil)
}
func checkHealth(serverURL *url.URL, backend *BackendHealthCheck) bool { func checkHealth(serverURL *url.URL, backend *BackendHealthCheck) bool {
client := http.Client{ client := http.Client{
Timeout: backend.requestTimeout, Timeout: backend.requestTimeout,
} }
resp, err := client.Get(serverURL.String() + backend.Path) req, err := backend.newRequest(serverURL)
if err != nil {
log.Errorf("Failed to create HTTP request [%s] for healthcheck: %s", serverURL, err)
return false
}
resp, err := client.Do(req)
if err == nil { if err == nil {
defer resp.Body.Close() defer resp.Body.Close()
} }

View file

@ -193,6 +193,73 @@ func TestSetBackendsConfiguration(t *testing.T) {
} }
} }
func TestNewRequest(t *testing.T) {
tests := []struct {
desc string
host string
port int
path string
expected string
}{
{
desc: "no port override",
host: "backend1:80",
port: 0,
path: "/test",
expected: "http://backend1:80/test",
},
{
desc: "port override",
host: "backend2:80",
port: 8080,
path: "/test",
expected: "http://backend2:8080/test",
},
{
desc: "no port override with no port in host",
host: "backend1",
port: 0,
path: "/health",
expected: "http://backend1/health",
},
{
desc: "port override with no port in host",
host: "backend2",
port: 8080,
path: "/health",
expected: "http://backend2:8080/health",
},
}
for _, test := range tests {
test := test
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
backend := NewBackendHealthCheck(
Options{
Path: test.path,
Port: test.port,
})
u := &url.URL{
Scheme: "http",
Host: test.host,
}
req, err := backend.newRequest(u)
if err != nil {
t.Fatalf("failed to create new backend request: %s", err)
}
actual := req.URL.String()
if actual != test.expected {
t.Fatalf("got %s for healthcheck URL, wanted %s", actual, test.expected)
}
})
}
}
func MustParseURL(rawurl string) *url.URL { func MustParseURL(rawurl string) *url.URL {
u, err := url.Parse(rawurl) u, err := url.Parse(rawurl)
if err != nil { if err != nil {