본문 바로가기
K8S

EKS Service type과 Ingress target-type

by 기린2 2024. 6. 17.

시작

EKS에서 yaml을 작성하며 헷갈리는 사항 몇 가지가 있습니다.

Service yaml 작성 시, type ClusterIP로 할지 Nodeport로 할지와

Ingress yaml 작성 시, target-type ip로 할지 instance로 할지 입니다.

service yaml과 ingress yaml

그래서 아래 4가지 케이스에 대해 적용해보며 확인하고 AWS에서는 어떻게 사용해야하는지 이야기 해보려고 합니다.

  • Service Type: ClusterIP / Ingress Target Type : ip
  • Service Type: NodePort / Ingress Target Type : ip
  • Service Type: ClusterIP / Ingress Target Type : instance
  • Service Type: NodePort / Ingress Target Type : instance

 

TEST 환경

  • TEST 환경은 EKS 클러스터의 Node 4EA에서 진행합니다.
  • 2개의 NginX Pod가 실행됩니다.

노드 4EA / NginX Pod 2EA

 

 

첫 번째 CASE (Service Type: ClusterIP / Ingress Target Type : ip)

 첫 번째 CASE의 경우 POD는 아래와 같이 생성됩니다.

nginx pod 정보

그리고 Ingress Target Group을 IP로 지정했으므로, Target Group을 확인해보면 Pod IP가 지정되어 있음을 볼 수 있습니다.

ingress target 정보

다음은 파드가 떠 있는 노드에서 iptables Rule과 파드가 없는 노드에서의 iptables Rule 입니다.

pod가 떠 있는 노드의 iptables Rule
pod가 떠 있지 않은 노드의 iptables Rule

Service Type이 ClusterIP 이기 때문에 노드 iptables Rule에는 특별한 설정이 더는 없습니다.

여기서 하나 확인 할 수 있는건 파드가 있는 노드건 없는 노드건, 둘 다 hello라는 Service로 요청이 온다면, DNAT Rule을 통해 목적지 파드로 보내고 있다는 겁니다.

즉, 그림처럼  Service Type이 ClusterIP이고 ingress가 따로 없다면 Pod는 클러스터 내부에서만 알 수 있는 상황이 됩니다.  

 

 

두 번째 CASE (Service Type : NodePort / Ingress Target Type : ip)

 두 번째 CASE의 경우 POD는 아래와 같이 생성됩니다.

nginx pod 정보

첫 번째와 동일하게 Ingress Target Group을 IP로 지정했으므로, Target Group을 확인해보면 Pod IP가 지정되어 있음을 볼 수 있습니다.

ingress target 정보

Service Type 이 NodePort인 경우는 hello라는 Service로 요청이 온다면, DNAT Rule을 통해 목적지 파드로 보내는 룰 이외에 아래 그림처럼 또 다른 룰이 더 만들어 집니다.

pod가 떠 있는 노드의 iptables Rule
pod가 떠 있지 않은 노드의 iptables Rule

바로 노드의 30155 포트를 열고, 30155 포트로 둘어온 요청을 KUBE-EXT-LWT6K5FLZ63QAKX7 체인으로 전달 (jump)한다는 것입니다. (빨간 화살표)

그리고 첫 번째 Case와 같이  KUBE-EXT-LWT6K5FLZ63QAKX7 체인은 Pod로 라우팅 하게 되는 것입니다.

즉, 아래 그림과 같습니다.

하지만 이 경우 문제가 있습니다. 바로 파드가 떠 있지 않은 노드에도 30155 port가 오픈된다는 점입니다. (이 경우의 해결 방법은 네 번째 Case에서 설명하겠습니다.)

그리고 외부에서 접근하기 위한 Ingress의 target-type이 ip이기 때문에, Target Group은 Instance가 아닌 Pod의 IP를 바라보므로, 굳이 node의 port를 열 필요도 없는 것입니다.

 

 

세 번째 CASE (Service Type: ClusterIP / Ingress Target Type : instance)

이 경우는 외부에서 Pod에 접근하기 위해 성립할 수 없는 조건입니다.

Ingress의 Target Group은 Instance로 설정이 되어있는데, 노드의 어떠한 포트로 가야하는지 내용도 없으며,

설령 무작위 어떤 한 포트로 가더라도, 그 포트가 KUBE-EXT-LWT6K5FLZ63QAKX7 체인으로 전달 (jump)한다는 iptables Rule도 없기 때문입니다.

 

 

네 번째 CASE (Service Type: NodePort / Ingress Target Type : instance)

두 번째 CASE의 경우 POD는 아래와 같이 생성됩니다.

nginx pod 정보

그리고 Ingress Target Group을 Instance로 지정했으므로, Target Group을 확인해보면 Instance의 특정 포트가 지정되어 있음을 볼 수 있습니다.

ingress target 정보

두 번째와 마찬가지로 Service Type 이 NodePort인 경우는 hello라는 Service로 요청이 온다면, DNAT Rule을 통해 목적지 파드로 보내는 룰 이외에 아래 그림처럼 또 다른 룰이 더 만들어 집니다.

pod가 떠 있는 노드의 iptables Rule
pod가 떠 있지 않은 노드의 iptables Rule

즉, 아래 그림과 같습니다. 

두 번째 CASE에서 언급했듯 이 경우는 문제가 있습니다. 바로 파드가 떠 있지 않은 노드에도 30155 port가 오픈된다는 점입니다.

해당 그림처럼 ALB가 POD가 없는 노드 쪽으로 요청을 보내도, iptables Rule을 통해 요청은 정상적으로 가겠지만, 불필요한 하나의 홉을 더 건넌다는 것입니다.

이러한 경우 파드가 떠 있지 않은 노드에 포트를 차단하는 방법이 있습니다.

service yaml에 externalTrafficPolicy를 Local로 설정!


바로 위의 yaml 처럼 "externalTrafficPolicy: Local" 로 설정해주는 것입니다.

해당 옵션 사용 시, 노드의 또 다른 iptables 룰이 생겨 파드가 떠있지 않은 노드의 포트를 차단할 수 있게 됩니다.

pod가 떠 있는 노드의 iptables Rule
pod가 떠 있지 않은 노드의 iptables Rule

위 그림처럼 파드가 떠 있지 않은 노드에는 30155로 들어오는 패킷을 DROP 시키는 iptables rule이 추가되어 노드의 포트를 차단하게 되는 것입니다.

(iptables는 위에 룰보다 아래 룰이 마지막으로 적용됩니다.) 

해당 옵션 적용 후, Ingress의 Target 그룹을 살펴보면,

pod가 떠 있지 않은 노드는 포트가 막혀 헬스체크가 안됨!

노드의 포트가 막혀있기 때문에 그림처럼 파드가 떠 있지 않은 노드는 Unhealthy 상태로 변하게 됩니다.

 

 

결론

네 가지의 CASE를 보면서 각각의 방식이 어떻게 동작하는지 알 수 있었습니다.

결론은 AWS 환경에서는 굳이 Service Type은 NodePort로 쓸 필요가 없으며, Ingress Target Type도 instance로 쓸 필요가 없다는 것입니다.

Service Type은 NodePort 사용 시, ingress target group을 ip로 하면, ingress가 노드 포트를 거쳐가지도 않는데 노드의 포트만 열린 상황이 되며,

Ingress Target Type을 instance로 사용 시, ip로 한 것과 달리 불필요하게 Ingress가 노드를 한번 더 거쳐 POD로 가기 때문입니다.