Session Affinity가 뭔가요?
A/B테스트를 진행한다고 해봅시다.
A와 B에 각각 들어가있는 기능들이 다를텐데, 사용자가 새로고침을 진행할 때마다 A에 들어갔다가, B에 들어갔다가 하면 앱에 일관성이 없어집니다. 이는 곧 나쁜 사용자 경험으로 이어지고, 회사 입장에서도 데이터를 수집할 때 제대로 된 수집이 불가능할 수 있습니다.
그래서 Istio에서는 Cookie, Header, SourceIP와 같은 값들을 hashing합니다.
해싱하게 되면 같은 쿠키/헤더/ip는 항상 같은 해시값을 가지게 되므로
똑같은 해시값은 똑같은 경로로 라우팅을 해주면 사용자 입장에서는 A에 고정, B에 고정됩니다.
이를 Session Affinity 라고 합니다.
Session Affinity 구현하기
*Session Affinity = Sticky Session, Seickiness라고도 부릅니다.
구현방법
DestinationRule의 trafficPolicy.loadBalancer.consistentHash 설정을 통해 Session Affinity를 구현할 수 있습니다.
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: my-service-sticky
spec:
host: my-service.default.svc.cluster.local
trafficPolicy:
loadBalancer:
consistentHash:
httpCookie:
name: session-cookie # 쿠키 이름 지정
ttl: 0s
# 쿠키 만료 설정 (0s = 브라우저 세션 종료 시 만료)
이렇게되면 envoy가 통신 시 쿠키를 생성하여 자동으로 session affinity를 보장합니다.
만약 httpheader를 통해 세션을 유지하고 싶을 경우 프론트 측에서 직접 header를 하나 만들어서 요청을 보내야 합니다.
Istio 1.17+
apiVersion: v1
kind: Service
metadata:
name: my-sticky-service
namespace: default
labels:
istio.io/persistent-session: "SESSION_ID"
# 위의 한 줄이 세션 스티키 활성화
spec:
selector:
app: my-app
ports:
- port: 80
targetPort: 8080
type: ClusterIP
Istio 1.17 버전 이후로는 위와 같은 한 줄의 코드로도 Session Affinity가 유지된다고 합니다.
다만 이는 실험적인 기능이라 제대로 동작하지 않을 수 있습니다. 2019년부터 제대로 동작하지 않는다는 github 포스팅이 있습니다.
Weighted Routing
A/B테스트를 진행한다고 해봅시다.
Session Affinity를 구성하면 사용자는 어느 하나의 버전에 고정되겠지만,
어떤 버전에 고정될지는 각각의 버전이 가지고 있는 pod 수에 따라 달라집니다.
만약 A버전의 pod 수가 8개이고, B버전의 pod 수가 2개라면
A버전에는 80%의 확률로, B버전에는 20%의 확률로 트래픽이 전송됩니다.
그럼 1%의 트래픽만 B버전으로 보내고싶을 경우 pod를 100개나 만들어야 할까요?
이를 방지하기 위해 Weighted Routing이 사용됩니다.
Weighted Routing을 적용하면 A버전의 pod 1개, B버전의 pod1개만 있더라도
전체 트래픽을 각각 99%와 1%로 나눠서 분배할 수 있습니다.
Weighted Routing 구현
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: reviews-destination
spec:
host: reviews.default.svc.cluster.local
subsets:
- name: v1
labels:
version: v1
- name: v2
labels:
version: v2
우선 DestinationRule을 사용해서 v1과 v2를 각각 구분해줍니다.
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: reviews-traffic-shift
spec:
hosts:
- reviews.default.svc.cluster.local
http:
- route:
- destination:
host: reviews.default.svc.cluster.local
subset: v1
weight: 80 # v1으로 80% 트래픽
- destination:
host: reviews.default.svc.cluster.local
subset: v2
weight: 20 # v2로 20% 트래픽
이후 VirtualService에서 destination을 각각 설정하여 트래픽을 얼마나 조정할 지 결정할 수 있습니다.
동시에 적용되지 않는 문제
다만 Session Affinity와 Weighted Routing이 동시 적용되지 않습니다.
이 이슈도 2019년부터 계속 해결되지 않는 문제인 것 같은데,
이를 해결하기 위해선 API Gateway까지 활용해야 합니다.
이유
session affinity와 weighted routing 중 weighted routing이 먼저 적용됩니다.
Envoy는 VirtualService의 라우팅 규칙(weight)을 평가한 뒤 해당 서브넷 클러스터로 트래픽을 보냅니다.
그 이후에 DestinationRule의 load balancer가 동작하는데, 이미 트래픽을 보낼 서브넷이 정해졌기때문에 session affinity가 적용되지 않습니다.
해결
API Gateway를 Springboot로 작성하여 트래픽을 받을 때 클라이언트의 IP를 1-99사이의 해시값으로 바꿉니다.
해당 해시값을 바탕으로 session affinity를 구현할 수 있습니다.
또한 해시값을 기준으로 1-69의 경우 A버전으로,
70-99의 경우 B버전으로 보낸다는 설정을 추가한다면 Weighted Routing도 구현할 수 있습니다.
kind: VirtualService
metadata:
name: svcb
spec:
hosts:
- svcb.prod.svc.cluster.local
http:
- match:
- headers:
ip-hash:
regex: "^([1-9]|([1-6][0-9])|70)" # 1-79
route:
- destination:
host: svcb
subset: v1
# 기본 라우팅 (헤더 없을 때)
- route:
- destination:
host: svcb
subset: v2
---
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: svcb
spec:
host: svcb
subsets:
- name: v1
labels:
version: v1
- name: v2
labels:
version: v2
'AWS Cloud School 8th > Kubernetes, Istio로 네트워크 관리하기' 카테고리의 다른 글
| 서비스 메시의 구성, 기능, 그리고 이점 (0) | 2025.04.29 |
|---|---|
| Service Mesh의 간단한 개념 (2) | 2025.04.29 |
| 프로젝트의 시작 (2) | 2025.04.29 |