AWS 기술 블로그
Amazon EKS에서 Topology Aware Hint 기능을 활용하여 Cross-AZ 통신 비용 절감하기
Amazon EKS로 클러스터 구성 시 일반적으로 고가용성을 위해서 모든 가용 영역(Availability Zone, AZ)에 워커 노드들을 배치합니다. 클러스터에서 실행되는 Pod들 또한 모든 AZ 에 배포되도록 설정하면 높은 가용성을 확보할 수 있습니다. 이 때 여러 AZ에 걸쳐 Pod 간 통신이 이루어지는데, 이를 Cross-AZ 통신이라고 합니다.
같은 Amazon VPC 내에서 Cross-AZ 통신이 발생하더라도 통신량에 비례하여 통신 비용이 발생합니다. 따라서 Cross-AZ 통신이 많은 경우 예상치 못한 비용을 지불해야할 수 있습니다. 대량의 Cross-AZ 통신으로 인한 비용이 부담되는 수준이라면 Topology Aware Hint 기능을 사용하여 Cross-AZ 통신을 줄이는 방법을 고려해볼 수 있습니다.
본 게시글에서는 Topology Aware Hint 기능에 대해 알아보고, Amazon EKS 클러스터에 어떻게 Topology Aware Hint 기능을 적용하여 Cross-AZ 통신 비용을 절감할 수 있는지 알아봅니다.
Topology Aware Hint 기능 소개
Topology Aware Hint는 쿠버네티스 서비스를 이용하여 Pod간 통신시, 쿠버네티스 서비스에 설정하여 통신 경로를 변경하는 기능입니다. 쿠버네티스 v1.21 버전에 처음 추가되었으며, v1.24 버전 이상부터는 별도의 설정 없이 Topology Aware Hint 기능을 이용할 수 있습니다. 이는 업스트림 쿠버네티스를 그대로 지원하는 Amaozon EKS에서도 동일하게 적용됩니다.
[그림 1] 쿠버네티스 서비스의 Cluster IP 이용 시 Topology Aware Hint 기능 적용에 따른 통신 경로
[그림 1]은 쿠버네티스 서비스의 Cluster IP를 이용하여 Client Pod에서 Server Pod로 패킷 전송 시 Topology Aware Hint 기능 적용에 따른 통신 경로를 나타내고 있습니다. 쿠버네티스 서비스의 Cluster IP로 전송한 패킷은 각 워커 노드에서 실행되는 kube-proxy가 설정한 iptables 규칙에 의해서 분배되어 Server Pod로 전송됩니다. 여기서 iptables 규칙은 리눅스 환경에서 패킷을 커널에서 제어하는 규칙을 의미합니다.
Topology Aware Hint 기능을 적용하지 않는다면 kube-proxy가 설정한 iptables 규칙은 임의의 Server Pod로 패킷을 전송하여 Cross-AZ 통신이 발생하게 됩니다. Topology Aware Hint 기능을 사용한다면 kube-proxy에 의해 iptables 규칙이 자신과 동일한 AZ에 위치하고 있는 Pod에게만 패킷을 전송하도록 설정하기 때문에 Cross-AZ 통신이 발생하지 않습니다.
이 때, Client Pod는 자신과 동일한 AZ에 존재하는 Server Pod로만 패킷을 전송할 수 있기 때문에 Server Pod는 각 AZ별로 2개 이상 배포하여 고가용성을 유지할 수 있도록 구성해야 합니다. [그림 1]에서도 각 AZ 별로 2개의 Server Pod를 구성하여 Server Pod가 갑자기 중단되거나, 신규 버전의 Server배포를 위해서 Server Pod가 교체되는 경우를 대비하고 있습니다.
[그림 2] 쿠버네티스 서비스의 Node Port 이용 시 Topology Aware Hint 기능 적용에 따른 통신 경로
Topology Aware Hint 기능은 쿠버네티스 서비스의 Cluster IP뿐만 아니라 Node Port에도 적용됩니다. [그림 2]는 Amazon EKS 클러스터 외부의 Client에서 쿠버네티스 서비스의 Node Port로 패킷 전송 시 Topology Aware Hint 기능 적용에 따른 통신 경로를 나타내고 있습니다. Cluster IP와 동일하게 Node Port의 경우에도 kube-proxy가 설정한 iptables 규칙은 자신과 동일한 AZ에 위치하고 있는 Server Pod에게만 패킷을 전송하기 때문에 Cross-AZ 통신이 발생하지 않습니다.
Topology Aware Hint 기능을 이용하기 위해 [Text 1]의 예시처럼 쿠버네티스 서비스에 “service.kubernetes.io/topology-aware-hints: auto” 어노테이션을 추가합니다. 다만 어노테이션을 추가한다고 Topology Aware Hint 기능이 반드시 적용되는 것은 아니고 몇가지 조건을 충족해야 합니다. Topology Aware Hint 기능 적용에 필요한 조건은 본 게시글 뒤에서 설명할 예정입니다.
[Text 2]는 Topology Aware Hint 기능 테스트를 위해 echo Server를 사용하며 배포를 위한 디플로이먼트와 서비스가 명시되어 있습니다. echo 디플로에먼트에 Replica는 12로 설정되어 있기 때문에 echo Server Pod가 총 12개 배포되어 동작합니다. echo 디플로이먼트에는 Topology Spread Constraints도 설정되어 있습니다. Topology Spread Constraints 기능은 Pod 배포 시 모든 AZ에 Pod를 고르게 분배하여 배포하는 역할을 수행합니다. 따라서 Amazon EKS 클러스터가 3개의 AZ를 이용하고 있다면 Pod는 각 AZ에 4개씩 배포됩니다.
echo 서비스는 셀렉터에 지정된 레이블을 이용하여 echo 디플로이먼트로 배포된 12개의 echo Server Pod를 묶어 하나의 논리적 그룹을 형성합니다. echo 서비스에는 Topology Hint Aware 기능 적용을 위한 어노테이션이 존재하지만, 주석 처리하여 Topology Hint Aware 기능이 동작하지 않도록 설정되어 있습니다. [Text 2]에는 echo Server의 디플로이먼트 및 서비스와 별개로 명령어 실행을 위한 Shell Pod도 포함하고 있습니다.
[그림 3] Topology Aware Hint 기능 Test를 위한 Topology 및 Topology Aware Hint 기능 적용에 따른 통신 경로
[그림 3]은 [Text 2]를 이용하여 실제 배포된 echo Server Pod, shell Pod의 Topology 및 Topology Aware Hint 기능 적용에 따른 통신 경로를 나타내고 있습니다. Topology Aware Hint 기능이 적용되어 있지 않는 경우에는 shell Pod에서 echo 서비스로 전송한 패킷은 모든 AZ의 Pod로 전달되지만, Topology Aware Hint 기능이 적용되면 shell Pod가 위치한 Availability Zone C의 echo Server Pod에게만 패킷이 전달됩니다.
$ kubectl exec -it shell -- bash
my-shell:~# curl -s echo | jq -r .environment.HOSTNAME
echo-7fc4564b7b-2c2g4
my-shell:~# curl -s echo | jq -r .environment.HOSTNAME
echo-7fc4564b7b-8mdrg
my-shell:~# curl -s echo | jq -r .environment.HOSTNAME
echo-7fc4564b7b-8bvg8
my-shell:~# curl -s echo | jq -r .environment.HOSTNAME
echo-7fc4564b7b-g7krq
my-shell:~# curl -s echo | jq -r .environment.HOSTNAME
echo-7fc4564b7b-4crhr
my-shell:~# curl -s echo | jq -r .environment.HOSTNAME
echo-7fc4564b7b-6f7lg
my-shell:~# curl -s echo | jq -r .environment.HOSTNAME
echo-7fc4564b7b-gg5jb
my-shell:~# curl -s echo | jq -r .environment.HOSTNAME
echo-7fc4564b7b-ktqz6
[Text 3] Topology Aware Hint 기능이 동작하지 않는 경우의 예시
[Text 3]은 [그림 3]과 동일한 형태의 Topology가 구성된 상태에서 Topology Aware Hint 기능이 적용되지 않을 경우, shell Pod에 접근하여 echo 서비스로 curl 요청을 전송하는 과정을 나타내고 있습니다. echo Server가 응답하는 값에는 echo Server가 동작하고 있는 노드의 호스트명이 포함되어 있습니다. 따라서 호스트명으로 curl 요청이 어느 echo Server Pod로 전송되었는지 파악할 수 있습니다. 8번의 curl 요청에 대해서 전부 다른 호스트명이 출력된 것을 확인할 수 있습니다. 즉 shell Pod가 위치하고 있는 AZ에 관계없이 curl의 요청이 모든 echo Server Pod에 전반적으로 골고루 분배되고 있다는 걸 추측할 수 있습니다.
[Text 4]는 [Text 2]에 명시된 echo 서비스의 Topology Aware Hint 어노테이션 주석을 제거하여 Topology Aware Hint 기능을 적용한 상태에서 curl 요청을 전송하는 과정을 나타내고 있습니다. echo서비스에 8번 요청해도 4개의 호스트명만 출력되는 것을 확인할 수 있습니다. Topology Aware Hint 기능이 적용 되었기 때문에 shell Pod가 위치한 Availability Zone C에 위치한 4개의 echo Server Pod에게만 curl 요청이 전달되었기 때문입니다.
Topology Aware Hint 기능 적용 조건
Topology Aware Hint 설정은 서비스에 Topology Hint 어노테이션만 붙이면 되지만, 앞서 언급한 것 처럼 어노테이션을 설정한다고 반드시 기능이 활성화 되는 건 아닙니다. Amazon EKS 클러스터의 컨트롤 플레인에 존재하는 쿠버네티스 컨트롤러 매니저에서 Topology Aware Hint가 적용되기 위한 조건을 먼저 확인한 이후 기능이 실제로 활성화 됩니다.
Topology Aware Hint가 실제 활성화 되기 위해서는 몇가지 조건이 필요한데 Amazon EKS 클러스터의 경우에 가장 중요한 조건은 각 AZ마다 필요한 최소한의 Pod 개수를 충족시키는 것입니다. 이는 Pod가 한쪽 AZ에만 치우쳐 있는 경우 Topology Aware Hint 기능으로 인해서 Pod의 개수가 적은 AZ에 존재하는 Pod들이 과한 부하를 받는 것을 방지하기 위해서 입니다.
[그림 4] Topology Aware Hint 기능 적용을 위한 AZ별 최소 Pod의 개수 계산 예제
각 AZ마다 필요한 최소한의 Pod의 개수는 각 AZ에 존재하는 워커 노드들의 CPU의 합의 비율로 결정이 됩니다. [그림 4]는 서비스에 연결된 Pod가 30개일 경우 각 AZ마다 필요한 최소한의 Pod 개수를 구하는 방법을 나타내고 있습니다. [그림 4]에서 위쪽은 각 AZ별로 모두 동일한 CPU의 개수를 갖고 있는 경우입니다. 따라서 AZ별로 필요한 최소한의 Pod의 개수는 동일합니다. [그림 4]의 아래쪽은 AZ 별로 CPU가 2:1:4로 할당된 경우입니다. 따라서 각 AZ별로 필요한 Pod의 개수도 대략적으로 2:1:4의 비율을 갖는 것을 알 수 있습니다.
각 AZ별로 필요한 Pod의 개수를 구할 때 1/1.2 상수를 곱하는 이유는 일부 Pod의 불균형을 허용하기 위해서 입니다. [그림 4]에서 각 AZ별로 모두 동일한 CPU의 개수를 갖고 있는 경우, 전체 30개의 Pod중에서 27(9+9+9)개를 제외한 나머지 3개의 Pod는 하나의 AZ에 치우쳐 있어도 Topology Aware Hint 기능이 동작하게 됩니다. 1/1.2 상수는 쿠버네티스 컨트롤러 매니저의 소스 코드에 고정된 값이기 때문에 변경이 불가능합니다.
실제 운영 시 각 AZ별 CPU의 개수를 파악한 후, 그에 비례하여 Pod를 배포하는 것은 매우 어려운 일입니다. 따라서 각 AZ별로 비슷한 규모의 워커 노드를 할당하고, 각 AZ별로 비슷한 Pod의 개수를 배포하여 Topology Aware Hint 적용 조건을 충족시키는게 좋습니다. Karpenter를 이용하는 환경이라면 [Text 2]와 같이 디플로이먼트에 Topology Spread Constraints설정을 통해서 각 AZ에 골고루 워커 노드와 Pod를 배포할 수 있습니다.
Topology Aware Hint 기능이 적용되었는지 확인하기 위해서는 서비스 생성시 같이 생성되는 EndpointSlice의 상태를 살펴보면 됩니다. EndpointSlice는 서비스를 통해서 패킷을 전달 받아야하는 Pod의 정보가 포함되어 있는 쿠버네티스의 객체입니다. [Text 5]는 Topology Aware Hint 기능을 활성화된 EndpointSlice의 일부를 나타내고 있습니다. endpoints 항목 하위에 hints 항목을 볼 수 있는데, 이 hints 항목이 존재하면 Topology Aware Hint 기능이 동작하고 있다는 걸 의미합니다.
[그림 5] Pod의 불균형 분배로 Topology Aware Hint 기능 미적용시 쿠버네티스 컨트롤러 매니저의 로그
만약 Topology Aware Hint 기능이 적용되지 않는다면 Amazon EKS 클러스터의 컨트롤 플레인에 존재하는 쿠버네티스 로그를 살펴봐야 합니다. 이 로그에는Topology Aware Hint 기능이 적용되지 못한 이유에 대한 정보가 포함되어 있습니다. [그림 5]는 각 AZ별로 Pod가 비슷하게 분배되지 않았을 경우 나타나는 쿠버네티스 컨트롤러 매니저의 로그 예시를 나타내고 있습니다.
Topology Aware Hint 기능 적용 예제
Istio 이용 시
Istio를 이용하는 경우 각 Pod마다 위치하고 있는 사이드카 프록시는 Istio에서 컨트롤 플레인의 역할을 수행하는 Istiod에 접속하여 설정에 필요한 다양한 정보를 수신합니다. Istiod는 Pod로 실행되기 때문에 사이드카 프록시는 Istiod Pod에 접속하는 경우 Istiod 서비스의 Cluster IP를 이용합니다. 즉, kube-proxy의 iptables 규칙을 통해서 접속하기 때문에 사이드카 프록시와 Istiod Pod 사이에 Cross-AZ 통신이 발생합니다.
[그림 6] Istio 이용 시 Topology Aware Hint 기능 적용에 따른 사이드카 프록시와 Istiod 사이의 통신 경로
[그림 6]은 Amazon EKS 클러스터에서 Istio를 이용하는 경우 사이드카 프록시에서 Istiod Pod까지 패킷 전송 시 Topology Aware Hint 기능 적용에 따른 통신 경로를 나타내고 있습니다. Istiod Pod를 디플로이먼트의 Topology Spread Constraints 기능을 통해서 모든 AZ에 균일하게 배포한 다음에 Istiod 서비스에 Topology Aware Hint 기능을 적용하면 사이드카 프록시와 Istiod Pod 사이의 Cross-AZ 통신을 제거할 수 있습니다.
단, Istio 환경에서 사이드카 프록시와 Istiod Pod 사이의 통신에서만 Topology Aware Hint 기능이 동작하며, Istio의 Virtual Service, Destination Rule을 사용하여 패킷을 분배할 경우에는 Istio에서 제공하는 Locality Load Balancing 기능을 활용해야 합니다.
인스턴스 대상 유형의 AWS Elastic Load Balancer (ELB) 이용 시
Amazon EKS 클러스터에서는 AWS ELB 이용 시 대상 유형을 지정할 수 있습니다. 대상 유형은 AWS ELB가 받은 트래픽을 kube-proxy의 iptables 규칙을 통해서 Pod에게 전달할지를 결정합니다. 대상 유형을 IP로 설정하면 트래픽은 kube-proxy의 iptables 규칙을 사용하지 않고 Pod로 전달되며, 대상 유형을 인스턴스로 설정하면 트래픽은 쿠버네티스 서비스의 Node Port를 통해서 kube-proxy의 iptables 규칙을 경유하여 Pod로 전달됩니다.
Amazon EKS 모범 사례에서는 IP 대상 유형의 AWS ELB 이용을 권장하고 있습니다. 하지만 다양한 이유로 인해서 Amazon EKS 클러스터에서 IP 대상 유형을 사용하지 못하고 인스턴스 대상 유형을 사용해야 하는 경우가 있습니다. 예를 들어 Amazon EKS 클러스터에서 Amazon VPC CNI가 아닌 다른 CNI 플러그인을 이용하고 있다면 반드시 대상 유형을 인스턴스로 설정해야 합니다.
[그림 7] 인스턴스 대상 유형의 AWS NLB 이용 시 Topology Aware Hint 기능 적용에 따른 통신 경로
[그림 8] 인스턴스 대상 유형의 AWS ALB 이용 시 Topology Aware Hint 기능 적용에 따른 통신 경로
대상 유형을 인스턴스로 설정하면 트래픽은 kube-proxy의 iptables 규칙을 경유하기 때문에 Cross-AZ 통신이 발생하게 됩니다. [그림 7]은 Amazon EKS 클러스터에서 대상 유형을 인스턴스로 설정한 AWS Network Load Balancer (NLB)를 사용하는 경우 Topology Aware Hint 기능 적용에 따른 통신 경로를 나타내고 있고, [그림 8]은 Amazon EKS 클러스터에서 대상 유형을 인스턴스로 설정한 AWS Application Load Balancer (ALB)를 사용하는 경우 Topology Aware Hint 기능 적용에 따른 통신 경로를 나타내고 있습니다.
Server Pod를 디플로이먼트의 Topology Spread Constraints기능을 통해서 모든 AZ에 균일하게 배포한 다음, Server 서비스에 Topology Aware Hint 기능 적용을 통해서 kube-proxy의 iptables 규칙과 Server 사이의 Cross-AZ 통신을 제거할 수 있습니다.
Ingress-nginx 이용 시
Amazon EKS 클러스터에서 HTTP와 같은 Client의 L7 요청을 처리하는 가장 쉬운 방법은 AWS ALB를 이용하는 방법입니다. 하지만 Nginx에서만 제공하는 기능을 이용하기 위해서 AWS ALB 대신 AWS NLB와 Ingress-nginx를 같이 이용하여 Client의 L7 요청을 처리할 수도 있습니다.
[그림 9] Ingress-nginx 이용 시Topology Aware Hint 기능 적용에 따른 통신 경로
[그림 9]는 AWS NLB와 Ingress-nginx를 이용하여 Client의 L7 요청을 처리하는 경우 Topology Aware Hint 기능 적용에 따른 통신 경로를 나타내고 있습니다. Ingress-nginx 서비스는 대상 유형이 IP 로 설정되어 있기 때문에 Client의 패킷은 NLB에서 Ingress-nginx Pod로 바로 전달됩니다. 이후에 Ingress-nginx Pod는 kube-proxy의 iptables 규칙과 유사하게 패킷을 Server Pod로 직접 전송합니다. 이 과정에서 Cross-AZ 통신이 발생합니다.
Ingress-nginx v1.6.8 버전부터 Topology Aware Hint 기능을 지원하기 때문에, Server 서비스에 Topology Aware Hint 기능을 적용하면 Ingress-nginx Pod는 Ingress-nginx Pod 자신과 동일한 AZ에 존재하는 Server Pod에게만 패킷을 전송합니다. 따라서 Ingress-nginx Pod와 Server Pod 모두 디플로이먼트의 Topology Spread Constraints 기능을 적용하여 모든 AZ에 균일하게 배포한 다음 Server 서비스에 Topology Aware Hint 기능을 적용하면 Cross-AZ 통신 을 제거할 수 있습니다.
결론
Amazon EKS 클러스터에서 쿠버네티스 서비스를 통해서 Pod 간 통신 시 Cross-AZ 통신이 발생하는 이유에 대해 알아보고, 이를 완화 시킬 수 있는 방법으로 Topology Aware Hint 기능을 살펴 보았습니다. 또한 Amazon EKS 클러스터에서 Topology Aware Hint 기능을 적용할 수 있는 Istio, AWS ELB, Ingress-nginx 관련 예시도 함께 알아보았습니다. Amazon EKS 클러스터에서 Cross-AZ 통신 비용이 부담되는 상황이라면, 비용 절감을 위한 방법으로 Topology Aware Hint 기능을 검토해 보시길 바랍니다.