kubernetes LoadBalancer Networking 분석
kube-proxy = iptable mode
CNI = Flannel
[kubernetes kube-proxy 관련 글 목록]
Kubernetes kube-proxy IPVS Mode 설정
Kubernetes NodePort Networking 분석 (kube-proxy : iptable mode)
kubernetes LoadBalancer Networking 분석 (kube-proxy : iptable mode)
kubernetes NodePort Networking 분석 (kube-proxy : IPVS mode)
환경
[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
[Test Pod 정보]
1개의 Pod 안에 tomcat 과 mysql container 2개를 생성
Pod는 worker node에 배포되어 있음
tomcat container port = 8080
mysql container port = 3306
tcpdump
External -> LoadBalancer IP -> Container
Pod는 Worker Node에 배포되어있는 상태.
환경
Tomcat Pod IP = 10.244.1.160
Service Object Type = LoadBalancer
LoadBalancer IP = 10.0.2.10
LoadBalancer NodePort = 31857
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 [LoadBalancer IP]:8080
명령으로 Pod에 접근
LoadBalancer Routing
1. Worker Node : enp0s3
VirtualBox의 경우 : enp0s3
VMware 의 경우 : ens192
에 해당하는 인터페이스임,
[명령어]
tcpdump -i enp0s3 -ne dst 10.0.2.10 -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.10 (LoadBalancer의 IP주소) [Port] 출발지 : 랜덤한 Port 번호 도착지 : webcache (LoadBalancer Service Object에 설정된 Port 8080) |
( 192.168.56.1, 52:54:00:12:35:00 )
external에서 접근한 ip주소와
vritualBox 의 Router Mac 주소
test환경은 virtualbox 환경이라 해당 ip로 접근함.
virtualbox 에서 포트포워딩 설정되어있음.
패킷 흐름 분석
해당 인터페이스의 내용은 virtualBox 포트포워딩에 관한 내용.
2. Worker Node : kube-proxy
LoadBalancer Type의 Service Object에 할당된 NodePort는 31857임으로
lsof 명령으로 31857 port를 사용하는 프로세스를 확인
[명령어]
lsof -i:31857
[netstat 명령]
netstat -nap | grep kube-proxy
[확인 결과]
kube-proxy pod가 모든 NodePort 수신하고 있음.
NodePort Type의 service object를 생성하면
kube-proxy가 모든 NodePort를 Port 예약하고
다른 프로세스가 사용할 수 없도록 함.
즉 test환경에서는
31857 port를 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-8fc7f -n kube-system
[kube-porxy mode 확인 결과]
아래 log 내용을 보아
default mode의 경우 : iptables
test 환경의 kube-proxy의 경우 default 설정을 사용했음으로
iptables mode로 동작함.
[kube-proxy iptable Mode란?]
kube-proxy의 iptables Mode는
netfilter를 사용해서 Chain Rule이라는 규칙을 지정한 뒤
패킷이 Chain Rule에 매칭되면 지정한 규칙에 따라서
패킷을 포워딩 함.
2.2. kube-proxy Netfiler (iptables) 도식화
2.3. kube-proxy Netfiler (iptables) 분석
2.3.1. 모든 패킷은 kube-proxy가 생성한 Netfiler를 거쳐야함.
[명령어]
iptables -t nat -L OUTPUT
[명령어]
iptables -t nat -L PREROUTING | column -t
[분석]
kube-proxy에 의해 생성된 KUBE-SERVICES Chain을 확인할 수 있음.
참고) PREROUTING Chain은 raw와 mangle에도 존재하는데 kube-proxy는 nat에만 규칙을 생성함.
[패킷 흐름]
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
[패킷 흐름]
패킷은 PREROUTING Chain Rule에 의해
KUBE-SERVICES Chain Rule의 영향을 받게됨.
KUBE-SERVICES에 설정된 src, dst, dpt 와
패킷 정보과 일치한다면 해당 KUBE-SERVICES Rule이 적용되지만
예제에서는
[MAC 정보] 출발지 : 52:54:00:12:35:00 (Master Node의 virbr0 인터페이스 MAC주소) 도착지 : 08:00:27:7c:5c:53 (Worker Node의 enp0s3 인터페이스 MAC주소) [IP주소 정보] 출발지 : 192.168.56.1 (external에서 접근한 ip주소) 도착지 : 10.0.2.10 (LoadBalancer의 IP주소) [Port] 출발지 : 랜덤한 Port 번호 도착지 : webcache (LoadBalancer Service Object에 설정된 Port 8080) |
위와같이 도착지 IP가 LoadBalancer의 IP이기 때문에
해당 IP인 10.0.2.10과 동일한 KUBE-FW-WIPTPKZMBRCHTN3G 가 적용됨.
2.3.3. KUBE-FW-WIPTPKZMBRCHTN3G Chain의 Rule을 확인
[명령어]
iptables -t nat -L KUBE-FW-WIPTPKZMBRCHTN3G -n | column -t
[분석]
KUBE-FW-WIPTPKZMBRCHTN3G Rule을 확인해보면
Kubernetes Cluster에 있는 모든 LoadBalancer Type의 Service Object정보를 확인할 수 있음
예제 환경에서는 LoadBalancer Type Service Objec가 한개 생성되어있어서 위와같이 1개를 확인할 수 있음
상위 정보의 주석부분( /* default/iksoon-tomcat-loadbalancer loadbalancer IP */)을 통해
namespace = default
service object name = iksoon-tomcat-loadbalancer loadbalancer
정보를 확인 할 수 있음
[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.10 (LoadBalancer의 IP주소) [Port] 출발지 : 랜덤한 Port 번호 도착지 : webcache (LoadBalancer Service Object에 설정된 Port 8080) |
Netfilter Mark로 패킷의 Source IP 주소를 Node(Host) IP주소로 변경함
해당 KUBE-MARK-MASQ을 통과한다는 증거는 아래와 같음
[명령어]
watch -n 1 iptables -t nat -L KUBE-FW-WIPTPKZMBRCHTN3G -nv
패킷이 KUBE-MARK-MASQ와 KUBE-SVC가 동시에 올라감.
[KUBE-MARK-DROP]
NAT가 활성화되지 않은 패킷에 Netfilter 마크를 추가함.
해당 패킷은 KUBE-FIREWALL체인 에서 폐기됨.
즉 정상적으로 Chain에 진입한 패킷인 아니면 폐기하기 위해 Mark하는 것으로 추정됨.
[패킷 흐름]
KUBE-FW-WIPTPKZMBRCHTN3G Rule에 있는
KUBE-SVC-WIPTPKZMBRCHTN3G로 이동함
2.3.4. KUBE-SVC-WIPTPKZMBRCHTN3G Chain의 Rule을 확인
[명령어]
iptables -t nat -L KUBE-SVC-WIPTPKZMBRCHTN3G -n | column -t
[분석]
KUBE-SVC-XXX Table에서는
iptables의 statistic 기능을 이용하여
요청 Packet은 Service를 구성하는 Pod들로 랜덤하고 균등하게
Load Balancing하는 역할을 수행함.
예제 환경에서 해당 service object에 매칭 되어있는 pod가 1개 임으로
위와같이 1개의 KUBE-SEP가 조회 되지만
Service Object에 다수의 pod가 매칭되는 환경이라면
다수의 KUBE-SEP가 생성되어 있고
해당 SEP 중에 한곳으로 Load Balancing 됨.
예시)
deployment replicaset 설정이 3인 경우
service obejct 에 3개의 pod가 매칭되어
아래와 같이 3개의 KUBE-SEP가 조회됨.
[패킷 흐름]
예제 환경에서는 KUBE-SEP가
KUBE-SEP-PWTXQ3FUHXN2P2HF 하나 있음으로
KUBE-SEP-PWTXQ3FUHXN2P2HF로 전달됨.
2.3.5. KUBE-SEP-PWTXQ3FUHXN2P2HF Chain의 Rule을 확인
[명령어]
iptables -t nat -L KUBE-SEP-PWTXQ3FUHXN2P2HF -n | column -t
[분석]
KUBE-MARK-MASQ와 DNAT가 존재함.
KUBE-SEP-XXX Table에서 요청 Packet은
Pod의 IP 및 Service에서 설정한 Port로 DNAT를 수행한다.
Pod의 IP로 DNAT된 요청 Packet은 CNI Plugin을 통해
구축된 Container Network를 통해서 해당 Pod에게 전달됨.
즉 Master Node의 cni0로 아래의 패킷을 만들어서 보냄.
[KUBE-MARK-MASQ]
Netfilter Mark로 패킷의 Source IP 주소를 Node(Host) IP주소로 변경함
상위 KUBE-FW 과정에서 TCP 패킷의 Source IP주소를 변경했고
여기서는 prot가 all로 설정되어 있는데
해당 KUBE-MARK-MASQ은 통과하지 않는것을 확인함.
위와같이 주장하는 근거는 아래와 같음
[명령어]
watch -n 1 iptables -t nat -L KUBE-SEP-PWTXQ3FUHXN2P2HF -nv
위와같이 KUBE-MARK-MASQ는 카운팅되지 않고 DNAT만 카운팅됨
[DNAT]
최종적으로 목표로 했던 Pod IP주소와 Port번호로 패킷을 변경해줌.
즉 kube-proxy가 생성한 Netfilter를 통과한 결과물임.
도착지 MAC 정보는 Host의 ARP Table에 따라서 입력됨.
[MAC 정보] 출발지 : 2e:bf:78:0e:53:8f (Master Node flannel.1인터페이스 MAC주소) 도착지 : ?? (Host의 ARP Tables 정보를 보고 도착지 IP에 해당하는 MAC으로 변경됨.) [IP주소 정보] 출발지 : 10.244.0.0 (master node의 flannel.1 인터페이스 ip주소) 도착지 : 10.244.1.160 (목적지 Pod의 IP주소로 변경) [Port] 출발지 : 랜덤한 Port 번호 도착지 : 8080 (LoadBalancer에 맵핑된 목적지 Pod의 Port번호로 변경) |
2.4. 패킷 흐름 분석
enp0s3 인터페이스로 전달된 모든 패킷은
kube-proxy가 설정한 Netfilter Chain Rule을 통과해야함.
netfilter를 통과하면서 Marking 된 패킷은 아래와 같이
출발지 정보는 Node(Host)의 정보로 변경되고
도착지 정보는 Pod의 정보로 변경됨.
[MAC 정보] 출발지 : 2e:bf:78:0e:53:8f (Master Node flannel.1인터페이스 MAC주소) 도착지 : ?? (Host의 ARP Tables 정보를 보고 도착지 IP에 해당하는 MAC으로 변경됨.) [IP주소 정보] 출발지 : 10.244.0.0 (master node의 flannel.1 인터페이스 ip주소) 도착지 : 10.244.1.117 (목적지 Pod의 IP주소로 변경) [Port] 출발지 : 랜덤한 Port 번호 도착지 : 8080 (LoadBalancer에 맵핑된 목적지 Pod의 Port번호로 변경) |
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을 확인.
CentOS의 경우 arp 명령으로 확인할 수 있지만 아래와 같은 방법으로도 확인할 수 있음
kubectl 명령은 Master Node에서 실행함.
[명령어]
kubectl get pod -o wide -n kube-system
명령으로 Worker Node에서 동작 중인 Flannel Pod를 확인
[명령어]
kubectl -it exec kube-flannel-ds-5qrzc -n kube-system -- arp -a
명령으로 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.160 -vv -c 10
패킷 흐름 분석
도착지 MAC정보가 ARP Tables의 Pod의 정보로 변경된 것을 확인할 수 있음
[MAC 정보] 출발지 : c2:c8:57:67:d6:27 ( Worker Node cni0 인터페이스 MAC주소) 도착지 : 16:34:aa:db:1f:25 ( ARP Table정보에서 확인했던 Pod Mac 주소) [IP주소 정보] 출발지 : 10.244.1.1 (Worker Node의 cni0 인터페이스 ip주소) 도착지 : 10.244.1.160 (목적지 Pod의 IP주소) [Port] 출발지 : 랜덤한 Port 번호 도착지 : webcache (목적지 Pod의 Port 8080) |
6. Worker Node : vethaa818364 (목적지 Pod)
[명령어]
tcpdump -i vethaa818364 -ne tcp -vv -c 10
vethaa818364 인터페이스가
목적지인 tomcat pod에 해당하는 인터페이스임.
Pod에 해당하는 veth 장비 찾기 참고 : Pod의 veth (Virtual Ethernet Interface) 장치 찾기
패킷 흐름 분석
cni0에서 확인했던 패킷을 Pod내부의 tomcat container가 받는 것을 확인할 수 있음.
[MAC 정보] 출발지 : c2:c8:57:67:d6:27 (Worker Node cni0 인터페이스 MAC주소) 도착지 : 16:34:aa:db:1f:25 ( ARP Table정보에서 확인했던 Pod Mac 주소) [IP주소 정보] 출발지 : 10.244.1.1 (Worker Node의 인터페이스 ip주소) 도착지 : 10.244.1.160 (목적지 Pod의 IP주소) [Port] 출발지 : 랜덤한 Port 번호 도착지 : webcache (목적지 Pod의 Port 8080) |
분석 완료...
제 글을 복사할 시 출처를 명시해주세요.
글에 오타, 오류가 있다면 댓글로 알려주세요! 바로 수정하겠습니다!
참고
[kube-proxy iptable netfilter]
https://rtfm.co.ua/en/kubernetes-service-load-balancing-kube-proxy-and-iptables/
https://kubernetes.io/docs/concepts/services-networking/service/
https://sktelecom-oslab.github.io/Virtualization-Software-Lab/ExposeService/
https://arthurchiao.art/blog/cracking-k8s-node-proxy/
[LoadBalancer Type의 경우 kubernetes Routing 참고]
https://www.stackrox.com/post/2020/01/kubernetes-networking-demystified/
[kubernetes routing]
https://ronaknathani.com/blog/2020/07/kubernetes-nodeport-and-iptables-rules/
[kube-proxy mode 설명]
https://ssup2.github.io/theory_analysis/Kubernetes_Service_Proxy/
https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies
[iptable 개념 : KUBE-MARK-MASQ 의 MASQUERADE 패킷]
'Kubernetes > 네트워크' 카테고리의 다른 글
kubernetes CNI 종류 확인 방법 (0) | 2021.11.04 |
---|---|
kubernetes NodePort Networking 분석 (kube-proxy : IPVS mode) (0) | 2021.05.07 |
Kubernetes NodePort Networking 분석 (kube-proxy : iptable mode) (1) | 2021.05.06 |
Kubernetes kube-proxy IPVS Mode 설정 (1) | 2021.03.19 |
Kubernetes kube-proxy Mode 분석 (4) | 2020.12.20 |