AKS에 배포된 TCP 서버에 외부 트래픽 라우팅 하기 - 2
개요
이전 글 [AKS에 배포된 TCP 서버에 외부 트래픽 라우팅 하기 - 1] 에서 AKS에 배포된 TCP 서버 애플리케이션에 외부 트래픽을 전달하기 위한 기본적인 설정 방법을 살펴보았습니다.
이번 글에서는 Load Balancer 사용 시 k8s 서비스의 기본 동작 방식으로 인해 발생할 수 있는 문제들을 살펴보고, 이를 해결하기 위한 방법을 알아보겠습니다.
배경 설명
k8s 서비스의 기본 트래픽 처리 방식
k8s의 서비스는 기본적으로 클러스터 내의 모든 Node를 통해 Pod에 접근할 수 있도록 설계되어 있습니다.
위 그림에서 볼 수 있듯이, Load Balancer로 들어온 트래픽은 클러스터 내의 모든 Node 중 임의의 Node로 전달될 수 있습니다. 이는 k8s Service 오브젝트의 기본 트래픽 정책(Cluster)에 의한 것으로, 일반적인 로드밸런싱과 고가용성 측면에서 장점이 있습니다.
하지만 TCP는 stateful한 프로토콜이므로, 다음과 같은 문제들을 고려해야 합니다.
- TCP 세션 지속성 문제
- TCP 연결은 클라이언트-서버 간의 상태를 유지해야 함
- 문제 상황:
- 클라이언트의 후속 요청이 다른 Pod로 라우팅될 경우
- 새로운 Pod에는 기존 TCP 연결 상태가 없음
- TCP handshake, 시퀀스 번호, 윈도우 크기 등의 상태 정보 유실
- 결과적으로 연결 거부 또는 오류 발생
- 예시 시나리오:
1 2 3 4 5 6 7 8 9 10 11
[이상적인 TCP 세션] 클라이언트 <---> 로드밸런서 <---> NodeA(TCP 상태 유지) - 시퀀스 번호 - 윈도우 크기 - 타이머 등 [세션 지속성 깨짐] 클라이언트 ---> 로드밸런서 ---> NodeB - TCP 상태 없음 - 연결 거부 - RST 패킷 발송
- 영향:
- 애플리케이션 장애
- 데이터 손실
- 성능 저하
- 사용자 경험 악화
- 불필요한 네트워크 홉(Network Hop)
- 클라이언트의 요청이 Pod가 없는 Node로 전달될 수 있음
- 추가적인 네트워크 홉으로 인한 지연 발생
- 클라이언트 IP 마스킹
- Node 간 트래픽 전달 과정에서 Source NAT 발생
- 실제 클라이언트 IP 추적 어려움
이처럼 TCP 서버 애플리케이션을 k8s에 배포할 때는 TCP 세션 지속성 문제를 고려하여 적절한 설정이 필요합니다. k8s Service의 externalTrafficPolicy: Local
설정을 통해 이 문제를 해결하는 방법을 살펴보겠습니다.
externalTrafficPolicy를 통한 문제 해결
externalTrafficPolicy 설정의 이해
k8s Service의 외부 트래픽 정책은 externalTrafficPolicy
필드를 통해 설정할 수 있습니다. 이 설정은 크게 Cluster
와 Local
두 가지 옵션이 있으며, 각각 다른 특징을 가지고 있습니다.
Cluster 정책 (기본값)
기본값인 Cluster
정책에서는:
- 모든 Node가 모든 Pod로 트래픽을 전달할 수 있음
- Pod 간 균등한 트래픽 분배
- 추가 네트워크 홉 발생
- 클라이언트 IP 가려짐 (SNAT 발생)
Local 정책
Local
정책으로 설정하면:
- Node는 자신에게 있는 Pod로만 트래픽 전달
- 불필요한 네트워크 홉 제거
- 클라이언트 IP 보존 (SNAT 없음)
트래픽 분배 방식의 차이
예를 들어 두 개의 Node가 있고, Node A에 1개, Node B에 2개의 Pod가 있는 상황을 가정해보겠습니다:
- Cluster 정책의 경우
- Load Balancer가 각 Node에 50:50으로 트래픽 분배
- 각 Node는 모든 Pod에 트래픽을 고르게 전달
- 결과적으로 각 Pod는 전체 트래픽의 33%씩 처리
- Local 정책의 경우
- Load Balancer는 여전히 Node 간 50:50 분배
- Node A의 단일 Pod는 전체 트래픽의 50% 처리
- Node B의 두 Pod는 각각 25%씩 처리
Local 정책으로 전환
앞서 살펴본 TCP 세션 지속성 문제를 해결하기 위해서는 Service의 externalTrafficPolicy
를 Local
로 설정해야 합니다. 이렇게 설정하면, Node는 외부 트래픽을 연결을 받은 Node에서 실행 중인 Pod으로만 전달합니다.
1
2
3
4
5
6
7
8
apiVersion: v1
kind: Service
metadata:
...
spec:
externalTrafficPolicy: Local # 핵심 설정
type: LoadBalancer
...
주의사항: Pod가 없는 Node로의 라우팅
Local 정책 사용 시 중요한 주의사항이 있습니다. Pod가 없는 Node로 트래픽이 전달되면 연결이 실패합니다:
1
2
3
4
[실패 시나리오]
1. Load Balancer가 NodeA로 트래픽 전달
2. NodeA에 Pod가 없는 경우
3. 연결 거부 (Connection Refused)
따라서 Load Balancer가 트래픽을 Pod가 있는 Node로만 전달하도록 보장해야 합니다.
Azure LoadBalancer의 Health Probe 설정을 통해 문제를 해결하는 방법을 살펴보겠습니다.
Azure LoadBalancer Health Probe 설정
AKS에서 TCP 서버의 Local 정책이 올바르게 동작하려면 Azure Load Balancer가 Pod가 있는 Node를 정확히 식별할 수 있어야 합니다.
Azure Load Balancer는 각 포트별로 상태 확인을 설정할 수 있는 Azure에서 제공하는 k8s 어노테이션을 제공합니다:
service.beta.kubernetes.io/port_{port}_health-probe_port
: 상태 확인용 포트
이 어노테이션을 Service에 적용하면 됩니다:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
apiVersion: v1
kind: Service
metadata:
...
annotations:
# TCP 서버 포트의 상태 확인
service.beta.kubernetes.io/port_9999_health-probe_port: "31573"
...
spec:
externalTrafficPolicy: Local
type: LoadBalancer
ports:
- protocol: TCP
nodePort: 31573
port: 9999
targetPort: 8888
name: tcp-client
...
이 설정은:
- 9999번 포트로 들어오는 트래픽에 대해
- NodePort 31573포트를 사용해 Node에 서비스의 엔드포인트가 있는지 확인합니다.
- 이를 통해 Azure LoadBalancer는 Pod가 있는 Node로만 트래픽을 전달할 수 있습니다.
결과적으로:
- Pod가 없는 Node로는 트래픽이 전달되지 않음
- TCP 연결의 세션 지속성이 보장됨
- 불필요한 네트워크 홉이 제거됨
결론
지금까지 AKS에서 TCP 서버 애플리케이션을 위한 LoadBalancer 서비스를 구성하면서 다음과 같은 설정들을 적용했습니다:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
apiVersion: v1
kind: Service
metadata:
annotations:
# 1. Azure 리소스 관리
# LB 리소스 그룹 지정
service.beta.kubernetes.io/azure-load-balancer-resource-group: rg-network
# 고정 공인 IP 사용
service.beta.kubernetes.io/azure-pip-name: kimmap-tcp-pip
# 2. Health Probe 설정
# TCP 프로토콜 사용
service.beta.kubernetes.io/port_9999_health-probe_protocol: "TCP"
# 상태 확인 포트
service.beta.kubernetes.io/port_9999_health-probe_port: "31573"
# 프로브 주기
service.beta.kubernetes.io/azure-load-balancer-health-probe-interval: "59"
spec:
type: LoadBalancer
# 3. TCP 세션 관리
# TCP 세션 지속성 보장, 클라이언트 IP 보존
externalTrafficPolicy: Local
ports:
# Health Probe와 동일한 포트 사용
- nodePort: 31573
# 서비스 포트
port: 9999
# 컨테이너 포트
targetPort: 8888
이러한 설정들을 통해 다음과 같은 요구사항들을 만족하는 서비스 구성이 가능해졌습니다:
- 고정 IP를 통한 안정적인 서비스 제공
- TCP 세션 지속성 보장
- 클라이언트 IP 보존
- 네트워크 지연 최소화
다만 이러한 설정들을 적용할 때는:
- NodePort 충돌 가능성
- Pod 분배 전략
- 적절한 레플리카 수 유지
등을 고려해야 하며, 운영 환경에 맞는 세부 조정이 필요할 수 있습니다.
이번 시리즈에서는 AKS에서 TCP 서버 애플리케이션의 트래픽을 라우팅하면서 겪을 수 있는 문제들과 그 해결 방법을 살펴보았습니다. 실제 운영 환경에서는 더 다양한 상황과 요구사항이 있을 수 있지만, 이 글에서 다룬 내용이 여러분의 환경에서 TCP 서버를 구성하는 데 도움이 되었기를 바랍니다.
감사합니다.