Skip to content

Commit

Permalink
Merge pull request #646 from jangie/add-backend-features-to-docker
Browse files Browse the repository at this point in the history
Add backend features to docker
  • Loading branch information
vdemeester committed Sep 14, 2016
2 parents 1e324ad + d89bdfb commit 4783c7f
Show file tree
Hide file tree
Showing 4 changed files with 159 additions and 11 deletions.
4 changes: 4 additions & 0 deletions docs/toml.md
Expand Up @@ -612,6 +612,10 @@ exposedbydefault = true
Labels can be used on containers to override default behaviour:
- `traefik.backend=foo`: assign the container to `foo` backend
- `traefik.backend.maxconn.amount=10`: set a maximum number of connections to the backend. Must be used in conjunction with the below label to take effect.
- `traefik.backend.maxconn.extractorfunc=client.ip`: set the function to be used against the request to determine what to limit maximum connections to the backend by. Must be used in conjunction with the above label to take effect.
- `traefik.backend.loadbalancer.method=drr`: override the default `wrr` load balancer algorithm
- `traefik.backend.circuitbreaker.expression=NetworkErrorRatio() > 0.5`: create a [circuit breaker](/basics/#backends) to be used against the backend
- `traefik.port=80`: register this port. Useful when the container exposes multiples ports.
- `traefik.protocol=https`: override the default `http` protocol
- `traefik.weight=10`: assign this weight to the container
Expand Down
87 changes: 76 additions & 11 deletions provider/docker.go
Expand Up @@ -2,6 +2,7 @@ package provider

import (
"errors"
"math"
"net/http"
"strconv"
"strings"
Expand Down Expand Up @@ -150,17 +151,24 @@ func (provider *Docker) Provide(configurationChan chan<- types.ConfigMessage, po

func (provider *Docker) loadDockerConfig(containersInspected []dockertypes.ContainerJSON) *types.Configuration {
var DockerFuncMap = template.FuncMap{
"getBackend": provider.getBackend,
"getIPAddress": provider.getIPAddress,
"getPort": provider.getPort,
"getWeight": provider.getWeight,
"getDomain": provider.getDomain,
"getProtocol": provider.getProtocol,
"getPassHostHeader": provider.getPassHostHeader,
"getPriority": provider.getPriority,
"getEntryPoints": provider.getEntryPoints,
"getFrontendRule": provider.getFrontendRule,
"replace": replace,
"getBackend": provider.getBackend,
"getIPAddress": provider.getIPAddress,
"getPort": provider.getPort,
"getWeight": provider.getWeight,
"getDomain": provider.getDomain,
"getProtocol": provider.getProtocol,
"getPassHostHeader": provider.getPassHostHeader,
"getPriority": provider.getPriority,
"getEntryPoints": provider.getEntryPoints,
"getFrontendRule": provider.getFrontendRule,
"hasCircuitBreakerLabel": provider.hasCircuitBreakerLabel,
"getCircuitBreakerExpression": provider.getCircuitBreakerExpression,
"hasLoadBalancerLabel": provider.hasLoadBalancerLabel,
"getLoadBalancerMethod": provider.getLoadBalancerMethod,
"hasMaxConnLabels": provider.hasMaxConnLabels,
"getMaxConnAmount": provider.getMaxConnAmount,
"getMaxConnExtractorFunc": provider.getMaxConnExtractorFunc,
"replace": replace,
}

// filter containers
Expand Down Expand Up @@ -191,6 +199,63 @@ func (provider *Docker) loadDockerConfig(containersInspected []dockertypes.Conta
return configuration
}

func (provider *Docker) hasCircuitBreakerLabel(container dockertypes.ContainerJSON) bool {
if _, err := getLabel(container, "traefik.backend.circuitbreaker.expression"); err != nil {
return false
}
return true
}

func (provider *Docker) hasLoadBalancerLabel(container dockertypes.ContainerJSON) bool {
if _, err := getLabel(container, "traefik.backend.loadbalancer.method"); err != nil {
return false
}
return true
}

func (provider *Docker) hasMaxConnLabels(container dockertypes.ContainerJSON) bool {
if _, err := getLabel(container, "traefik.backend.maxconn.amount"); err != nil {
return false
}
if _, err := getLabel(container, "traefik.backend.maxconn.extractorfunc"); err != nil {
return false
}
return true
}

func (provider *Docker) getCircuitBreakerExpression(container dockertypes.ContainerJSON) string {
if label, err := getLabel(container, "traefik.backend.circuitbreaker.expression"); err == nil {
return label
}
return "NetworkErrorRatio() > 1"
}

func (provider *Docker) getLoadBalancerMethod(container dockertypes.ContainerJSON) string {
if label, err := getLabel(container, "traefik.backend.loadbalancer.method"); err == nil {
return label
}
return "wrr"
}

func (provider *Docker) getMaxConnAmount(container dockertypes.ContainerJSON) int64 {
if label, err := getLabel(container, "traefik.backend.maxconn.amount"); err == nil {
i, errConv := strconv.ParseInt(label, 10, 64)
if errConv != nil {
log.Errorf("Unable to parse traefik.backend.maxconn.amount %s", label)
return math.MaxInt64
}
return i
}
return math.MaxInt64
}

func (provider *Docker) getMaxConnExtractorFunc(container dockertypes.ContainerJSON) string {
if label, err := getLabel(container, "traefik.backend.maxconn.extractorfunc"); err == nil {
return label
}
return "request.host"
}

func (provider *Docker) containerFilter(container dockertypes.ContainerJSON, exposedByDefaultFlag bool) bool {
_, err := strconv.Atoi(container.Config.Labels["traefik.port"])
if len(container.NetworkSettings.Ports) == 0 && err != nil {
Expand Down
63 changes: 63 additions & 0 deletions provider/docker_test.go
Expand Up @@ -1016,6 +1016,69 @@ func TestDockerLoadDockerConfig(t *testing.T) {
},
},
},
{
containers: []docker.ContainerJSON{
{
ContainerJSONBase: &docker.ContainerJSONBase{
Name: "test1",
},
Config: &container.Config{
Labels: map[string]string{
"traefik.backend": "foobar",
"traefik.frontend.entryPoints": "http,https",
"traefik.backend.maxconn.amount": "1000",
"traefik.backend.maxconn.extractorfunc": "somethingelse",
"traefik.backend.loadbalancer.method": "drr",
"traefik.backend.circuitbreaker.expression": "NetworkErrorRatio() > 0.5",
},
},
NetworkSettings: &docker.NetworkSettings{
NetworkSettingsBase: docker.NetworkSettingsBase{
Ports: nat.PortMap{
"80/tcp": {},
},
},
Networks: map[string]*network.EndpointSettings{
"bridge": {
IPAddress: "127.0.0.1",
},
},
},
},
},
expectedFrontends: map[string]*types.Frontend{
"frontend-Host-test1-docker-localhost": {
Backend: "backend-foobar",
PassHostHeader: true,
EntryPoints: []string{"http", "https"},
Routes: map[string]types.Route{
"route-frontend-Host-test1-docker-localhost": {
Rule: "Host:test1.docker.localhost",
},
},
},
},
expectedBackends: map[string]*types.Backend{
"backend-foobar": {
Servers: map[string]types.Server{
"server-test1": {
URL: "http://127.0.0.1:80",
Weight: 1,
},
},
CircuitBreaker: &types.CircuitBreaker{
Expression: "NetworkErrorRatio() > 0.5",
},
LoadBalancer: &types.LoadBalancer{
Method: "drr",
},
MaxConn: &types.MaxConn{
Amount: 1000,
ExtractorFunc: "somethingelse",
},
},
},
},
}

provider := &Docker{
Expand Down
16 changes: 16 additions & 0 deletions templates/docker.tmpl
@@ -1,4 +1,20 @@
[backends]{{range .Containers}}
{{if hasCircuitBreakerLabel .}}
[backends.backend-{{getBackend .}}.circuitbreaker]
expression = "{{getCircuitBreakerExpression .}}"
{{end}}

{{if hasLoadBalancerLabel .}}
[backends.backend-{{getBackend .}}.loadbalancer]
method = "{{getLoadBalancerMethod .}}"
{{end}}

{{if hasMaxConnLabels .}}
[backends.backend-{{getBackend .}}.maxconn]
amount = {{getMaxConnAmount . }}
extractorfunc = "{{getMaxConnExtractorFunc . }}"
{{end}}

[backends.backend-{{getBackend .}}.servers.server-{{.Name | replace "/" "" | replace "." "-"}}]
url = "{{getProtocol .}}://{{getIPAddress .}}:{{getPort .}}"
weight = {{getWeight .}}
Expand Down

0 comments on commit 4783c7f

Please sign in to comment.