docs: clarify multi-levels stickiness
This commit is contained in:
parent
60de577a5f
commit
6e92c20edb
4 changed files with 231 additions and 32 deletions
|
@ -646,7 +646,6 @@ referencing services in the [`IngressRoute`](#kind-ingressroute) objects, or rec
|
||||||
* services [Weighted Round Robin](#weighted-round-robin) load balancing.
|
* services [Weighted Round Robin](#weighted-round-robin) load balancing.
|
||||||
* services [mirroring](#mirroring).
|
* services [mirroring](#mirroring).
|
||||||
|
|
||||||
|
|
||||||
#### Server Load Balancing
|
#### Server Load Balancing
|
||||||
|
|
||||||
More information in the dedicated server [load balancing](../services/index.md#load-balancing) section.
|
More information in the dedicated server [load balancing](../services/index.md#load-balancing) section.
|
||||||
|
@ -916,6 +915,154 @@ More information in the dedicated [mirroring](../services/index.md#mirroring-ser
|
||||||
|
|
||||||
Specifying a namespace attribute in this case would not make any sense, and will be ignored (except if the provider is `kubernetescrd`).
|
Specifying a namespace attribute in this case would not make any sense, and will be ignored (except if the provider is `kubernetescrd`).
|
||||||
|
|
||||||
|
#### Stickiness and load-balancing
|
||||||
|
|
||||||
|
As explained in the section about [Sticky sessions](../../services/#sticky-sessions), for stickiness to work all the way,
|
||||||
|
it must be specified at each load-balancing level.
|
||||||
|
|
||||||
|
For instance, in the example below, there is a first level of load-balancing because there is a (Weighted Round Robin) load-balancing of the two `whoami` services,
|
||||||
|
and there is a second level because each whoami service is a `replicaset` and is thus handled as a load-balancer of servers.
|
||||||
|
|
||||||
|
??? "Stickiness on two load-balancing levels"
|
||||||
|
|
||||||
|
```yaml tab="IngressRoute"
|
||||||
|
apiVersion: traefik.containo.us/v1alpha1
|
||||||
|
kind: IngressRoute
|
||||||
|
metadata:
|
||||||
|
name: ingressroutebar
|
||||||
|
namespace: default
|
||||||
|
|
||||||
|
spec:
|
||||||
|
entryPoints:
|
||||||
|
- web
|
||||||
|
routes:
|
||||||
|
- match: Host(`example.com`) && PathPrefix(`/foo`)
|
||||||
|
kind: Rule
|
||||||
|
services:
|
||||||
|
- name: wrr1
|
||||||
|
namespace: default
|
||||||
|
kind: TraefikService
|
||||||
|
```
|
||||||
|
|
||||||
|
```yaml tab="Weighted Round Robin"
|
||||||
|
apiVersion: traefik.containo.us/v1alpha1
|
||||||
|
kind: TraefikService
|
||||||
|
metadata:
|
||||||
|
name: wrr1
|
||||||
|
namespace: default
|
||||||
|
|
||||||
|
spec:
|
||||||
|
weighted:
|
||||||
|
services:
|
||||||
|
- name: whoami1
|
||||||
|
kind: Service
|
||||||
|
port: 80
|
||||||
|
weight: 1
|
||||||
|
sticky:
|
||||||
|
cookie:
|
||||||
|
name: lvl2
|
||||||
|
- name: whoami2
|
||||||
|
kind: Service
|
||||||
|
weight: 1
|
||||||
|
port: 80
|
||||||
|
sticky:
|
||||||
|
cookie:
|
||||||
|
name: lvl2
|
||||||
|
sticky:
|
||||||
|
cookie:
|
||||||
|
name: lvl1
|
||||||
|
```
|
||||||
|
|
||||||
|
```yaml tab="K8s Service"
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: whoami1
|
||||||
|
|
||||||
|
spec:
|
||||||
|
ports:
|
||||||
|
- protocol: TCP
|
||||||
|
name: web
|
||||||
|
port: 80
|
||||||
|
selector:
|
||||||
|
app: whoami1
|
||||||
|
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: whoami2
|
||||||
|
|
||||||
|
spec:
|
||||||
|
ports:
|
||||||
|
- protocol: TCP
|
||||||
|
name: web
|
||||||
|
port: 80
|
||||||
|
selector:
|
||||||
|
app: whoami2
|
||||||
|
```
|
||||||
|
|
||||||
|
```yaml tab="Deployment (to illustrate replicas)"
|
||||||
|
kind: Deployment
|
||||||
|
apiVersion: apps/v1
|
||||||
|
metadata:
|
||||||
|
namespace: default
|
||||||
|
name: whoami1
|
||||||
|
labels:
|
||||||
|
app: whoami1
|
||||||
|
|
||||||
|
spec:
|
||||||
|
replicas: 2
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: whoami1
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: whoami1
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: whoami1
|
||||||
|
image: containous/whoami
|
||||||
|
ports:
|
||||||
|
- name: web
|
||||||
|
containerPort: 80
|
||||||
|
|
||||||
|
---
|
||||||
|
kind: Deployment
|
||||||
|
apiVersion: apps/v1
|
||||||
|
metadata:
|
||||||
|
namespace: default
|
||||||
|
name: whoami2
|
||||||
|
labels:
|
||||||
|
app: whoami2
|
||||||
|
|
||||||
|
spec:
|
||||||
|
replicas: 2
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: whoami2
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: whoami2
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: whoami2
|
||||||
|
image: containous/whoami
|
||||||
|
ports:
|
||||||
|
- name: web
|
||||||
|
containerPort: 80
|
||||||
|
```
|
||||||
|
|
||||||
|
To keep a session open with the same server, the client would then need to specify the two levels within the cookie for each request, e.g. with curl:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -H Host:example.com -b "lvl1=default-whoami1-80; lvl2=http://10.42.0.6:80" http://localhost:8000/foo
|
||||||
|
```
|
||||||
|
|
||||||
|
assuming `10.42.0.6` is the IP address of one of the replicas (a pod then) of the `whoami1` service.
|
||||||
|
|
||||||
### Kind `IngressRouteTCP`
|
### Kind `IngressRouteTCP`
|
||||||
|
|
||||||
`IngressRouteTCP` is the CRD implementation of a [Traefik TCP router](../routers/index.md#configuring-tcp-routers).
|
`IngressRouteTCP` is the CRD implementation of a [Traefik TCP router](../routers/index.md#configuring-tcp-routers).
|
||||||
|
@ -1386,6 +1533,7 @@ or referencing TLS stores in the [`IngressRoute`](#kind-ingressroute) / [`Ingres
|
||||||
tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0=
|
tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0=
|
||||||
tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCi0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0=
|
tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCi0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0=
|
||||||
```
|
```
|
||||||
|
|
||||||
## Further
|
## Further
|
||||||
|
|
||||||
Also see the [full example](../../user-guides/crd-acme/index.md) with Let's Encrypt.
|
Also see the [full example](../../user-guides/crd-acme/index.md) with Let's Encrypt.
|
||||||
|
|
|
@ -167,8 +167,12 @@ For now, only round robin load balancing is supported:
|
||||||
|
|
||||||
#### Sticky sessions
|
#### Sticky sessions
|
||||||
|
|
||||||
When sticky sessions are enabled, a cookie is set on the initial request to track which server handles the first response.
|
When sticky sessions are enabled, a cookie is set on the initial request and response to let the client know which server handles the first response.
|
||||||
On subsequent requests, the client is forwarded to the same server.
|
On subsequent requests, to keep the session alive with the same server, the client should resend the same cookie.
|
||||||
|
|
||||||
|
!!! info "Stickiness on multiple levels"
|
||||||
|
|
||||||
|
When chaining or mixing load-balancers (e.g. a load-balancer of servers is one of the "children" of a load-balancer of services), for stickiness to work all the way, the option needs to be specified at all required levels. Which means the client needs to send a cookie with as many key/value pairs as there are sticky levels.
|
||||||
|
|
||||||
!!! info "Stickiness & Unhealthy Servers"
|
!!! info "Stickiness & Unhealthy Servers"
|
||||||
|
|
||||||
|
@ -226,6 +230,80 @@ On subsequent requests, the client is forwarded to the same server.
|
||||||
httpOnly: true
|
httpOnly: true
|
||||||
```
|
```
|
||||||
|
|
||||||
|
??? example "Setting Stickiness on all the required levels -- Using the [File Provider](../../providers/file.md)"
|
||||||
|
|
||||||
|
```toml tab="TOML"
|
||||||
|
## Dynamic configuration
|
||||||
|
[http.services]
|
||||||
|
[http.services.wrr1]
|
||||||
|
[http.services.wrr1.weighted.sticky.cookie]
|
||||||
|
name = "lvl1"
|
||||||
|
[[http.services.wrr1.weighted.services]]
|
||||||
|
name = "whoami1"
|
||||||
|
weight = 1
|
||||||
|
[[http.services.wrr1.weighted.services]]
|
||||||
|
name = "whoami2"
|
||||||
|
weight = 1
|
||||||
|
|
||||||
|
[http.services.whoami1]
|
||||||
|
[http.services.whoami1.loadBalancer]
|
||||||
|
[http.services.whoami1.loadBalancer.sticky.cookie]
|
||||||
|
name = "lvl2"
|
||||||
|
[[http.services.whoami1.loadBalancer.servers]]
|
||||||
|
url = "http://127.0.0.1:8081"
|
||||||
|
[[http.services.whoami1.loadBalancer.servers]]
|
||||||
|
url = "http://127.0.0.1:8082"
|
||||||
|
|
||||||
|
[http.services.whoami2]
|
||||||
|
[http.services.whoami2.loadBalancer]
|
||||||
|
[http.services.whoami2.loadBalancer.sticky.cookie]
|
||||||
|
name = "lvl2"
|
||||||
|
[[http.services.whoami2.loadBalancer.servers]]
|
||||||
|
url = "http://127.0.0.1:8083"
|
||||||
|
[[http.services.whoami2.loadBalancer.servers]]
|
||||||
|
url = "http://127.0.0.1:8084"
|
||||||
|
```
|
||||||
|
|
||||||
|
```yaml tab="YAML"
|
||||||
|
## Dynamic configuration
|
||||||
|
http:
|
||||||
|
services:
|
||||||
|
wrr1:
|
||||||
|
weighted:
|
||||||
|
sticky:
|
||||||
|
cookie:
|
||||||
|
name: lvl1
|
||||||
|
services:
|
||||||
|
- name: whoami1
|
||||||
|
weight: 1
|
||||||
|
- name: whoami2
|
||||||
|
weight: 1
|
||||||
|
|
||||||
|
whoami1:
|
||||||
|
loadBalancer:
|
||||||
|
sticky:
|
||||||
|
cookie:
|
||||||
|
name: lvl2
|
||||||
|
servers:
|
||||||
|
- url: http://127.0.0.1:8081
|
||||||
|
- url: http://127.0.0.1:8082
|
||||||
|
|
||||||
|
whoami2:
|
||||||
|
loadBalancer:
|
||||||
|
sticky:
|
||||||
|
cookie:
|
||||||
|
name: lvl2
|
||||||
|
servers:
|
||||||
|
- url: http://127.0.0.1:8083
|
||||||
|
- url: http://127.0.0.1:8084
|
||||||
|
```
|
||||||
|
|
||||||
|
To keep a session open with the same server, the client would then need to specify the two levels within the cookie for each request, e.g. with curl:
|
||||||
|
|
||||||
|
```
|
||||||
|
curl -b "lvl1=whoami1; lvl2=http://127.0.0.1:8081" http://localhost:8000
|
||||||
|
```
|
||||||
|
|
||||||
#### Health Check
|
#### Health Check
|
||||||
|
|
||||||
Configure health check to remove unhealthy servers from the load balancing rotation.
|
Configure health check to remove unhealthy servers from the load balancing rotation.
|
||||||
|
|
|
@ -101,7 +101,7 @@ curl [-k] https://your.example.com/tls
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
curl [-k] http://your.example.com:8000/notls
|
curl http://your.example.com:8000/notls
|
||||||
```
|
```
|
||||||
|
|
||||||
Note that you'll have to use `-k` as long as you're using the staging server of Let's Encrypt, since it is not an authorized certificate authority on systems where it hasn't been manually added.
|
Note that you'll have to use `-k` as long as you're using the staging server of Let's Encrypt, since it is not an authorized certificate authority on systems where it hasn't been manually added.
|
||||||
|
|
|
@ -1,9 +1,6 @@
|
||||||
package v1alpha1
|
package v1alpha1
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/containous/traefik/v2/pkg/config/dynamic"
|
"github.com/containous/traefik/v2/pkg/config/dynamic"
|
||||||
"github.com/containous/traefik/v2/pkg/types"
|
"github.com/containous/traefik/v2/pkg/types"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
@ -82,30 +79,6 @@ type LoadBalancerSpec struct {
|
||||||
Weight *int `json:"weight,omitempty"`
|
Weight *int `json:"weight,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsServersLB reports whether lb is a load-balancer of servers
|
|
||||||
// (as opposed to a traefik load-balancer of services).
|
|
||||||
func (lb LoadBalancerSpec) IsServersLB() (bool, error) {
|
|
||||||
if lb.Name == "" {
|
|
||||||
return false, errors.New("missing Name field in service")
|
|
||||||
}
|
|
||||||
if lb.Kind == "" || lb.Kind == "Service" {
|
|
||||||
return true, nil
|
|
||||||
}
|
|
||||||
if lb.Kind != "TraefikService" {
|
|
||||||
return false, fmt.Errorf("invalid kind value: %v", lb.Kind)
|
|
||||||
}
|
|
||||||
if lb.Port != 0 ||
|
|
||||||
lb.Scheme != "" ||
|
|
||||||
lb.HealthCheck != nil ||
|
|
||||||
lb.Strategy != "" ||
|
|
||||||
lb.PassHostHeader != nil ||
|
|
||||||
lb.ResponseForwarding != nil ||
|
|
||||||
lb.Sticky != nil {
|
|
||||||
return false, fmt.Errorf("service of kind %v is incompatible with Kubernetes Service related fields", lb.Kind)
|
|
||||||
}
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Service defines an upstream to proxy traffic.
|
// Service defines an upstream to proxy traffic.
|
||||||
type Service struct {
|
type Service struct {
|
||||||
LoadBalancerSpec
|
LoadBalancerSpec
|
||||||
|
|
Loading…
Reference in a new issue