Azure Load Balancer in AKS
Behavior of AKS Load Balancer
Azure Load Balancer in AKS
Behavior of AKS Load Balancer
Load Balancer & NodePort
Kubernetes 클러스터내 Application을 외부로 노출시키기 위해선 로드밸런서(Load Balancer)와 노드포트(NodePort) 타입의 서비스를 사용 할 수 있다.
다만 일반적으로 노드포트 대비 로드밸런서의 장점이 많기 때문에 로드밸런서를 사용한다.
노드포트는 모든 노드의 제한된 범위의 특수한 포트(30000~32767)를 사용해야 하고, 각 노드의 IP 주소를 인바운드 엔드포인트로 노출해야 하며, 만약 특정 노드 장애 발생시 해당 노드를 향한 클라이언트 요청을 처리할 방법이 없기 때문에 가용성 측면에서 취약한 부분이 존재한다. 또한 노드 각각에 Public IP Address가 할당되어 있지 않다면 Application을 향한 외부의 접근은 불가하다.
때문에 Load Balancer 타입의서비스를 사용해서 단일 진입점과 서비스 포트를 노출하고 Healthy한 노드를 향해서만 트래픽을 부하분산 하도록 구성하는 것이 보다 안정적이고 효율적이다.
Load Balancer in AKS
AKS(Azure Kubernetes Service)에서는 로드밸런서 타입의 서비스를 생성하면 Azure Load Balancer 리소스를 통해 Load Balancer 서비스 사용을 하도록 Cloud Controller가 구성되어 있다.
AKS가 사용하는 Azure Load Balancer는 Public IP 주소를 Frontend IP 주소로 할당 받아 외부 인터넷에서 접근할 수 있는 로드밸런서로 사용할수도 있고, 노드풀과 같은 VNET 주소를 Frontend IP 주소로 할당받아서 내부용 로드밸런서를 사용할 수 있다.
MC로 시작하는 AKS Managed Resource Group을 보면 Public IP 사용하는 로드밸런서는 “kubernetes”, 내부 VNET IP를 사용하는 로드밸런서는 “kubernetes-internal” 이름으로 리소스가 생성된다.
내부용 로드밸런서는 ILB(Internal Load Balancer)로 불리며 로드밸런서 서비스를 생성할때 Annotation에 service.beta.kubernetes.io/azure-load-balancer-internal 주석을 기입하면 생성된다.
ILB는 별도의 추가 주석이 없다면 AKS 노드풀 서브넷 대역의 IP 주소를 Frontend IP 주소로 할당받지만 만약 노드풀과 다른 서브넷 주소 대역을 ILB의 IP 주소로 할당하고 싶다면, service.beta.kubernetes.io/azure-load-balancer-internal-subnet 주석을 통해 서브넷을 명시적으로 선언할 수 있다. 또한, service.beta.kubernetes.io/azure-load-balancer-ipv4 주석으로 고정 IP 주소를 지정할 수 도 있다.
이밖에도 service.beta.kubernetes.io/azure-load-balancer-tcp-idle-timeout 주석을 통해 TCP 유휴 타임아웃 시간 등 세밀한 조정이 가능하다.
Behavior of AKS Load Balancer
쿠버네티스 환경에서 LB를 통한 인바운드 트래픽 흐름은 노드포트 30000~32767를 통해 흐르는 것처럼 이해할 수 있다.
Client -> Load Balancer Frontend IP:Port -> NodeIP:NodePort -> Iptables (DNAT) -> Endpoint(Pod) IP:Port
NodeIP:NodePort는 Iptables에 의해 ‘Service(ClusterIP)’로 논리적으로 매핑되지만, ClusterIP는 가상의 IP 주소이기 때문에 실제 패킷의 DNAT는 서비스 Selector에 의해 맵핑된 Endpoint IP인 Pod의 IP 주소와 Port로 DNAT된다.
하지만, Azure Load Balancer의 리소스 레벨 구성을 살펴보면 NodePort는 실제 패킷 흐름 설정에는 들어가 있지 않고, 헬스프로브 설정에서만 사용되도록 구성되어 있다.
실제 트래픽 흐름 규칙인 Load Balancer Rule을 보면 LB Frontend IP:ServicePort -> NodeIP:ServicePort 로 트래픽을 전달하는 설정을 확인할 수 있다.
노드 레벨에서 노드포트와 서비스포트로 들어오는 트래픽이 어떻게 같은 백앤드 파드를 향해 흘러가는지 알아보자.
이를 위해서는 직접 노드의 Iptables 체인을 살펴보면 알 수 있다. 노드 커널 조회권한을 가진 파드를 생성해서 iptables -t nat -L KUBE-SERVICES -n -v 명령을 통해 Iptables 서비스 체인을 확인할 수 있다.
조회된 서비스 체인 중 KUBE-EXT-XXXX 체인에 Azure Load Balancer Rule에 대한 내용(Frontend IP, Service Port)을 확인할 수 있다. 해당 서비스 체인을 시작으로 계속하여 조회하면 아래와 같은 순서로 DNAT되는 규칙을 볼 수 있다.
KUBE-EXT-XXXX (LB 서비스 체인)
└─ KUBE-SVC-XXXX (Service 체인)
└─ KUBE-SEP-YYYY (Endpoint 체인)
└─ DNAT → PodIP:TargetPort
헬스프로브가 사용하는 노드포트에 대한 서비스체인은 iptables -t nat -L KUBE-NODEPORTS -n -v 명령으로 확인할 수 있는데, 이때 LB Rule이 설정되어 있는 동일한 KUBE-EXT-XXXX 서비스 체인을 확인할 수 있다.
서비스 체인 이름은 같지만 노드포트에 대한 내용으로 설정되어 있다. 하지만 KUBE-EXT-XXXX 서비스 체인을 계속하여 조회해 보면 결국 동일한 서비스, 엔드포인트로 향하는 규칙인 것을 알 수 있게 된다.
KUBE-EXT-XXXX (NodePort 서비스 체인)
└─ KUBE-SVC-XXXX (Service 체인)
└─ KUBE-SEP-YYYY (Endpoint 체인)
└─ DNAT → PodIP:TargetPort
즉, 노드에 노드포트(NodePort)로 들어오든, 서비스 포트(ServicePort)로 들어오든 Iptables 입장에서는 같은 서비스 체인을 사용하기 때문에 동일한 백앤드 파드로 트래픽을 보낼 수 있다.
보다 자세한 내용은 다음 블로그에서 추가적으로 확인할 수 있다.
externalTrafficPolicy에 의해 달라지는 부하분산 및 헬스프로브 동작
LB의 부하분산과 헬스프로브 동작은 externalTrafficPolicy에 의해 약간 달라질 수 있다.
먼저 부하분산 동작을 살펴보자.
externalTrafficPolicy는 Cluster일 경우 어느 노드로 트래픽이 들어가든 상관없이 노드의 Iptables에 의해 백앤드 파드로 트래픽을 보낼 수 있다. 이 과정에서 패킷의 Source IP는 NodeIP로 SNAT 되어 들어간다. 때문에 백앤드 Application에서는 실제 클라이언트 IP를 확인할 수 없다.
부하분산보다 클라이언트 IP 확인이 중요하다면 이러한 동작을 바꿔야 한다.
이럴때 사용할 수 있는 설정이 externalTrafficPolicy을 Local로 설정하는 것이다. 해당 설정은 백앤드 파드가 존재하는 노드로만 로드밸런서의 부하분산이 동작하도록 할 수 있다. 즉, 백앤드 Application이 구동중인 노드로만 트래픽이 전달되기 때문에 노드의 IP로 SNAT 처리할 필요가 없어 클라이언트 IP를 보존할 수 있다.
그렇다면, 백앤드 파드가 존재하는 노드를 LB는 어떻게 식별하는지 알아보자.
AKS는 “externalTrafficPolicy: Local”로 설정된 로드밸런서 오브젝트가 생성되면 해당 로드밸런서 오브젝트를 위한 헬스프로브가 설정된다. 헬스프로브 설정을 보면 healthCheckNodePort 로 세팅된 노드포트와 http 프로토콜, /healthz 경로가 설정되어 있다.
이는 실제 백앤드 파드로 헬스 체크 쿼리를 하는것이 아니라 NodeIP:healthCheckNodePort/healthz로 쿼리하여 해당 노드에 백앤드 파드가 존재하는지 검사한다는 의미로 해석된다.
실제로 노드로 진입하여 localhost:healthCheckNodePort/healthz로 curl을 사용해 쿼리해보면 해당 노드에 엔드포인트가 존재하는지 여부를 확인할 수 있고, 이는 실제 백앤드 파드의 응답이 아닌 것을 확인할 수 있다.
실제 쿼리 응답 결과 예시는 다음 블로그에서 확인할 수 있다.
생소한 healthCheckNodePort 는 클러스터에서 LB 서비스 조회시 확인되는 노드포트가 아니라 서비스 스펙에 새롭게 추가되어 있는 모습이 보인다. 즉, spec.ports.nodePort로 할당된 포트가 아니라 별도의 스펙인 spec.healthCheckNodePort에 설정된 노드 포트를 사용한다.
일반적으로 “externalTrafficPolicy: Cluster”로 설정된 로드밸런서의 헬스프로브는 spec.ports.nodePort로 할당된 노드포트로 TCP 프로토콜을 사용해 헬스체크용 패킷을 보내서 실제 백앤드 파드와 통신할 수 있는지 체크한다.
추가적으로 “externalTrafficPolicy: Cluster”인 상태에서 spec.ports.appProtocol을 설정하면 헬스프로브의 TCP세팅을 HTTP(s)로 바꿀 수 있다. (externalTrafficPolicy가 Local일 경우 appProtocol 세팅은 무시된다.) 서비스 스펙에서 appProtocol을 http(s)로 세팅하면 Azure LB의 헬스프로브 세팅에서도 프로토콜이 http(s)로 세팅되고, Path 도 기본값인 / 로 설정된다. 만약 Path까지 커스텀 하고자 한다면 서비스의 Annotation에 service.beta.kubernetes.io/azure-load-balancer-health-probe-request-path 주석을 추가하면 된다.
appProtocol 설정은 오픈소스 ingress nginx controller에도 설정되어 있기 때문에 AKS 환경에 ingress nginx controller를 배포할 계획이라면 Helm Value에 service.beta.kubernetes.io/azure-load-balancer-health-probe-request-path: /healthz 주석을 설정해야 한다.
# Helm Chart Values
# Azure LB Probe Setting
service:
annotations:
service.beta.kubernetes.io/azure-load-balancer-health-probe-request-path: /healthz
Ref.
부하분산 헬스프로브 설정을 커스텀하고자 한다면 다음 공식문서를 참고하면 좋다.
externalTrafficPolicy 및 spec.ports.protocol, spec.ports.appProtocol 에 따른 동작 방식 차이를 확인하고자 한다면 다음 섹션을 참고하면 좋다.
AKS 환경에서 externalTrafficPolicy에 대한 내용을 다룬 블로그, 보다 심층적인 내용을 다루고 있다.
AKS 노드 Iptables 조회 결과를 기반으로 자세한 트래픽 흐름 내용을 확인할 수 있는 블로그
메타데이터
- post_id
- 340de6dc040b
- slug
- azure-load-balancer-in-aks-340de6dc040b
- url
- https://medium.com/@hyukjuner/azure-load-balancer-in-aks-340de6dc040b
- canonical_url
- https://medium.com/@hyukjuner/azure-load-balancer-in-aks-340de6dc040b
- author_url
- https://medium.com/@hyukjuner
- status
- ok
- fetched_at
- 2026-07-13 12:04:51