이쿠의 슬기로운 개발생활

함께 성장하기 위한 보안 개발자 EverNote 내용 공유

클라우드/Kubernetes

76. kubernetes NodePort Networking 분석 (kube-proxy : IPVS mode)

이쿠우우 2021. 5. 7. 20:30
반응형

 

 

 

 

 

kubernetes NodePort Networking 분석

kube-proxy : IPVS mode

CNI = Flannel

 

 


[kubernetes kube-proxy 관련 글 목록]

54. Kubernetes kube-proxy Mode 분석

70. Kubernetes kube-proxy IPVS Mode 설정

74. Kubernetes NodePort Networking 분석 (kube-proxy : iptable mode)

75. kubernetes LoadBalancer Networking 분석 (kube-proxy : iptable mode)

76. kubernetes NodePort Networking 분석 (kube-proxy : IPVS mode)


 

 

 

목적

 

kube-proxy IPVS mode의 경우

NodePort로 접근하는 Networking 과정일 분석함.

 


 

 

글을 읽는데 필요한 기초 개념

 

IPVS (IP Virtual Server)란?

IPVS는 Netfilter Framework 기반으로 구현된

Linux Kernel Level에서 동작하는 

Layer-4(Transport Layer) Load Balancing Tool임.

Least-Connection 및 Round-Robin을 포함한 다양한 Load Balancing 알고리즘을 지원함.

 

[IPVS 동작원리]

패킷을 효과적으로 추적하고 Route하기 위해 IPVS는 Linux Kernel에 

IPVS 테이블을 생성함.

해당 테이블은 해쉬 테이블임.

IPVS 테이블은 ipvsadm 명령을 사용해서 지속적으로 업데이트가 가능함.

해당 테이블 목록에 따라 특정 End Point로 

설정된 Load Balancing 알고리즘을 사용해서 Load Balancing을 수행함.

 

[ IPVS에서 사용하는 Netfilter Hook Function ]

Netfilter Framework 기반으로 구현되어 

제공되는 Netfilter Hook Function 목록 중 아래의 6개를 사용해서 구현함.

ip_vs_remote_request()

ip_vs_local_request()

ip_vs_reply()

ip_vs_local_reply()

ip_vs_forward_icmp()

 

[IPVS 설정 명령]

ipvsadm

 

[IPVS Tunneling 종류]

Direct Routing

NAT

IPIP 

 

[IPVS를 사용하는 환경]

Linux에서 제공하는 L4 Load Balancer 솔루션인 LVS( Linux Virtual Server )에서도 

Packet Load Balancing 메카니즘으로 IPVS를 사용되고 있음.

 

참고

https://ssup2.github.io/theory_analysis/Linux_LVS_IPVS/

https://github.com/kubernetes/kubernetes/blob/master/pkg/proxy/ipvs/README.md

 

IPSET이란?

iptables의 확장 기능인 ipset은 IP들의 집합을 별도로 만들어 관리 할 수 있음. 

iptables 유틸을 통해 관리 할 때보다 

ipset은 좀 더 다양한 방법으로 많은 IP 셋을 관리 할 수 있는 이점과 

인덱싱 데이터 저장 방식으로 인해 

iptables로 5000건 이상의 룰셋이 등록 되었을때 

시스템의 성능이 급격하게 떨어지는 반면 

ipset은 많은 IP 세트를 설정 하더라도 성능이 심각하게 떨어지지 않는 장점이있음. 

iptables는 차단하고자하는 룰셋 목록이 많아지면 유지보수가 힘든데

이를 해결하기 위한 방법으로 IPSET을 많이 이용함.

 

 

 


 

 

환경

 

[Master Node server]

OS = CentOS 7

리눅스 커널 버전 : Linux 3.10.0-1062.el7.x86_64

docker version : 1.13.1

docker api verison : 1.26

 

[Worker Node server]

OS = CentOS 7

리눅스 커널 버전 : Linux 3.10.0-1062.el7.x86_64

docker version : 1.13.1

api verison : 1.26

 

[kubernetes version]

1.19.2

 

 

[생성되어있는 Pod, Service 정보]

pod1 IP = 10.244.1.160

pod2 IP = 10.244.1.169

pod3 IP = 10.244.1.170

pod4 IP = 10.244.1.171

 

NodePort Service = 10.109.153.101:30001

LoadBalancer Service = external 10.0.2.10:31857 

 

 


 

 

tcpdump 

External -> Worker Node IP:NodePort-> Container  

 

Pod는 Worker Node에 배포되어있는 상태.

 

 

환경

 

Tomcat Pod 

pod1 IP = 10.244.1.160

pod2 IP = 10.244.1.169

pod3 IP = 10.244.1.170

pod4 IP = 10.244.1.171

 

Service Object Type = NodePort 30001

Tomcat Pod 배포 Node = Worker Node

Tomcat Pod Port = 8080

 

Master Node IP = 10.0.2.15

Worker Node IP = 10.0.2.4

 

이외의 정보는 문서 상단위 "환경"에서 설명한 정보와 동일

 

external 환경에서 

curl [Worker Node IP]:NodePort

명령으로 Pod에 접근

 

 

 

 

 

NodePort Routing 

 

 

 

 

 

 

 

 

1. Worker Node : enp0s3 

 

 

 

VirtualBox의 경우 : enp0s3

VMware 의 경우 : ens192

에 해당하는 인터페이스임,

 

[명령어]

tcpdump -i enp0s3 -ne tcp  port 30001 -vv -c 10

[MAC 정보]
출발지 : 52:54:00:12:35:00  (VirtualBox Router Mac)
도착지 : 08:00:27:7c:5c:53  (Worker Node의 enp0s3 인터페이스 MAC주소)



[IP주소 정보]
출발지 : 192.168.56.1  (external에서 접근한 ip주소)
도착지 : 10.0.2.4 (Worker Node의 IP주소)


[Port]
출발지 : 랜덤한 Port 번호
도착지 : pago-services1 (설정된 NodePort 번호 30001)

( 192.168.56.1, 52:54:00:12:35:00 )

external에서 접근한 ip주소와

vritualBox 의 Router Mac 주소

 

test환경은 virtualbox 환경이라 해당 ip로 접근함.

virtualbox 에서 포트포워딩 설정되어있음.

 

 

 

패킷 흐름 분석

해당 인터페이스의 내용은 virtualBox 포트포워딩에 관한 내용.

모든 Kubernetes Node의 해당 인터페이스에서 

NodePort로 설정된 Port 를 감시하고 있으면

external network에서 nodePort를 통해 접근한다는 것을 확인 가능.

 

 

 

 

2. Worker Node : kube-proxy

 

 

 

nodeport = 30001임으로

lsof 명령으로 30001 port를 사용하는 프로세스를 확인

 

[명령어]

lsof -i:30001

 

[netstat 명령]

netstat -nap | grep kube-proxy

 

 

[확인 결과]

kube-proxy pod가 모든 NodePort 수신하고 있음.

NodePort Type의 service object를 생성하면

kube-proxy가 모든 NodePort를 포트 예약하고 

다른 프로세스가 사용할 수 없도록 함. 

즉 test환경에서는

30001대 port를 kube-proxy가 수신하고 있어서

해당 패킷은 kube-proxy로 감.

 

 

 

 

 

 

2.1. kube-proxy Mode 확인

 

Kube-proxy는 user space, iptables, ipvs 와 같은 mode로 동작할 수 있음으로

어떤 mode로 동작하는지 확인해야함.

 

[kube-proxy mode 확인 명령어]

kubectl logs -f [베포되어있는 kube-proxy pod 이름] -n kube-system

예) kubectl logs -f pod/kube-proxy-gcvm4 -n kube-system

 

[kube-porxy mode 확인 결과]

아래 log 내용을 보아

IPVS mode로 동작함.

 

 

 

 

2.2. kube-proxy Netfiler (iptables) 도식화

 

 

 

 

 

 

2.3. kube-proxy iptables 분석 

 

kube-proxy IPVS mode에서 IPVS는 Load Balancing은 가능하지만

패킷 Filtering, hairpin-masquerade , SNAT 등등의 작업은 처리 할 수 없음.

그래서 iptables 기능을 최소한으로 사용해야했으며 

kube-proxy iptables mode 처럼 많은 룰을 생성하면 안되니

ipset기술을 사용하여 문제를 해결함.

 

 

2.3.1. 모든 패킷은 kube-proxy가 생성한 iptables chain를 거쳐야함.

 

 

[명령어]

iptables -t nat -L OUTPUT

 

[분석]

kube-proxy에 의해 생성된 KUBE-SERVICES Chain을 확인할 수 있음.

 

[패킷 흐름]

enp0s3 인터페이스로 전달된 모든 패킷은

Host의 Network Namespace로 전달되고

해당 패킷은 OUTPUT Rule 중 

src = anywhere

dst = anywhere 

Rule에 매칭되어 

kube-proxy가 생성한 KUBE-SERVICES Chain Rule에 적용되기 시작함.

 

 

2.3.2. KUBE-SERVICES Chain의 Rule을 확인

 

[명령어]

iptables -t nat -L KUBE-SERVICES -n  | column -t

 

[분석]

 

(KUBE-LOAD-BALANCER)

패킷은 Chain 규칙에 따라 가장 처음에 있는 KUBE-LOAD-BALANCER Chain의 조건 에 맞는지 확인함

조건에 맞는지 확인하기 위해 KUBE-LOAD-BALANCER Chain과 match-set 되어있는

ipset 리스트 중 하나인 KUBE-LOAD-BALANCER를 확인하게 됨.

ipset -L 명령 목록 중 KUBE-LOAD-BALANCER를 확인해보면 아래와 같이

Members로 10.0.2.10:8080만 등록되어있음.

현재 Packet의 dst는 10.0.2.4:30001 임으로 매칭 되지 않아서 

해당 Chain은 적용되지 않고 통과하게 됨

적용되지 않고 통과된다는 증거로 아래의 그림을 확인해 보면

KUBE-LOAD-BALANCER의 pkts는 증가하지 않는것을 확인 할 수 있음.

 

(KUBE-MARK-MASQ)

패킷은 그 다음인 KUBE-MARK-MASQ Chain 조건을 확인함

조건을 확인해보니 source ip가 일치하지 않아 Chain을 타지 않고

다음으로 패킷은 KUBE-NODE-PORT Chain으로 가게됨.

 

(KUBE-NODE-PORT)

KUBE-NODE-PORT Chain source destination에 해당함으로 

KUBE-NODE-PORT Chain을 분석해야함.

 

 

[패킷 흐름]

KUBE-NODEPORTS Chain으로 전달됨.

 

 

2.3.3. KUBE-NODE-PORT Chain의 Rule을 확인

 

[명령어]

iptables -t nat -L KUBE-NODE-PORT  -n

 

[분석]

KUBE-NODE-PORT Chain은 match-set : KUBE-NODE-PORT-TCP 로 연결되어

ipset 리스트 중 KUBE-NODE-PORT-TCP를 확인하게 됨.

 

[명령어]

ipset -L

결과 중 KUBE-NODE-PORT-TCP 부분을 확인

참고로 해당 ipset -L 명령 결과는 kubernetes 를 구성하는 모든 Node가 동일함.

 

위와 같이  ipset의 KUBE-NODE-PORT-TCP 에 30001 항목이 있고

패킷의 정보 중 dst Port를 확인해보면 

[MAC 정보]
출발지 : 52:54:00:12:35:00  (VirtualBox Router Mac)
도착지 : 08:00:27:7c:5c:53  (Worker Node의 enp0s3 인터페이스 MAC주소)



[IP주소 정보]
출발지 : 192.168.56.1  (external에서 접근한 ip주소)
도착지 : 10.0.2.4 (Worker Node의 IP주소)


[Port]
출발지 : 랜덤한 Port 번호
도착지 : pago-services1 (설정된 NodePort 번호 30001)

위와같이 Port가 30001번으로 일치함.

해당 ipset Rule 매칭이 됨으로 

패킷은 KUBE-NODE-PORT Chain에 적용됨.

적용 되었다는 증거는 아래와 같음

KUBE-NODE-PORT pkts 만 증가되는 것을 확인 가능

그래서 KUBE-MARK-MASQ Chain을 타게됨.

 

 

[KUBE-MARK-MASQ]

KUBE-MARK-MASQ는 iptables의 MASQUERADE (SNAT)옵션에 해당함.

external network에서 kubernetes cluster에 생성된 service obejct로 접근 할 시

접근하는 모든 패킷에 Netfilter Mart를 추가하는 역할을 함. 

[MAC 정보]
출발지 : c2:c8:57:67:d6:27  (Worker Node(Host) cni0 인터페이스 MAC주소 변경됨)
도착지 : 08:00:27:7c:5c:53  (Worker Node의 enp0s3 인터페이스 MAC주소)



[IP주소 정보]
출발지 : 10.244.1.1 (Worker Node(Host)의  cni0인터페이스 IP주소로 변경됨)
도착지 : 10.0.2.4 (Worker Node의 IP주소)


[Port]
출발지 : 랜덤한 Port 번호로 변경됨.
도착지 : pago-services1 (설정된 NodePort 번호 30001)

 

Netfilter Mark로 패킷의 Source IP 주소를 Node(Host) IP주소로 변경함

해당 KUBE-MARK-MASQ를 탄다는 증거

위와 같이 KUBE-NODE-PORT Chain의 KUBE-MARK-MASQ Chain pkts가 증가는것을 확인가능

 

[패킷 흐름]

출발지 정보를 SNAT으로 변경하고 

패킷은 IPVS로 전달됨.

(iptables Netfilter Chain을 나온 패킷이 어떻게 IPVS로 가는지에 대한 

자세한 부분은 리서치가 추가로 필요함.)

 

 

2.3.4. IPVS Hash Tables 확인

 

[명령어]

ipvsadm -Ln

해당 ipvsadm -Ln명령은 kubernetes 를 구성하는 모든 Node가 동일함.

 

[분석]

상위의 결과 중에 매칭되는것을 찾기 위해 

패킷의 정보 중 dst 정보를 확인해보면 

[MAC 정보]
출발지 : c2:c8:57:67:d6:27  (Worker Node(Host) cni0 인터페이스 MAC주소)
도착지 : 08:00:27:7c:5c:53  (Worker Node의 enp0s3 인터페이스 MAC주소)



[IP주소 정보]
출발지 : 10.244.1.1 (Worker Node(Host)의  cni0인터페이스 IP주소)
도착지 : 10.0.2.4 (Worker Node의 IP주소)


[Port]
출발지 : 랜덤한 Port 번호
도착지 : pago-services1 (설정된 NodePort 번호 30001)

 

dst = 10.0.2.4:30001

정보를 확인할 수 있음.

해당 정보는 IPVS Hash Tables의 

TCP  10.0.2.4:30001 rr
  -> 10.244.1.160:8080            Masq    1      0          0
  -> 10.244.1.169:8080            Masq    1      0          0
  -> 10.244.1.170:8080            Masq    1      0          0
  -> 10.244.1.171:8080            Masq    1      0          0

결과와 일치함.

해당 Tables 하위의 4개 IP가 목적지 Pod 목록의 IP와 일치함.

rr 표시를 보고 해당 목록 중 대상 결정은

Round-Robin 알고리즘을 통해서 결정된다는 것을 알 수 있음.

 

[패킷 흐름]

IPVS를 통해 DNAT이 수행되어 

목적지가 해당 Pod목록 중 1개로 선정됨.

[MAC 정보]
출발지 : c2:c8:57:67:d6:27  (Worker Node(Host) cni0 인터페이스 MAC주소)
도착지 : ??? (Host의 ARP Tables 정보를 보고 도착지 IP에 해당하는 MAC으로 변경됨.)



[IP주소 정보]
출발지 : 10.244.1.1 (Worker Node(Host)의  cni0인터페이스 IP주소)
도착지 : 10.244.1. 160, 169, 170, 171 중 1개 (목적지 Pod의 주소)


[Port]
출발지 : 랜덤한 Port 번호
도착지 : 8080 (목적지 Pod의 Port)

 

 

 

2.4. 패킷 흐름 분석

enp0s3 인터페이스로 전달된 모든 패킷은

kube-proxy가 설정한 iptables, ipset Chain Rule을 통해

IPVS로 전달됨.

IPVS에서는 Hash Tables 정보를 확인하고 

다수의 Pod 목록 중 1개를 선택하기 위해 

Load Balancing을 수행함.

IPVS를 통과하면서 Marking 된 패킷은 아래와 같이

출발지 정보는 Node(Host)의 정보로 변경되고

도착지 정보는 Pod의 정보로 변경됨.

[MAC 정보]
출발지 : c2:c8:57:67:d6:27  (Worker Node(Host) cni0 인터페이스 MAC주소)
도착지 : ??? (Host의 ARP Tables 정보를 보고 도착지 IP에 해당하는 MAC으로 변경됨.)



[IP주소 정보]
출발지 : 10.244.1.1 (Worker Node(Host)의  cni0인터페이스 IP주소)
도착지 : 10.244.1. 160, 169, 170, 171 중 1개 (목적지 Pod의 주소)


[Port]
출발지 : 랜덤한 Port 번호
도착지 : 8080 (목적지 Pod의 Port)

 

해당 패킷은 다음 인터페이스인 cni0로 전달됨.

 

 

 

 

 

3. Worker Node : flannel.1 확인

 

[명령어]

tcpdump -i flannel.1   -ne tcp  -vv -c 10

 

패킷 흐름 분석

worker node의 flannel.1 인터페이스에 어떠한 패킷도 들어오지 않음.

 

 

 

 

 

4. Worker Node : flannel.1의 ARP Table확인

 

flannel.1에 어떠한 패킷도 들어오지 않는 이유를 확인하기 위해

Worker Node의 ARP Table을 확인

 

 

[명령어]

arp -an

해당 명령은 Worker Node에서 실행 

명령으로 Worker Node Flannel 의 ARP Table확인

 

[확인 결과]

이전에 통신을 했었음으로 ARP Table 목적지 Pod의 IP주소와 Mac주소가 있음.

그래서 패킷이 Worker Node의 flannel.1에서 탐지되지 않고

cni0 인터페이스로 바로 이동함.

 

 

 

 

 

5. Worker Node : cni0 확인

 

 

[명령어]

tcpdump -i cni0 -ne tcp and dst 10.244.1.171 -vv -c 10

 

 

패킷 흐름 분석

도착지 MAC정보가 ARP Tables의 Pod의 정보로 변경된것을 확인할 수 있음

[MAC 정보]
출발지 : c2:c8:57:67:d6:27  (Worker Node의 cni0 인터페이스 MAC주소)
도착지 : 3e:52:d6:a9:e3:94 ( ARP Table정보에서 확인했던 Pod Mac 주소)


[IP주소 정보]
출발지 : 10.244.1.1  (Worker Node cni0 인터페이스 IP)
도착지 : 10.244.1.171 중 1개 (목적지 Pod의 IP주소)


[Port]
출발지 : 랜덤한 Port 번호
도착지 : webcache (목적지 Pod의 Port : 8080)

 

 

 

 

 

 

 

6. Worker Node : veth60fa0925 (목적지 Pod)

 

 

[명령어]

tcpdump -i veth60fa0925  -ne tcp  -vv -c 10

 

veth60fa0925 인터페이스가 

목적지인 tomcat pod에 해당하는 인터페이스임.

Pod에 해당하는 veth 장비 찾기 참고 : Pod의 veth (Virtual Ethernet Interface) 장치 찾기

 

 

 

패킷 흐름 분석

cni0에서 확인했던 패킷을 Pod내부의 tomcat container가 받는것을 확인할 수 있음.

[MAC 정보]
출발지 : c2:c8:57:67:d6:27  (Worker Node의 cni0 인터페이스 MAC주소)
도착지 : 3e:52:d6:a9:e3:94 ( ARP Table정보에서 확인했던 Pod Mac 주소)



[IP주소 정보]
출발지 : 10.244.1.1  (Worker Node cni0 인터페이스 IP)
도착지 : 10.244.1.171 중 1개 (목적지 Pod의 IP주소)


[Port]
출발지 : 랜덤한 Port 번호
도착지 : webcache (목적지 Pod의 Port : 8080)

 

 

 

분석 완료..

 

 

 

 

 

 


제 글을 복사할 시 출처를 명시해주세요.
글에 오타, 오류가 있다면 댓글로 알려주세요! 바로 수정하겠습니다!


 

 

 

 

 

반응형