본문 바로가기
스터디 이야기/Kubernetes Advanced Networking Study

[KANS] Calico CNI

by lakescript 2024. 9. 19.
더보기

이 스터디는 CloudNet@에서 진행하는 KANS 스터디를 참여하면서 공부하는 내용을 기록하는 블로그 포스팅입니다.

CloudNet@에서 제공해주는 자료들을 바탕으로 작성되었습니다.

 

Calico CNI

Calico는 Kubernetes를 포함하여 다양한 플랫폼에서 워크로드의 네트워크 통신과 네트워크 보안 기능을 제공합니다. 특히 Kubernetes CNI 를 준수하여, Pod를 위한 네트워크 통신 환경을 구성할 수 있습니다. 이때 Caclio CNI는 Kubernetes에서 Calico는 Pod에 IP를 할당하고, 각 Pod의 네트워크 트래픽을 관리합니다. 또한 네트워크 정책을 통해 Pod 간의 통신을 제어하고, 외부 트래픽과의 연결을 설정합니다.

 

Component Architecture (공식 문서)

https://docs.tigera.io/calico/latest/reference/architecture/overview#calico-components

 

 

Component Architecture 

파드 생성 시 Calicon Datastore를 참고해, CNI 플러그인과 CNI IPAM 플러그인을 통해서 파드의 네트워크 설정을 하게 되며, 해당 파드가 다른 노드의 파드와 통신하는 경우에는 bird로 학습한 네트워크 정보를 felix에 의해서 호스트의 라우팅 테이블과 IPtables 를 통해서 이루어 지게 됩니다. 

calico CNI 설치시 Daemonsets 으로 calico-node 파드가 생성됩니다. 이때, calico-node 파드에는 3가지(bird/felix/confd) 중요한 프로그램이 동작합니다.

kubectl, calicoctl

kubectl 이나 calicoctl 를 사용하여 파드를 생성하거나, calico 설정을 변경 시 calico datastore에 정보가 반영됩니다. 

 

Caclico Datastore

caclico 동작을 위한 data들을 저장하는 곳으로, 쿠버네티스 API 저장소 (기본 설정값) 혹은 ETCD 를 선택할 수 있습니다. 

 

bird

오픈 소스 소프트웨어 routing daemon 프로그램 (공식문서) 입니다. 즉, 해당 노드의 파드 네트워크 대역(podCIDR)을 BGP 라우팅 프로토콜을 통해서 Advertise합니다. 이를 통해 각 노드는 다른 모든 노드의 파드 대역과 통신을 할 수 있습니다. 즉, BGP Peer에 라우팅 정보 전달 및 수신, BGP RR(Route Reflector)을 담당합니다. 

 

felix

bird로 학습한 상대방 노드의 파드 네트워크 대역을 호스트의 라우팅 테이블에 최종적으로 업데이트하는 역할을 하며, IPtables
규칙 설정 관리를 합니다. 즉, 인터페이스 관리, 라우팅 정보 관리, ACL 관리, 상태 체크를 담당합니다.

 

condif

BGP 설정 등으로 calico datastore에 변경이 발생하면 bird의 변경된 설정 파일을 만들고, 변경된 설정 파일을 반영하게 합니다. 즉, calico global 설정과 BGP 설정 변경 시 트리거하여 bird에 적용합니다.

 

실습

실습 환경 구성

2개의 네트워크 대역이 존재하며, 위와 같이 master node, worker-node를 분배하여 구성합니다.

 

node 정보 확인

kubectl get node -o wide

위의 명령어를 통해 node의 정보를 살펴보겠습니다.

위에서 구상했던 대로 k8s-m, k8s-w0, k8s-w1, k8s-w2의 node가 생성되어 있고, 각 IP도 정상적으로 설정되어 있습니다.

네트워크 인터페이스 확인(Calico CNI 설치 전)

ip -c route

시스템의 라우팅 테이블의 목록에 대해 색을 입혀 출력해보겠습니다.

 

  • 기본 게이트웨이는 192.168.10.1로 설정되어 있으며, ens5 인터페이스를 통해 모든 패킷이 기본적으로 전송됩니다.
  • 192.168.0.2로 가는 경로도 동일한 게이트웨이 192.168.10.1을 사용합니다.
  • 192.168.10.0/24 네트워크는 ens5 인터페이스에 직접 연결된 네트워크입니다.
  • 192.168.10.1도 같은 네트워크의 일부로, 직접 연결된 상태입니다.

IP 주소 및 관련 정보 확인(Calico CNI 설치 전)

ip -c addr

네트워크 인터페이스에 할당된 IP 주소와 관련된 정보를 출력합니다.

 

 

- 루프백(lo) 인터페이스는 내부 테스트 및 시스템 자체 통신 용도로 사용되며, 현재 127.0.0.1과 ::1 IPv4 및 IPv6 주소를 가지고 있습니다. 

- 이더넷 네트워크 장치(ens5)는 192.168.10.10/24 IPv4 주소와 링크-로컬 IPv6 주소를 가지고 있습니다. DHCP로 동적으로 IPv4 주소를 할당받았으며, Jumbo Frame 사이즈(9001바이트)를 지원합니다.

iptables 갯수 확인(Calico CNI 설치 전)

iptables -t filter -L | wc -l
iptables -t nat -L | wc -l

패킷을 차단하거나 허용하는 규칙을 정의하는 filter와 NAT(Network Address Translation) 작업에 관련된 규칙을 정의하는 nat의 갯수를 확인해보겠습니다.

현재는 각각 50개, 48개인 것을 확인하실 수 있습니다.

Pod 확인(Calico CNI 설치 전)

kubectl get po -o wide -A

현재 coredns pod는 pending 상태인 것을 알 수 있습니다.

 

CNI Binary 파일 목록 확인(Caclico CNI 배포 전)

tree /opt/cni/bin/

 

Calico CNI 설치

calico cni yaml 파일 다운로드

curl -O https://raw.githubusercontent.com/projectcalico/calico/v3.28.1/manifests/calico.yaml

 

CALICO_IPV4POOL_BLOCK_SIZE 설정

vim calico.yaml

# 4946번째 라인에 아래 내용 입력
...
            # Block size to use for the IPv4 POOL created at startup. Block size for IPv4 should be in the range 20-32. default 26
            - name: CALICO_IPV4POOL_BLOCK_SIZE
              value: "24"
...

cacico yaml 파일을 열어 CALICO_IPV4POOL_BLOCK_SIZE을 추가합니다.

CALICO_IPV4POOL_BLOCK_SIZE는 Calico가 노드에 IP 주소를 할당할 때 한 번에 얼마나 많은 IP 주소를 할당할지 결정하는 값으로, CIDR 접두어로 나타내며, 기본값은 26입니다. (즉, 기본적으로 한 블록에는 64개의 IP 주소가 포함됩니다.)

 

Caclico CNI 배포

kubectl apply -f caclico.yaml

CNI Binary 파일 목록 확인(Caclico CNI 배포 후)

tree /opt/cni/bin/

 

위에서 확인한 것과는 다르게 calico와 calico-ipam이 설치된것을 확인하실 수 있습니다.

 

네트워크 인터페이스 확인(Calico CNI 설치 후)

ip -c route

Caclico CNI를 설치하고 난 후의 라우트 네트워크 인터페이스 목록을 확인해보겠습니다.

 

  • 기본 경로는 192.168.10.1을 통해 설정되었고, ens5 인터페이스를 사용합니다.
  • 이때 여러 경로(2,4,5,6라인)는 bird 라우팅 데몬을 통해 설정된 경로이며, 대부분 터널 인터페이스(tunl0)를 통해 다른 네트워크로 연결됩니다.
  • 172.16.116.0/24 네트워크로 가는 경로는 "블랙홀"로 설정되어, 해당 네트워크로 향하는 패킷은 삭제됩니다.
  • 192.168.10.0/24 네트워크는 로컬 네트워크이고, ens5 인터페이스를 통해 연결됩니다.
blackhole은 특정 네트워크로 향하는 트래픽을 의도적으로 차단하거나 드랍(삭제)하는 라우팅 규칙입니다. 이는 해당 네트워크로 전달되는 모든 패킷을 더 이상 처리하지 않고, 그냥 버리도록 하는 설정입니다.

 

IP 주소 및 관련 정보 확인(Calico CNI 설치 후)

ip -c addr

 

  • lo 인터페이스는 127.0.0.1 IPv4와 ::1 IPv6 주소를 가지고 있습니다.
  • ens5 인터페이스는 192.168.10.10/24 IPv4 주소와 링크-로컬 IPv6 주소를 가지고 있습니다.
  • tunl0 인터페이스는 IPIP 터널 인터페이스로서 다른 네트워크와 터널링을 통해 연결된 상태이며, 72.16.116.0/32 주소를 사용합니다.

 

iptables 갯수 확인(Calico CNI 설치 후)

iptables -t filter -L | wc -l
iptables -t nat -L | wc -l

Calico CNI 설치 이후 50,48개였던 iptables filter와 nat이 108, 12개로 2배 이상 증가한 것을 확인하실 수 있습니다.

 

Pod 확인(Calico CNI 설치 후)

kubectl get po -o wide -A

calico CNI와 관련된 Pod들과 coredns가 정상적으로 배포되었는지 확인해보겠습니다.

 

calicoctl 설치

curl -L https://github.com/projectcalico/calico/releases/download/v3.28.1/calicoctl-linux-amd64 -o calicoctl
chmod +x calicoctl && mv calicoctl /usr/bin

caclicoctl 바이너리 파일을 받아온 후 실행 권한을 부여하고 실행할 수 있게 /usr/bin 디렉토리로 옮깁니다.

 

calicoctl version

 

Calico 구성 요소 확인

Caclico CNI는 deamonset으로 각 노드에 calico-node 파드가 동작하여, 해당 파드에 bird, felix, confd 등이 동작합니다, 추가로 Calico-controller-pod는 deployment로 생성합니다.

daemonset 확인

 kubectl get daemonset -n kube-system

calico-node라는 daemonset이 배포되어있는 것을 확인하실 수 있습니다.

 

calico-node pod 확인

kubectl get pod -n kube-system -l k8s-app=calico-node -o wide

k8s-app=calico-node라는 label이 붙은 pod 목록을 확인해보겠습니다.

node별로 pod가 띄어져있는 것을 확인하실 수 있습니다.

 

calico-kube-controllers 확인

kubectl get deploy -n kube-system calico-kube-controllers

calico-kube-controllers를 확인해보겠습니다.

 

Calico IPAM 정보 확인 

calicoctl ipam show

Calico CNI 를 사용한 파드가 생성된 노드에 pod CIDR 네트워크 대역 확인해보겠습니다.

 

 

host-local IPAM 정보 확인

kubectl get nodes -o jsonpath='{.items[*].spec.podCIDR}' ;echo

k8s-m 노드의 podCIDR은 host-local 대신 Calico IPAM 를 사용합니다. 그렇기 때문에 워커 노드마다 할당된 dedicated subnet (podCIDR) 확인해보겠습니다.

 

CNI Plugin 정보 확인

cat /etc/cni/net.d/10-calico.conflist | jq

"datastore_type": "kubernetes"으로 Caclico datastore는 Kubernetes API를 사용하는 것을 알 수 있고, ipam.type: calico-ipam으로 IPAM 은 칼리코 자체 IPAM 을 사용하는 것을 알 수 있습니다.

 

calicoctl node 정보 확인

calicoctl node status

bird 데몬(BGP)을 통한 BGP 네이버(neighbor) 연결 정보를 확인합니다. (자기 자신의 정보 제외)

bgp peer 는 노드의 IP로 연결하는 것을 알 수 있습니다.

ippool 정보 확인

calicoctl get ippool -o wide

클러스터가 사용하는 IP 대역 정보와 Calico 모드의 정보를 확인해보겠습니다. 위의 명령어는 Calico mode의 정보나 IP Pool의 정보, 해당 IP Pool이 어떤 CIDR을 사용하는지, NAT을 사용하는지 등에 대한 다양한 기본 정보들을 보여줍니다.

 

파드와 서비스 사용 네트워크 대역 정보 확인

kubectl cluster-info dump | grep -m 2 -E "cluster-cidr|service-cluster-ip-range"

kubectl get cm -n kube-system kubeadm-config -oyaml | grep -i subnet

 

calico endpoint (파드)의 정보 확인

calicoctl get workloadEndpoint -o wide -A

 

Calico 네트워크 플러그인을 사용하는 여러 워크로드(Pod)의 네트워크 엔드포인트 정보를 확인해보겠습니다. 이때, host network namespace를 사용하는 것들은 나오지 않습니다. 

 

각각의 Pod(WORKLOAD)는 고유한 IP 주소와 Calico 가상 네트워크 인터페이스를 가지고 있으며, 프로파일은 네트워크 정책을 관리하는 데 사용됩니다. 이때, k8s-w0 노드에서 실행 중인 calico-kube-controllers 및 coredns Pod들이 각각의 IP 주소와 네트워크 인터페이스를 통해 네트워크에 연결되어 있습니다.

 

노드에서 컨테이너(프로세스) 확인

ps axf

위의 명령어를 통해 실행중인 프로세스 목록을 확인해보겠습니다. 

caclico 관련 프로세스를 보면 bird, felix, confd등 확인됩니다.

 

 

Pod <-> Pod 통신(Pod 간 통신)


동일 Node내의 Pod 간 통신은 내부에서 직접 통신됩니다. 이때 tunnel 인터페이스는 관여하지 않습니다.

 

Pod -> 외부 통신 (Pod에서 외부통신)

Pod에서 외부(인터넷) 통신 시에는 해당 Node의 네트워크 인터페이스 IP 주소로 MASQUERADE(출발지 IP가 변경) 되어서 외부에 연결됩니다. 그리고, calico 기본 설정은 natOutgoing: true이기 때문에 iptables 에 MASQUERADE Rule(룰) 에 의해서 외부에 연결됩니다. 이때 파드와 외부간 직접 통신에서는 tunnel 인터페이스는 관여하지 않습니다.

 

다른 Node에서 Pod ↔ Pod 간 통신

IPIP 모드

Pod에서 다른 Node에서 Pod간 통신 시에는 IPIP 터널(기본값) 모드를 통해서 이루어 집니다. 각 Node에 Pod 네트워크 대역은 Bird 에 의해서 BGP로 전달 되며, Felix에 의해서 Host의 라우팅 테이블에 자동으로 설정됩니다. 이때, 다른 tunl0 인터페이스를 통해 IP 헤더에 감싸져서 상대측 Node로 도달 후 tunl0 인터페이스에서 Outer 헤더를 제거하고 내부의 Pod와 통신합니다.

 

Direct 모드

Pod의 네트워크 통신 패킷이 출발지 Node의 라우팅 정보를 보고 목적지 Node로 원본 패킷을 그대로 전달합니다. 하지만 Node1의 Pod에서 Node2의 Pod나 or Node3의 Pod통신을 확인해보면 되지 않습니다. 하지만 통신이 되지 않는데요. 그 이유는Node1의 eth0s8을 빠져나온 패킷은 router의 이름의 리눅스 가상머신으로 전달되는데, 이때 router 가상머신은 쿠버네티스 노드에 속하지 않습니다. 그렇기 때문에 PodCIDR 정보가 없어서 전달해주지 못하게 됩니다. 결국 Pod의 트래픽은 default routing에 의해 enp0s3으로 빠져나가면서 통신이 되지 않습니다. 하지만 완전히 안되는 것이 아니기 때문에 route 가상머신에 Overlay 네트워크 기법으로 라우팅 처리를 하게되면 통신이 가능하게 됩니다. (아래의 코드 참고)

# router 장비에 직접 각 노드의 파드 대역 라우팅 정보를 추가
## 아래 대역은 k8s-m 에서 calicoctl ipam show --show-blocks 출력되는 각 노드가 관리하는 파드의 대역(예시)
ip route add 172.16.34.0/24 via 192.168.20.100
ip route add 172.16.116.0/24 via 192.168.10.10
ip route add 172.16.158.0/24 via 192.168.10.101
ip route add 172.16.184.0/24 via 192.168.10.102

 

enp0s3, enp0s8? eth0?

ethX와 enp0sX은 모두 같은 네트워크 인터페이스 이름을 부르는 방식입니다. 이 둘의 차이는 리눅스에서 네트워크 인터페이스 이름을 명명하는 방식에서 비롯됩니다. 여기서 enpXsY 방식인 enpXs3와 enp0s8는 PCI 버스 0의 각각 3번 슬롯과 8번 슬롯에 연결된 네트워크 어댑터를 의미합니다. eth0은 첫 번째 네트워크 인터페이스는 eth0, 두 번째는 eth1 등으로 할당되는 이전에 불렸던 방식인 ethX 형식입니다. 즉, ethX 방식은 간단하지만 네트워크 장치의 변경이 있을 때 이름이 달라질 수 있는 단점이 있습니다. 그러나 enpXsY 방식은 장치의 물리적 위치를 반영하여 이름을 정하고, 재부팅이나 장치 변경에도 인터페이스 이름이 일관되게 유지되기 때문에 관리가 더 쉽고 안정적입니다.