Flannel CNI Networking 분석
Flannel CNI Networking
CNI 란?
CNI : Container Network Interface
컨테이너 간의 네트워킹을 제어할 수 있는 플러그인을 만들기 위한 표준.
Kubernetes Cluster 내부는 Master Node 에 의해 여러 컨테이너가 생성 삭제 복구를 반복하고 있음
그에 따라 각 컨테이너의 고정적이지 않고 재할당이 빈번함.
이러한 특징을 해결하기 위해 Kubernetes Cluster는 가상 네트워크가 구성되어 있는데
기본적으로는 Worker Node 의 kube-proxy 가 네트워크를 관리하지만
보다 효율적인 네트워크 환경을 구성하기 위해
다양한 네트워크 관련 Addon 이 제공됨
종류)
- Ingress, Flannel, ACI, Calico, Canal, Cilium, CNI-Genie, Contiv, Multus, NSX-T, Nuage, Romana, Weave Net
Flannel 이란?
서로 다른 노드에 있는 pod 간 통신을 완성하기 위해서는 관련 기능을 제공하는 CNI가 필요함
Flannel 은 대표적인 CNI 종류 중 하나.
Flannel은 Kubernetes 용으로 설계된 오버레이 네트워크(overlay network)로
L3 네트워크를 구성하는 가장 간단하고 쉬운 방법을 제공함.
Kubernetes에 Flannel을 적용하게 되면 모든 Node에서 flanneld라는 에이전트를 실행하고
각 Node에 flannel.1 인터페이스를 할당함.
Flannel은 Kubernetes API 통해 etcd에
네트워크 구성, Pod에 할당된 IP 정보 등을 저장함.
Flannel은 VXLAN를 사용하여 원래의 패킷을 한번 더 감싸서 서로 다른 Node간 통신이 되도록 함.
실제로 각 Node 의
cni0 인터페이스 = VXLAN의 vSwitch와 같은 역할을 수행함.
flannel.1 인터페이스 = VXLAN의 VTEP과 같은 역할을 수행함.
오버레이 네트워크
물리 네트워크 위에 성립 되는 가상의 컴퓨터 네트워크.
실제 노드 간의 네트워크 위에 별도 Flat한 네트워크를 구성하는 것을 의미.
쉽게 비유를 하자면
서울에서 부산까지 가는데
차로 가게 되면
기존의 도로망 체제를 유지하면서
신호등도 거치고 여러 톨게이트를 거쳐야해서 복잡한데
비행기를 타면
김포공항에서 부산행 비행기 티켓을 끊고 한방에 가는 것과 같은 원리
VXLAN
[등장배경]
가상화 기반의 서버에서는 다량의 MAC Address Table을 보유하게 되는데
네트워크 Switch에서 최대 수용할 수 있는 MAC에 한계,
가상화 환경에서 네트워크 단일 도메인의 VLAN 숫자의 한계라는 문제가 발생함.
이러한 가상화 기술로 인해 발생하는 여러 네트워크 문제점을 해결하기 위해
VXLAN이 등장함.
[VXLAN 특징]
VLAN에 X (eXtensible)을 더한 것으로 L2 Network 의 확장성을 의미함
VXLAN Header + UDP + IP 기반으로 전송.
VXLAN Header는 24bit VXLAN Network ID로 구성됨.
학습하지 못한 목적지 VM MAC은 VXLAN기술 기반으로 IP Mulicast를 이용하여
목적지 VM MAC 주소가 있는 스위치에 전송
(Broadcast, Multicast Unknown destination)
VM MAC은 VXLAN기반의 Encapsulation된 패킷을 받아서 목적지
VM ARP Table을 학습하는 방식.
학습한 목적지 VM MAC은 P2P Tunnel로 직접 해당 스위치와 통신.
[VXLAN Address Learning 과정]
[VXLAN Unicast 과정]
Flannel Port
UDP = 8285
Flannel이 UDP 백엔드를 사용하여 캡슐화 된 패킷을 보내기 위해서 사용.
UDP = 8472
커널이 vxlan 백엔드를 사용하여 캡슐화 된 패킷을 보내기 위해서 사용.
예시 환경에서는 8472만 사용.
Flannel이 필요한 이유.
Kuberentes Default Network 구조를 사용하게 되면
상위 그림과 같이 Worker Node 가 2개 생성될 시
Pod 에 부여되는 IP주소가 동일하여
Worker Node 1 의 Pod 에서 Worker Node 2 Pod 로 통신을 할 시 IP가 같아서 문제 발생.
쉽게 말해서 다른 Worker Node 에 있는 Pod 간의 네트워크 통신에 문제가 생김.
kubernetes 에서는 다른 Worker Node 의 Pod 간 통신 문제점을 해결하기 위해 Kubernetes Addon 중 CNI 를 사용.
대표적으로 사용되는 CNI 는 flannel 이 있음.
flannel을 사용하면 상위 그림과 같이 다른 Worker Node 간 Pod의 IP가 동일하지 않아서
라우터를 거쳐서 Pod 간 통신이 가능해짐.
flannel CNI를 설치 후 ifconfig 확인 시
아래와 같이 flannel 과 cni0 장치가 추가됨을 확인 가능.
Pod 의 Ip를 확인해 보면 더이상 Docker 의 네트워크를 사용하지 않고
Flannel 장치를 사용해서 Ip가 할당되는 것을 확인 할 수 있음.
Master Node의 bridge Network를 예로 확인해보면
생성된 2개의 Pod의 veth 가상 네트워크 인터페이스가
cni0 에 바인딩된 것을 확인할 수 있음.
참고) flannel을 사용하지 않았다면 docker0 에 바인딩됨.
Flannel Network 구조 확인
구조 확인을 위한 예시 환경
아래와 같이 2개의 Pod가
서로 다른 Worker Node에 존재하고 있음
iksoon-deployment-tomcat = Worker Node 1
iksoon-deployment-mysql = Worker Node 2
[Worker Node 1과 2는 서로 다른 subnet를 가지고 있음]
Worker Node 1 = 10.244.1.0/24
Worker Node 2 = 10.244.2.0/24
Worker Node에 배포된 Pod 정보 확인
[IP 확인] : pod의 eth0
iksoon-deployment-tomcat = 10.244.1.96
iksoon-deployment-mysql = 10.244.2.108
[MAC 확인] : pod의 eth0
iksoon-deployment-tomcat = 76:b6:ae:e6:46:27
iksoon-deployment-mysql = 56:f3:bb:53:86:45
[veth 확인] : 각 Pod의 eth0 해당하는 veth 확인
iksoon-deployment-tomcat = vethd75fcd5d (Worker Node 1)
iksoon-deployment-mysql = vethd13b09a4 (Worker Node 2)
veth 란?
Virtual Ethernet.
linux namespace를 할당 받으면
network namespace도 생성되는데
network namespace에는 오직 veth만 할당 받을 수 있음
veth는 항상 쌍으로 생성이 되는데
한쪽에서 입력된 데이터는 다른쪽에서 수신할 수 있음
해당 Container가 할당받은 veth 경우는
한쪽은 물리적인 네트워크 인터페이스와 연결되어있고
다른 한쪽은 linux network namespace 와 연결되어
Container와 host간에 통신이 가능해짐
[ cni0 bridge 확인] : cni0 bridge에 매칭되어있는 veth 확인 ]
Worker Node 1
Worker Node 2
확인 결과
생성된 Pod의 eth0 인터페이스는
배포된 Worker Node의 veth에 연결이 되어있고
veth 는 Worker Node의 cni0 bridge에 연결이 되어있음.
그리고 각 Node에 생성된 flannel.1 과 cnio bridge 의 subnet이 동일함.
즉 eth0-veth-cni0-flannel.1 으로 연결되어 있음
Flannel CNI Routing 분석 1.
동일한 Worker Node에 있는 Pod 네트워크
동일한 Worker Node라면 통신과정이 매우 간단함.
컨테이너A가 같은 Worker Node에 있는 컨테이너B와 통신한다면
1. 컨테이너A의 veth인터페이스를 통해 트래픽이 Worker Node의 cni0 bridge로 push됨.
2. cni0 bridge는 ARP Broadcast를 해서 컨테이너B의 VM MAC을 가져 오려고 시도함.
3. 동일한 Worker Noded에 컨테이너B가 있기 때문에 성공적으로 컨테이너B의 VM MAC을 가져옴.
4. 가져온 MAC을 확인하고 cni0 ARP Table에 저장한 뒤 cni0 bridge를 통해 통신함.
동일한 workerNode에 있는 Pod로 네트워킹 할 경우
cni0 인터페이스가 패킷을 전달함.
trace 명령으로 확인해본 결과.
Flannel CNI Routing 분석 2.
서로 다른 Worker Node에 있는 Pod 네트워크
Worker Node 1에 배포되어 있는 Pod의 컨테이너A가
Worker Node 2에 배포되어 있는 Pod의 컨테이너B에 ping을 한다면.
1.
컨테이너A가 ICMP packet 을 만듬
[ ICMP Pack의 정보 ]
[MAC 정보] 출발지 : 76:b6:ae:e6:46:27 (컨테이너A의 MAC) 도착지 : ???? (컨테이너 B의 MAC) [IP주소 정보] 출발지 : 10.244.1.96 (컨테이너A의 IP) 도착지 : 10.244.2.108 (컨테이너B의 IP) |
하지만 정보중에 도착지 컨테이너B의 MAC주소를 아직 모름.
2.
컨테이너A는 목적지의 network subnet을 보고 다른 Worker Node에 있다는것을 인지함.
목적지로 가고싶지만 ARP Table이 없음.
따라서 통신을 위해 패킷을 기본 게이트웨이 즉 cni0 bridge로 보내야함.
Worker Node 1의 cni0 인터페이스 IP = 10.244.1.1
결과적으로 POD1은 cni0 즉 10.244.1.1의 MAC 주소를
얻기 위해 ARP Request (Layer 2 Broadcast)를 보냄.
3.
Worker Node 1의 cni0가 ARP Request를 받고
cni0의 MAC주소 : 7a:cc:a8:18:fa:12 를
respone함.
4.
컨테이너A는 respone을 받아서 ICMP Packet을 완성함.
도착지의 MAC 정보가 cni0로 설정됨.
[ ICMP Pack의 정보 ]
[MAC 정보] 출발지 : 76:b6:ae:e6:46:27 (컨테이너A의 MAC) 도착지 : 7a:cc:a8:18:fa:12 (Worker Node1의 cni0 MAC) [IP주소 정보] 출발지 : 10.244.1.96 (컨테이너A의 IP) 도착지 : 10.244.2.108 (컨테이너B의 IP) |
5.
ICMP가 전달되기 시작.
Worker Node 1의 cni0에서 tcpdump로 ICMP를 확인해보면 아래와 같이 정보를 확인할 수 있음.
[명령어]
tcpdump -i cni0 -ne icmp -vv -c 1
[MAC 정보] 출발지 : 76:b6:ae:e6:46:27 (컨테이너A의 MAC) 도착지 : 7a:cc:a8:18:fa:12 (Worker Node 1 cni0 의 MAC) [IP주소 정보] 출발지 : 10.244.1.96 (컨테이너A의 IP) 도착지 : 10.244.2.108 (컨테이너B의 IP) |
6.
Routing Table확인
10.244.2.0/24 대역대의 서브넷은 flannel1.1 인터페이스로 Static Routing 되어있는 것을 확인가능
7.
Worker Node 1의 cni0는 패킷을 Switching/ IP Rewrite를 해서
flannel.1 인터페이스로 보냄
[명령어]
tcpdump -i flannel.1 -ne icmp -vv -c 1
[MAC 정보] 출발지 : 4a:b3:de:c2:2b:be (Worker Node 1의 flannel.1인터페이스 MAC주소) 도착지 : 52:be:91:fd:4c:ea (Worker Node 2의 flannel.1인터페이스 MAC주소) [IP주소 정보] 출발지 : 10.244.1.96 (컨테이너A의 IP) 도착지 : 10.244.2.108 (컨테이너B의 IP) |
(참고)
Worker Node 1의 flannel.1인터페이스 MAC주소
Worker Node 2의 flannel.1인터페이스 MAC주소
도착지 Worker Node 2의 flannel.1의 MAC은 어떻게 알아낸걸까?
Flannel은 VXLAN 기반으로 동작하고 있음.
도착지의 MAC 정보도 VXLAN을 통해서 알아낸것임.
Worker Node 2의 VTEP IP 즉 flannel.1의 IP : 10.244.2.0 에 대한 정보는
Worker Node 1의 에서 실행되는 Flannel Pod의 ARP Table에 학습되고 기록됨.
Worker Node 1이 Worker Node 2의 flannel.1 MAC 주소를 알아온 과정 요약
[ 1. ]
Worker Node 1의 cni0 (VXLAN의 vSwitch역할과 동일)인터페이스가
패킷을 받으면 목적지 IP를 확인하고
이에 매칭되는 MAC값이 있는지 ARP Table을 확인함
하지만 최초 시도이기 때문에 매칭되는 MAC주소가 없음
cni0는 MAC주소를 알아오기 위해 ARP Request를 Flannel.1로 보냄
[ 2. ]
Worker Node 1의 Flannel.1(VXLEN의 VTEP 역할과 동일) 인터페이스가
Multicast 를 통해 연결되어있는 모든 Worker Node에게 ARP Request 를 날림.
[ 3. ]
해당 ARP Request를 받은 모든 Worker Node는
ARP Request 정보 중 출발지 정보를 보고
Worker Node 1의 Flannel.1 인터페이스의 MAC주소를
각자 자신의 cni0 ARP Table에 저장함.
[ 4. ]
많은 Worker Node 중에 2가 자신에게 해당 IP와 매칭이 되는 Pod가 배포되어있는것을 확인함.
Worker Node 2는 Unicast로 Worker Node 1에 ARP Respone을 보냄
ARP Respone에는 목적지 IP에 해당하는 MAC주소
즉 Worker Node 2의 Flannel.1 인터페이스의 MAC주소가 포함되어있음
[ 5. ]
Worker Node 1에서 해당 ARP Respone을 받고
ARP Table에 Worker Node 2의 Flannel.1 인터페이스의 MAC주소를 저장함.
[ 6. ]
그 뒤로 Worker Node 1과 2는 Unicast로 통신됨.
실제로 Worker Node 1의 Flannel Pod에서 ARP Table확인해봄.
kubetl get pod -o wide -n kube-system
명령으로 Worker Node 1에서 동작 중인 Flannel Pod를 확인
kubectl -it exec kube-flannel-ds-amd64-47fjz -n kube-system --arp -a
명령으로 Worker Node 1 Flannel 의 ARP Table확인
8.
Worker Node 1의 cni0 보낸 패킷을 Flannel.1 인터페이스가 받음.
Flannel.1 인터페이스는 VXLAN에서 VTEP과 동일한 역할을 수행하는데
그 작업으로 받은 패킷을 VXLAN 패킷으로 만들기 위해
패킷을 UDP를 사용하여 실제 네트워크를 통해 전송하기 위해 L3패킷으로 encapsulated (캡슐화)함.
UDP Header에는 출발지와 목적지 정보가 저장되어있고 Port는 8285 혹은 8472로 설정되어있음.
[VXLAN 패킷 예제]
9.
Worker Node 1 Flannel.1 인터페이스가 ens192로
VXLAN 캡슐화된 패킷을 보냄
[명령어]
tcpdump -i ens192 -ne udp -vv -c 1
Outer-packet [MAC 정보] 출발지 : 00:0c:2a:8c:0e:30 (Worker Node 1의 ens192인터페이스의 MAC) 도착지 : 00:0c:2a:8c:0e:31 (Worker Node 2의 ens192인터페이스의 MAC) [IP주소 정보] 출발지 : 10.2.13.128 (Worker Node 1의 IP) 도착지 : 10.2.13.129 (Worker Node 2의 IP) [Port 정보] 출발지 : 36473 도착지 : 8472 VXLAN으로 캡슐화 된 패킷 정보 [MAC 정보] 출발지 : 4a:b3:de:c2:2b:be (Worker Node 1의 flannel.1인터페이스 MAC주소) 도착지 : 52:be:91:fd:4c:ea (Worker Node 2의 flannel.1인터페이스 MAC주소) [IP주소 정보] 출발지 : 10.244.1.96 (컨테이너A의 IP) 도착지 : 10.244.2.108 (컨테이너B의 IP) |
[ 패킷의 특징 ]
외부로 나가는 Packet의 경우
목적지 Port가 8472 또는 8285로 설정되는데
위 예시에서는 8472로 설정됨
그리고 해당 패킷을 보면
출발지 컨테이너A의 MAC 주소는(76:b6:ae:e6:46:27)
찾아볼 수 없음
10.
ens192 인터페이스가 패킷을 Worker Node 2 로 보내기 위해
먼저 패킷을 ens192와 연결되어 있는 default Router로 전송함.
11.
Router는 패킷을 Worker Node 2로 전송하게 되고
보낼때와 역순으로
Worker Node2 의
ens192 -> flannel.1 -> cni0 -> veth0 -> eth0 로 전달됨.
Outer-packet [MAC 정보] 출발지 : 00:0c:2a:8c:0e:30 (Worker Node 1의 ens192인터페이스의 MAC) 도착지 : 00:0c:2a:8c:0e:31 (Worker Node 2의 ens192인터페이스의 MAC) [IP주소 정보] 출발지 : 10.2.13.128 (Worker Node 1의 IP) 도착지 : 10.2.13.129 (Worker Node 2의 IP) [Port 정보] 출발지 : 36473 도착지 : 8472 VXLAN으로 캡슐화 된 패킷 정보 [MAC 정보] 출발지 : 4a:b3:de:c2:2b:be (Worker Node 1의 flannel.1인터페이스 MAC주소) 도착지 : 52:be:91:fd:4c:ea (Worker Node 2의 flannel.1인터페이스 MAC주소) [IP주소 정보] 출발지 : 10.244.1.96 (컨테이너A의 IP) 도착지 : 10.244.2.108 (컨테이너B의 IP) |
[MAC 정보] 출발지 : 4a:b3:de:c2:2b:be (Worker Node 1의 flannel.1인터페이스 MAC주소) 도착지 : 52:be:91:fd:4c:ea (Worker Node 2의 flannel.1인터페이스 MAC주소) [IP주소 정보] 출발지 : 10.244.1.96 (컨테이너A의 IP) 도착지 : 10.244.2.108 (컨테이너B의 IP) |
[MAC 정보] 출발지 : 0e:c5:40:04:31:42 (Worker Node 2 cni0 의 MAC) 도착지 : 56:f3:bb:53:86:45 (컨테이너B의 MAC) [IP주소 정보] 출발지 : 10.244.1.96 (컨테이너A의 IP) 도착지 : 10.244.2.108 (컨테이너B의 IP) |
제 글을 복사할 시 출처를 명시해주세요.
글에 오타, 오류가 있다면 댓글로 알려주세요! 바로 수정하겠습니다!
참고
[Flannel network]
https://medium.com/@abhishek.amjeet/a-closer-look-at-networking-with-kubernetes-45710a3889e1
https://itnext.io/kubernetes-journey-up-and-running-out-of-the-cloud-flannel-c01283308f0e
https://jonnung.dev/kubernetes/2020/02/24/kubernetes-pod-networking/
https://docker-k8s-lab.readthedocs.io/en/latest/docker/docker-flannel.html
[ VXLAN ]
https://youngmind.tistory.com/entry/Network-Overlay-VXLAN-%EB%B6%84%EC%84%9D-1