[네떡스터디🔥kans] 쿠버네티스 서비스 이해하기 - NodePort


CloudNet@ 가시다님이 진행하는 쿠버네티스 네트워크 스터디 KANS 3기 내용을 정리한 글입니다.

 

NodePort는 Kubernetes에서 외부 트래픽을 클러스터 내부의 특정 서비스로 전달할 수 있게 해주는 가장 간단한 방법 중 하나입니다. 클러스터의 각 노드에 특정 포트(기본 30000-32767)를 열어 외부 네트워크에서 노드의 IP와 포트를 통해 서비스에 접근할 수 있도록 해주며, 이를 통해 클러스터 외부에서도 서비스를 사용할 수 있습니다. 이번 글에서는 NodePort를 생성하는 방법과 함께 NodePort가 내부적으로 어떻게 동작하는지 자세히 살펴보겠습니다.

 

클러스터 정보

 

쿠버네티스 버전: 1.31

pod cidr: 10.10.0.0/16

service. cidr: 10.200.1.0/24

kube-proxy: iptables 모드

 

NodePort

nodeport의 통신 흐름

 

이번에는 mendhak/http-https-echo 이미지를 deployment로 띄워서 테스트해보도록 하겠습니다. 

 

외부에서 접속하는 것처럼 하기 위해 docker 명령어를 이용해서 kind 외부에 컨테이너를 띄우도록 하겠습니다. 네트워크를 kind 브릿지를 선택하도록 하면 통신이 되도록 됩니다. 

docker run -d --rm --name mypc --network kind nicolaka/netshoot sleep infinity

 

ping으로 테스트해보면 통신이 되는것 확인했습니다. 

 

 

🌐 iptables를 통한 Pod 간 통신 흐름

deploy-echo 서비스는 워커노드에만 떠있는데 control plane노드의 노드포트를 통해서도 통신이 되는지 확인해보도록 한다.

CNODE=192.168.228.5
NPORT=$(kubectl get service svc-nodeport -o jsonpath='{.spec.ports[0].nodePort}')
docker exec -it mypc curl -s --connect-timeout 1 $CNODE:$NPORT | jq

 

호출결과는 controlplane에서 신기하게도 control plane에 없는 파드에서 응답이 왔다.

 

 

conntrack 테이블을 조회해보면 목적지 아이피가 192.168.228.5로 설정되어있는것을 볼 수 있다. 

 

그리고 워커노드에서 확인해보면 출발지가 192.168.228.5로 바뀌어서 들어온것을 알 수 있다.

 

그림으로 그려보면 이렇다

 

iptables의 패킷 바이트 카운트 초기화

iptables -t filter --zero; iptables -t nat --zero; iptables -t mangle --zero

 

🔍 ngrep으로 흐름 분석해보기

다음의 명령어를 이용해서 패킷을 캡쳐해본다.

ngrep -t -W byline -d eth0 '' 'tcp port 8080'

 

ngrep으로 보았을 때 192.168.228.5 -> 10.10.2.3 으로 가는 내용이 찍혀있고 다시 다른 노드로 갔던 패킷이 controlplane으로 돌아와서 클라이언트로 가는 것을 확인할 수 있다.

 

그리고 이렇게 네트워크 홉이 늘어나면서 파드는 실제 클라이언트의 아이피를 유실하게 되는 문제가 발생한다.

 

🚦 Traffic policies - externalTrafficPolicy

clusterIP 모드에서 internalTrafficPolicy 모드를 확인했다면, 이번에는 externalTrafficPolicy 모드를 살펴볼 차례이다.

NodePort 모드를 설정하면 internalTrafficPolicy와 externalTrafficPolicy 두 가지를 모두 설정할 수 있어, 클러스터 내부에서 요청할 때와 외부에서 요청을 받을 때의 동작 방식을 분리할 수 있다. internalTrafficPolicy가 1.21버전에서 추가되었는데 그전까지는 내부 통신 방식과 외부 통신방식을 분리할 수 없어 externalTrafficPolicy를 Local로 하였을 때 내부 통신마저도 같은 노드에 파드가 없으면 통신이 되지 않았습니다. 두가지 모드가 생겨 외부 통신과 내부 통신을 나눌 수 있어 NodePort를 다양한 방식으로 사용할 수 있게 되었습니다.

 

동작 방식은 internalTrafficPolicy와 유사하며, Cluster와 Local 두 가지 모드가 있다.

  • Cluster (기본값): 모든 노드에 있는 파드로 트래픽을 분산시킨다. 이를 통해 모든 파드가 균등하게 트래픽을 처리할 수 있도록 한다.
  • Local: 해당 노드에 존재하는 파드로만 트래픽을 전달한다. 이렇게 설정하면 외부 네트워크를 경유하지 않고 동일한 노드 내에서 통신이 이루어져 레이턴시를 최소화할 수 있다. 하지만, 해당 서비스의 파드가 노드에 없을 경우 해당 노드에서는 트래픽이 처리되지 않아 조심해야한다.

Local로 설정하였을 때 iptables 룰

-A KUBE-EXT-3C3CHF4S6K2A4IXF -s 10.10.0.0/16 -m comment --comment "pod traffic for default/svc-nodeport-session:svc-webport external destinations" -j KUBE-SVC-3C3CHF4S6K2A4IXF
-A KUBE-EXT-3C3CHF4S6K2A4IXF -m comment --comment "masquerade LOCAL traffic for default/svc-nodeport-session:svc-webport external destinations" -m addrtype --src-type LOCAL -j KUBE-MARK-MASQ
-A KUBE-EXT-3C3CHF4S6K2A4IXF -m comment --comment "route LOCAL traffic for default/svc-nodeport-session:svc-webport external destinations" -m addrtype --src-type LOCAL -j KUBE-SVC-3C3CHF4S6K2A4IXF
-A KUBE-NODEPORTS -d 127.0.0.0/8 -p tcp -m comment --comment "default/svc-nodeport-session:svc-webport" -m tcp --dport 30512 -m nfacct --nfacct-name  localhost_nps_accepted_pkts -j KUBE-EXT-3C3CHF4S6K2A4IXF
-A KUBE-NODEPORTS -p tcp -m comment --comment "default/svc-nodeport-session:svc-webport" -m tcp --dport 30512 -j KUBE-EXT-3C3CHF4S6K2A4IXF
-A KUBE-SEP-77SJ4I7457Z62DTI -s 10.10.1.3/32 -m comment --comment "default/svc-nodeport-session:svc-webport" -j KUBE-MARK-MASQ
-A KUBE-SEP-77SJ4I7457Z62DTI -p tcp -m comment --comment "default/svc-nodeport-session:svc-webport" -m tcp -j DNAT --to-destination 10.10.1.3:8080
-A KUBE-SEP-DRJTE5LNTDWTQUB4 -s 10.10.3.3/32 -m comment --comment "default/svc-nodeport-session:svc-webport" -j KUBE-MARK-MASQ
-A KUBE-SEP-DRJTE5LNTDWTQUB4 -p tcp -m comment --comment "default/svc-nodeport-session:svc-webport" -m tcp -j DNAT --to-destination 10.10.3.3:8080
-A KUBE-SEP-F5ZJ2HSHTSMZVRJB -s 10.10.2.3/32 -m comment --comment "default/svc-nodeport-session:svc-webport" -j KUBE-MARK-MASQ
-A KUBE-SEP-F5ZJ2HSHTSMZVRJB -p tcp -m comment --comment "default/svc-nodeport-session:svc-webport" -m tcp -j DNAT --to-destination 10.10.2.3:8080
-A KUBE-SERVICES -d 10.200.1.191/32 -p tcp -m comment --comment "default/svc-nodeport-session:svc-webport cluster IP" -m tcp --dport 9000 -j KUBE-SVC-3C3CHF4S6K2A4IXF
-A KUBE-SVC-3C3CHF4S6K2A4IXF ! -s 10.10.0.0/16 -d 10.200.1.191/32 -p tcp -m comment --comment "default/svc-nodeport-session:svc-webport cluster IP" -m tcp --dport 9000 -j KUBE-MARK-MASQ
-A KUBE-SVC-3C3CHF4S6K2A4IXF -m comment --comment "default/svc-nodeport-session:svc-webport -> 10.10.1.3:8080" -m statistic --mode random --probability 0.33333333349 -j KUBE-SEP-77SJ4I7457Z62DTI
-A KUBE-SVC-3C3CHF4S6K2A4IXF -m comment --comment "default/svc-nodeport-session:svc-webport -> 10.10.2.3:8080" -m statistic --mode random --probability 0.50000000000 -j KUBE-SEP-F5ZJ2HSHTSMZVRJB
-A KUBE-SVC-3C3CHF4S6K2A4IXF -m comment --comment "default/svc-nodeport-session:svc-webport -> 10.10.3.3:8080" -j KUBE-SEP-DRJTE5LNTDWTQUB4
-A KUBE-SVL-3C3CHF4S6K2A4IXF -m comment --comment "default/svc-nodeport-session:svc-webport -> 10.10.2.3:8080" -j KUBE-SEP-F5ZJ2HSHTSMZVRJB