Approach summary: prefer cookie-based affinity at the Ingress layer (sticky cookie) when you have multiple LBs/proxies or client IPs are NATed; use Service.spec.sessionAffinity=ClientIP only when you can preserve real client IPs and accept limitations. Always plan for HPA: pods may be removed so make affinity soft — back with shared session store or make app stateless.Example: Service using ClientIP affinityyaml
apiVersion: v1
kind: Service
metadata:
name: my-svc
spec:
type: ClusterIP
selector:
app: my-app
ports:
- port: 80
targetPort: 8080
sessionAffinity: ClientIP
sessionAffinityConfig:
clientIP:
timeoutSeconds: 10800 # 3 hours
Example: nginx Ingress cookie affinity annotationsyaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: my-ingress
annotations:
kubernetes.io/ingress.class: "nginx"
nginx.ingress.kubernetes.io/affinity: "cookie"
nginx.ingress.kubernetes.io/session-cookie-name: "MYSESSION"
nginx.ingress.kubernetes.io/session-cookie-hash: "sha1"
nginx.ingress.kubernetes.io/session-cookie-max-age: "3600"
spec:
rules: ...
Example: Traefik v2 (use Cookie middleware)- Define Cookie middleware (CRD) that sets cookie name/path/secure.- Attach middleware to IngressRoute/Ingress via router.middlewares annotation.Key concepts and reasoning:- Service.sessionAffinity=ClientIP is handled at kube-proxy level (IP hash). It requires that the source IP seen by kube-proxy is stable. In cloud environments with external LBs or nodePort/ClusterIP chains, source IP may be NATed, breaking affinity.- Ingress-level cookie affinity places a cookie in client responses; Ingress will route requests with that cookie to the same backend pod (Ingress controller must support sticky module).- Cookie-based is more resilient across multiple LB layers because the cookie travels with HTTP; but some LBs may strip or rewrite cookies—confirm LB passthrough.- HPA autoscaling: when pods scale down, existing cookies may point to gone pods. Mitigations: - Use shared session store (Redis, DB) so any pod can serve requests. - Use readiness probes + connection draining: set terminationGracePeriodSeconds and ensure ingress controller honors Pod readiness before removing endpoints (use pod readiness gates / pod termination lifecycle). - Use sessionAffinity timeout (shorter) so traffic migrates quickly after scale events. - Use sticky cookie with dynamic balancing (ingress keeps mapping) rather than per-pod fixed IPs.Common pitfalls:- Multi-LB layers: cloud LBs may terminate TLS and reissue requests with new source IP — ClientIP affinity fails. Either preserve client IP (proxy protocol / externalTrafficPolicy: Local) or use cookie affinity.- Pod rescheduling: pods move to new nodes; ClientIP mapping may still point to old node IP; kube-proxy updates endpoints but clients tied to IP lose affinity.- Ingress controllers differ: nginx vs nginx-plus vs traefik vs contour — verify exact annotation names and whether controller uses cookie or consistent-hash module.- Affinity persistence: autoscaling down removes endpoints; cookies pointing to terminated pods will 502/5xx until client gets new cookie. Ensure graceful termination and fallback behavior.- TLS termination: if TLS is terminated upstream, ensure cookies aren’t marked secure/inaccessible by clients.Best practices:- Prefer stateless services + external session store.- If stickiness is required, use Ingress cookie affinity + short TTL and a shared backing store.- Test in your cloud environment: confirm client IP preservation, cookie passthrough, and behavior during scale-up/scale-down.- Add observability: metrics for cookie hits/misses, 5xx during drains, and endpoint churn.This combination (Ingress cookie + shared session or graceful draining) gives robust sticky behavior across multiple ingress controllers and HPA events.