Adds weight on ServersLoadBalancer
This commit is contained in:
parent
f4f3dbe1f5
commit
3174c69c66
12 changed files with 187 additions and 2 deletions
|
@ -178,6 +178,7 @@
|
||||||
- "traefik.http.services.service02.loadbalancer.sticky.cookie.secure=true"
|
- "traefik.http.services.service02.loadbalancer.sticky.cookie.secure=true"
|
||||||
- "traefik.http.services.service02.loadbalancer.server.port=foobar"
|
- "traefik.http.services.service02.loadbalancer.server.port=foobar"
|
||||||
- "traefik.http.services.service02.loadbalancer.server.scheme=foobar"
|
- "traefik.http.services.service02.loadbalancer.server.scheme=foobar"
|
||||||
|
- "traefik.http.services.service02.loadbalancer.server.weight=42"
|
||||||
- "traefik.tcp.middlewares.tcpmiddleware01.ipallowlist.sourcerange=foobar, foobar"
|
- "traefik.tcp.middlewares.tcpmiddleware01.ipallowlist.sourcerange=foobar, foobar"
|
||||||
- "traefik.tcp.middlewares.tcpmiddleware02.ipwhitelist.sourcerange=foobar, foobar"
|
- "traefik.tcp.middlewares.tcpmiddleware02.ipwhitelist.sourcerange=foobar, foobar"
|
||||||
- "traefik.tcp.middlewares.tcpmiddleware03.inflightconn.amount=42"
|
- "traefik.tcp.middlewares.tcpmiddleware03.inflightconn.amount=42"
|
||||||
|
|
|
@ -58,9 +58,11 @@
|
||||||
|
|
||||||
[[http.services.Service02.loadBalancer.servers]]
|
[[http.services.Service02.loadBalancer.servers]]
|
||||||
url = "foobar"
|
url = "foobar"
|
||||||
|
weight = 42
|
||||||
|
|
||||||
[[http.services.Service02.loadBalancer.servers]]
|
[[http.services.Service02.loadBalancer.servers]]
|
||||||
url = "foobar"
|
url = "foobar"
|
||||||
|
weight = 42
|
||||||
[http.services.Service02.loadBalancer.healthCheck]
|
[http.services.Service02.loadBalancer.healthCheck]
|
||||||
scheme = "foobar"
|
scheme = "foobar"
|
||||||
mode = "foobar"
|
mode = "foobar"
|
||||||
|
|
|
@ -65,7 +65,9 @@ http:
|
||||||
maxAge: 42
|
maxAge: 42
|
||||||
servers:
|
servers:
|
||||||
- url: foobar
|
- url: foobar
|
||||||
|
weight: 42
|
||||||
- url: foobar
|
- url: foobar
|
||||||
|
weight: 42
|
||||||
healthCheck:
|
healthCheck:
|
||||||
scheme: foobar
|
scheme: foobar
|
||||||
mode: foobar
|
mode: foobar
|
||||||
|
|
|
@ -240,7 +240,9 @@ THIS FILE MUST NOT BE EDITED BY HAND
|
||||||
| `traefik/http/services/Service02/loadBalancer/passHostHeader` | `true` |
|
| `traefik/http/services/Service02/loadBalancer/passHostHeader` | `true` |
|
||||||
| `traefik/http/services/Service02/loadBalancer/responseForwarding/flushInterval` | `42s` |
|
| `traefik/http/services/Service02/loadBalancer/responseForwarding/flushInterval` | `42s` |
|
||||||
| `traefik/http/services/Service02/loadBalancer/servers/0/url` | `foobar` |
|
| `traefik/http/services/Service02/loadBalancer/servers/0/url` | `foobar` |
|
||||||
|
| `traefik/http/services/Service02/loadBalancer/servers/0/weight` | `42` |
|
||||||
| `traefik/http/services/Service02/loadBalancer/servers/1/url` | `foobar` |
|
| `traefik/http/services/Service02/loadBalancer/servers/1/url` | `foobar` |
|
||||||
|
| `traefik/http/services/Service02/loadBalancer/servers/1/weight` | `42` |
|
||||||
| `traefik/http/services/Service02/loadBalancer/serversTransport` | `foobar` |
|
| `traefik/http/services/Service02/loadBalancer/serversTransport` | `foobar` |
|
||||||
| `traefik/http/services/Service02/loadBalancer/sticky/cookie/httpOnly` | `true` |
|
| `traefik/http/services/Service02/loadBalancer/sticky/cookie/httpOnly` | `true` |
|
||||||
| `traefik/http/services/Service02/loadBalancer/sticky/cookie/maxAge` | `42` |
|
| `traefik/http/services/Service02/loadBalancer/sticky/cookie/maxAge` | `42` |
|
||||||
|
|
|
@ -143,6 +143,36 @@ The `url` option point to a specific instance.
|
||||||
url = "http://private-ip-server-1/"
|
url = "http://private-ip-server-1/"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
The `weight` option allows for weighted load balancing on the servers.
|
||||||
|
|
||||||
|
??? example "A Service with Two Servers with Weight -- Using the [File Provider](../../providers/file.md)"
|
||||||
|
|
||||||
|
```yaml tab="YAML"
|
||||||
|
## Dynamic configuration
|
||||||
|
http:
|
||||||
|
services:
|
||||||
|
my-service:
|
||||||
|
loadBalancer:
|
||||||
|
servers:
|
||||||
|
- url: "http://private-ip-server-1/"
|
||||||
|
weight: 2
|
||||||
|
- url: "http://private-ip-server-2/"
|
||||||
|
weight: 1
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
```toml tab="TOML"
|
||||||
|
## Dynamic configuration
|
||||||
|
[http.services]
|
||||||
|
[http.services.my-service.loadBalancer]
|
||||||
|
[[http.services.my-service.loadBalancer.servers]]
|
||||||
|
url = "http://private-ip-server-1/"
|
||||||
|
weight = 2
|
||||||
|
[[http.services.my-service.loadBalancer.servers]]
|
||||||
|
url = "http://private-ip-server-2/"
|
||||||
|
weight = 1
|
||||||
|
```
|
||||||
|
|
||||||
#### Load-balancing
|
#### Load-balancing
|
||||||
|
|
||||||
For now, only round robin load balancing is supported:
|
For now, only round robin load balancing is supported:
|
||||||
|
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -55,6 +56,56 @@ func (s *DockerSuite) TestSimpleConfiguration() {
|
||||||
require.NoError(s.T(), err)
|
require.NoError(s.T(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *DockerSuite) TestWRRServer() {
|
||||||
|
tempObjects := struct {
|
||||||
|
DockerHost string
|
||||||
|
DefaultRule string
|
||||||
|
}{
|
||||||
|
DockerHost: s.getDockerHost(),
|
||||||
|
DefaultRule: "Host(`{{ normalize .Name }}.docker.localhost`)",
|
||||||
|
}
|
||||||
|
|
||||||
|
file := s.adaptFile("fixtures/docker/simple.toml", tempObjects)
|
||||||
|
|
||||||
|
s.composeUp()
|
||||||
|
|
||||||
|
s.traefikCmd(withConfigFile(file))
|
||||||
|
|
||||||
|
whoami1IP := s.getComposeServiceIP("wrr-server")
|
||||||
|
whoami2IP := s.getComposeServiceIP("wrr-server2")
|
||||||
|
|
||||||
|
// Expected a 404 as we did not configure anything
|
||||||
|
err := try.GetRequest("http://127.0.0.1:8000/", 500*time.Millisecond, try.StatusCodeIs(http.StatusNotFound))
|
||||||
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
|
err = try.GetRequest("http://127.0.0.1:8080/api/http/services", 1000*time.Millisecond, try.BodyContains("wrr-server"))
|
||||||
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
|
repartition := map[string]int{}
|
||||||
|
for i := 0; i < 4; i++ {
|
||||||
|
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/whoami", nil)
|
||||||
|
req.Host = "my.wrr.host"
|
||||||
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
|
response, err := http.DefaultClient.Do(req)
|
||||||
|
require.NoError(s.T(), err)
|
||||||
|
assert.Equal(s.T(), http.StatusOK, response.StatusCode)
|
||||||
|
|
||||||
|
body, err := io.ReadAll(response.Body)
|
||||||
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
|
if strings.Contains(string(body), whoami1IP) {
|
||||||
|
repartition[whoami1IP]++
|
||||||
|
}
|
||||||
|
if strings.Contains(string(body), whoami2IP) {
|
||||||
|
repartition[whoami2IP]++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(s.T(), 3, repartition[whoami1IP])
|
||||||
|
assert.Equal(s.T(), 1, repartition[whoami2IP])
|
||||||
|
}
|
||||||
|
|
||||||
func (s *DockerSuite) TestDefaultDockerContainers() {
|
func (s *DockerSuite) TestDefaultDockerContainers() {
|
||||||
tempObjects := struct {
|
tempObjects := struct {
|
||||||
DockerHost string
|
DockerHost string
|
||||||
|
|
35
integration/fixtures/wrr_server.toml
Normal file
35
integration/fixtures/wrr_server.toml
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
[global]
|
||||||
|
checkNewVersion = false
|
||||||
|
sendAnonymousUsage = false
|
||||||
|
|
||||||
|
[api]
|
||||||
|
insecure = true
|
||||||
|
|
||||||
|
[log]
|
||||||
|
level = "DEBUG"
|
||||||
|
noColor = true
|
||||||
|
|
||||||
|
[entryPoints]
|
||||||
|
|
||||||
|
[entryPoints.web]
|
||||||
|
address = ":8000"
|
||||||
|
|
||||||
|
[providers.file]
|
||||||
|
filename = "{{ .SelfFilename }}"
|
||||||
|
|
||||||
|
## dynamic configuration ##
|
||||||
|
|
||||||
|
[http.routers]
|
||||||
|
[http.routers.router]
|
||||||
|
service = "service1"
|
||||||
|
rule = "Path(`/whoami`)"
|
||||||
|
|
||||||
|
[http.services]
|
||||||
|
|
||||||
|
[http.services.service1.loadBalancer]
|
||||||
|
[[http.services.service1.loadBalancer.servers]]
|
||||||
|
url = "{{ .Server1 }}"
|
||||||
|
weight = 3
|
||||||
|
[[http.services.service1.loadBalancer.servers]]
|
||||||
|
url = "{{ .Server2 }}"
|
||||||
|
|
|
@ -36,3 +36,14 @@ services:
|
||||||
labels:
|
labels:
|
||||||
traefik.http.Routers.Super.Rule: Host(`my.super.host`)
|
traefik.http.Routers.Super.Rule: Host(`my.super.host`)
|
||||||
traefik.http.Services.powpow.LoadBalancer.server.Port: 2375
|
traefik.http.Services.powpow.LoadBalancer.server.Port: 2375
|
||||||
|
|
||||||
|
wrr-server:
|
||||||
|
image: traefik/whoami
|
||||||
|
labels:
|
||||||
|
traefik.http.Routers.wrr-server.Rule: Host(`my.wrr.host`)
|
||||||
|
traefik.http.Services.wrr-server.LoadBalancer.server.Weight: 4
|
||||||
|
wrr-server2:
|
||||||
|
image: traefik/whoami
|
||||||
|
labels:
|
||||||
|
traefik.http.Routers.wrr-server.Rule: Host(`my.wrr.host`)
|
||||||
|
traefik.http.Services.wrr-server.LoadBalancer.server.Weight: 1
|
||||||
|
|
|
@ -809,6 +809,49 @@ func (s *SimpleSuite) TestUDPServiceConfigErrors() {
|
||||||
require.NoError(s.T(), err)
|
require.NoError(s.T(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *SimpleSuite) TestWRRServer() {
|
||||||
|
s.createComposeProject("base")
|
||||||
|
|
||||||
|
s.composeUp()
|
||||||
|
defer s.composeDown()
|
||||||
|
|
||||||
|
whoami1IP := s.getComposeServiceIP("whoami1")
|
||||||
|
whoami2IP := s.getComposeServiceIP("whoami2")
|
||||||
|
|
||||||
|
file := s.adaptFile("fixtures/wrr_server.toml", struct {
|
||||||
|
Server1 string
|
||||||
|
Server2 string
|
||||||
|
}{Server1: "http://" + whoami1IP, Server2: "http://" + whoami2IP})
|
||||||
|
|
||||||
|
s.traefikCmd(withConfigFile(file))
|
||||||
|
|
||||||
|
err := try.GetRequest("http://127.0.0.1:8080/api/http/services", 1000*time.Millisecond, try.BodyContains("service1"))
|
||||||
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
|
repartition := map[string]int{}
|
||||||
|
for i := 0; i < 4; i++ {
|
||||||
|
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/whoami", nil)
|
||||||
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
|
response, err := http.DefaultClient.Do(req)
|
||||||
|
require.NoError(s.T(), err)
|
||||||
|
assert.Equal(s.T(), http.StatusOK, response.StatusCode)
|
||||||
|
|
||||||
|
body, err := io.ReadAll(response.Body)
|
||||||
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
|
if strings.Contains(string(body), whoami1IP) {
|
||||||
|
repartition[whoami1IP]++
|
||||||
|
}
|
||||||
|
if strings.Contains(string(body), whoami2IP) {
|
||||||
|
repartition[whoami2IP]++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(s.T(), 3, repartition[whoami1IP])
|
||||||
|
assert.Equal(s.T(), 1, repartition[whoami2IP])
|
||||||
|
}
|
||||||
|
|
||||||
func (s *SimpleSuite) TestWRR() {
|
func (s *SimpleSuite) TestWRR() {
|
||||||
s.createComposeProject("base")
|
s.createComposeProject("base")
|
||||||
|
|
||||||
|
|
|
@ -227,6 +227,7 @@ func (r *ResponseForwarding) SetDefaults() {
|
||||||
// Server holds the server configuration.
|
// Server holds the server configuration.
|
||||||
type Server struct {
|
type Server struct {
|
||||||
URL string `json:"url,omitempty" toml:"url,omitempty" yaml:"url,omitempty" label:"-"`
|
URL string `json:"url,omitempty" toml:"url,omitempty" yaml:"url,omitempty" label:"-"`
|
||||||
|
Weight *int `json:"weight,omitempty" toml:"weight,omitempty" yaml:"weight,omitempty" label:"weight"`
|
||||||
Scheme string `json:"-" toml:"-" yaml:"-" file:"-"`
|
Scheme string `json:"-" toml:"-" yaml:"-" file:"-"`
|
||||||
Port string `json:"-" toml:"-" yaml:"-" file:"-"`
|
Port string `json:"-" toml:"-" yaml:"-" file:"-"`
|
||||||
}
|
}
|
||||||
|
|
|
@ -1128,6 +1128,11 @@ func (in *RouterTLSConfig) DeepCopy() *RouterTLSConfig {
|
||||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
func (in *Server) DeepCopyInto(out *Server) {
|
func (in *Server) DeepCopyInto(out *Server) {
|
||||||
*out = *in
|
*out = *in
|
||||||
|
if in.Weight != nil {
|
||||||
|
in, out := &in.Weight, &out.Weight
|
||||||
|
*out = new(int)
|
||||||
|
**out = **in
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1180,7 +1185,9 @@ func (in *ServersLoadBalancer) DeepCopyInto(out *ServersLoadBalancer) {
|
||||||
if in.Servers != nil {
|
if in.Servers != nil {
|
||||||
in, out := &in.Servers, &out.Servers
|
in, out := &in.Servers, &out.Servers
|
||||||
*out = make([]Server, len(*in))
|
*out = make([]Server, len(*in))
|
||||||
copy(*out, *in)
|
for i := range *in {
|
||||||
|
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if in.HealthCheck != nil {
|
if in.HealthCheck != nil {
|
||||||
in, out := &in.HealthCheck, &out.HealthCheck
|
in, out := &in.HealthCheck, &out.HealthCheck
|
||||||
|
|
|
@ -319,7 +319,7 @@ func (m *Manager) getLoadBalancerServiceHandler(ctx context.Context, serviceName
|
||||||
|
|
||||||
proxy = tracingMiddle.NewService(ctx, serviceName, proxy)
|
proxy = tracingMiddle.NewService(ctx, serviceName, proxy)
|
||||||
|
|
||||||
lb.Add(proxyName, proxy, nil)
|
lb.Add(proxyName, proxy, server.Weight)
|
||||||
|
|
||||||
// servers are considered UP by default.
|
// servers are considered UP by default.
|
||||||
info.UpdateServerStatus(target.String(), runtime.StatusUp)
|
info.UpdateServerStatus(target.String(), runtime.StatusUp)
|
||||||
|
|
Loading…
Reference in a new issue