제목 : CNI 와 Calico Network
본문의 내용은 이후에도 업데이트 될 수 있음
내용의 결과값은 #1 에서 초기 구성한 ( controller , worker ) 시스템에서 진행
CNI ( Container Network Interface )
CNCF 에서 정의한 컨테이너 네트워크 인터페이스.
Kubernetes는 기본적으로 Kubenet 이라는 CNI 를 준수하는 Network Plugin 을 갖고 있다.
하지만 복합적이고 다양한 구조의 네트워킹 사용 용도로는 다소 부족하여, 외부 네트워크 플러그인의 필요성이 대두되었다.
Calico Network?
vRouter 기반의 라우터(L3) 기반의 네트워크 관리 모델이다.
Kubernetes 의 서비스 Network 를 구성하는 CNI 를 준수하는 Network Plugin 중 하나이다.
쿠버네티스의 기본 네트워크 서비스는 기능적으로 부족한 부분이 있는데 자주 사용되는 외부 플러그인이다.
프로젝트 공식 Document URL : https://projectcalico.docs.tigera.io/reference/
Non-overlay 방식의 라우팅
# Direct 방식
- BGP(Border Gateway Protocol) 인 BIRD 를 통해 다른 노드 간 라우팅 정보를 동적으로 공유하여 통신할 수 있다
즉 Pod 에서 다른 Pod 로 직접 네트워킹하는 방식으로 성능적으로 가장 뛰어나다.
각 Node 에 존재하는 Calico Pod 는 BGP를 처리하는 Peer 라고 볼 수 있다.
보통 같은 서브넷에 존재하는 노드들을 묶는 방법으로 사용된다. ( ex: 라우터 밑의 여러 서버 )
Overlay Network 방식의 라우팅
Workload IP( ex: 서비스 컨테이너) 를 모르는 다른 네트워크와의 통신 기법을 의미한다.
여러 노드의 네트워크 패킷을 캡슐화(Encaptulation) 하여 논리적인 공통(L2) 네트워크를 구성하는 방식이다.
캡슐화 패킷 패턴 : 외부 패킷에는 Node 간 IP 정보가 있고, 내부에는 POD 간의 IP 정보가 있다.
# IP in IP 방식(Default)
- 각 노드의 네트워크 위치가 달라 Direct 방식으로 통신하기 어려운 경우
IP 패킷 정보를 캡슐화하여 tunl0(tunneling) 인터페이스를 거쳐 노드 간 통신을 가능하게 연결한다.
이후 Direct 와 마찬가지로 BGP (BIRD) 를 통해 노드 간 라우팅 정보를 다른 Node 와 동적(IPVS)으로 전달 및 갱신한다.
Calico Routing 기본 방식이다.
# VXLAN 방식
- 가령 물리적 네트워크의 노드와 특정 퍼블릭 클라우드의 다른 네트워크를 연결할 경우
퍼블릭 클라우드 쪽의 라우터 설정 차이 등으로 IP in IP 방식을 쓰지 못할 수 있다. ( ex: Azure )
이럴 경우 Calico 에서는 라우터를 거치는 BGP 프로토콜을 사용하지 않고 가상의 VXLAN 인터페이스를 Node 간에 생성하여
L2 캡슐화된 UDP 패킷을 서로 통신할 수 있게 하는 방법이다.
IP in IP 방식에 비해 패킷의 크기가 조금 더 크지만 대규모의 워크로드 시스템이 아닌 한 큰 체감은 없다.
# Cross-subnet 방식
캡슐화가 필요한 경우(네트워크가 다를 경우)에서만 선택적인 캡슐화 구성도 가능하다.
( 일부 구간/전체 구간 )
패킷 암호화 ( 옵션 )
# WireGuard
Calico 는 모든 유형의 라우팅 방식에서 패킷 암호화 기능을 별도로 추가할 수 있다.
모든 네트워크 성능의 저하가 발생하게 되므로 적절한 계획 수립이 필요하다.
Calicoctl
Controller 에서 Calico Network 정보를 확인할 수 있는 유틸리티이다.
Host의 단독 명령어 또는 kubectl 에 plugin 타입으로도 설치할 수 있다.
# Host 의 명령어 타입으로 설치
$ cd /usr/local/bin
$ sudo curl -L https://github.com/projectcalico/calico/releases/download/v3.22.1/calicoctl-linux-amd64 -o calicoctl
$ sudo chmod +x calicoctl
# Check
Calico 가 관리하는 Network 의 Pool과 Block 을 확인할 수 있다.
$ sudo calicoctl ipam show --show-blocks
+----------+--------------------+-----------+------------+--------------+
| GROUPING | CIDR | IPS TOTAL | IPS IN USE | IPS FREE |
+----------+--------------------+-----------+------------+--------------+
| IP Pool | 192.168.0.0/16 | 65536 | 5 (0%) | 65531 (100%) |
| Block | 192.168.136.0/26 | 64 | 4 (6%) | 60 (94%) |
| Block | 192.168.153.192/26 | 64 | 1 (2%) | 63 (98%) |
+----------+--------------------+-----------+------------+--------------+
BGP 프로토콜을 사용해 라우팅 정보를 전달중인 노드를 확인할 수 있다
$ sudo calicoctl node status
Calico process is running.
IPv4 BGP status
+----------------+-------------------+-------+----------+-------------+
| PEER ADDRESS | PEER TYPE | STATE | SINCE | INFO |
+----------------+-------------------+-------+----------+-------------+
| 203.248.23.215 | node-to-node mesh | up | 05:27:05 | Established |
+----------------+-------------------+-------+----------+-------------+
Block 에서 사용중인 관련 인터페이스들
$ route -n | egrep "tun|cali|\*"
192.168.136.0 0.0.0.0 255.255.255.192 U 0 0 0 *
192.168.136.1 0.0.0.0 255.255.255.255 UH 0 0 0 calibc6c3028870
192.168.136.2 0.0.0.0 255.255.255.255 UH 0 0 0 calid6edae09645
192.168.136.3 0.0.0.0 255.255.255.255 UH 0 0 0 calic6bfd11bfbe
192.168.153.192 203.248.23.215 255.255.255.192 UG 0 0 0 tunl0
어떤 Pod가 어떤 calicxxxxxx 인터페이스를 사용하는지 확인하려면 다음의 명령어를 사용한다
System(default) Namespace 까지 보이게 하려면 -A 옵션을 추가한다.
$ calicoctl get workloadendpoint -A
NAMESPACE WORKLOAD NODE NETWORKS INTERFACE
kube-system calico-kube-controllers-56fcbf9d6b-nlqg2 user-controller 192.168.136.2/32 calid6edae09645
kube-system coredns-64897985d-jgj5s user-controller 192.168.136.3/32 calic6bfd11bfbe
kube-system coredns-64897985d-vbpn4 user-controller 192.168.136.1/32 calibc6c3028870
Calico 인터페이스는 Veth type(Pair) 이다.
$ ip -br -c link show type veth
calibc6c3028870@if3 UP ee:ee:ee:ee:ee:ee <BROADCAST,MULTICAST,UP,LOWER_UP>
calid6edae09645@if4 UP ee:ee:ee:ee:ee:ee <BROADCAST,MULTICAST,UP,LOWER_UP>
calic6bfd11bfbe@if4 UP ee:ee:ee:ee:ee:ee <BROADCAST,MULTICAST,UP,LOWER_UP>
Calico Management Pod
특정 Daemon이 아닌 Pod 형태로 존재한다.
Controller 와 Worker Node 에 각각 실행되는 Pod 가 다름을 체크한다.
$ kubectl get pods -o wide -n kube-system
NAME READY STATUS RESTARTS AGE IP NODE
calico-kube-controllers-56fcbf9d6b-nlqg2 1/1 Running 0 30m 192.168.136.2 user-controller
calico-node-8cts6 1/1 Running 0 30m 10.0.2.15 user-controller
calico-node-mb9n6 1/1 Running 0 29m 10.0.2.15 user-worker
Calico의 설정 정보 등의 DB 값은 etcd 라는 서비스의 datastore 에 저장된다.
$ kubectl get pods -o wide -n kube-system | grep -i etcd
etcd-user-controller 1/1 Running 0 39m 10.0.2.15 user-controller
Calico Felix
쿠버네티스 Pod 네트워크에서 kube-proxy 상위 역할을 해 주는 컴포넌트이다.
etcd 에서 정보들을 읽어와서 Pod Network 의 라우팅 테이블과 룰을 관리한다.
kube-proxy 가 iptables / ipvs Mode 일경우 그에 맞춰 관리해준다.
iptables 라인이 너무 클 경우 성능에 영향이 있을 수 있어 플러그인에 의한 ipvs 방식이 선호된다.
※ IPVS = Hash 값으로 동적으로 라우팅 테이블을 관리하는 로드밸런서 알고리즘
$ sudo iptables -t nat -S | grep -i cali
$ sudo iptables -t filter -S | grep -i cali
Networking 과정
IP in IP Networking
Controller Node 에서 Worker Node 에 있는 Pod 네트워크로 통신을 시도한다
1) Controller의 192.168.136.2 Pod 에서 Worker의 192.168.153.193 Pod 로 통신
2) Controller의 Pod 인터페이스(veth) 는 Pair 인 Host의 calico (veth) 인터페이스로 ARP 요청
3) Host 의 Calico 인터페이스는 Worker Pod 의 네트워크 정보를 모르므로 ARP 응답 할 수 없어야 함
4) 하지만 Calico 인터페이스는 link-local 타입(내부 브로드캐스트 학습)으로써 HOST 가 BIRD 에 의해 학습한 Worker 의 라우팅 정보를 알고 있음
5) Controller의 Calico 인터페이스는 vRouter에 설정된 ARP_Proxy 기능을 활용하여 Worker 의 ARP 응답을 대신 해 줌
6) BIRD는 Tunl0 --> Host 의 실제 인터페이스를 통해 상대 노드와 Pod 정보를 추가한 캡슐화 된 패킷으로 통신
7) 그림에는 보이지 않지만 만약 다른 노드와 통신이 아닌 퍼블릭 대역과 통신을 한다면
Felix 를 통한 SNAT ( MASQUERADE ) 를 설정하여 tunl0 을 거치지 않고 HOST 의 ens33 으로 바로 연결.
Packet Check
# 다른 네트워크 ( Controllor POD <---> Worker POD ) 간 Ping 체크를 진행한다
$ kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
hello-776c774f98-894tt 1/1 Running 0 13d 192.168.153.193 user-worker <none> <none>
hi 1/1 Running 0 13d 192.168.136.5 user-controller <none> <none>
# Worker POD --> Container POD. 컨테이너에 Ping 명령이 없어 Pod 밖의 Host 에서 컨테이너 PID 를 따서 진행했다.
$ sudo nsenter -t 225201 -n ping 192.168.136.5
64 bytes from 192.168.136.5: icmp_seq=627 ttl=62 time=0.709 ms
64 bytes from 192.168.136.5: icmp_seq=628 ttl=62 time=0.675 ms
64 bytes from 192.168.136.5: icmp_seq=629 ttl=62 time=0.727 ms
64 bytes from 192.168.136.5: icmp_seq=630 ttl=62 time=0.797 ms
64 bytes from 192.168.136.5: icmp_seq=631 ttl=62 time=0.887 ms
# Controller 에서 패킷 캡쳐를 한다. IPIP 구조는 API 공인 인터페이스를 통해 터널링하므로, API 인터페이스든 터널링 인터페이스든 상관없다.
$ sudo tcpdump -i enp0s8 -nn proto 4 -w test.pcap
# Wireshark 로 확인한다.
1) 두 POD IP의 다른 네트워트 간 정상 ICMP 통신을 하고 있는 부분을 확인할 수 있다.
2) MAC 확인시 Controller 와 Worker Node 의 API 공인IP 인터페이스 간 통신임을 확인했다.
# Controller
3: enp0s8: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
link/ether 08:00:27:39:ce:bd brd ff:ff:ff:ff:ff:ff
inet 203.248.23.214/25 brd 203.248.23.255 scope global enp0s8
# Worker
3: enp0s8: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
link/ether 08:00:27:bc:85:3a brd ff:ff:ff:ff:ff:ff
inet 203.248.23.215/25 brd 203.248.23.255 scope global enp0s8
3) IPv4 Protocol 확인 시 2개의 헤더를 확인할 수 있다.
# 공인망의 Outer IP 와 POD 네트워크인 Inner IP 2개를 확인할 수 있다.
Outer IP 헤더가 InnerIP 를 밖에서 캡슐로 감싼 IP-IP Protocol 임을 확인할 수 있다.
4) Messages 에서 통신에 정상 문제가 없음을 확인했다.

참고자료
Network Overlay : https://ikcoo.tistory.com/117