Adds weight on ServersLoadBalancer

This commit is contained in:
Julien Salleyron 2024-01-26 01:44:05 +01:00 committed by GitHub
parent f4f3dbe1f5
commit 3174c69c66
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 187 additions and 2 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View 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 }}"

View file

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

View file

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

View file

@ -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:"-"`
} }

View 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

View file

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