Ingress 를 만들 때 백엔드 파드로 요청을 나누는 방식을 지정할 수 있다. 다만 ingress-nginx 가 실제로 받는 값은 nginx 의 upstream 지시어 이름과 다르다. nginx 문서에서 본 ip_hash 나 random 을 그대로 애너테이션에 적으면 조용히 무시되고 기본값으로 돈다. 이 문서는 실제로 동작하는 설정을 정리한다.
ingress-nginx 는 자체 구현한 로드밸런서를 쓰기 때문에 고를 수 있는 값이 제한적이다.
| 값 | 동작 |
|---|---|
round_robin |
기본값. 엔드포인트를 차례로 돈다 |
ewma |
최근 응답 시간에 가중치를 둬 느린 엔드포인트로 덜 보낸다 |
metadata:
annotations:
nginx.ingress.kubernetes.io/load-balance: "ewma"
클러스터 전체 기본값은 컨트롤러 ConfigMap 의 load-balance 키로 바꾼다. Ingress 애너테이션이 그보다 우선한다.
least_conn, ip_hash, random 같은 값은 nginx 의 upstream 지시어이지 ingress-nginx 애너테이션의 값이 아니다. 쓰는 컨트롤러 버전의 애너테이션 문서에서 허용 값을 먼저 확인한다.
클라이언트 IP 기준으로 고정하려면 해시 키를 지정한다.
metadata:
annotations:
nginx.ingress.kubernetes.io/upstream-hash-by: "$binary_remote_addr"
키에는 nginx 변수를 쓸 수 있으므로 요청 경로나 헤더로도 고정할 수 있다.
nginx.ingress.kubernetes.io/upstream-hash-by: "$request_uri"
해시 방식은 엔드포인트 수가 바뀌면 배치가 흔들린다. 파드가 늘거나 줄 때 세션이 끊겨도 되는 용도(캐시 지역성 등)에 맞고, 로그인 세션 유지에는 아래의 쿠키 방식이 낫다.
프록시가 앞에 있으면 $binary_remote_addr 은 프록시의 주소가 된다. 이때는 실제 클라이언트 IP 를 받도록 컨트롤러에 use-forwarded-headers 를 켜고 신뢰할 프록시 대역을 지정해야 한다.
애플리케이션이 세션을 메모리에 들고 있어 같은 파드로 계속 보내야 한다면 쿠키 방식을 쓴다.
metadata:
annotations:
nginx.ingress.kubernetes.io/affinity: "cookie"
nginx.ingress.kubernetes.io/affinity-mode: "persistent"
nginx.ingress.kubernetes.io/session-cookie-name: "route"
nginx.ingress.kubernetes.io/session-cookie-max-age: "3600"
nginx.ingress.kubernetes.io/session-cookie-path: "/"
affinity-mode 를 balanced(기본값)로 두면 엔드포인트가 바뀔 때 세션을 다시 흩뿌린다. persistent 는 원래 파드가 살아 있는 한 계속 그리로 보낸다. 배포 중 세션 유실이 문제라면 persistent 를 쓰되, 파드 간 부하가 기울 수 있다는 점을 감수해야 한다.
가장 좋은 해결은 어피니티를 쓰지 않아도 되도록 세션을 외부 저장소(Redis 등)로 빼는 것이다. 어피니티는 그때까지의 임시 방편으로 본다.
같은 호스트와 경로에 두 번째 Ingress 를 만들어 일부만 새 서비스로 보낸다.
metadata:
annotations:
nginx.ingress.kubernetes.io/canary: "true"
nginx.ingress.kubernetes.io/canary-weight: "10"
헤더나 쿠키로 대상을 고르는 방법도 있다.
nginx.ingress.kubernetes.io/canary-by-header: "X-Canary"
nginx.ingress.kubernetes.io/canary-by-header-value: "always"
카나리 Ingress 는 본 Ingress 와 같은 host 와 path 를 가져야 하고, 클래스도 같아야 한다. 헤더 조건이 가중치보다 먼저 평가된다.
애너테이션을 적었다고 반영된 것은 아니다. 컨트롤러가 만든 설정을 직접 본다.
kubectl exec -n ingress-nginx <controller-pod> -- cat /etc/nginx/nginx.conf | grep -A5 "upstream"
kubectl logs -n ingress-nginx <controller-pod> | grep -i "annotation"
값이 잘못됐으면 컨트롤러 로그에 애너테이션을 무시했다는 경고가 남는 경우가 많다.
ClusterIP 이더라도 ingress-nginx 는 kube-proxy 를 거치지 않고 엔드포인트로 직접 보낸다. 따라서 서비스의 sessionAffinity: ClientIP 는 Ingress 를 통한 트래픽에 영향을 주지 않는다. 어피니티는 Ingress 쪽에서 설정해야 한다.