IT/Container&k8s
Calico 기본 통신(동일 노드에서 Pod - Pod 간)
Hayley Shim
2024. 9. 22. 02:15
안녕하세요. Kubernetes Advanced Networking Study(=KANS) 3기 모임에서 스터디한 내용을 정리했습니다. 해당 글에서는 Calico의 동일 노드에서 파드 간 통신에 대해 자세히 알아보겠습니다.
파드(Pod) ↔ 파드간 통신
- iptables FORWARD Rule 에 정책(허용) 사용
- calice# 인터페이스에 proxy arp 설정으로 파드에서 바라보는 게이트웨이의 MAC 정보를 파드가 전달 받음
- 동일 노드 내에 파드 간 통신에서는 tunnel 인터페이스는 미 관여
파드 배포 전 기본 상태 확인
# 네트워크 인터페이스 정보 확인 : 터널(ipip) 인터페이스가 존재!
ip -c -d addr show tunl0
5: tunl0@NONE: <NOARP,UP,LOWER_UP> mtu 1480 qdisc noqueue state UNKNOWN group default qlen 1000
link/ipip 0.0.0.0 brd 0.0.0.0 promiscuity 0 minmtu 0 maxmtu 0
ipip any remote any local any ttl inherit nopmtudisc numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535
inet 172.16.158.0/32 scope global tunl0
valid_lft forever preferred_lft forever
# 네트워크 네임스페이스 확인
lsns -t net
# 네트워크 라우팅 경로 정보 확인
# 이중 bird 는 bird 데몬이 BGP 라우팅 프로토콜에 의해 파드 네트워크 대역을 전달받거나 전달하는 경로 → 각각 노드의 파드 대역입니다
ip -c route | grep bird
172.16.34.0/24 via 192.168.20.100 dev tunl0 proto bird onlink
blackhole 172.16.116.0/24 proto bird
172.16.158.0/24 via 192.168.10.101 dev tunl0 proto bird onlink
172.16.184.0/24 via 192.168.10.102 dev tunl0 proto bird onlink
# 아래 tunl0 Iface 에 목적지 네트워크 대역은 ipip 인캡슐레이션에 의해서 각 노드에 전달됩니다 → 각각 노드의 파드 대역입니다
route -n
root@k8s-w1:~# route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
172.16.34.0 192.168.20.100 255.255.255.0 UG 0 0 0 tunl0
172.16.116.0 0.0.0.0 255.255.255.0 U 0 0 0 *
172.16.158.0 192.168.10.101 255.255.255.0 UG 0 0 0 tunl0
172.16.184.0 192.168.10.102 255.255.255.0 UG 0 0 0 tunl0
192.168.0.2 192.168.10.1 255.255.255.255 UGH 100 0 0 ens5
192.168.10.0 0.0.0.0 255.255.255.0 U 100 0 0 ens5
192.168.10.1 0.0.0.0 255.255.255.255 UH 100 0 0 ens5
...
# (옵션) iptables rule 갯수 확인 >> iptables rule 이 모든 노드에서 똑같나요? 다른가요?
iptables -t filter -S | grep cali | wc -l
iptables -t nat -S | grep cali | wc -l
Quiz. blackhole 라우팅은 왜 있을까요?
- if there is no blackhole, packet will get forwarded (according to the best match rule) through the default gateway and you don't usually want this.[참고]
파드 배포 후 상태 확인
- 노드(k8s-w1)에 파드 2개 생성 후 확인
# [터미널1] k8s-m 모니터링
watch -d calicoctl get workloadEndpoint
# [터미널2] k8s-m 모니터링
# 파드 생성 : 노드의 이름이 다를 경우 아래 yaml 파일 다운로드 후 수정해서 사용하시면 됩니다
curl -s -O https://raw.githubusercontent.com/gasida/NDKS/main/4/node1-pod2.yaml
kubectl apply -f node1-pod2.yaml
# 생성된 파드 정보 확인
kubectl get pod -o wide
root@k8s-m:~# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod1 1/1 Running 0 34s 172.16.228.78 k8s-w1 <none> <none>
pod2 1/1 Running 0 34s 172.16.228.79 k8s-w1 <none> <none>
# calicoctl 이용한 endpoint 확인 : veth 정보도 출력!
calicoctl get workloadendpoints
WORKLOAD NODE NETWORKS INTERFACE
pod1 k8s-w1 172.16.158.2/32 calice0906292e2
pod2 k8s-w1 172.16.158.1/32 calibd2348b4f67
- 파드 생성 된 이후 다시 정보 확인
# 네트워크 인터페이스 정보 확인 : calice#~ 2개 추가됨!, 각각 net ns(네임스페이스) 0, 1로 호스트와 구별됨
ip -c link
root@k8s-w1:~# ip -c link
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: enp0s3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP mode DEFAULT group default qlen 1000
link/ether 02:70:da:68:71:43 brd ff:ff:ff:ff:ff:ff
3: enp0s8: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP mode DEFAULT group default qlen 1000
link/ether 08:00:27:1b:e3:db brd ff:ff:ff:ff:ff:ff
4: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN mode DEFAULT group default
link/ether 02:42:34:f0:2c:88 brd ff:ff:ff:ff:ff:ff
5: tunl0@NONE: <NOARP,UP,LOWER_UP> mtu 1480 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
link/ipip 0.0.0.0 brd 0.0.0.0
8: calibd2348b4f67@if4: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1480 qdisc noqueue state UP mode DEFAULT group default
link/ether ee:ee:ee:ee:ee:ee brd ff:ff:ff:ff:ff:ff link-netnsid cni-348afd47-fece-7c08-4488-9046da1e6139
9: calice0906292e2@if4: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1480 qdisc noqueue state UP mode DEFAULT group default
link/ether ee:ee:ee:ee:ee:ee brd ff:ff:ff:ff:ff:ff link-netnsid cni-3342c988-81a5-ebfa-058d-193dbbcb1d81
# 네트워크 네임스페이스 확인 : 아래 2개 pause(infra 컨테이너)가 각각 파드별로 생성됨 - 위 link-netnsid 0, link-netnsid 1 매칭됨
lsns -t net
root@k8s-w1:~# lsns -t net
NS TYPE NPROCS PID USER NETNSID NSFS COMMAND
4026531840 net 131 1 root unassigned /sbin/init
4026532216 net 2 31336 65535 0 /run/netns/cni-348afd47-fece-7c08-4488-9046da1e6139 /pause
4026532277 net 2 31372 65535 1 /run/netns/cni-3342c988-81a5-ebfa-058d-193dbbcb1d81 /pause
# 파드의 IP/32bit 호스트 라우팅 대역이 라우팅 테이블에 추가됨
ip -c route
root@k8s-w1:~# ip -c route
172.16.158.1 dev calibd2348b4f67 scope link
172.16.158.2 dev calice0906292e2 scope link
...
# (옵션) iptables rule 갯수 확인 : 필터(filter)에 기본 39 에서 91개로 룰(rule)이 많이 추가 되었다 >> 파드가 많아지면 rule 갯수는? >> 노드별로 똑같나요? 다른가요?
root@k8s-w1:~# iptables -t filter -S | grep cali | wc -l
91
root@k8s-w1:~# iptables -t nat -S | grep cali | wc -l
15
파드에 Shell 접속 후 확인
- 파드 1 Shell 접속
# 마스터 노드에서 아래 실행
kubectl exec pod1 -it -- zsh
...
# 아래 처럼 호스트 네트워크 인터페이스 9번인 caliceY와 veth 연결되어 있다
# IP는 32bit 서브넷을 가진다
pod1> ip -c addr
...
4: eth0@if9: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1480 qdisc noqueue state UP group default
link/ether ee:2f:83:31:9b:d0 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 172.16.158.2/32 brd 172.16.158.2 scope global eth0
valid_lft forever preferred_lft forever
# 라우팅 정보에 169.254.1.1 를 디폴트 게이트웨이 주소로 사용
pod1> route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
0.0.0.0 169.254.1.1 0.0.0.0 UG 0 0 0 eth0
169.254.1.1 0.0.0.0 255.255.255.255 UH 0 0 0 eth0
# ARP 정보는 현재 아무것도 없다
ip -c neigh
- 172.16.158.4/32 eth0@if12 인터페이스와 통신하지 않는 모든 대역은 169.254.1.1로 보낸다
- 파드 2 Shell 접속
# 마스터 노드에서 아래 실행
kubectl exec pod2 -it -- zsh
...
# 아래 처럼 if12 는 호스트 네트워크 인터페이스 12번인 calice0906292e2와 veth 연결되어 있다
# IP는 32bit 서브넷을 가진다
pod2> ip -c addr
...
4: eth0@if13: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1480 qdisc noqueue state UP group default
link/ether 7e:61:c6:fe:4f:8f brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 172.16.228.79/32 brd 172.16.228.79 scope global eth0
valid_lft forever preferred_lft forever
# 라우팅 정보에 169.254.1.1 를 디폴트 게이트웨이 주소로 사용
pod2> route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
0.0.0.0 169.254.1.1 0.0.0.0 UG 0 0 0 eth0
169.254.1.1 0.0.0.0 255.255.255.255 UH 0 0 0 eth0
# ARP 정보는 현재 아무것도 없다
ip neighbor show
파드1 → 파드2 ping 통신 및 확인
# 파드1 Shell 에서 실행 : 정상 통신!
ping -c 10 <파드2 IP>
# 게이트웨이 169.254.1.1 의 MAC 주소를 ARP 에 의해서 학습되었다
ip -c -s neigh
파드 간 통신 이해를 위한 준비
노드에서 실행)
# iptables 필터 테이블에 FORWARD 리스트 중 cali-FORWARD 룰 정보를 필터링해서 watch 로 확인
watch -d -n 1 "iptables -v --numeric --table filter --list FORWARD | egrep '(cali-FORWARD|pkts)'"
# (마스터노드) 파드 연결된 veth 를 변수를 확인
VETH1=$(calicoctl get workloadEndpoint | grep pod1 | awk '{print $4}')
echo $VETH1
VETH2=$(calicoctl get workloadEndpoint | grep pod2 | awk '{print $4}')
echo $VETH2
# (워커노드1) 위에서 확인한 파드 연결된 veth 를 변수에 지정
VETH1=calice0906292e2
VETH2=calibd2348b4f67
# 노드1 calice# 인터페이스의 proxy arp 설정 확인
# cat /proc/sys/net/ipv4/conf/<자신의 pod1에 연결된 calice# 이름>/proxy_arp
cat /proc/sys/net/ipv4/conf/$VETH1/proxy_arp
cat /proc/sys/net/ipv4/conf/$VETH2/proxy_arp
# 파드1 혹은 파드2에 veth 로 연결된 호스트 네트워크 인터페이스 calice# 중 1개 선택해서 tcpdump
tcpdump -i $VETH1 -nn
tcpdump -i $VETH2 -nn
# 노드에서 확인
# iptables 에 기본 FORWARD 는 DROP 이지만, 아래 cali-FORWARD Rule에 의해 허용되며, pkts 카운트가 증가한다
iptables -v --numeric --table filter --list FORWARD | egrep '(cali-FORWARD|pkts)'
watch -d "iptables -v --numeric --table filter --list FORWARD | egrep '(cali-FORWARD|pkts)'"
# 파드1에서 게이트웨이의 IP인 169.254.1.1 의 MAC 주소를 알기 위해서 ARP Request 를 보낸다
# 이때 veth 연결된 calice#~ 에 proxy arp 설정이 되어 있고, 자신의 mac 주소(ee:ee:ee:ee:ee:ee)를 알려주고, 이후 정상 통신됨
tcpdump -i $VETH1 -nn
tcpdump -i $VETH2 -nn
root@k8s-w1:~# tcpdump -i calice0906292e2 -nn
08:25:44.216545 ARP, Request who-has 169.254.1.1 tell 172.16.228.78, length 28
08:25:44.216566 ARP, Reply 169.254.1.1 is-at ee:ee:ee:ee:ee:ee, length 28
08:25:45.144618 IP 172.16.228.78 > 172.16.228.79: ICMP echo request, id 45451, seq 7, length 64
08:25:45.144674 IP 172.16.228.79 > 172.16.228.78: ICMP echo reply, id 45451, seq 7, length 64
- iptables 에 기본 FORWARD 는 DROP 이지만, 아래 cali-FORWARD Rule에 의해 허용되며, pkts 카운트가 증가하는 것을 알 수 있다.
# 호스트에서 calice0906292e2 의 MAC 주소 다시 확인
root@k8s-w1:~# ip -c -d link
...
12: calice0906292e2@if4: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1480 qdisc noqueue state UP mode DEFAULT group default
link/ether ee:ee:ee:ee:ee:ee brd ff:ff:ff:ff:ff:ff link-netnsid 0 promiscuity 0 minmtu 68 maxmtu 65535
veth addrgenmode eui64 numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535
다음 실습을 위해 파드 삭제 kubectl delete -f node1-pod2.yaml