-
Notifications
You must be signed in to change notification settings - Fork 8.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Nginx sticky annotations #258
Nginx sticky annotations #258
Conversation
related #259 |
@@ -0,0 +1,58 @@ | |||
# Sticky Session |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Suggest a directory structure like
examples/
affinity/
cookie/
nginx/
haproxy/
client
nginx/
gce/
...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
✅
@@ -0,0 +1,58 @@ | |||
# Sticky Session | |||
|
|||
This example demonstrates how to Stickness in a Ingress. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Suggest: how to achieve session affinity using cookies
Though unlike haproxy we're not allowed to pick the cookie right? nginx just uses a hash of the request IIRC
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hum, actually this session affinity is through cookie. The sticky module captures the cookie, and routes the request based on this. I didn't deployed more than one NGINX to check if the hash that the cookie assigns keeps the same, between them, but I think it did, as the hash is based on the upstream name.
But I will change the description as suggested.
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. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Suggest: you will also need to deploy multiple replicas of your application that show up as endpoints for the Service referenced in the Ingress object, to test session stickyness.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
✅
Also, you need to have a deployment with replica > 1. Using a deployment with only one replica doesn't set the 'sticky' cookie. | ||
|
||
## Deployment | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Suggest something like:
Session stickyness is achieved through 3 annotations on the Ingress, as shown in the example.
Name | Description | Values |
---|---|---|
ingress.kubernetes.io/sticky-enabled annotation | enables stickyness | true/false |
ingress.kubernetes.io/sticky-name | (does what?) | route, (and what are the possible values) |
ingress.kubernetes.io/sticky-hash | does what? | sha1 (does what, and what are the possible values) |
You can create the ingress to test this
$ kubectl create -f
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
✅
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, I've changed this once again to reflect the suggested in https://github.com/kubernetes/ingress/pull/258/files/68093193184cc7d18d539dfa6876e2e98d445ec8#diff-56f736cfe94c996068145b331c2fd2c6
7s 7s 1 {nginx-ingress-controller } Normal CREATE default/nginx-test | ||
|
||
|
||
$ curl -I http://stickyingress.example.com |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do they need DNS name or will IPs suffice?
Suggest using only IPs if that'll do
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You have to use IP, as this is a per vhost configuration
) | ||
|
||
const ( | ||
stickyEnabled = "ingress.kubernetes.io/sticky-enabled" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we really need an enabled? Can we achieve this with a single annotation like: ingress.kubernetes.io/affinity=cookie
(defaults cookie and hash, unless they're specified, and if affinity is set to eg: client, then hash and cookie values are ignored)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@bprashanth You mean only one annotation, with ingress.kubernetes.io/affinity=cookie,hash=md5,name=route
?
|
||
const ( | ||
stickyEnabled = "ingress.kubernetes.io/sticky-enabled" | ||
stickyName = "ingress.kubernetes.io/sticky-name" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please rename to session-cookie-name and leave a comment like:
// If a cookie with this name exists, its value is used as an index into the list of available backends.
const ( | ||
stickyEnabled = "ingress.kubernetes.io/sticky-enabled" | ||
stickyName = "ingress.kubernetes.io/sticky-name" | ||
stickyHash = "ingress.kubernetes.io/sticky-hash" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please rename to session-cookie-hash-algorithm
// This is the algorithm used by nginx to generate a value for the session cookie, if
// one isn't supplied and affintiy is set to "cookie".
controllers/nginx/configuration.md
Outdated
@@ -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 | |||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please link to the example from here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
✅
Content-Type: text/html | ||
Content-Length: 612 | ||
Connection: keep-alive | ||
Set-Cookie: route=a9907b79b248140b56bb13723f72b67697baac3d; Path=/; HttpOnly |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please mention that if the cookies is supplied by a user, it is honored (right? but what if it's some random string or int?), but if it isn't supplied, nginx will generate one and the client is expected to remember this value and supply it on the next request to map to the same backend.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually this is the behaviour of any sticky session cookie (also when you use them in Apache with mod_proxy). If the user changes the cookie, it looses the affinity and a new one is generated.
@bprashanth Made all the reviews, but couldn't test yet. Will test tomorrow at my corporate environment. If you could, anyway take a look in the changes to see if I've missed something (but no merging yet) I would appreciate. Thanks :) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM sans nits
|
||
Session stickyness is achieved through 3 annotations on the Ingress, as shown in the [example](sticky-ingress.yaml). | ||
|
||
|Name|Description|Values| |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This isn't showing up as a table, I guess you need a little more formatting: https://help.github.com/articles/organizing-information-with-tables/
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, corrected it
// If a cookie with this name exists, | ||
// its value is used as an index into the list of available backends. | ||
annotationAffinityCookieName = "ingress.kubernetes.io/session-cookie-name" | ||
defaultAffinityCookieName = "route" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Better default name still makes sense, as suggested here #258 (comment)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the default name used by nginx sticky module. I don't think using SESSIONID or BACKEND a good idea, as this might also be a bit confusing with something related to the app.
But INGRESSCOOKIE is something we can use here.
Also, there is no guideline (I couldn't find) of best practices when setting a Custom HTTP Header. Amazon uses them all as Lower case. Google uses them all Upper case.
I will make this change here, just in case.
type AffinityConfig struct { | ||
// The type of affinity that will be used | ||
AffinityType string `json:"type"` | ||
CookieAffinityConfig CookieAffinityConfig `json:"cookieconfig"` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please change the name of the field to "Cookie" and make this a pointer. See the pattern here: https://github.com/kubernetes/kubernetes/blob/master/pkg/apis/extensions/types.go#L642
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Doesn't this colide with the requested here: #258 (comment)
Also, isn't a better naming convention using something like 'CookieConfig'? Maybe changing this to 'Cookie' only can cause the impression that he have here something that manages cookies at all, that could lead to some misunderstanding of the code, right?
// AffinityConfig describes the per ingress session affinity config | ||
type AffinityConfig struct { | ||
// The type of affinity that will be used | ||
AffinityType string `json:"type"` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm fine with including the type as a field since this is an internal api, but we don't really need a string indicating the type when we can just check for a field with the same name being non-nil right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was thinking in something like HAProxy.
You can have affinity per IP, or per cookie, and this makes other configurable parameters different, like described here
So I think it's fine to have the selected type, as you can take actions based on it's value.
Also, in nginx.tmpl (as an example), we only have 'cookie' type. So if I only check for nil values, I could misconfigure my ingress, like setting a value like 'affinity: per-ip' and thinking it would work in NGINX ingress
if err != nil { | ||
at = "" | ||
} | ||
//cookieAffinityConfig = CookieAffinityParse(ing) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why are you keeping this code?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
forgot that when was debugging the code. Just removed it
} | ||
//cookieAffinityConfig = CookieAffinityParse(ing) | ||
switch at { | ||
case "cookie": |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In other places we just check for non-nil: https://github.com/kubernetes/kubernetes/blob/master/pkg/api/validation/validation.go#L346, but I'm fine with the way you have it right now.
However, please define the string "cookie" as a const, i.e somewhere, probably types.go, put
type Affinity string
const (
Cookie Affinity = "cookie"
Client Affinity = "client"
)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@bprashanth you mean a new types.go inside the 'sessionaffinity' directory where we can put every constant, or directly in 'core/pkg/ingress/types.go' ?
Hash string `json:"hash"` | ||
} | ||
|
||
type affinity struct { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please define this under NewParser but above (a affinity) Parse
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok
name: nginx-test | ||
annotations: | ||
kubernetes.io/ingress.class: "nginx" | ||
ingress.kubernetes.io/affinity: "cookie" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So re: my previous comment about one annotation, if just ingress.kubernetes.io/affinity
is supplied and nothing else, we still get sticky with default alg and default cookie name correct?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, that's correct. If you supply only affinity=cookie, the default alg and cookie name will be the set here
Only question I have is about the default name of the cookie, and if we can pick one better than "route" Travis failure looks legit
|
@@ -31,7 +31,7 @@ const ( | |||
// If a cookie with this name exists, | |||
// its value is used as an index into the list of available backends. | |||
annotationAffinityCookieName = "ingress.kubernetes.io/session-cookie-name" | |||
defaultAffinityCookieName = "route" | |||
defaultAffinityCookieName = "INGRESSCOOKIE" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a naming convention we can follow for HTTP headers? ALL CAPS, or first Cap, or X-Blah, would be great if you could dig up some doc or rfc and reference it here, but otherwise just a simple comment about the CAPS is ok
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@bprashanth I've got this, but no reference to sticky / cookie session:
https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
I've seen in there also that it's common to use something like CamelCase (IngressCookie) as an example.
Headers containing X- are going to be deprecated, as the following RFC:
https://tools.ietf.org/html/rfc6648
So we can keep this as all caps by now (as does Google), and reopen this discussion later?
The RFC section that deals with Requests and Responses Headers is the following: https://tools.ietf.org/html/rfc2616#section-5.3
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure, whatever makes sense. Something like this will be hard to change without a deprecation notice, because people will probably have clients written to it, but I suppose they can always set the cookie name explicitly back to INGRESSCOOKIE via annotation.
Google uses all caps? for what?
so cool! |
@bprashanth Please don't merge yet. Couldn't test the behaviour here in my environment (after all the changes). Will do this tomorrow and report you. Is there any other think I need to change by now? Tks for the hard work! |
Cool, will wait for you to ack. LGTM otherwise. |
controllers/nginx/configuration.md
Outdated
@@ -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| | |||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i suggest add ingress.kubernetes.io/sticky-expire to control cookie expire time.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@fanhaozzu Agreed. I just think that by now we could merge this, and add this feature later (we can open a new issue for this one).
This is because this item would need some more work to validate it's input, as this is a time pattern. Maybe using this
Can we work in this item latelly?
@bprashanth Some considerations after the tests:
With these observations made, I think it's fine to merge this PR to Master. @fanhaozzu do you mind opening that new issue with your request of setting also the cookie expiration? I'll work on that ASAP (or you can take that and also make your very own PR) Thanks guys. |
Cool, @rikatz thanks for the pr, merging |
…-sessions obsolete
Nginx sticky annotations #258 made the global enable-sticky-sessions obsolete
This PR adds support for configuring the 'sticky' directive per Ingress, with annotations, instead of enabling or disabling it globally through ConfigMap.
Also it adds support for configuring the name of the cookie that will be used to route the requests, and the hash that will be used to generate the cookie.
In the future, as this is a per-Ingress feature, I think it would be better to disable and remove this configuration from ConfigMap and keep it only per Ingress.