3주차 [1] - Calico CNI
Calico CNI 동작과 통신흐름을 이해한다.
1. Calico 기본 통신 이해
Calico는 k8s의CNI입니다.
네트워크 보안기능 지원
k8s가 아닌 플랫폼간 통신 지원
실습을 위한 환경 생성합니다.

스택을 먼저 생성해줍니다.

기본 k8s 정보를 확인합니다.
kubectl config rename-context "kubernetes-admin@kubernetes" "HomeLab"
kubens default
kubectl cluster-info
kubectl get node -owide
kubectl get service,ep
kubectl get pod -A -owide
tree /opt/cni/bin/
ls -l /opt/cni/bin/
ip -c route
ip -c addr
iptables -t filter -L
iptables -t nat -L
iptables -t filter -L | wc -l
iptables -t nat -L | wc -l


Calico CNI를설치합니다.
kubectl apply -f https://raw.githubusercontent.com/gasida/KANS/main/kans3/calico-kans.yaml
curl -L https://github.com/projectcalico/calico/releases/download/v3.28.1/calicoctl-linux-amd64 -o calicoctl
chmod +x calicoctl && mv calicoctl /usr/bin
calicoctl version
kubectl get pod -A -o wide

1.1 Calico CNI 알아보기
Calico는 k8s의 Node간 통신을 연결하는 CNI Plugin 입니다.

calicoctl은 파드를 생성하거나, calico 설정이 변경될때, datastore에 정보가 변경됩니다.
calico datastore(A) 는 k8s API 저장소를 사용하며, calico 동작을 위해 데이터를 저장합니다.
bird(B) 는 오픈 소스 소프트웨어 라우팅 데몬 프로그램으로, pod의 CIDR을 광고합니다. 이를 통해서 각 노드는 다른 노드들의 pod들과 통신 할 수 있습니다.
felix(C)는 버드로 알아낸 pod 대역을 라우팅 테이블에 업데이트 하는 역할을 하고 IP Tables 규칙을 설정 합니다.
confd는 가벼운 오픈소스 설정 변경 관리 툴입니다. calico datastore 에 변경이 발생하면 bird의 변경된 설정 파일을 만들고 반영하게 합니다.
calico는 자체 IPAM 플러그인 을 제공합니다. (pod 에 할당할 IP 대역)
파드 생성할때, CNI 플러그인과 CNI IPAM 플러그인을 통해서 파드의 네트워크 설정을 하고, 통신의 경우 노드 felix의 iptables과 라우팅 테이블 통해서 이루어지게됩니다.

## kdd 의미는 쿠버네티스 API 를 데이터저장소로 사용 : k8s API datastore(kdd)
calicoctl version
# calico 관련 정보 확인
kubectl get daemonset -n kube-system
kubectl get pod -n kube-system -l k8s-app=calico-node -owide
kubectl get deploy -n kube-system calico-kube-controllers
kubectl get pod -n kube-system -l k8s-app=calico-kube-controllers -owide
# 칼리코 IPAM 정보 확인 : 칼리코 CNI 를 사용한 파드가 생성된 노드에 podCIDR 네트워크 대역 확인 - 링크
calicoctl ipam show
# Block 는 각 노드에 할당된 podCIDR 정보
calicoctl ipam show --show-blocks
calicoctl ipam show --show-borrowed
calicoctl ipam show --show-configuration

ipam show를 보면 각각 노드가 가지고 있는 대역을 확인할 수 있습니다.


iptables -t filter -S | grep cali

1.2 파드 <-> 파드 간 통신

1.2.1 [실습] - 기본 상태 확인

// 1. interface 확인합니다.
ip -c -d addr show tunl0
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.116.0/32 scope global tunl0
valid_lft forever preferred_lft forever
// 2. 네트워크 네임스페이스를 확인합니다.
lsns -t net
// 3. 라우팅 정보를 확인합니다.
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
// 4. 아래 tunl0 interface 에 목적지 네트워크 대역은 ipip encapsulation으로 전달됩니다.
route -n
(⎈|HomeLab:default) root@k8s-m:~# route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
0.0.0.0 192.168.10.1 0.0.0.0 UG 100 0 0 ens5
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

여기서 블랙홀 라우팅은
라우팅 루핑 문제
를 해결하기 위함
1.2.2 [실습] - 파드 배포 후 상태 확인
// pod 2개를 배포합니다.
curl -s -O https://raw.githubusercontent.com/gasida/NDKS/main/4/node1-pod2.yaml
kubectl apply -f node1-pod2.yaml
// pod 정보 확인
kubectl get pod -o wide
(⎈|HomeLab:default) root@k8s-m:~# 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
// pod에 들어가서 실제로 caliceY와 veth 연결 확인
(⎈|HomeLab:default) root@k8s-m:~# kubectl exec pod1 -it -- zsh
dP dP dP
88 88 88
88d888b. .d8888b. d8888P .d8888b. 88d888b. .d8888b. .d8888b. d8888P
88' `88 88ooood8 88 Y8ooooo. 88' `88 88' `88 88' `88 88
88 88 88. ... 88 88 88 88 88. .88 88. .88 88
dP dP `88888P' dP `88888P' dP dP `88888P' `88888P' dP
Welcome to Netshoot! (github.com/nicolaka/netshoot)
Version: 0.13
// 첫번째확인
ip -c addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host proto kernel_lo
valid_lft forever preferred_lft forever
2: tunl0@NONE: <NOARP> mtu 1480 qdisc noop state DOWN group default qlen 1000
link/ipip 0.0.0.0 brd 0.0.0.0
3: eth0@if7: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 8981 qdisc noqueue state UP group default qlen 1000
link/ether 66:55:fb:e8:31:53 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 172.16.158.2/32 scope global eth0
valid_lft forever preferred_lft forever
inet6 fe80::6455:fbff:fee8:3153/64 scope link proto kernel_ll
valid_lft forever preferred_lft forever
// 두번째 파드도 확인
(⎈|HomeLab:default) root@k8s-m:~# kubectl exec pod2 -it -- zsh
dP dP dP
88 88 88
88d888b. .d8888b. d8888P .d8888b. 88d888b. .d8888b. .d8888b. d8888P
88' `88 88ooood8 88 Y8ooooo. 88' `88 88' `88 88' `88 88
88 88 88. ... 88 88 88 88 88. .88 88. .88 88
dP dP `88888P' dP `88888P' dP dP `88888P' `88888P' dP
Welcome to Netshoot! (github.com/nicolaka/netshoot)
Version: 0.13
// 첫번째확인
ip -c addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host proto kernel_lo
valid_lft forever preferred_lft forever
2: tunl0@NONE: <NOARP> mtu 1480 qdisc noop state DOWN group default qlen 1000
link/ipip 0.0.0.0 brd 0.0.0.0
3: eth0@if6: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 8981 qdisc noqueue state UP group default qlen 1000
link/ether 86:0d:b1:c7:cf:fa brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 172.16.158.1/32 scope global eth0
valid_lft forever preferred_lft forever
inet6 fe80::840d:b1ff:fec7:cffa/64 scope link proto kernel_ll
valid_lft forever preferred_lft forever
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 프록시를 사용하게 됩니다.
arp 테이블은 mac address를 ip로 변환해주는 프로토콜입니다.
호스트간 통신시에 각각의 arp 테이블에 대해서 ip를 resolving하지 않고, 프록시 arp 테이블에 대해서 resolving 하는 메커니즘입니다.

1.3 파드 -> 외부(인터넷) 통신

1.3.1 최종 통신 흐름
1.3.2 파드 배포 전 기본상태 확인
(⎈|HomeLab:default) root@k8s-m:~# calicoctl get ippool -o wide
NAME CIDR NAT IPIPMODE VXLANMODE DISABLED DISABLEBGPEXPORT SELECTOR
default-ipv4-ippool 172.16.0.0/16 true Always Never false false all()
// Masquerade 동작 확인해봅니다.
(⎈|HomeLab:default) root@k8s-m:~# iptables -n -t nat --list cali-nat-outgoing
Chain cali-nat-outgoing (1 references)
target prot opt source destination
MASQUERADE all -- 0.0.0.0/0 0.0.0.0/0 /* cali:flqWnvo8yq4ULQLa */ match-set cali40masq-ipam-pools src ! match-set cali40all-ipam-pools dst random-fully
(⎈|HomeLab:default) root@k8s-m:~# ipset list cali40masq-ipam-pools
Name: cali40masq-ipam-pools
Type: hash:net
Revision: 7
Header: family inet hashsize 1024 maxelem 1048576 bucketsize 12 initval 0x0d1bf90a
Size in memory: 504
References: 1
Number of entries: 1
Members:
172.16.0.0/16

1.3.3 파드 배포 및 외부 통신 확인인
// pod 1개 생성
curl -O https://raw.githubusercontent.com/gasida/NDKS/main/4/node1-pod1.yaml
kubectl apply -f node1-pod1.yaml
# 워커노드에서 실행
# iptables NAT MASQUERADE 모니터링 : pkts 증가 확인
watch -d 'iptables -n -v -t nat --list cali-nat-outgoing'
// 여기 pkts 의 숫자가 증가한 것을 확인 할 수 있음
Every 2.0s: iptables -n -v -t nat --list cali-nat-outgoing k8s-w1: Sun Sep 22 00:46:30 2024
Chain cali-nat-outgoing (1 references)
pkts bytes target prot opt in out source destination
7 468 MASQUERADE all -- * * 0.0.0.0/0 0.0.0.0/0 /* cali:flqWnvo8yq4ULQLa */ match-set cali4
0masq-ipam-pools src ! match-set cali40all-ipam-pools dst random-fully
# (컨트롤플레인 노드) 파드 연결된 veth 를 변수를 확인
VETH1=$(calicoctl get workloadEndpoint | grep pod1 | awk '{print $4}')
echo $VETH1
# 패킷 덤프 실행 >> 어떤 인터페이스에서 패킷 캡쳐 확인이 되나요?
tcpdump -i any -nn icmp
tcpdump -i $VETH1 -nn icmp
tcpdump -i tunl0 -nn icmp
tcpdump -i ens5 -nn icmp # [실습환경 A Type]
root@k8s-w1:~# tcpdump -i $VETH1 -nn icmp
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on calice0906292e2, link-type EN10MB (Ethernet), snapshot length 262144 bytes
00:43:29.291259 IP 172.16.158.3 > 8.8.8.8: ICMP echo request, id 68, seq 1, length 64
00:43:29.324548 IP 8.8.8.8 > 172.16.158.3: ICMP echo reply, id 68, seq 1, length 64
00:43:30.292642 IP 172.16.158.3 > 8.8.8.8: ICMP echo request, id 68, seq 2, length 64
00:43:30.325814 IP 8.8.8.8 > 172.16.158.3: ICMP echo reply, id 68, seq 2, length 64
00:43:31.293941 IP 172.16.158.3 > 8.8.8.8: ICMP echo request, id 68, seq 3, length 64
00:43:31.327278 IP 8.8.8.8 > 172.16.158.3: ICMP echo reply, id 68, seq 3, length 64
00:43:32.295390 IP 172.16.158.3 > 8.8.8.8: ICMP echo request, id 68, seq 4, length 64
00:43:32.328588 IP 8.8.8.8 > 172.16.158.3: ICMP echo reply, id 68, seq 4, length 64
00:43:33.296907 IP 172.16.158.3 > 8.8.8.8: ICMP echo request, id 68, seq 5, length 64
00:43:33.330267 IP 8.8.8.8 > 172.16.158.3: ICMP echo reply, id 68, seq 5, length 64
00:43:34.298414 IP 172.16.158.3 > 8.8.8.8: ICMP echo request, id 68, seq 6, length 64
00:43:34.331650 IP 8.8.8.8 > 172.16.158.3: ICMP echo reply, id 68, seq 6, length 64
12 packets captured
12 packets received by filter
0 packets dropped by kernel
root@k8s-w1:~# tcpdump -i $VETH1 -nn icmp
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on calice0906292e2, link-type EN10MB (Ethernet), snapshot length 262144 bytes
00:45:08.811711 IP 172.16.158.3 > 8.8.8.8: ICMP echo request, id 90, seq 1, length 64
00:45:08.846996 IP 8.8.8.8 > 172.16.158.3: ICMP echo reply, id 90, seq 1, length 64


1.4 다른 노드에서 파드 <-> 파트 간 통신

1.4.1 최종 통신 흐름
각 노드에 파드 네트워크는 BIRD를 통해서 전파되고, FELIX에 의해 라우팅 테이블이 업데이트됩니다.
다른 노드 간 파드 통신시에는 IPIP 모드를 통해서 이루어지게 됩니다.

이렇게 protocol이 IPIP로 잡혀서 헤더값을 패킷에서 관찰 할 수 있습니다.
1.4.2 파드 배포 전 기본 상태 확인

(⎈|HomeLab:default) root@k8s-m:~# route | head -2 ; route -n | grep tunl0
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.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
// 나머지 노드들의 대역을 호스트 테이블에 있는것을 확인할 수 있다!
// tunl0 인터페이스 에 있는것을 확인할 수 있다.
1.4.3 파드 배포
curl -s -O https://raw.githubusercontent.com/gasida/NDKS/main/4/node2-pod2.yaml
kubectl apply -f node2-pod2.yaml
(⎈|HomeLab:default) root@k8s-m:~# curl -s -O https://raw.githubusercontent.com/gasida/NDKS/main/4/node2-pod2.yaml
kubectl apply -f node2-pod2.yaml
pod/pod1 unchanged
pod/pod2 created
(⎈|HomeLab:default) root@k8s-m:~# calicoctl get workloadendpoints
WORKLOAD NODE NETWORKS INTERFACE
pod1 k8s-w1 172.16.158.3/32 calice0906292e2
pod2 k8s-w2 172.16.184.1/32 calibd2348b4f67
// pods 배포 확인
// 라우팅 테이블: node 1에서 통신 대상의 ip 를 게이트웨이로 가지고 있는것을 확인할 수 있습니다.
root@k8s-w1:~# route -n | head -2 ; route -n | grep 172.16.
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
1.4.4 파드간 통신 실행 확인

// node 1에서 tcp dump 를 각각 interface 대상으로 뜹니다.
tcpdump -i tunl0 -nn
// pod1 에 접속해서 ping을 날려줍니다.
kubectl exec pod1 -it -- zsh
ping -c 10 172.16.34.0

패킷을 확인해보면, IPIP로 통신한것을 확인할 수 있습니다.
Last updated