Skip to content

Commit

Permalink
Allow overriding port for backend healthchecks
Browse files Browse the repository at this point in the history
  • Loading branch information
bakins authored and ldez committed May 19, 2017
1 parent c7281df commit 13e8a87
Show file tree
Hide file tree
Showing 3 changed files with 114 additions and 13 deletions.
34 changes: 22 additions & 12 deletions docs/basics.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
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.
often the health check should be executed (the default being 30 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
LB rotation pool.
Expand All @@ -311,6 +312,16 @@ For example:
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 are simply defined using a `URL`. You can also apply a custom `weight` to each server (this will be used by load-balancing).
Expand Down Expand Up @@ -346,17 +357,17 @@ Here is an example of backends and servers definition:

# 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).


## 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:

- [Key-value Store](/basics/#key-value-stores)
Expand Down Expand Up @@ -398,18 +409,18 @@ Træfik supports several Key-value stores:

- [Consul](https://consul.io)
- [etcd](https://coreos.com/etcd/)
- [ZooKeeper](https://zookeeper.apache.org/)
- [ZooKeeper](https://zookeeper.apache.org/)
- [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.

## Dynamic Træfik configuration

The dynamic configuration concerns :
The dynamic configuration concerns :

- [Frontends](/basics/#frontends)
- [Backends](/basics/#backends)
- [Servers](/basics/#servers)
- [Backends](/basics/#backends)
- [Servers](/basics/#servers)

Træfik can hot-reload those rules which could be provided by [multiple configuration backends](/toml/#configuration-backends).

Expand All @@ -427,7 +438,7 @@ List of Træfik available commands with description :             
- `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.

Each command may have related flags.
Each command may have related flags.
All those related flags will be displayed with :

```bash
Expand All @@ -439,4 +450,3 @@ Note that each command is described at the beginning of the help section:
```bash
$ traefik --help
```

26 changes: 25 additions & 1 deletion healthcheck/healthcheck.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@ package healthcheck
import (
"context"
"fmt"
"net"
"net/http"
"net/url"
"strconv"
"sync"
"time"

Expand All @@ -27,6 +29,7 @@ func GetHealthCheck() *HealthCheck {
// Options are the public health check options.
type Options struct {
Path string
Port int
Interval time.Duration
LB LoadBalancer
}
Expand Down Expand Up @@ -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 {
client := http.Client{
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 {
defer resp.Body.Close()
}
Expand Down
67 changes: 67 additions & 0 deletions healthcheck/healthcheck_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
u, err := url.Parse(rawurl)
if err != nil {
Expand Down

0 comments on commit 13e8a87

Please sign in to comment.