Search

Calico 통신

Tags
k8s
cni
calico
Created
2024/09/14 11:58
Created time
2024/09/14 02:58
category
kubernetes

개요

Flannel과 마찬가지로 Calico에서의 통신 흐름을 탐구
Pod 별 네트워크 네임스페이스 / 호스트 네트워크 네임스페이스의 구분은 이전 글과 거의 동일

동일 노드에서의 Pod 간 통신

동일 노드에서의 Pod 간 통신에서는 내부에서 직접적으로 통신
IPTABLES의 FORWARD 정책을 이용하고, 동일 노드에서의 Pod 간 통신에서는 tunl0 인터페이스는 관여하지 않음
Calico 인터페이스의 Proxy ARP 설정 덕에 디폴트 GW의 MAC 주소를 통신하려는 Pod가 받을 수 있음 (169.254.1.1)
169.254.1.1을 호스트에 설정하지 않고도 Proxy ARP로 처리할 수 있다는 것이며,Pod와 Calico 인터페이스는 P2P로 통신하기 때문에 호스트 L2에 도달하지 않으므로 모든 Calico 인터페이스가 동일한 맥주소 (ee:ee:ee:ee:ee:ee)를 사용해도 문제가 없음

노드 초기 상태 확인

# 터널 인터페이스 존재 확인 ip -c -d addr show tunl0 # 호스트 네트워크 네임스페이스 확인 lsns -t net # 라우팅 경로 확인 # 루핑 방지를 위해 Blackhole이 존재 ip -c route | grep bird # tunl0 인터페이스를 사용하는 주소 대역은 IPIP 캡슐화 되어 각 노들 전달 # 즉, 각 노드의 podCIDR을 의미 route -n
Shell
복사

Pod 배포 (node1-pod2.yaml)

노드 이름을 직접 기재했고, 이미지는 네트워크 장애 처리에 용이한 netshoot을 이용
apiVersion: v1 kind: Pod metadata: name: pod1 spec: nodeName: k8s-w1 containers: - name: pod1 image: nicolaka/netshoot command: ["tail"] args: ["-f", "/dev/null"] terminationGracePeriodSeconds: 0 --- apiVersion: v1 kind: Pod metadata: name: pod2 spec: nodeName: k8s-w1 containers: - name: pod2 image: nicolaka/netshoot command: ["tail"] args: ["-f", "/dev/null"] terminationGracePeriodSeconds: 0
Shell
복사
# Pod 확인 kubectl get po -A -o wide | grep default # endpoint 및 veth 확인 calicoctl get workloadendpoints
Shell
복사

변경된 노드 상태 확인

# Calico 인터페이스 2개가 추가됨을 확인 ip -c link # Pod 별 Pause 컨테이너 생성으로 네트워크 네임스페이스 생성 lsns -t net # Calico 내용이 라우팅 테이블에 반영 ip -c route
Shell
복사

Pod 통신 이전 상태

# Pod 1 접속 kubectl exec -it pod1 -- zsh # 호스트의 12번 인터페이스와 veth 연결 ip -c addr # 라우팅 정보로 169.254.1.1을 디폴트 GW로 이용 route -n # ARP 정보 없음 ip -c neigh
Shell
복사
# Pod 2 접속 kubectl exec -it pod2 -- zsh # 호스트의 13번 인터페이스와 veth 연결 ip -c addr # 라우팅 정보로 169.254.1.1을 디폴트 GW로 이용 route -n # ARP 정보 없음 ip -c neigh
Shell
복사

Pod 간 통신 준비

# 좌측은 마스터 노드 # IPTABLES 필터 테이블의 FORWARD 리스트에서 cali-FORWARD를 확인 watch -d -n 1 "iptables -v --numeric --table filter --list FORWARD | egrep '(cali-FORWARD|pkts)'" # 중간은 마스터 노드 # Pod를 통해 veth를 확인 VETH1=$(calicoctl get workloadEndpoint | grep pod1 | awk '{print $4}') VETH2=$(calicoctl get workloadEndpoint | grep pod2 | awk '{print $4}') echo $VETH1 echo $VETH2 # 우측 상하단은 워커 노드 VETH1=... VETH2=... # Proxy ARP 설정을 확인 # cat /proc/sys/net/ipv4/conf/<veth>/proxy_arp cat /proc/sys/net/ipv4/conf/$VETH1/proxy_arp cat /proc/sys/net/ipv4/conf/$VETH2/proxy_arp # 우측 상단 # veth1 tcpdump tcpdump -i $VETH1 -nn # 우측 하단 # veth2 tcpdump tcpdump -i $VETH2 -nn
Shell
복사

Pod 간 통신 실행

# Pod 1 접속 kubectl exec -it pod1 -- zsh # ping 10회 수행 ping -c 10 <pod2-ip> # 디폴트 GW 169.254.1.1의 MAC 주소를 ARP에 의해 학습 ip -c -s neigh
Shell
복사
pod1에서 디폴트 GW인 169.254.1.1의 MAC 주소를 알기 위해 ARP Request 전송하고, veth인 Calico 인터페이스에서 Proxy ARP 설정이 되어 있기 때문에 자신의 MAC 주소인 ee:ee:ee:ee:ee:ee를 알려주면서 정상 통신 가능
** 다음 실습을 위해 pod1, pod2를 삭제

외부로의 통신

Pod가 위치한 노드의 네트워크 인터페이스 IP 주소로 MASQUERADE 되는 방식으로 Pod가 외부로 통신
Calico의 기본 설정은 natOutgoing: true이므로 MASQUERADE로 연결이 가능한 것이고, 해당 방식 역시 tunl0 인터페이스는 통신에 관여하지 않음
natOutgoing: false로 설정 시, NAT의 MASQUERADE 정책이 삭제되면서 tcpdump에 요청 패킷만 있고 응답 패킷은 없음

노드 초기 상태 확인

# natOutgoing 값이 true임을 확인 calicoctl get ippool -o wide # 노드에서 외부로 통신을 위한 MASQUERADE 정책을 확인 iptables -n -t nat --list cali-nat-outgoing # MASQUERADE 엔트리 관련 헤더 데이터를 나열 # https://linux.die.net/man/8/ipset ipset list cali40masq-ipam-pools
Shell
복사

Pod 배포 (node1-pod1.yaml)

노드 이름을 직접 기재
apiVersion: v1 kind: Pod metadata: name: pod1 spec: nodeName: k8s-w1 containers: - name: pod1 image: nicolaka/netshoot command: ["tail"] args: ["-f", "/dev/null"] terminationGracePeriodSeconds: 0
Shell
복사

외부로의 통신 준비

# 좌측은 마스터 노드 # iptables NAT MASQUERADE 모니터링 watch -d 'iptables -n -v -t nat --list cali-nat-outgoing' # 중간은 마스터 노드 # Pod 연결된 veth 변수를 확인 VETH1=$(calicoctl get workloadEndpoint | grep pod1 | awk '{print $4}') echo $VETH1 # 우측 1,2,3,4 창 # 위에서 확인한 veth를 변수에 지정 VETH1=... # 우측 1,2,3,4 창 tcpdump -i any -nn icmp tcpdump -i $VETH1 -nn icmp tcpdump -i tunl0 -nn icmp tcpdump -i ens5 -nn icmp
Shell
복사

외부로의 통신 실행

# Pod 1 접속 kubectl exec -it pod1 -- zsh # ping 10회 수행 ping -c 10 8.8.8.8
Shell
복사
192.186.10.101은 노드의 외부로 통신할 수 있는 네트워크 인터페이스로, 외부 통신 시 IP 주소 변경되어서 기록되는 것을 확인 가능
** 다음 실습을 위해 pod1을 삭제

다른 노드에서의 Pod 간 통신

이전 두 사례와 달리 서로 다른 노드에서의 Pod 간 통신에서는 IPIP 모드의 tunl0 인터페이스를 이용하여 통신이 이뤄짐
IPIP 모드에서는 tunl0 인터페이스를 거치면서 IP 헤더를 한 번 더 감싸면서 전달되고, 목적지에서의 tunl0 인터페이스에서 벗겨지면서 Pod로 도달
각 노드의 네트워크 대역은 bird에 의해서 BGP로 광고 및 전파되고, felix에 의해서 해당 내용이 반영

노드 초기 상태 확인

# 마스터 및 워커 노드에서 실행 # 특정 노드에서 나머지 노드들의 podCIDR을 테이블로 보유하고 tunl0에서 이용 route | head -2 ; route -n | grep tunl0 # 노드 별 tunl0 정보 확인 # Calico를 이용하면 Pod의 인터페이스도 기본 MTU 1480을 사용 ifconfig tunl0
Shell
복사

Pod 배포 (node2-pod2.yaml)

노드 이름을 직접 기재
apiVersion: v1 kind: Pod metadata: name: pod1 spec: nodeName: k8s-w1 containers: - name: pod1 image: nicolaka/netshoot command: ["tail"] args: ["-f", "/dev/null"] terminationGracePeriodSeconds: 0 --- apiVersion: v1 kind: Pod metadata: name: pod2 spec: nodeName: k8s-w2 containers: - name: pod2 image: nicolaka/netshoot command: ["tail"] args: ["-f", "/dev/null"] terminationGracePeriodSeconds: 0
Shell
복사
# 마스터 노드에서 실행 # 워커 노드 1, 2에 생성된 endpoint 확인 calicoctl get workloadendpoints # 워커 노드에서 실행 # Pod 생성 후에는 각 상대방 노드의 IP를 전달 받아 저장 route -n | head -2 ; route -n | grep 172.16.
Shell
복사

Pod 간 통신 준비

# 좌측은 워커 노드 # tunl0 인터페이스 TX/RX 패킷 수 확인 watch -d 'ifconfig tunl0 | head -2 ; ifconfig tunl0 | grep bytes' # tunl0 인터페이스 패킷 덤프 # 파일 저장은 tcpdump -i tunl0 -nn -w <filename> tcpdump -i tunl0 -nn # 노드 인터페이스 패킷 덤프 # 파일 저장은 tcpdump -i ens5 -nn proto 4 -w <filename> tcpdump -i ens5 -nn proto 4
Shell
복사

Pod 간 통신 실행

# pod1 접속 kubectl exec pod1 -it -- zsh # ping 10회 수행 ping -c 10 <pod2-ip>
Shell
복사
tunl0 인터페이스의 TX/RX 패킷 수 증가된 것을 확인할 수 있고, tunl0 인터페이스 상에서는 Pod의 IP를 확인할 수 있으며 노드 인터페이스에서는 노드들 간의 전달을 위한 IP를 확인 가능
** 다음 실습을 위해 pod1, pod2를 삭제

참고

** Flannel에서 사용했던 VXLAN과 IPIP 방식의 차이가 궁금하여 정리
기능
IPIP
VXLAN
캡슐화 방식
IP 패킷을 다른 IP 패킷에 캡슐화
UDP 패킷을 VXLAN 헤더에 캡슐화
헤더 오버헤드
20바이트
8바이트
지원하는 네트워크
모든 IP 네트워크
UDP를 지원하는 네트워크
보안
암호화를 지원하지 않음
UDP를 통해 암호화를 지원
IPIP, VXLAN 모두 오버레이 네트워크에서 사용되는 터널링 프로토콜
단순 패킷 전송에 중점을 둔 IPIP, 더 많은 네트워킹 기능을 지원하는 VXLAN