Adds support for configuring stickness per Ingress

This commit is contained in:
Ricardo Pchevuzinske Katz 2017-02-10 12:24:16 -02:00
parent 79e186cb77
commit 6809319318
7 changed files with 112 additions and 15 deletions

View file

@ -51,6 +51,9 @@ The following annotations are supported:
|[ingress.kubernetes.io/upstream-max-fails](#custom-nginx-upstream-checks)|number|
|[ingress.kubernetes.io/upstream-fail-timeout](#custom-nginx-upstream-checks)|number|
|[ingress.kubernetes.io/whitelist-source-range](#whitelist-source-range)|CIDR|
|[ingress.kubernetes.io/sticky-enabled](#sticky-session)|true or false|
|[ingress.kubernetes.io/sticky-name](#sticky-session)|string|
|[ingress.kubernetes.io/sticky-hash](#sticky-session)|string|
@ -177,6 +180,20 @@ To configure this setting globally for all Ingress rules, the `whitelist-source-
Please check the [whitelist](examples/whitelist/README.md) example.
### Sticky Session
The annotation `ingress.kubernetes.io/sticky-enabled` enables stickness in all Upstreams of an Ingress. This way, a request will always be directed to the same upstream server.
You can also specify the name of the cookie that will be used to route the requests with the annotation `ingress.kubernetes.io/sticky-name`. The default is to create a cookie named 'route'.
The annotation `ingress.kubernetes.io/sticky-hash` defines which algorithm will be used to 'hash' the used upstream. Default value is `md5` and possible values are `md5`, `sha1` and `index`.
This feature is implemented by the third party module *nginx-sticky-module-ng* (https://bitbucket.org/nginx-goodies/nginx-sticky-module-ng).
The workflow used to define which upstream server will be used is explained here: https://bitbucket.org/nginx-goodies/nginx-sticky-module-ng/raw/08a395c66e425540982c00482f55034e1fee67b6/docs/sticky.pdf
### **Allowed parameters in configuration ConfigMap**
**proxy-body-size:** Sets the maximum allowed size of the client request body. See NGINX [client_max_body_size](http://nginx.org/en/docs/http/ngx_http_core_module.html#client_max_body_size).

View file

@ -186,7 +186,7 @@ http {
{{range $name, $upstream := $backends}}
upstream {{$upstream.Name}} {
{{ if $upstream.StickySession.Enabled }}
sticky hash={{$upstream.StickySession.Hash}} route={{$upstream.StickySession.Hash}} httponly;
sticky hash={{$upstream.StickySession.Hash}} name={{$upstream.StickySession.Name}} httponly;
{{ else }}
least_conn;
{{ end }}

View file

@ -19,10 +19,11 @@ package stickysession
import (
"regexp"
"github.com/golang/glog"
"k8s.io/kubernetes/pkg/apis/extensions"
"k8s.io/ingress/core/pkg/ingress/annotations/parser"
ing_errors "k8s.io/ingress/core/pkg/ingress/errors"
)
const (
@ -59,28 +60,28 @@ func NewParser() parser.IngressAnnotation {
// rule used to configure the sticky directives
func (a sticky) Parse(ing *extensions.Ingress) (interface{}, error) {
// Check if the sticky is enabled
se, _ := parser.GetBoolAnnotation(stickyEnabled, ing)
se, err := parser.GetBoolAnnotation(stickyEnabled, ing)
if err != nil {
se = false
}
glog.V(3).Infof("Ingress %v: Setting stickness to %v", ing.Name, se)
// Get the Sticky Cookie Name
sn, _ := parser.GetStringAnnotation(stickyName, ing)
sn, err := parser.GetStringAnnotation(stickyName, ing)
if sn == "" {
if err != nil || sn == "" {
glog.V(3).Infof("Ingress %v: No value found in annotation %v. Using the default %v", ing.Name, stickyName, defaultStickyName)
sn = defaultStickyName
}
sh, _ := parser.GetStringAnnotation(stickyHash, ing)
sh, err := parser.GetStringAnnotation(stickyHash, ing)
if sh == "" {
if err != nil || !stickyHashRegex.MatchString(sh) {
glog.V(3).Infof("Invalid or no annotation value found in Ingress %v: %v: %v. Setting it to default %v", ing.Name, stickyHash, sh, defaultStickyHash)
sh = defaultStickyHash
}
if !stickyHashRegex.MatchString(sh) {
return &StickyConfig{
Name: "",
Enabled: false,
Hash: "",
}, ing_errors.NewInvalidAnnotationContent(stickyHash, sh)
return &StickyConfig{
Name: sn,
Enabled: se,

View file

@ -196,6 +196,8 @@ func TestStickySession(t *testing.T) {
{map[string]string{annotationStickyEnabled: "true", annotationStickyHash: "md5", annotationStickyName: "route"}, true, "md5", "route"},
{map[string]string{annotationStickyEnabled: "true", annotationStickyHash: "", annotationStickyName: "xpto"}, true, "md5", "xpto"},
{map[string]string{annotationStickyEnabled: "true", annotationStickyHash: "", annotationStickyName: ""}, true, "md5", "route"},
{map[string]string{}, false, "md5", "route"},
{nil, false, "md5", "route"},
}
for _, foo := range fooAnns {

View file

@ -136,7 +136,7 @@ type Backend struct {
// Endpoints contains the list of endpoints currently running
Endpoints []Endpoint `json:"endpoints"`
// StickySession contains the StickyConfig object with stickness configuration
StickySession *stickysession.StickyConfig `json:"stickysession"`
StickySession stickysession.StickyConfig `json:"stickysession,omitempty"`
}
// Endpoint describes a kubernetes endpoint in an backend

View file

@ -0,0 +1,58 @@
# Sticky Session
This example demonstrates how to Stickness in a Ingress.
## Prerequisites
You will need to make sure you Ingress targets exactly one Ingress
controller by specifying the [ingress.class annotation](/examples/PREREQUISITES.md#ingress-class),
and that you have an ingress controller [running](/examples/deployment) in your cluster.
Also, you need to have a deployment with replica > 1. Using a deployment with only one replica doesn't set the 'sticky' cookie.
## Deployment
The following command instructs the controller to set Stickness in all Upstreams of an Ingress
```console
$ kubectl create -f sticky-ingress.yaml
```
## Validation
You can confirm that the Ingress works.
```console
$ kubectl describe ing nginx-test
Name: nginx-test
Namespace: default
Address:
Default backend: default-http-backend:80 (10.180.0.4:8080,10.240.0.2:8080)
Rules:
Host Path Backends
---- ---- --------
stickyingress.example.com
/ nginx-service:80 (<none>)
Annotations:
sticky-enabled: true
sticky-hash: sha1
sticky-name: route
Events:
FirstSeen LastSeen Count From SubObjectPath Type Reason Message
--------- -------- ----- ---- ------------- -------- ------ -------
7s 7s 1 {nginx-ingress-controller } Normal CREATE default/nginx-test
$ curl -I http://stickyingress.example.com
HTTP/1.1 200 OK
Server: nginx/1.11.9
Date: Fri, 10 Feb 2017 14:11:12 GMT
Content-Type: text/html
Content-Length: 612
Connection: keep-alive
Set-Cookie: route=a9907b79b248140b56bb13723f72b67697baac3d; Path=/; HttpOnly
Last-Modified: Tue, 24 Jan 2017 14:02:19 GMT
ETag: "58875e6b-264"
Accept-Ranges: bytes
```
In the example avove, you can see a line containing the 'Set-Cookie: route' setting the right defined stickness cookie.

View file

@ -0,0 +1,19 @@
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: nginx-test
annotations:
kubernetes.io/ingress.class: "nginx"
ingress.kubernetes.io/sticky-enabled: "true"
ingress.kubernetes.io/sticky-name: "route"
ingress.kubernetes.io/sticky-hash: "sha1"
spec:
rules:
- host: stickyingress.example.com
http:
paths:
- backend:
serviceName: nginx-service
servicePort: 80
path: /