3주차 [1] - Calico CNI

  • Calico CNI 동작과 통신흐름이해한다.

1. Calico 기본 통신 이해

  • Calico는 k8s의CNI입니다.

    • 네트워크 보안기능 지원

    • k8s가 아닌 플랫폼간 통신 지원

  • 실습을 위한 환경 생성합니다.

실습을 진행하게될 환경

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

  1. 기본 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
  1. 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
calico 설치 확인

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
ip table들에 대한 규칙 확인

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
namespace 확인
  • 여기서 블랙홀 라우팅은 라우팅 루핑 문제를 해결하기 위함

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