Compare commits

...

16 Commits

Author SHA1 Message Date
Emile Vauge
31c7aba8c4 Merge pull request #57 from vdemeester/no-more-sleep-in-integration
Update integration setups to use libcompose events
2015-10-12 15:43:08 +02:00
Vincent Demeester
45ea23ecc1 Update integration setups to use libcompose events
Now that docker/libcompose#55 is merged, use it \o/
No more sleeps !

Signed-off-by: Vincent Demeester <vincent@sbr.pm>
2015-10-12 08:44:56 +02:00
Vincent Demeester
661ac977d3 Merge pull request #55 from ldez/feature/json-case
refactor(rest) : add json annotation on configuration
2015-10-10 14:52:50 +02:00
Fernandez Ludovic
c11cf801ca refactor(rest) : add json annotation on configuration
- update Web UI
- update documentation
2015-10-10 14:24:36 +02:00
Vincent Demeester
adca5dc55b Merge pull request #52 from EmileVauge/emilevauge-add-traefik-domain
Add traefik.domain label #51
2015-10-09 23:40:31 +02:00
Emile Vauge
641638ba3e Merge branch 'master' into emilevauge-add-traefik-domain 2015-10-09 20:33:25 +02:00
Vincent Demeester
fb7457eba0 Merge pull request #54 from EmileVauge/better-doc-on-kv-structure
Update of the doc about kv
2015-10-09 14:13:03 +02:00
emile
ddf1922eba Update doc about kv structure https://github.com/EmileVauge/traefik/issues/40 2015-10-09 10:34:56 +02:00
Emile Vauge
13f621a9ed Merge branch 'master' into emilevauge-add-traefik-domain 2015-10-08 22:56:34 +02:00
Vincent Demeester
74c5562c2b Merge pull request #43 from ldez/feature/health-graph
Health graph
2015-10-08 22:55:41 +02:00
Fernandez Ludovic
09320b99f9 docs(webui): update screenshots of the web ui 2015-10-08 22:32:39 +02:00
Fernandez Ludovic
a422f775e6 refactor(webui): homepage link correction 2015-10-08 22:32:39 +02:00
Fernandez Ludovic
facc936fe4 docs: enhance Health API documentation 2015-10-08 22:32:38 +02:00
Fernandez Ludovic
28458345b4 feat(webui): new Health screen
- add realtime chart
  - Total Status Code Count
  - Average response time
  - remove status code count
  - D3 & NVD3 & Angular NVD3
2015-10-08 22:32:38 +02:00
emile
f126e7585d Doc update with traefik.domain label 2015-10-08 21:25:13 +02:00
emile
27eae04e87 Added traefik.domain label. Corrects https://github.com/EmileVauge/traefik/issues/50 2015-10-08 21:21:51 +02:00
26 changed files with 650 additions and 263 deletions

10
Godeps/Godeps.json generated
View File

@@ -189,19 +189,19 @@
"Rev": "f39987afe8d611407887b3094c03d6ba6a766a67"
}, {
"ImportPath": "github.com/docker/libcompose/docker",
"Rev": "aad672800904307e96a2c21cad1420f3080e0f35"
"Rev": "79ef5d150f053a5b12f16b02d8844ed7cf33611a"
}, {
"ImportPath": "github.com/docker/libcompose/logger",
"Rev": "aad672800904307e96a2c21cad1420f3080e0f35"
"Rev": "79ef5d150f053a5b12f16b02d8844ed7cf33611a"
}, {
"ImportPath": "github.com/docker/libcompose/lookup",
"Rev": "aad672800904307e96a2c21cad1420f3080e0f35"
"Rev": "79ef5d150f053a5b12f16b02d8844ed7cf33611a"
}, {
"ImportPath": "github.com/docker/libcompose/project",
"Rev": "aad672800904307e96a2c21cad1420f3080e0f35"
"Rev": "79ef5d150f053a5b12f16b02d8844ed7cf33611a"
}, {
"ImportPath": "github.com/docker/libcompose/utils",
"Rev": "aad672800904307e96a2c21cad1420f3080e0f35"
"Rev": "79ef5d150f053a5b12f16b02d8844ed7cf33611a"
}, {
"ImportPath": "github.com/docker/libtrust",
"Rev": "9cbd2a1374f46905c68a4eb3694a130610adc62a"

View File

@@ -72,7 +72,8 @@ Refer to the [benchmarks section](docs/index.md#benchmarks) in the documentation
You can access to a simple HTML frontend of Træfik.
![HTML frontend](docs/img/web.frontend.png)
![Web UI Providers](docs/img/web.frontend.png)
![Web UI Health](docs/img/traefik-health.png)
## Contributing

View File

@@ -32,38 +32,45 @@ func NewGlobalConfiguration() *GlobalConfiguration {
return globalConfiguration
}
// Backend configuration
type Backend struct {
Servers map[string]Server
CircuitBreaker *CircuitBreaker
LoadBalancer *LoadBalancer
Servers map[string]Server `json:"servers,omitempty"`
CircuitBreaker *CircuitBreaker `json:"circuitBreaker,omitempty"`
LoadBalancer *LoadBalancer `json:"loadBalancer,omitempty"`
}
// LoadBalancer configuration
type LoadBalancer struct {
Method string
Method string `json:"method,omitempty"`
}
// CircuitBreaker configuration
type CircuitBreaker struct {
Expression string
Expression string `json:"expression,omitempty"`
}
// Server configuration
type Server struct {
URL string
Weight int
URL string `json:"url,omitempty"`
Weight int `json:"weight,omitempty"`
}
// Route configuration
type Route struct {
Rule string
Value string
Rule string `json:"rule,omitempty"`
Value string `json:"value,omitempty"`
}
// Frontend configuration
type Frontend struct {
Backend string
Routes map[string]Route
Backend string `json:"backend,omitempty"`
Routes map[string]Route `json:"routes,omitempty"`
}
// Configuration of a provider
type Configuration struct {
Backends map[string]*Backend
Frontends map[string]*Frontend
Backends map[string]*Backend `json:"backends,omitempty"`
Frontends map[string]*Frontend `json:"frontends,omitempty"`
}
// Load Balancer Method

View File

@@ -22,40 +22,6 @@ type DockerProvider struct {
Domain string
}
var DockerFuncMap = template.FuncMap{
"getBackend": func(container docker.Container) string {
for key, value := range container.Config.Labels {
if key == "traefik.backend" {
return value
}
}
return getHost(container)
},
"getPort": func(container docker.Container) string {
for key, value := range container.Config.Labels {
if key == "traefik.port" {
return value
}
}
for key := range container.NetworkSettings.Ports {
return key.Port()
}
return ""
},
"getWeight": func(container docker.Container) string {
for key, value := range container.Config.Labels {
if key == "traefik.weight" {
return value
}
}
return "0"
},
"replace": func(s1 string, s2 string, s3 string) string {
return strings.Replace(s3, s1, s2, -1)
},
"getHost": getHost,
}
func (provider *DockerProvider) Provide(configurationChan chan<- configMessage) error {
if dockerClient, err := docker.NewClient(provider.Endpoint); err != nil {
log.Errorf("Failed to create a client for docker, error: %s", err)
@@ -105,6 +71,47 @@ func (provider *DockerProvider) Provide(configurationChan chan<- configMessage)
}
func (provider *DockerProvider) loadDockerConfig(dockerClient *docker.Client) *Configuration {
var DockerFuncMap = template.FuncMap{
"getBackend": func(container docker.Container) string {
for key, value := range container.Config.Labels {
if key == "traefik.backend" {
return value
}
}
return getHost(container)
},
"getPort": func(container docker.Container) string {
for key, value := range container.Config.Labels {
if key == "traefik.port" {
return value
}
}
for key := range container.NetworkSettings.Ports {
return key.Port()
}
return ""
},
"getWeight": func(container docker.Container) string {
for key, value := range container.Config.Labels {
if key == "traefik.weight" {
return value
}
}
return "0"
},
"getDomain": func(container docker.Container) string {
for key, value := range container.Config.Labels {
if key == "traefik.domain" {
return value
}
}
return provider.Domain
},
"replace": func(s1 string, s2 string, s3 string) string {
return strings.Replace(s3, s1, s2, -1)
},
"getHost": getHost,
}
configuration := new(Configuration)
containerList, _ := dockerClient.ListContainers(docker.ListContainersOptions{})
containersInspected := []docker.Container{}

BIN
docs/img/traefik-health.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 61 KiB

After

Width:  |  Height:  |  Size: 53 KiB

View File

@@ -4,17 +4,17 @@ ___
# <a id="top"></a> Documentation
* [Basics](#basics)
* [Global configuration](#global)
* [File backend](#file)
* [API backend](#api)
* [Docker backend](#docker)
* [Mesos/Marathon backend](#marathon)
* [Consul backend](#consul)
* [Etcd backend](#etcd)
* [Zookeeper backend](#zk)
* [Boltdb backend](#boltdb)
* [Benchmarks](#benchmarks)
- [Basics](#basics)
- [Global configuration](#global)
- [File backend](#file)
- [API backend](#api)
- [Docker backend](#docker)
- [Mesos/Marathon backend](#marathon)
- [Consul backend](#consul)
- [Etcd backend](#etcd)
- [Zookeeper backend](#zk)
- [Boltdb backend](#boltdb)
- [Benchmarks](#benchmarks)
## <a id="basics"></a> Basics
@@ -29,12 +29,12 @@ Basically, Træfɪk is a http router, which sends traffic from frontends to http
Frontends can be defined using the following rules:
* `Headers`: Headers adds a matcher for request header values. It accepts a sequence of key/value pairs to be matched. For example: `application/json`
* `HeadersRegexp`: Regular expressions can be used with headers as well. It accepts a sequence of key/value pairs, where the value has regex support. For example: `application/(text|json)`
* `Host`: Host adds a matcher for the URL host. It accepts a template with zero or more URL variables enclosed by `{}`. Variables can define an optional regexp pattern to be matched: `www.traefik.io`, `{subdomain:[a-z]+}.traefik.io`
* `Methods`: Methods adds a matcher for HTTP methods. It accepts a sequence of one or more methods to be matched, e.g.: `GET`, `POST`, `PUT`
* `Path`: Path adds a matcher for the URL path. It accepts a template with zero or more URL variables enclosed by `{}`. The template must start with a `/`. For exemple `/products/` `/articles/{category}/{id:[0-9]+}`
* `PathPrefix`: PathPrefix adds a matcher for the URL path prefix. This matches if the given template is a prefix of the full URL path.
- `Headers`: Headers adds a matcher for request header values. It accepts a sequence of key/value pairs to be matched. For example: `application/json`
- `HeadersRegexp`: Regular expressions can be used with headers as well. It accepts a sequence of key/value pairs, where the value has regex support. For example: `application/(text|json)`
- `Host`: Host adds a matcher for the URL host. It accepts a template with zero or more URL variables enclosed by `{}`. Variables can define an optional regexp pattern to be matched: `www.traefik.io`, `{subdomain:[a-z]+}.traefik.io`
- `Methods`: Methods adds a matcher for HTTP methods. It accepts a sequence of one or more methods to be matched, e.g.: `GET`, `POST`, `PUT`
- `Path`: Path adds a matcher for the URL path. It accepts a template with zero or more URL variables enclosed by `{}`. The template must start with a `/`. For exemple `/products/` `/articles/{category}/{id:[0-9]+}`
- `PathPrefix`: PathPrefix adds a matcher for the URL path prefix. This matches if the given template is a prefix of the full URL path.
A frontend is a set of rules that forwards the incoming http traffic to a backend.
@@ -44,19 +44,19 @@ Frontends can be defined using the following rules:
A backend is responsible to load-balance the traffic coming from one or more frontends to a set of http servers.
Various methods of load-balancing is supported:
* `wrr`: Weighted Round Robin
* `drr`: Dynamic Round Robin: increases weights on servers that perform better than others. It also rolls back to original weights if the servers have changed.
- `wrr`: Weighted Round Robin
- `drr`: Dynamic Round Robin: increases weights on servers that perform better than others. It also rolls back to original weights if the servers have changed.
A circuit breaker can also be applied to a backend, preventing high loads on failing servers.
It can be configured using:
* Methods: `LatencyAtQuantileMS`, `NetworkErrorRatio`, `ResponseCodeRatio`
* Operators: `AND`, `OR`, `EQ`, `NEQ`, `LT`, `LE`, `GT`, `GE`
- Methods: `LatencyAtQuantileMS`, `NetworkErrorRatio`, `ResponseCodeRatio`
- Operators: `AND`, `OR`, `EQ`, `NEQ`, `LT`, `LE`, `GT`, `GE`
For example:
* `NetworkErrorRatio() > 0.5`
* `LatencyAtQuantileMS(50.0) > 50`
* `ResponseCodeRatio(500, 600, 0, 600) > 0.5`
- `NetworkErrorRatio() > 0.5`
- `LatencyAtQuantileMS(50.0) > 50`
- `ResponseCodeRatio(500, 600, 0, 600) > 0.5`
## <a id="global"></a> Global configuration
@@ -114,7 +114,7 @@ For example:
Like any other reverse proxy, Træfɪk can be configured with a file. You have two choices:
* simply add your configuration at the end of the global configuration file `traefik.toml` :
- simply add your configuration at the end of the global configuration file `traefik.toml` :
```toml
# traefik.toml
@@ -158,7 +158,7 @@ logLevel = "DEBUG"
value = "/test"
```
* or put your rules in a separate file, for example `rules.tml`:
- or put your rules in a separate file, for example `rules.tml`:
```toml
# traefik.toml
@@ -230,89 +230,108 @@ address = ":8080"
# KeyFile = "traefik.key"
```
* `/`: provides a simple HTML frontend of Træfik
- `/`: provides a simple HTML frontend of Træfik
![HTML frontend](img/web.frontend.png)
![Web UI Providers](img/web.frontend.png)
![Web UI Health](img/traefik-health.png)
* `/health`: `GET` json metrics
- `/health`: `GET` json metrics
```sh
$ curl -s "http://localhost:8080/health" | jq .
{
"average_response_time_sec": 0,
"average_response_time": "0",
"total_response_time_sec": 0,
"total_response_time": "0",
"total_count": 0,
"pid": 12861,
"uptime": "7m12.80607635s",
"uptime_sec": 432.80607635,
"time": "2015-09-22 10:25:16.448023473 +0200 CEST",
"unixtime": 1442910316,
"status_code_count": {},
"total_status_code_count": {},
"count": 0
// Træfɪk PID
"pid": 2458,
// Træfɪk server uptime (formated time)
"uptime": "39m6.885931127s",
// Træfɪk server uptime in seconds
"uptime_sec": 2346.885931127,
// current server date
"time": "2015-10-07 18:32:24.362238909 +0200 CEST",
// current server date in seconds
"unixtime": 1444235544,
// count HTTP response status code in realtime
"status_code_count": {
"502": 1
},
// count HTTP response status code since Træfɪk started
"total_status_code_count": {
"200": 7,
"404": 21,
"502": 13
},
// count HTTP response
"count": 1,
// count HTTP response
"total_count": 41,
// sum of all response time (formated time)
"total_response_time": "35.456865605s",
// sum of all response time in seconds
"total_response_time_sec": 35.456865605,
// average response time (formated time)
"average_response_time": "864.8016ms",
// average response time in seconds
"average_response_time_sec": 0.8648016000000001
}
```
* `/api`: `GET` configuration for all providers
- `/api`: `GET` configuration for all providers
```sh
$ curl -s "http://localhost:8080/api" | jq .
{
"file": {
"Frontends": {
"frontends": {
"frontend2": {
"Routes": {
"routes": {
"test_2": {
"Value": "/test",
"Rule": "Path"
"value": "/test",
"rule": "Path"
}
},
"Backend": "backend1"
"backend": "backend1"
},
"frontend1": {
"Routes": {
"routes": {
"test_1": {
"Value": "test.localhost",
"Rule": "Host"
"value": "test.localhost",
"rule": "Host"
}
},
"Backend": "backend2"
"backend": "backend2"
}
},
"Backends": {
"backends": {
"backend2": {
"LoadBalancer": {
"Method": "drr"
"loadBalancer": {
"method": "drr"
},
"CircuitBreaker": null,
"Servers": {
"servers": {
"server2": {
"Weight": 2,
"weight": 2,
"URL": "http://172.17.0.5:80"
},
"server1": {
"Weight": 1,
"URL": "http://172.17.0.4:80"
"weight": 1,
"url": "http://172.17.0.4:80"
}
}
},
"backend1": {
"LoadBalancer": {
"Method": "wrr"
"loadBalancer": {
"method": "wrr"
},
"CircuitBreaker": {
"Expression": "NetworkErrorRatio() > 0.5"
"circuitBreaker": {
"expression": "NetworkErrorRatio() > 0.5"
},
"Servers": {
"servers": {
"server2": {
"Weight": 1,
"URL": "http://172.17.0.3:80"
"weight": 1,
"url": "http://172.17.0.3:80"
},
"server1": {
"Weight": 10,
"URL": "http://172.17.0.2:80"
"weight": 10,
"url": "http://172.17.0.2:80"
}
}
}
@@ -374,14 +393,14 @@ watch = true
# filename = "docker.tmpl"
```
Labels can be used on containers to override default behaviour:
* `traefik.backend=foo`: assign the container to `foo` backend
* `traefik.port=80`: register this port. Useful when the container exposes multiples ports.
* `traefik.weight=10`: assign this weight to the container
* `traefik.enable=false`: disable this container in Træfɪk
* `traefik.host=bar`: override the default routing from {containerName}.{domain} to bar.{domain}
- `traefik.backend=foo`: assign the container to `foo` backend
- `traefik.port=80`: register this port. Useful when the container exposes multiples ports.
- `traefik.weight=10`: assign this weight to the container
- `traefik.enable=false`: disable this container in Træfɪk
- `traefik.host=bar`: override the default routing from `{containerName}.{domain}` to `bar.{domain}`
* `traefik.domain=traefik.localhost`: override the default domain
## <a id="marathon"></a> Marathon backend
@@ -435,12 +454,13 @@ domain = "marathon.localhost"
Labels can be used on containers to override default behaviour:
* `traefik.backend=foo`: assign the application to `foo` backend
* `traefik.port=80`: register this port. Useful when the application exposes multiples ports.
* `traefik.weight=10`: assign this weight to the application
* `traefik.enable=false`: disable this application in Træfɪk
* `traefik.host=bar`: override the default routing from {appName}.{domain} to bar.{domain}
* `traefik.prefixes=pf1,pf2`: use PathPrefix(es) instead of hostname for routing, use filename="providerTemplates/marathon-prefix.tmpl" with this option
- `traefik.backend=foo`: assign the application to `foo` backend
- `traefik.port=80`: register this port. Useful when the application exposes multiples ports.
- `traefik.weight=10`: assign this weight to the application
- `traefik.enable=false`: disable this application in Træfɪk
- `traefik.host=bar`: override the default routing from `{appName}.{domain}` to `bar.{domain}`
- `traefik.prefixes=pf1,pf2`: use `PathPrefix(es)` instead of hostname for routing, use `filename="providerTemplates/marathon-prefix.tmpl"` with this option
* `traefik.domain=traefik.localhost`: override the default domain
## <a id="consul"></a> Consul backend
@@ -482,6 +502,45 @@ prefix = "traefik"
# filename = "consul.tmpl"
```
The Keys-Values structure should look (using `prefix = "/traefik"`):
- backend 1
| Key | Value |
|--------------------------------------------------------|-----------------------------|
| `/traefik/backends/backend1/circuitbreaker/expression` | `NetworkErrorRatio() > 0.5` |
| `/traefik/backends/backend1/servers/server1/url` | `http://172.17.0.2:80` |
| `/traefik/backends/backend1/servers/server1/weight` | `10` |
| `/traefik/backends/backend1/servers/server2/url` | `http://172.17.0.3:80` |
| `/traefik/backends/backend1/servers/server2/weight` | `1` |
- backend 2
| Key | Value |
|-----------------------------------------------------|------------------------|
| `/traefik/backends/backend2/loadbalancer/method` | `drr` |
| `/traefik/backends/backend2/servers/server1/url` | `http://172.17.0.4:80` |
| `/traefik/backends/backend2/servers/server1/weight` | `1` |
| `/traefik/backends/backend2/servers/server2/url` | `http://172.17.0.5:80` |
| `/traefik/backends/backend2/servers/server2/weight` | `2` |
- frontend 1
| Key | Value |
|----------------------------------------------------|------------------|
| `/traefik/frontends/frontend1/backend` | `backend2` |
| `/traefik/frontends/frontend1/routes/test_1/rule` | `Host` |
| `/traefik/frontends/frontend1/routes/test_1/value` | `test.localhost` |
- frontend 2
| Key | Value |
|----------------------------------------------------|------------|
| `/traefik/frontends/frontend2/backend` | `backend1` |
| `/traefik/frontends/frontend2/routes/test_2/rule` | `Path` |
| `/traefik/frontends/frontend2/routes/test_2/value` | `/test` |
## <a id="etcd"></a> Etcd backend
Træfɪk can be configured to use Etcd as a backend configuration:
@@ -522,6 +581,45 @@ Træfɪk can be configured to use Etcd as a backend configuration:
# filename = "etcd.tmpl"
```
The Keys-Values structure should look (using `prefix = "/traefik"`):
- backend 1
| Key | Value |
|--------------------------------------------------------|-----------------------------|
| `/traefik/backends/backend1/circuitbreaker/expression` | `NetworkErrorRatio() > 0.5` |
| `/traefik/backends/backend1/servers/server1/url` | `http://172.17.0.2:80` |
| `/traefik/backends/backend1/servers/server1/weight` | `10` |
| `/traefik/backends/backend1/servers/server2/url` | `http://172.17.0.3:80` |
| `/traefik/backends/backend1/servers/server2/weight` | `1` |
- backend 2
| Key | Value |
|-----------------------------------------------------|------------------------|
| `/traefik/backends/backend2/loadbalancer/method` | `drr` |
| `/traefik/backends/backend2/servers/server1/url` | `http://172.17.0.4:80` |
| `/traefik/backends/backend2/servers/server1/weight` | `1` |
| `/traefik/backends/backend2/servers/server2/url` | `http://172.17.0.5:80` |
| `/traefik/backends/backend2/servers/server2/weight` | `2` |
- frontend 1
| Key | Value |
|----------------------------------------------------|------------------|
| `/traefik/frontends/frontend1/backend` | `backend2` |
| `/traefik/frontends/frontend1/routes/test_1/rule` | `Host` |
| `/traefik/frontends/frontend1/routes/test_1/value` | `test.localhost` |
- frontend 2
| Key | Value |
|----------------------------------------------------|------------|
| `/traefik/frontends/frontend2/backend` | `backend1` |
| `/traefik/frontends/frontend2/routes/test_2/rule` | `Path` |
| `/traefik/frontends/frontend2/routes/test_2/value` | `/test` |
## <a id="zk"></a> Zookeeper backend
Træfɪk can be configured to use Zookeeper as a backend configuration:
@@ -561,6 +659,44 @@ Træfɪk can be configured to use Zookeeper as a backend configuration:
#
# filename = "zookeeper.tmpl"
```
The Keys-Values structure should look (using `prefix = "/traefik"`):
- backend 1
| Key | Value |
|--------------------------------------------------------|-----------------------------|
| `/traefik/backends/backend1/circuitbreaker/expression` | `NetworkErrorRatio() > 0.5` |
| `/traefik/backends/backend1/servers/server1/url` | `http://172.17.0.2:80` |
| `/traefik/backends/backend1/servers/server1/weight` | `10` |
| `/traefik/backends/backend1/servers/server2/url` | `http://172.17.0.3:80` |
| `/traefik/backends/backend1/servers/server2/weight` | `1` |
- backend 2
| Key | Value |
|-----------------------------------------------------|------------------------|
| `/traefik/backends/backend2/loadbalancer/method` | `drr` |
| `/traefik/backends/backend2/servers/server1/url` | `http://172.17.0.4:80` |
| `/traefik/backends/backend2/servers/server1/weight` | `1` |
| `/traefik/backends/backend2/servers/server2/url` | `http://172.17.0.5:80` |
| `/traefik/backends/backend2/servers/server2/weight` | `2` |
- frontend 1
| Key | Value |
|---------------------------------------------------|------------------|
| `/traefik/frontends/frontend1/backend | `backend2` |
| `/traefik/frontends/frontend1/routes/test_1/rule | `Host` |
| `/traefik/frontends/frontend1/routes/test_1/value | `test.localhost` |
- frontend 2
| Key | Value |
|----------------------------------------------------|------------|
| `/traefik/frontends/frontend2/backend` | `backend1` |
| `/traefik/frontends/frontend2/routes/test_2/rule` | `Path` |
| `/traefik/frontends/frontend2/routes/test_2/value` | `/test` |
## <a id="boltdb"></a> BoltDB backend
@@ -607,7 +743,7 @@ Træfɪk can be configured to use BoltDB as a backend configuration:
Here are some early Benchmarks between Nginx and Træfɪk acting as simple load balancers between two servers.
* Nginx:
- Nginx:
```sh
$ docker run -d -e VIRTUAL_HOST=test1.localhost emilevauge/whoami
@@ -667,10 +803,9 @@ Percentage of the requests served within a certain time (ms)
98% 12
99% 13
100% 36 (longest request)
```
* Træfɪk:
- Træfɪk:
```sh
$ docker run -d -l traefik.backend=test1 -l traefik.host=test1 emilevauge/whoami
@@ -730,5 +865,4 @@ Percentage of the requests served within a certain time (ms)
98% 11
99% 13
100% 22 (longest request)
```

View File

@@ -9,3 +9,36 @@
logLevel = "DEBUG"
[file]
# rules
[backends]
[backends.backend1]
[backends.backend1.circuitbreaker]
expression = "NetworkErrorRatio() > 0.5"
[backends.backend1.servers.server1]
url = "http://172.17.0.2:80"
weight = 10
[backends.backend1.servers.server2]
url = "http://172.17.0.3:80"
weight = 1
[backends.backend2]
[backends.backend2.LoadBalancer]
method = "drr"
[backends.backend2.servers.server1]
url = "http://172.17.0.4:80"
weight = 1
[backends.backend2.servers.server2]
url = "http://172.17.0.5:80"
weight = 2
[frontends]
[frontends.frontend1]
backend = "backend2"
[frontends.frontend1.routes.test_1]
rule = "Host"
value = "test.localhost"
[frontends.frontend2]
backend = "backend1"
[frontends.frontend2.routes.test_2]
rule = "Path"
value = "/test"

View File

@@ -9,7 +9,6 @@ import (
"path/filepath"
"testing"
"text/template"
"time"
"github.com/docker/libcompose/docker"
"github.com/docker/libcompose/project"
@@ -72,7 +71,7 @@ func (s *MarathonSuite) SetUpSuite(c *check.C) {
type BaseSuite struct {
composeProject *project.Project
listenChan chan project.ProjectEvent
listenChan chan project.Event
started chan bool
stopped chan bool
deleted chan bool
@@ -82,14 +81,12 @@ func (s *BaseSuite) TearDownSuite(c *check.C) {
// shutdown and delete compose project
if s.composeProject != nil {
s.composeProject.Down()
// Waiting for libcompose#55 to be merged
// <-s.stopped
time.Sleep(2 * time.Second)
<-s.stopped
defer close(s.stopped)
s.composeProject.Delete()
// Waiting for libcompose#55 to be merged
// <-s.deleted
time.Sleep(2 * time.Second)
<-s.deleted
defer close(s.deleted)
}
}
@@ -103,32 +100,32 @@ func (s *BaseSuite) createComposeProject(c *check.C, name string) {
c.Assert(err, checker.IsNil)
s.composeProject = composeProject
s.listenChan = make(chan project.ProjectEvent)
s.started = make(chan bool)
s.stopped = make(chan bool)
s.deleted = make(chan bool)
s.listenChan = make(chan project.Event)
go s.startListening(c)
composeProject.AddListener(s.listenChan)
composeProject.Start()
// FIXME Wait for compose to start
// Waiting for libcompose#55 to be merged
// <-s.started
time.Sleep(2 * time.Second)
// Wait for compose to start
<-s.started
defer close(s.started)
}
func (s *BaseSuite) startListening(c *check.C) {
for event := range s.listenChan {
// FIXME Remove this when it's working (libcompose#55)
// fmt.Fprintf(os.Stdout, "Event: %s (%v)\n", event.Event, event)
// FIXME Add a timeout on event
if event.Event == project.PROJECT_UP_DONE {
// FIXME Add a timeout on event ?
if event.EventType == project.EventProjectStartDone {
s.started <- true
}
if event.Event == project.PROJECT_DOWN_DONE {
if event.EventType == project.EventProjectDownDone {
s.stopped <- true
}
if event.Event == project.PROJECT_DELETE_DONE {
if event.EventType == project.EventProjectDeleteDone {
s.deleted <- true
}
}

View File

@@ -21,42 +21,6 @@ type MarathonProvider struct {
NetworkInterface string
}
var MarathonFuncMap = template.FuncMap{
"getPort": func(task marathon.Task) string {
for _, port := range task.Ports {
return strconv.Itoa(port)
}
return ""
},
"getHost": func(application marathon.Application) string {
for key, value := range application.Labels {
if key == "traefik.host" {
return value
}
}
return strings.Replace(application.ID, "/", "", 1)
},
"getWeight": func(application marathon.Application) string {
for key, value := range application.Labels {
if key == "traefik.weight" {
return value
}
}
return "0"
},
"getPrefixes": func(application marathon.Application) ([]string, error) {
for key, value := range application.Labels {
if key == "traefik.prefixes" {
return strings.Split(value, ","), nil
}
}
return []string{}, nil
},
"replace": func(s1 string, s2 string, s3 string) string {
return strings.Replace(s3, s1, s2, -1)
},
}
func (provider *MarathonProvider) Provide(configurationChan chan<- configMessage) error {
config := marathon.NewDefaultConfig()
config.URL = provider.Endpoint
@@ -91,6 +55,49 @@ func (provider *MarathonProvider) Provide(configurationChan chan<- configMessage
}
func (provider *MarathonProvider) loadMarathonConfig() *Configuration {
var MarathonFuncMap = template.FuncMap{
"getPort": func(task marathon.Task) string {
for _, port := range task.Ports {
return strconv.Itoa(port)
}
return ""
},
"getHost": func(application marathon.Application) string {
for key, value := range application.Labels {
if key == "traefik.host" {
return value
}
}
return strings.Replace(application.ID, "/", "", 1)
},
"getWeight": func(application marathon.Application) string {
for key, value := range application.Labels {
if key == "traefik.weight" {
return value
}
}
return "0"
},
"getDomain": func(application marathon.Application) string {
for key, value := range application.Labels {
if key == "traefik.domain" {
return value
}
}
return provider.Domain
},
"getPrefixes": func(application marathon.Application) ([]string, error) {
for key, value := range application.Labels {
if key == "traefik.prefixes" {
return strings.Split(value, ","), nil
}
}
return []string{}, nil
},
"replace": func(s1 string, s2 string, s3 string) string {
return strings.Replace(s3, s1, s2, -1)
},
}
configuration := new(Configuration)
applications, err := provider.marathonClient.Applications(nil)

View File

@@ -10,5 +10,5 @@
backend = "backend-{{getBackend $container}}"
[frontends.frontend-{{$host}}.routes.route-host-{{$host}}]
rule = "Host"
value = "{{$host}}.{{$.Domain}}"
value = "{{$host}}.{{getDomain $container}}"
{{end}}

View File

@@ -22,6 +22,6 @@
backend = "backend{{.ID | replace "/" "-"}}"
[frontends.frontend-{{getHost $app | replace "/" "-"}}.routes.route-host-{{getHost $app | replace "/" "-"}}]
rule = "Host"
value = "{{getHost $app | replace "/" "-"}}.{{$.Domain}}"
value = "{{getHost $app | replace "/" "-"}}.{{getDomain .}}"
{{end}}
{{end}}

View File

@@ -15,5 +15,5 @@
backend = "backend{{.ID | replace "/" "-"}}"
[frontends.frontend-{{getHost . | replace "/" "-"}}.routes.route-host-{{getHost . | replace "/" "-"}}]
rule = "Host"
value = "{{getHost . | replace "/" "-"}}.{{$.Domain}}"
value = "{{getHost . | replace "/" "-"}}.{{getDomain .}}"
{{end}}

View File

@@ -1,4 +1,4 @@
(function () {
(function (d3) {
'use strict';
angular.module('traefik.section.health')
@@ -6,21 +6,199 @@
var vm = this;
vm.health = Health.get();
vm.graph = {
averageResponseTime: {},
totalStatusCodeCount: {}
};
vm.graph.totalStatusCodeCount.options = {
"chart": {
type: 'discreteBarChart',
height: 200,
margin: {
top: 20,
right: 20,
bottom: 40,
left: 55
},
x: function (d) {
return d.label;
},
y: function (d) {
return d.value;
},
showValues: true,
valueFormat: function (d) {
return d3.format('d')(d);
},
transitionDuration: 50,
yAxis: {
axisLabelDistance: 30
}
},
"title": {
"enable": true,
"text": "Total Status Code Count",
"css": {
"textAlign": "center"
}
}
};
vm.graph.totalStatusCodeCount.data = [
{
key: "Total Status Code Count",
values: [
{
"label": "200",
"value": 0
}
]
}
];
/**
* Update Total Status Code Count graph
*
* @param {Object} totalStatusCodeCount Object from API
*/
function updateTotalStatusCodeCount(totalStatusCodeCount) {
// extract values
vm.graph.totalStatusCodeCount.data[0].values = [];
for (var code in totalStatusCodeCount) {
if (totalStatusCodeCount.hasOwnProperty(code)) {
vm.graph.totalStatusCodeCount.data[0].values.push({
label: code,
value: totalStatusCodeCount[code]
});
}
}
// Update Total Status Code Count graph render
if (vm.graph.totalStatusCodeCount.api) {
vm.graph.totalStatusCodeCount.api.update();
} else {
console.error('fail');
}
}
vm.graph.averageResponseTime.options = {
chart: {
type: 'lineChart',
height: 200,
margin: {
top: 20,
right: 40,
bottom: 40,
left: 55
},
transitionDuration: 50,
x: function (d) {
return d.x;
},
y: function (d) {
return d.y;
},
useInteractiveGuideline: true,
xAxis: {
tickFormat: function (d) {
return d3.time.format('%X')(new Date(d));
}
},
yAxis: {
tickFormat: function (d) {
return d3.format(',.1f')(d);
}
}
},
"title": {
"enable": true,
"text": "Average response time",
"css": {
"textAlign": "center"
}
}
};
var initialPoint = {
x: Date.now() - 3000,
y: 0
};
vm.graph.averageResponseTime.data = [
{
values: [initialPoint],
key: 'Average response time (ms)',
type: 'line',
color: '#2ca02c'
}
];
/**
* Update average response time graph
*
* @param {Number} x Coordinate X
* @param {Number} y Coordinate Y
*/
function updateAverageResponseTimeGraph(x, y) {
// x multiply 1000 by because unix time is in seconds and JS Date are in milliseconds
var data = {
x: x * 1000,
y: y * 1000
};
vm.graph.averageResponseTime.data[0].values.push(data);
// limit graph entries
if (vm.graph.averageResponseTime.data[0].values.length > 100) {
vm.graph.averageResponseTime.data[0].values.shift();
}
// Update Average Response Time graph render
if (vm.graph.averageResponseTime.api) {
vm.graph.averageResponseTime.api.update();
}
}
/**
* Load all graph's datas
*
* @param {Object} health Health data from server
*/
function loadData(health) {
// Load datas and update Average Response Time graph render
updateAverageResponseTimeGraph(health.unixtime, health.average_response_time_sec);
// Load datas and update Total Status Code Count graph render
updateTotalStatusCodeCount(health.total_status_code_count);
// set data's view
vm.health = health;
}
/**
* Action when load datas failed
*
* @param {Object} error Error state object
*/
function erroData(error) {
vm.health = {};
$log.error(error);
}
// first load
Health.get(loadData, erroData);
// Auto refresh data
var intervalId = $interval(function () {
Health.get(function (health) {
vm.health = health;
}, function (error) {
vm.health = {};
$log.error(error);
});
Health.get(loadData, erroData);
}, 3000);
// Stop auto refresh when page change
$scope.$on('$destroy', function () {
$interval.cancel(intervalId);
});
}]);
})();
})(d3);

View File

@@ -2,45 +2,42 @@
<h1 class="text-danger">
<span class="glyphicon glyphicon-heart" aria-hidden="true"></span> Health
</h1>
<ul class="list-group">
<li class="list-group-item">
<span>Average response time sec :</span><span class="badge">{{healthCtrl.health.average_response_time_sec}}</span>
</li>
<li class="list-group-item">
<span>Average response time :</span><span class="badge">{{healthCtrl.health.average_response_time}}</span>
</li>
<li class="list-group-item">
<span>Total response time sec :</span><span class="badge">{{healthCtrl.health.total_response_time_sec}}</span>
</li>
<li class="list-group-item">
<span>Total response time :</span><span class="badge">{{healthCtrl.health.total_response_time}}</span>
</li>
<li class="list-group-item">
<span>Total count :</span><span class="badge">{{healthCtrl.health.total_count}}</span>
</li>
<li class="list-group-item">
<span>PID :</span><span class="badge">{{healthCtrl.health.pid}}</span>
</li>
<li class="list-group-item">
<span>Uptime :</span><span class="badge">{{healthCtrl.health.uptime}}</span>
</li>
<li class="list-group-item">
<span>Uptime sec :</span><span class="badge">{{healthCtrl.health.uptime_sec}}</span>
</li>
<li class="list-group-item">
<span>Time :</span><span class="badge">{{healthCtrl.health.time}}</span>
</li>
<li class="list-group-item">
<span>Unixtime :</span><span class="badge">{{healthCtrl.health.unixtime}}</span>
</li>
<li class="list-group-item">
<span>Status code count :</span><span class="badge">{{healthCtrl.health.status_code_count}}</span>
</li>
<li class="list-group-item">
<span>Total status code count :</span><span class="badge">{{healthCtrl.health.total_status_code_count}}</span>
</li>
<li class="list-group-item">
<span>Count :</span><span class="badge">{{healthCtrl.health.count}}</span>
</li>
</ul>
<div class="row">
<div class="col-md-6">
<div>
<nvd3 options="healthCtrl.graph.averageResponseTime.options" data="healthCtrl.graph.averageResponseTime.data" api="healthCtrl.graph.averageResponseTime.api"></nvd3>
</div>
<ul class="list-group">
<li class="list-group-item">
<span>Total response time :</span><span class="badge">{{healthCtrl.health.total_response_time}}</span>
</li>
</ul>
<ul class="list-group">
<li class="list-group-item">
<span>PID :</span><span class="badge">{{healthCtrl.health.pid}}</span>
</li>
<li class="list-group-item">
<span>Uptime :</span><span class="badge">{{healthCtrl.health.uptime}}</span>
</li>
</ul>
</div>
<div class="col-md-6">
<div>
<nvd3 options="healthCtrl.graph.totalStatusCodeCount.options" data="healthCtrl.graph.totalStatusCodeCount.data" api="healthCtrl.graph.totalStatusCodeCount.api"></nvd3>
</div>
<ul class="list-group">
<li class="list-group-item">
<span>Total count :</span><span class="badge">{{healthCtrl.health.total_count}}</span>
</li>
<li class="list-group-item">
<span>Count :</span><span class="badge">{{healthCtrl.health.count}}</span>
</li>
</ul>
</div>
</div>
</div>

View File

@@ -9,15 +9,15 @@
<td><em>URL</em></td>
<td><em>Weight</em></td>
</tr>
<tr data-ng-repeat="(serverId, server) in backendCtrl.backend.Servers">
<tr data-ng-repeat="(serverId, server) in backendCtrl.backend.servers">
<td>{{serverId}}</td>
<td><code><a data-ng-href="{{server.url}}">{{server.URL}}</a></code></td>
<td>{{server.Weight}}</td>
<td><code><a data-ng-href="{{server.url}}">{{server.url}}</a></code></td>
<td>{{server.weight}}</td>
</tr>
</table>
</div>
<div class="panel-footer" data-ng-show="backendCtrl.backend.LoadBalancer || backendCtrl.backend.CircuitBreaker">
<span data-ng-show="backendCtrl.backend.LoadBalancer" class="label label-success">Load Balancer: {{backendCtrl.backend.LoadBalancer.Method}}</span>
<span data-ng-show="backendCtrl.backend.CircuitBreaker" class="label label-success">Circuit Breaker: {{backendCtrl.backend.CircuitBreaker.Expression}}</span>
<div class="panel-footer" data-ng-show="backendCtrl.backend.loadBalancer || backendCtrl.backend.circuitBreaker">
<span data-ng-show="backendCtrl.backend.loadBalancer" class="label label-success">Load Balancer: {{backendCtrl.backend.loadBalancer.method}}</span>
<span data-ng-show="backendCtrl.backend.circuitBreaker" class="label label-success">Circuit Breaker: {{backendCtrl.backend.circuitBreaker.expression}}</span>
</div>
</div>

View File

@@ -9,14 +9,14 @@
<td><em>Rule</em></td>
<td><em>Value</em></td>
</tr>
<tr data-ng-repeat="(routeId, route) in frontendCtrl.frontend.Routes">
<tr data-ng-repeat="(routeId, route) in frontendCtrl.frontend.routes">
<td>{{routeId}}</td>
<td>{{route.Rule}}</td>
<td><code>{{route.Value}}</code></td>
<td>{{route.rule}}</td>
<td><code>{{route.value}}</code></td>
</tr>
</table>
</div>
<div data-bg-show="frontendCtrl.frontend.Backend" class="panel-footer">
<span class="label label-warning" role="button" data-toggle="collapse" href="#{{frontendCtrl.frontend.Backend}}" aria-expanded="false">{{frontendCtrl.frontend.Backend}}</span>
<div data-bg-show="frontendCtrl.frontend.backend" class="panel-footer">
<span class="label label-warning" role="button" data-toggle="collapse" href="#{{frontendCtrl.frontend.backend}}" aria-expanded="false">{{frontendCtrl.frontend.backend}}</span>
</div>
</div>

View File

@@ -4,12 +4,12 @@
<div class="row tabset-row__providers">
<div class="col-md-6">
<div data-ng-repeat="(frontendId, frontend) in provider.Frontends">
<div data-ng-repeat="(frontendId, frontend) in provider.frontends">
<frontend-monitor data-provider-id="providerId" data-frontend-id="frontendId" data-frontend="frontend"></frontend-monitor>
</div>
</div>
<div class="col-md-6">
<div data-ng-repeat="(backendId, backend) in provider.Backends">
<div data-ng-repeat="(backendId, backend) in provider.backends">
<backend-monitor data-provider-id="providerId" data-backend-id="backendId" data-backend="backend"></backend-monitor>
</div>
</div>

View File

@@ -5,6 +5,7 @@
.module('traefik.section', [
'ui.router',
'ui.bootstrap',
'nvd3',
'traefik.section.providers',
'traefik.section.health'
]);

View File

@@ -1,7 +1,7 @@
{
"name": "traefik",
"version": "1.0.0",
"homepage": "https://github.com/ldez/traefik",
"homepage": "http://traefik.io",
"authors": [
"Fernandez Ludovic <ludovic.fernandez@zenika.com>"
],
@@ -20,6 +20,7 @@
"bootstrap": "~3.3.5",
"angular-resource": "~1.4.7",
"angular-ui-router": "~0.2.15",
"angular-bootstrap": "~0.13.4"
"angular-bootstrap": "~0.13.4",
"angular-nvd3": "~1.0.2"
}
}

File diff suppressed because one or more lines are too long

5
static/bower_components/d3/d3.min.js vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -13,6 +13,9 @@
<link rel="stylesheet" href="bower_components/bootstrap/dist/css/bootstrap-theme.min.css">
<link rel="stylesheet" href="bower_components/angular-bootstrap/ui-bootstrap-csp.css">
<!-- NVD3 -->
<link rel="stylesheet" href="bower_components/nvd3/build/nv.d3.min.css">
<link rel="stylesheet" href="style.css">
</head>
@@ -57,6 +60,9 @@
<script src="bower_components/angular-resource/angular-resource.min.js"></script>
<script src="bower_components/angular-ui-router/release/angular-ui-router.min.js"></script>
<script src="bower_components/angular-bootstrap/ui-bootstrap-tpls.min.js"></script>
<script src="bower_components/d3/d3.min.js"></script>
<script src="bower_components/nvd3/build/nv.d3.min.js"></script>
<script src="bower_components/angular-nvd3/dist/angular-nvd3.min.js"></script>
<!-- end vendors -->
<script src="app/traefik.js"></script>

View File

@@ -26,4 +26,7 @@ Do `npm install` and `bower install`
- [UI Router - Documentation](https://github.com/angular-ui/ui-router/wiki)
- [Bootstrap](http://getbootstrap.com)
- [Angular Bootstrap](https://angular-ui.github.io/bootstrap)
- [D3](http://d3js.org)
- [D3 - Documentation](https://github.com/mbostock/d3/wiki)
- [NVD3](http://nvd3.org)
- [Angular nvD3](http://krispo.github.io/angular-nvd3)