Add sticky session support to Traefik.

This change adds sticky session support, by using the new
oxy/rr/StickySession feature.

To use it, set traefik.backend.sticky to true.

This is currently only implemented in the wrr load balancer, and against
the Marathon backend, but lifting it should be very doable.

In the wrr load balancer, a cookie called _TRAEFIK_SERVERNAME will be
set with the backend to use.  If the cookie is altered to an invalid
backend server, or the server is removed from the load balancer, the
next server will be used instead.

Otherwise, the cookie will be checked in Oxy's rr on access and if valid
the connection will be wired through to it.
This commit is contained in:
Owen Marshall 2016-05-13 10:22:11 -04:00 committed by Emile Vauge
parent a13549cc28
commit dc52abf4ce
No known key found for this signature in database
GPG key ID: D808B4C167352E59
5 changed files with 21 additions and 1 deletions

View file

@ -138,6 +138,7 @@ func (provider *Marathon) loadMarathonConfig() *types.Configuration {
"getMaxConnAmount": provider.getMaxConnAmount,
"getLoadBalancerMethod": provider.getLoadBalancerMethod,
"getCircuitBreakerExpression": provider.getCircuitBreakerExpression,
"getSticky": provider.getSticky,
}
applications, err := provider.marathonClient.Applications(nil)
@ -347,6 +348,13 @@ func (provider *Marathon) getProtocol(task marathon.Task, applications []maratho
return "http"
}
func (provider *Marathon) getSticky(application marathon.Application) string {
if sticky, err := provider.getLabel(application, "traefik.backend.sticky"); err == nil {
return sticky
}
return "false"
}
func (provider *Marathon) getPassHostHeader(application marathon.Application) string {
if passHostHeader, err := provider.getLabel(application, "traefik.frontend.passHostHeader"); err == nil {
return passHostHeader

View file

@ -107,7 +107,7 @@ func TestMarathonLoadConfig(t *testing.T) {
},
},
CircuitBreaker: nil,
LoadBalancer: nil,
LoadBalancer: &types.LoadBalancer{Sticky: false},
},
},
},

View file

@ -537,12 +537,16 @@ func (server *Server) loadConfig(configurations configs, globalConfiguration Glo
log.Errorf("Skipping frontend %s...", frontendName)
continue frontend
}
lbMethod, err := types.NewLoadBalancerMethod(configuration.Backends[frontend.Backend].LoadBalancer)
if err != nil {
log.Errorf("Error loading load balancer method '%+v' for frontend %s: %v", configuration.Backends[frontend.Backend].LoadBalancer, frontendName, err)
log.Errorf("Skipping frontend %s...", frontendName)
continue frontend
}
stickysession := configuration.Backends[frontend.Backend].LoadBalancer.Sticky
switch lbMethod {
case types.Drr:
log.Debugf("Creating load-balancer drr")
@ -565,6 +569,12 @@ func (server *Server) loadConfig(configurations configs, globalConfiguration Glo
}
case types.Wrr:
log.Debugf("Creating load-balancer wrr")
if stickysession {
cookiename := "_TRAEFIK_SERVERNAME"
log.Debugf("... setting to sticky session with cookie named %v", cookiename)
sticky := roundrobin.NewStickySession(cookiename)
rr, _ = roundrobin.New(saveBackend, roundrobin.EnableStickySession(sticky))
}
lb = rr
for serverName, server := range configuration.Backends[frontend.Backend].Servers {
url, err := url.Parse(server.URL)

View file

@ -14,6 +14,7 @@
{{ if hasLoadBalancerLabels . }}
[backends.backend{{getFrontendBackend . }}.loadbalancer]
method = "{{getLoadBalancerMethod . }}"
sticky = {{getSticky .}}
{{end}}
{{ if hasCircuitBreakerLabels . }}
[backends.backend{{getFrontendBackend . }}.circuitbreaker]

View file

@ -24,6 +24,7 @@ type MaxConn struct {
// LoadBalancer holds load balancing configuration.
type LoadBalancer struct {
Method string `json:"method,omitempty"`
Sticky bool `json:"sticky,omitempty"`
}
// CircuitBreaker holds circuit breaker configuration.