KANS 스터디 3주차 - Calico CNI 1편

유형욱·2022년 2월 5일
0

KANS 스터디

목록 보기
4/9
post-thumbnail

KANS 스터디 3주차 - Calico CNI 1편

개요

실습환경

실습 환경은 K8S v1.22.6, 노드 OS(Ubuntu 20.04.3 LTS) , CNI(Calico v3.21.4, IPIP, NAT enable) , IPTABLES proxy mode / 회사 내부 라우터는 Quagga 로 구성

참고 : w3만 대역이 다름 => OverayNetwork 확인을 위해


1. Calico 기본 통신 이해

1) Calico CNI 알아보기

(1) Calico 소개 - 링크

Calico is an open source networking and network security solution for containers, virtual machines, and native host-based workloads. Calico supports a broad range of platforms including Kubernetes, OpenShift, Mirantis Kubernetes Engine (MKE), OpenStack, and bare metal services.

Calico is an open source networking and network security solution for containers, virtual machines, and native host-based workloads. Calico supports a broad range of platforms including Kubernetes, OpenShift, Mirantis Kubernetes Engine (MKE), OpenStack, and bare metal services.

(2) Calico 컴포넌트 - 참고

칼리코 다양한 컴포넌트로 구성되어있다. 그 중에서 대표적으로 사용되는 컴포넌트는 다음과 같다.

  • Felix (필릭스) : 인터페이스 관리, 라우팅 정보 관리, ACL 관리, 상태 체크

    • Programs routes and ACLs, and anything else required on the host to provide desired connectivity for the endpoints on that host. Runs on each machine that hosts endpoints. Runs as an agent daemon.
  • BIRD (버드): BGP Peer 에 라우팅 정보 전파 및 수신, BGP RR(Route Reflector)

    • Gets routes from Felix and distributes to BGP peers on the network for inter-host routing. Runs on each node that hosts a Felix agent. Open source, internet routing daemon.
  • Confd : calico global 설정과 BGP 설정 변경 시(트리거) BIRD 에 적용해줌. config 설정값이 자동으로 트리거되어 반영됨.

    • Monitors Calico datastore for changes to BGP configuration and global defaults such as AS number, logging levels, and IPAM information. Open source, lightweight configuration management tool.
  • Dikasted : Enforces network policy for Istio service mesh. Runs on a cluster as a sidecar proxy to Istio Envoy.

  • Calico CNI Plugin : k8s 에 calico 네트워킹 환경을 제공

    • Provides Calico networking for Kubernetes clusters
  • Datastore plugin : calico 설정 정보를 저장하는 곳 - k8s API datastore(kdd) 혹은 etcd 중 선택

    • Increases scale by reducing each node’s impact on the datastore
  • Calico IPAM plugin : 클러스터 내에서 파드에 할당할 IP 대역

    • Uses Calico’s IP pool resource to control how IP addresses are allocated to pods within the cluster
    • Calico에서 별도로 제공하는 IP 관리대역
  • calico-kube-controllers : calico 동작 관련 감시(watch)

  • Typha : Datastore 와 felix, confd 다수간 연결하지 않고 Datastore 는 단일 Typha(중계자 역할) 로 연결, Typha 는 상태 캐시 및 이벤트 중복 제거 등으로 대규모 환경에서 부하를 감소 할 수 있음, 기본적으로 설치는 되지만 설정 구성되어 있지 않음

    • Increases scale by reducing each node’s impact on the datastore. Runs as a daemon between the datastore and instances of Felix. Installed by default, but not configured.
  • calicoctl : calico 오브젝트를 CRUD 할 수 있다, 즉 datastore 접근 가능

    • Command line interface to create, read, update, and delete Calico objects. calicoctl command line is available on any host with network access to the Calico datastore as either a binary or a container

❓ 참고 : k8s API datastore(kdd) 와 ETCD 비교

(3) Calico 구성요소

BGP로 Pod IP 대역 정보 전파한다.
특히, 쿠버네티스 내부의 기능 뿐만 아니라 물리적인 Router 장비와 연동해서 사용이 가능하다.

<이미지 출처 : 가시다님 노션>

구성요소 확인

데몬셋으로 각 노드에 calico-node 파드가 동작하여, 해당 파드에 bird, felix, confd 등이 동작 + Calico 컨트롤러 파드는 디플로이먼트로 생성

  • 버전 확인
calicoctl version
Client Version:    v3.21.4
Git commit:        220d04c94
Cluster Version:   v3.21.4
Cluster Type:      k8s,bgp,kubeadm,kdd

#### (내용이 누락되어 저장된 관계로 다시작성 예정)

  • 마스터 노드
  • IPAM 정보 확인
  • Block은 각 노드에 할당된 podCIDR
  • 워커 노드마다 할당된 dedicated subnet (podCIDR) 확인

❓ Quiz. 바로 위 Calico IPAM 과 아래 출력되는 IPAM 의 차이는 무엇일까요? 우선순위 확인 - 링크 , Flannel 과 차이

  • 별도의 IPAM 이 있다면 무엇을 할 수 있을까요? (예. 특정 파드/네임스페이스에 파드의 네트워크 대역을 추가 및 삭제)
  1. Kubernetes annotations
  2. CNI configuration (Calico IPAM)
  3. IP pool node selectors

🤔 요약 : 쿠버네티스를 설치하면 기본적으로 Host Local IPAM이 동작한다.(각 노드별로 podCIDR을 할당) 하지만, Calico에서는 별도의 IPAM이 할당된다. 대다수의 CNI는 Pod의 IP를 별도로 관리하기 위한 IPAM 사용한다.

(추가) : Flannel은 호스트의 IPAM 기능을 사용하지만(전용 IPAM기능이 없음) 다른 CNI(Calico 등)는 별도의 IPAM 대역을 할당한다.
즉, 심플하고 아주 간단한 CNI!

  • CNI Plugin 정보 확인 - 링크
tree /etc/cni/net.d/
/etc/cni/net.d/
├── 10-calico.conflist
└── calico-kubeconfig

cat /etc/cni/net.d/10-calico.conflist
...
			"datastore_type": "kubernetes",
      		"ipam": {
          		"type": "calico-ipam"
      },
...
  • calico endpoint (파드)의 정보 확인 / 노드별 podCIDR 확인
calicoctl get workloadEndpoint -A

kubectl get nodes -o jsonpath='{.items[*].spec.podCIDR}' ;echo
  • 노드에서 컨테이너(프로세스) 확인 : pstree
    • bird : BGP 라우팅 프로토콜에서 상대방 Pod 정보 전달
    • felix : iptables rule을 조작(추가)
    • confd : 변경된 설정값을 트리거하여 반영
ps axf

2) 파드(Pod) ↔ 파드(Pod) 통신(같은 노드)

(1) 최종 통신 흐름

❗ 결론 : 동일 노드 내의 파드 간 통신은 내부에서 직접 통신됨

  • iptables FORWARD Rule 에 정책(허용) 사용
    • Host Network NS을 거치기 때문
  • calice# 인터페이스에 proxy arp 설정으로 파드에서 바라보는 게이트웨이의 MAC 정보를 파드가 전달 받음
    • 참고 링크 : Proxy ARP - 링크

(2) 파드 배포 전 기본 상태 확인

파드 생성 전 노드(k8s-w1) Shell 에서 기본 정보 확인

  • 네트워크 인터페이스 정보 확인 : 터널(ipip) 인터페이스가 존재!
root@k8s-w1:~# 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
  • 네트워크 네임스페이스 확인
root@k8s-w1:~# lsns -t net
        NS TYPE NPROCS PID USER    NETNSID NSFS                      COMMAND
4026531992 net     126   1 root unassigned /run/docker/netns/default /sbin/init
  • 네트워크 라우팅 경로 정보 확인

이중 bird 는 bird 데몬이 BGP 라우팅 프로토콜에 의해 파드 네트워크 대역을 전달받거나 전달하는 경로 → 각각 노드의 파드 대역입니다

root@k8s-w1:~# ip -c route | grep bird
blackhole 172.16.158.0/24 proto bird
172.16.34.0/24 via 192.168.20.100 dev tunl0 proto bird onlink
172.16.116.0/24 via 192.168.10.10 dev tunl0 proto bird onlink
172.16.184.0/24 via 192.168.10.102 dev tunl0 proto bird onlink

🖊 요약 : 172.x대역이 192.x대역으로 통신하도록 BIRD에 의해서 BGP 프로코콜을 통해 상대방 podCIRD을 학습

❓ Quiz. blackhole 라우팅은 왜 있을까요?
정답 : 루핑방지(?)

(3) 파드 배포 후 상태 확인

  • 노드(k8s-w1)에 파드 2개 생성
curl -LO https://raw.githubusercontent.com/gasida/NDKS/main/4/node1-pod2.yaml

kubectl apply -f node1-pod2.yaml

(calico-k8s:default) root@k8s-m:~# k get pods -o wide
NAME   READY   STATUS    RESTARTS   AGE   IP             NODE     NOMINATED NODE   READINESS GATES
pod1   1/1     Running   0          82s   172.16.158.1   k8s-w1   <none>           <none>
pod2   1/1     Running   0          82s   172.16.158.2   k8s-w1   <none>           <none>
  • 파드에 Shell 접속 후 확인

(4) 파드간 통신 실행 및 확인(심화)

파드간 통신 실행 이해를 위한 준비

노드에서 실행)
# iptables 필터 테이블에 FORWARD 리스트 중 cali-FORWARD 룰 정보를 필터링해서 watch 로 확인
watch -d -n 1 "iptables -v --numeric --table filter --list FORWARD | egrep '(cali-FORWARD|pkts)'"

파드1에서 실행)
# pod1 -> pod2 ping 통신
ping -c 1 172.16.158.2

즉, cali-FORWARD 라고하는 체인 룰에의해 pod간 통신이 가능 => Clico 컴포넌트에서 iptables Rule을 조작해준다.

  • 해당 부분은 추후 작성 예정 내용이 어려워서 이해가 안됨😢
# (마스터노드) 파드 연결된 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 혹은 파드2에 veth 로 연결된 호스트 네트워크 인터페이스 calice# 중 1개 선택해서 tcpdump
tcpdump -i $VETH1 -nn
tcpdump -i $VETH2 -nn

# 노드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

root@k8s-w1:~# cat /proc/sys/net/ipv4/conf/calice0906292e2/proxy_arp
1
root@k8s-w1:~# cat /proc/sys/net/ipv4/conf/calibd2348b4f67/proxy_arp
1

🖊 요약 : calico 의 파드와 peer 로 연결된 veth cali# 인터페이스에 반드시 proxy_arp = 1 로 활성화가 필요하다.

3) 파드(Pod) ➡ 외부(인터넷) 통신

(1) 최종 통신 흐름

❗ 결론 : 파드에서 외부(인터넷) 통신 시에는 해당 노드의 네트워크 인터페이스 IP 주소로 MASQUERADE(출발지 IP가 변경) 되어서 외부에 연결됨

  • calico 기본 설정은 natOutgoing: true 이다. 즉, iptables 에 MASQUERADE Rule(룰) 에 의해서 외부에 연결됨
  • calice# 인터페이스에 proxy arp 설정
  • 파드와 외부간 직접 통신에서는 tunnel 인터페이스는 미 관여

(2) calico 설정 정보 확인 & 노드에 iptables 확인

  • 마스터 노드에서 확인 : natOutgoing 의 기본값은 true 이다
(calico-k8s: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()

✔ 참고 : EKS에서 NAT 활성/비활성 기능과 동일. NAT를 비활성화 시키고 Network 팀에서 관리하는 장비를 통해서 관리할 수도 있음

  • 노드에서 확인 : 노드에서 외부로 통신 시 MASQUERADE 동작 Rule 확인
root@k8s-w1:~# 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

(3) 파드 배포 및 외부 통신 확인

노드(k8s-w1)에 파드 1개 생성

외부 통신 확인

  • iptables NAT MASQUERADE 모니터링 : pkts 증가 확인

😁 참고 : The right way to check the weather - 링크

curl wttr.in/seoul
curl 'wttr.in/seoul?format=3'
curl 'wttr.in/busan?format=3'
curl v3.wttr.in/Seoul.sxl
curl wttr.in/Moon
curl wttr.in/:help

calico 설정을 natOutgoing: false 로 변경(옵션)

  • calico 설정을 natOutgoing: false 로 변경
# 마스터노드에서 calico ippools 에서 nat 설정 변경
calicoctl get ippool default-ipv4-ippool -o yaml | sed -e "s/natOutgoing: true/natOutgoing: false/" | calicoctl apply -f -

4) 파드(Pod) ↔ 파드(Pod) 통신(다른 노드)

(1) 최종 통신 흐름

❗ 결론 : 다른 노드 환경에서 파드 간 통신 시에는 IPIP 터널(기본값) 모드를 통해서 이루어 집니다

  • 각 노드에 파드 네트워크 대역은 Bird 에 의해서 BGP 로 광고 전파/전달 되며, Felix 에 의해서 호스트의 라우팅 테이블에 자동으로 추가 및 삭제 됩니다
  • 다른 노드 간의 파드 통신은 tunl0 인터페이스를 통해 IP 헤더에 감싸져서 상대측 노드로 도달 후 tunl0 인터페이스에서 Outer 헤더를 제거하고 내부의 파드와 통신됩니다 - 링크

(2) 파드 배포 전 기본 상태 확인

노드의 tunl0 정보 확인

# 터널 인터페이스에 IP가 할당되어 있고, MTU는 1480(IP헤더 20바이트 추가를 고려)을 사용하며, 현재 TX/RX 카운트는 0 이다
# Calico 사용 시 파드의 인터페이스도 기본 MTU 1480 을 사용한다
ifconfig tunl0

root@k8s-w1:~# ifconfig tunl0
tunl0: flags=193<UP,RUNNING,NOARP>  mtu 1480
        inet 172.16.158.0  netmask 255.255.255.255
        tunnel   txqueuelen 1000  (IPIP Tunnel)
        RX packets 48  bytes 7227 (7.2 KB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 49  bytes 3343 (3.3 KB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

root@k8s-w2:~# ifconfig tunl0
tunl0: flags=193<UP,RUNNING,NOARP>  mtu 1480
        inet 172.16.184.0  netmask 255.255.255.255
        tunnel   txqueuelen 1000  (IPIP Tunnel)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

(3) 파드 배포

노드1과 노드2에 각각 파드 1개씩 생성

(calico-k8s: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 created
pod/pod2 created

(calico-k8s:default) root@k8s-m:~# calicoctl get workloadendpoints
WORKLOAD   NODE     NETWORKS          INTERFACE
pod1       k8s-w1   172.16.158.4/32   calice0906292e2
pod2       k8s-w2   172.16.184.1/32   calibd2348b4f67

파드 생성 후 상태

  • 파드간 통신을 처리하는 라우팅 정보 확인 : 각각 상대방 노드의 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.184.0    192.168.10.102  255.255.255.0   UG    0      0        0 tunl0

root@k8s-w2:~# route -n | head -2 ; route -n | grep 172.16.
Kernel IP routing table
...
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
172.16.158.0    192.168.10.101  255.255.255.0   UG    0      0        0 tunl0

(4) 파드간 통신 실행 및 확인

파드간 통신 실행 이해를 위한 준비 ⇒ IPIP(IP Protocol 4 허용이 필요, Azure 는 불가능)

  • 지금까지 tunl0 인터페이스가 사용되지 않았는데, 다른 노드의 파드간 통신시에 사용되는지 확인을 해보자

# k8s-w1
# 패킷 덤프 : tunl0 - 터널 인터페이스에 파드간 IP 패킷 정보 확인!
root@k8s-w1:~# tcpdump -i tunl0 -nn
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on tunl0, link-type RAW (Raw IP), capture size 262144 bytes
07:42:40.800707 IP 172.16.158.4 > 172.16.184.1: ICMP echo request, id 41434, seq 1, length 64
07:42:40.802513 IP 172.16.184.1 > 172.16.158.4: ICMP echo reply, id 41434, seq 1, length 64
07:42:41.802181 IP 172.16.158.4 > 172.16.184.1: ICMP echo request, id 41434, seq 2, length 64
07:42:41.803339 IP 172.16.184.1 > 172.16.158.4: ICMP echo reply, id 41434, seq 2, length 64
07:42:42.802875 IP 172.16.158.4 > 172.16.184.1: ICMP echo request, id 41434, seq 3, length 64
07:42:42.803880 IP 172.16.184.1 > 172.16.158.4: ICMP echo reply, id 41434, seq 3, length 64

# k8s-w2
# tunl0 인터페이스 TX/RX 패킷 카운트 모니터링 확인 : TX/RX 패킷 카운트가 각각 10개로 증가했다

root@k8s-w2:~# watch -d 'ifconfig tunl0 | head -2 ; ifconfig tunl0 | grep bytes'

Every 2.0s: ifconfig tunl0 | head -2 ; ifconfig tunl0 | grep bytes                                                        k8s-w2: Sat Feb  5 07:44:37 2022
tunl0: flags=193<UP,RUNNING,NOARP>  mtu 1480
        inet 172.16.184.0  netmask 255.255.255.255
        RX packets 10  bytes 840 (840.0 B)
        TX packets 10  bytes 840 (840.0 B)
Every 2.0s: ifconfig tunl0 | head -2 ; ifconfig tunl0 | grep bytes                                                        k8s-w2: Sat Feb  5 07:44:41 2022

  • 패킷 덤프 : eth0(enp#~) - IP Outer 헤더 안쪽에 IP 헤더 1개가 더 있음을 알 수 있다!
root@k8s-w1:~# tcpdump -i enp0s8 -nn proto 4
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on enp0s8, link-type EN10MB (Ethernet), capture size 262144 bytes
07:49:08.954221 IP 192.168.10.101 > 192.168.10.102: IP 172.16.158.4 > 172.16.184.1: ICMP echo request, id 61463, seq 1, length 64 (ipip-proto-4)
07:49:08.955571 IP 192.168.10.102 > 192.168.10.101: IP 172.16.184.1 > 172.16.158.4: ICMP echo reply, id 61463, seq 1, length 64 (ipip-proto-4)
07:49:09.955240 IP 192.168.10.101 > 192.168.10.102: IP 172.16.158.4 > 172.16.184.1: ICMP echo request, id 61463, seq 2, length 64 (ipip-proto-4)
07:49:09.956057 IP 192.168.10.102 > 192.168.10.101: IP 172.16.184.1 > 172.16.158.4: ICMP echo reply, id 61463, seq 2, length 64 (ipip-proto-4)
07:49:10.956993 IP 192.168.10.101 > 192.168.10.102: IP 172.16.158.4 > 172.16.184.1: ICMP echo request, id 61463, seq 3, length 64 (ipip-proto-4)
07:49:10.958197 IP 192.168.10.102 > 192.168.10.101: IP 172.16.184.1 > 172.16.158.4: ICMP echo reply, id 61463, seq 3, length 64 (ipip-proto-4)
...

  • 직접 캡처해서 살펴보자!
tcpdump -i enp0s8 -nn proto 4 -w /tmp/calico-ipip.pcap

패킷 캡처결과 Outer IP 헤더에서는 IPIP 프로토콜로 통신을 하는 것을 알 수 있었고, Inner IP 헤더에서는 실제 ICMP 프로토콜을 통해 정보가 전달된 것을 알 수 있었다.
즉, 실제 Pod가 다른 노드에 통신(Overay)할 때 Outer IP 헤더를 감싸서 노드의 IP 주소로 NAT되어 통신된 것을 알 수있었다.


3주차 1편에서는 Calico CNI에 대한 특/장점에 대해서 알아보았고, 기본적인 통신에 대핸 이해를 할 수 있었다.
작성하다보니 그동안 궁금했던 IPAM에 대한 설명이나 Overay Network에 대한 이해를 할 수 있는 시간이었다.
또한, Calico를 구성하고 있는 각종 컴포넌트의 역할에 대해서도 확실히 숙지할 수 있었다.

다음 2편에서는 Calico CNI에서 제공하는 각종 Mode에 대해서 알아볼 예정이다.

profile
DevOps를 꿈꾸는 엔지니어 입니다.

1개의 댓글

comment-user-thumbnail
2022년 3월 7일

안녕하세요 kubernetes calico 통신 해보려는데
저는 다른 워커노드에있는 파드끼리의 통신이 되지 않습니다 ㅜㅜ 혹시 이유가 뭔지 알 수 있을까요?
Centos7 ncloud에서 kubespray로 배포했습니다!
글 중 ip protocol 4 허용 << 이 부분은 어떤 걸 말하는 걸까요?

답글 달기