[Kubernetes] 20. 서비스와 네트워크 (6편) - Session Affinity, ExternalTrafficPolicy

JIWON·2025년 7월 8일

Kubernetes

목록 보기
20/32
post-thumbnail

Kubernetes Service 트래픽 제어 및 부가 기능

기본적인 서비스(Service)의 동작 방식 외에, 트래픽을 보다 정교하게 제어하기 위해 사용하는 두 가지 중요한 옵션인 Session AffinityExternalTrafficPolicy에 대해 정리한다.

이 기능들은 서비스의 동작 방식을 미세 조정하여 특정 요구사항(세션 유지, 원본 IP 보존 등)을 충족시켜야 할 때 필수적으로 사용된다.


1. Session Affinity (Sticky Session)

Session Affinity는 특정 클라이언트의 모든 요청을 항상 동일한 파드(Pod)로 보내도록 설정하는 기능이다.
'스티키 세션(Sticky Session)'이라고도 하며, 클라이언트의 IP 주소를 기반으로 동작한다.

1) 왜 필요한가?

일반적으로 쿠버네티스 서비스는 라운드 로빈(Round Robin) 방식으로 요청을 여러 파드에 분산한다. 웹 애플리케이션이 Stateless(상태 없음)라면 문제가 없지만, 사용자의 세션 정보(로그인 상태, 장바구니 등)를 파드 내 메모리에 저장하는 애플리케이션의 경우 문제가 된다. 매번 다른 파드로 요청이 전달되면 저장된 세션 정보가 없어 연결이 끊기거나 데이터가 유실될 수 있기 때문이다. Session Affinity는 이러한 문제를 해결한다.

2) 주요 특징 및 설정

  • 동작 원리: ClusterIP 서비스 등에서 활성화하면, 첫 트래픽이 연결된 파드로 전송된 후, 동일한 클라이언트 IP에서 발생하는 후속 트래픽도 계속해서 같은 파드로 전송된다.

  • 설정 방법: 서비스 매니페스트 파일의 spec.sessionAffinityspec.sessionAffinityConfig 필드를 사용하여 설정한다.

  • 기본값: spec.sessionAffinity 의 기본값은 None 으로, 이 경우 일반적인 로드 밸런싱(랜덤/라운드 로빈) 방식으로 동작한다.

  • 구현: 각 노드의 iptables 규칙을 통해 구현되며, sessionAffinityConfig.clientIP.timeoutSeconds 를 통해 세션이 고정되는 시간(타임아웃)을 설정할 수 있다.

⚠️ NodePort 서비스와 사용 시 주의사항

NodePort 서비스에도 Session Affinity를 설정할 수는 있다. 하지만 세션 어피니티는 각 노드의 kube-proxy 를 통해 개별 노드 단위로 동작한다. 즉, 클라이언트가 node1:30080으로 들어왔을 때 배정된 파드와, node2:30080으로 들어왔을 때 배정되는 파드가 다를 수 있다. 따라서 외부 로드밸런서가 없는 순수 NodePort 환경에서는 세션 유지를 100% 보장하기 어렵기 때문에 권장하지 않는다.

3) 실습 : Session Affinity 적용

직접 Session Affinity를 설정하고 curl 테스트를 통해 파드가 고정되는지 확인해 본다.

1. 테스트용 디플로이먼트 생성 (sample-deployment.yaml)

먼저 응답을 해줄 Nginx 기반의 애플리케이션을 배포한다.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: sample-deployment
spec:
  replicas: 3
  selector:
    matchLabels:
      app: sample-app
  template:
    metadata:
      labels:
        app: sample-app
    spec:
      containers:
        - name: nginx-container
          image: amsy810/echo-nginx:v2.0

2. Session Affinity가 적용된 서비스 생성 (sample-session-affinity.yaml)

sessionAffinity: ClientIP 설정을 추가하여 서비스를 생성한다.

# sample-session-affinity.yaml
apiVersion: v1
kind: Service
metadata:
  name: sample-session-affinity
spec:
  sessionAffinity: ClientIP # Session Affinity 활성화
  sessionAffinityConfig:
    clientIP:
      timeoutSeconds: 10800 # 세션 타임아웃(초 단위)
  type: LoadBalancer # 또는 ClusterIP
  selector:
    app: sample-app
  ports:
    - name: http-port
      protocol: TCP
      port: 8080
      targetPort: 80

이 설정으로 인해 동일 IP에서의 요청은 지정된 시간 동안 동일한 파드로 전송된다.

3. 테스트용 파드 생성 및 요청 보내기

요청을 보낼 클라이언트 역할을 할 파드(sample-pod)를 생성한다.

# sample-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: sample-pod
spec:
  containers:
  - name: tools-container
    image: amsy810/tools:v2.0

생성 후 해당 파드에 접속하여 curl 명령어를 반복적으로 보내본다.

# 테스트 파드 접속
$ kubectl exec -it sample-pod -- /bin/bash

# 반복 요청 테스트
root@sample-pod:/# curl http://sample-session-affinity.default.svc.cluster.local:8080
Host=sample-session-affinity...  Path=/  From=sample-deployment-5d9fcfcbb6-b99tg  ClientIP=10.244.1.87  XFF=

root@sample-pod:/# curl http://sample-session-affinity.default.svc.cluster.local:8080
Host=sample-session-affinity...  Path=/  From=sample-deployment-5d9fcfcbb6-b99tg  ClientIP=10.244.1.87  XFF=

결과 확인: From= 뒤에 나오는 파드 이름(sample-deployment-5d9fcfcbb6-b99tg)이 계속 동일한 것을 볼 수 있다. 스티키 세션이 정상적으로 동작하고 있음을 의미한다.


2. ExternalTrafficPolicy: 노드 간 통신 최적화 및 IP 보존

NodePortLoadBalancer 타입의 서비스를 사용할 때 트래픽 라우팅 정책을 결정하는 옵션이다.

1) 기본 동작(Cluster)과 문제점

기본적으로 서비스는 externalTrafficPolicy: Cluster 로 설정되어 있다. 이 경우 2단계 로드밸런싱이 일어난다.

  • 1단계: 외부 요청이 특정 노드(Node A)에 도착한다.

  • 2단계: Node A는 클러스터 내의 모든 파드를 대상으로 다시 로드밸런싱을 수행한다. 이때 대상 파드가 Node B에 있다면, 트래픽을 Node B로 다시 보낸다(Hop 발생).

문제점:

  • 네트워크 비효율: 불필요한 네트워크 홉(Hop)이 추가되어 지연 시간이 늘어난다.

  • Source IP 유실: 2단계 로드밸런싱 과정에서 SNAT(Source Network Address Translation)가 발생하여, 파드는 실제 클라이언트 IP가 아닌 요청을 전달한 노드의 IP를 보게 된다.

2) externalTrafficPolicy: Local 설정

이러한 문제를 해결하기 위해 정책을 Local 로 변경할 수 있다.

  • Cluster (기본값): 노드에 도착한 트래픽을 다른 노드의 파드까지 포함하여 로드밸런싱한다. (부하 분산 유리, IP 유실, 홉 발생)

  • Local: 노드에 도착한 트래픽을 해당 노드 내에 있는 파드에게만 전송한다.

    • 장점: 다른 노드로 트래픽을 보내지 않아 네트워크 효율이 좋고, SNAT가 발생하지 않아 클라이언트의 원본 IP 주소를 보존할 수 있다.

    • 단점: 트래픽이 들어온 노드에 해당 파드가 없으면 패킷이 드롭(Drop)된다. (NodePort 사용 시 주의)
      따라서 파드가 모든 노드에 배포되는 데몬셋(DaemonSet) 과 같은 특정 상황이나, 클라이언트 IP 보존이 반드시 필요한 경우에 사용하는 것이 좋다.

3) 파드 위치 확인

실습 전, 파드가 어떤 노드에 배치되어 있는지 확인해본다.

kubectl get pods -o custom-columns="NAME:{metadata.name},Node:{spec.nodeName},NodeIP:{status.hostIP}"

# 실행 결과 예시
NAME                                Node      NodeIP
sample-deployment-5d9fcfcbb6-b99tg  worker1   192.168.56.101
sample-deployment-5d9fcfcbb6-p4mtq  worker2   192.168.56.102
sample-deployment-5d9fcfcbb6-x9b4t  worker2   192.168.56.102

Local 정책을 쓰면 worker1 으로 들어온 요청은 무조건 worker1 에 있는 파드가 처리하게 된다.

4) NodePort 서비스에 Local 정책 적용

1. 서비스 생성 (sample-nodeport-local.yaml)

externalTrafficPolicy: Local 을 적용한 NodePort 서비스를 만든다.

# sample-nodeport-local.yaml
apiVersion: v1
kind: Service
metadata:
  name: sample-nodeport-local
spec:
  type: NodePort
  externalTrafficPolicy: Local
  selector:
    app: sample-app
  ports:
    - name: http-port
      protocol: TCP
      port: 8080
      targetPort: 80
      nodePort: 30085

2. 동작 확인 및 IP 보존 확인

각 워커 노드의 IP와 NodePort(30085)를 통해 요청을 보내본다.

# worker1(192.168.56.101)로 요청 -> worker1에 있는 파드(b99tg)가 응답
$ curl -s http://192.168.56.101:30085
Host=192.168.56.101  Path=/  From=sample-deployment-5d9fcfcbb6-b99tg  ClientIP=192.168.56.100  XFF=

# worker2(192.168.56.102)로 요청 -> worker2에 있는 파드(p4mtq)가 응답
$ curl -s http://192.168.56.102:30085
Host=192.168.56.102  Path=/  From=sample-deployment-5d9fcfcbb6-p4mtq  ClientIP=192.168.56.100  XFF=

결과 분석:

  1. 트래픽 지역성: 요청을 보낸 노드(192.168.56.101)에 있는 파드가 직접 응답했다. 다른 노드로 건너가지 않았다.

  2. IP 보존: ClientIP 항목을 보면 SNAT 된 노드 IP가 아니라, 요청을 보낸 실제 클라이언트 IP(192.168.56.100)가 찍히는 것을 확인할 수 있다.

📌 LoadBalancer 서비스에서의 동작

NodePort에서는 파드가 없는 노드로 요청 시 드롭되지만, LoadBalancer 서비스 타입에서 Local 정책을 쓰면 더 안전하다.

클라우드 제공업체의 로드밸런서는 헬스 체크(Health Check) 기능을 통해 파드가 존재하는 노드만 식별하여 트래픽을 보낸다. 따라서 파드가 없는 노드로는 애초에 트래픽을 보내지 않아 패킷 드롭 문제를 방지하면서도, Local 정책의 이점(IP 보존, 성능 향상)을 누릴 수 있다.


정리하자면:

  • 세션 유지가 필요하다면? 👉 Session Affinity

  • 클라이언트의 원본 IP가 필요하거나 네트워크 홉을 줄이고 싶다면? 👉 ExternalTrafficPolicy: Local

0개의 댓글