Merge pull request #646 from jangie/add-backend-features-to-docker

Add backend features to docker
This commit is contained in:
Vincent Demeester 2016-09-14 22:48:59 +02:00 committed by GitHub
commit 4783c7f70a
4 changed files with 159 additions and 11 deletions

View file

@ -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

View file

@ -2,6 +2,7 @@ package provider
import (
"errors"
"math"
"net/http"
"strconv"
"strings"
@ -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
@ -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 {

View file

@ -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{

View file

@ -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 .}}