[네떡스터디🔥kans] Calico CNI 알아보기

 

CloudNet@ 가시다님이 진행하는 쿠버네티스 네트워크 스터디 KANS 3기 내용을 정리한 글입니다.

 

이번 포스트에서는 CNI 중에서 네트워크 정책 기능을 제공하는 Calico에 대해 알아보겠습니다.

 

 

Calico 컴포넌트

 

데이터 저장소

Kubernetes API Datastore는 Calico에서 네트워크 설정 정보를 Kubernetes API와 통합해 저장하는 방식입니다. 이 방식에서는 설정 정보를 Kubernetes API를 통해 관리하며, 설정 데이터는 ConfigMap이나 Custom Resource Definition(CRD) 형태로 저장됩니다. 직접적으로 Kubernetes API의 데이터베이스에 접근하지 않고, Kubernetes의 표준 API를 통해 네트워크 설정을 관리합니다. 별도의 추가 설정을 하지 않으면 Calico는 기본적으로 Kubernetes API Datastore를 사용하도록 설정되어 있습니다.

쿠버네티스 API와 통합함으로써 Calico는 네이티브 쿠버네티스 자원처럼 관리될 수 있으며, 별도의 etcd 클러스터를 구축할 필요 없이 Kubernetes 환경에서 일관되게 동작할 수 있습니다.

 

Bird

Bird는 라우팅 데몬 프로그램으로, 노드의 파드 네트워크 대역을 BGP(Border Gateway Protocol) 라우팅 프로토콜을 통해 광고합니다. BGP 피어링 상태를 확인하려면 calicoctl node status 명령어를 사용하면 됩니다. BGP는 노드 간의 node-to-node 피어링 외에도 Route Reflector 방식을 통해 연결할 수 있습니다.

 

calico-node 데몬셋에는 Bird뿐만 아니라 아래에서 설명할 Felixconfd도 포함되어 있습니다.

 

calico-node는 hostNetwork를 사용 중이며, 어떤 인터페이스를 사용할지는 자동으로 결정됩니다. 자동 설정을 원하지 않으면 직접 인터페이스를 지정해줘야 합니다.

 

BGP 광고 확인하기

✔︎ 호스트 노드의 인터페이스 확인하기

호스트 노드의 인터페이스를 확인할 때는 루프백과 터널 인터페이스를 제외해야 합니다. 이 서버에서는 loopback과 tunnel 인터페이스를 제외하면 ens5 인터페이스만 남으며, calico-node는 hostNetwork를 사용하기 때문에 이 인터페이스가 calico-node의 네트워크 인터페이스로 사용됩니다.

 

✔︎ tcpdump로 확인하기

BGP 광고는 기본적으로 179 포트를 사용하므로, 특별한 설정이 없다면 tcpdump를 통해 179 포트로 전송되는 트래픽을 확인할 수 있습니다.

첫 번째 패킷에서, k8s-m 호스트가 BGP(Border Gateway Protocol) 포트인 179를 통해 k8s-w0 호스트의 임의 포트인 55161로 패킷을 전송하고 있습니다. 이 패킷은 BGP 프로토콜의 일부로, 데이터를 즉시 전송해야 함을 나타내는 [P.] 플래그를 포함하고 있습니다. 시퀀스 번호는 2717584943에서 시작해 2717584962까지의 데이터를 보내며, ack 번호는 4098803667로 설정되어 있습니다. 이는 k8s-w0에서 이전에 수신한 데이터에 대한 응답을 받았음을 의미합니다. 이 패킷의 윈도우 크기는 490으로 설정되어 있으며, 19바이트 길이의 BGP 데이터를 담고 있습니다.
두 번째 패킷은 이에 대한 k8s-w0의 응답입니다. 이 패킷은 데이터 없이 순수한 ACK 패킷으로, k8s-m 호스트로부터 받은 패킷에 대해 정상적으로 수신되었음을 알리고 있습니다. 여기서 중요한 것은 시퀀스 번호가 없고, ack 번호가 19로 설정되어 있다는 점입니다. 즉, k8s-w0는 k8s-m에서 전송한 19바이트의 BGP 데이터를 정상적으로 받았음을 의미합니다. 이 응답 패킷의 윈도우 크기는 491로 설정되어 있으며, 추가 데이터를 수신할 수 있는 공간을 나타냅니다.

 

설정 확인해보기

bird.cfg 파일을 보면 노드에 대한 BGP 설정이 포함되어 있는 것을 확인할 수 있습니다.

 

다음은 bird.cfg 설정의 일부로, Calico에서 BGP 피어링을 설정하는 방법을 보여줍니다. 주요 내용은 다음과 같습니다

  1. BGP 피어 정의: neighbor 필드를 통해 BGP 피어의 IP 주소(예: k8s-w2 노드의 IP 주소: 192.168.10.102)와 AS 번호(64512)를 지정합니다. 이 설정은 두 호스트 간에 BGP 세션을 설정하는 데 사용됩니다.
  2. 로컬 주소 설정: source address 필드에서 BGP 세션을 설정할 때 사용할 로컬 주소를 192.168.20.100으로 지정합니다. 이는 k8s-w0 노드에서 동작 중인 calico-node 설정에 따른 것으로, 해당 노드의 IP 주소를 BGP 트래픽 전송 시 사용하게 됩니다.
  3. 라우트 가져오기(Import): import all은 피어로부터 수신된 모든 라우트를 필터링 없이 수용한다는 의미입니다. 즉, 모든 경로 정보를 받아들입니다
  4. 라우트 내보내기(Export): export filter 필드에서는 Calico의 로직에 따라 특정 조건을 만족하는 경로만 피어에게 내보내고, 나머지는 reject로 거부합니다. 이 설정은 주로 워크로드(컨테이너 등)의 경로만 내보내도록 설계되어 있습니다.
# For peer /bgp/v1/host/k8s-w2/ip_addr_v4
protocol bgp Mesh_192_168_10_102 from bgp_template {
  neighbor 192.168.10.102 as 64512;
  source address 192.168.20.100;  # The local address we use for the TCP connection
  import all;        # Import all routes, since we don't know what the upstream
                     # topology is and therefore have to trust the ToR/RR.
  export filter {
    calico_export_to_bgp_peers(true);
    reject;
  };  # Only want to export routes for workloads.
}

 

노드 추가시 BGP 통신 과정

🚨 네트워크에 대해 잘 모르는 사람이 확인한 내용입니다. 틀릴 수 있습니다.

노드 추가시 새로운 노드에서 광고를 위한 연결 흐름

reading from file node-add.pcap, link-type EN10MB (Ethernet), snapshot length 262144
22:20:41.871461 IP k8s-w2.35883 > k8s-m.bgp: Flags [S], seq 3683895623, win 62727, options [mss 8961,sackOK,TS val 1287376573 ecr 0,nop,wscale 7], length 0
22:20:41.871538 IP k8s-m.bgp > k8s-w2.35883: Flags [S.], seq 2917420891, ack 3683895624, win 62643, options [mss 8961,sackOK,TS val 1147304279 ecr 1287376573,nop,wscale 7], length 0
22:20:41.871679 IP k8s-w2.35883 > k8s-m.bgp: Flags [.], ack 1, win 491, options [nop,nop,TS val 1287376574 ecr 1147304279], length 0
22:20:41.871753 IP k8s-w2.35883 > k8s-m.bgp: Flags [P.], seq 1:64, ack 1, win 491, options [nop,nop,TS val 1287376574 ecr 1147304279], length 63: BGP
22:20:41.871771 IP k8s-m.bgp > k8s-w2.35883: Flags [.], ack 64, win 489, options [nop,nop,TS val 1147304279 ecr 1287376574], length 0
22:20:41.871808 IP k8s-m.bgp > k8s-w2.35883: Flags [P.], seq 1:64, ack 64, win 489, options [nop,nop,TS val 1147304279 ecr 1287376574], length 63: BGP
22:20:41.871936 IP k8s-w2.35883 > k8s-m.bgp: Flags [.], ack 64, win 491, options [nop,nop,TS val 1287376574 ecr 1147304279], length 0
22:20:41.871954 IP k8s-m.bgp > k8s-w2.35883: Flags [P.], seq 64:83, ack 64, win 489, options [nop,nop,TS val 1147304279 ecr 1287376574], length 19: BGP
22:20:41.872023 IP k8s-w2.35883 > k8s-m.bgp: Flags [P.], seq 64:83, ack 64, win 491, options [nop,nop,TS val 1287376574 ecr 1147304279], length 19: BGP
22:20:41.914714 IP k8s-w2.35883 > k8s-m.bgp: Flags [.], ack 83, win 491, options [nop,nop,TS val 1287376617 ecr 1147304279], length 0
22:20:41.914765 IP k8s-m.bgp > k8s-w2.35883: Flags [P.], seq 83:158, ack 83, win 489, options [nop,nop,TS val 1147304322 ecr 1287376574], length 75: BGP
22:20:41.914878 IP k8s-w2.35883 > k8s-m.bgp: Flags [.], ack 158, win 491, options [nop,nop,TS val 1287376617 ecr 1147304322], length 0
22:20:42.916325 IP k8s-w2.35883 > k8s-m.bgp: Flags [P.], seq 83:135, ack 158, win 491, options [nop,nop,TS val 1287377618 ecr 1147304322], length 52: BGP
22:20:42.959361 IP k8s-m.bgp > k8s-w2.35883: Flags [.], ack 135, win 489, options [nop,nop,TS val 1147305367 ecr 1287377618], length 0
22:20:42.959908 IP k8s-w2.35883 > k8s-m.bgp: Flags [P.], seq 135:158, ack 158, win 491, options [nop,nop,TS val 1287377662 ecr 1147305367], length 23: BGP
22:20:42.959957 IP k8s-m.bgp > k8s-w2.35883: Flags [.], ack 158, win 489, options [nop,nop,TS val 1147305367 ecr 1287377662], length 0
(⎈|HomeLab:default) root@k8s-m:~# ^C
(⎈|HomeLab:default) root@k8s-m:~# tcpdump -r node-add.pcap -vv
reading from file node-add.pcap, link-type EN10MB (Ethernet), snapshot length 262144
22:20:41.871461 IP (tos 0xc0, ttl 64, id 24674, offset 0, flags [DF], proto TCP (6), length 60)
    k8s-w2.35883 > k8s-m.bgp: Flags [S], cksum 0x52b7 (correct), seq 3683895623, win 62727, options [mss 8961,sackOK,TS val 1287376573 ecr 0,nop,wscale 7], length 0
22:20:41.871538 IP (tos 0xc0, ttl 255, id 0, offset 0, flags [DF], proto TCP (6), length 60)
    k8s-m.bgp > k8s-w2.35883: Flags [S.], cksum 0x95ef (incorrect -> 0x9800), seq 2917420891, ack 3683895624, win 62643, options [mss 8961,sackOK,TS val 1147304279 ecr 1287376573,nop,wscale 7], length 0
22:20:41.871679 IP (tos 0xc0, ttl 64, id 24675, offset 0, flags [DF], proto TCP (6), length 52)
    k8s-w2.35883 > k8s-m.bgp: Flags [.], cksum 0xd6e1 (correct), seq 1, ack 1, win 491, options [nop,nop,TS val 1287376574 ecr 1147304279], length 0
22:20:41.871753 IP (tos 0xc0, ttl 64, id 24676, offset 0, flags [DF], proto TCP (6), length 115)
    k8s-w2.35883 > k8s-m.bgp: Flags [P.], cksum 0xb780 (correct), seq 1:64, ack 1, win 491, options [nop,nop,TS val 1287376574 ecr 1147304279], length 63: BGP
	Open Message (1), length: 63
	  Version 4, my AS 64512, Holdtime 240s, ID k8s-w2
	  Optional parameters, length: 34
	    Option Capabilities Advertisement (2), length: 32
	      Multiprotocol Extensions (1), length: 4
		AFI IPv4 (1), SAFI Unicast (1)
		0x0000:  0001 0001
	      Route Refresh (2), length: 0
	      Graceful Restart (64), length: 6
		Restart Flags: [R], Restart Time 120s
		  AFI IPv4 (1), SAFI Unicast (1), Forwarding state preserved: yes
		0x0000:  8078 0001 0180
	      32-Bit AS Number (65), length: 4
		 4 Byte AS 64512
		0x0000:  0000 fc00
	      Multiple Paths (69), length: 4
		AFI IPv4 (1), SAFI Unicast (1), Send/Receive: Both
		0x0000:  0001 0103
	      Enhanced Route Refresh (70), length: 0
		no decoder for Capability 70
	      Long-lived Graceful Restart (71), length: 0
22:20:41.871771 IP (tos 0xc0, ttl 255, id 15773, offset 0, flags [DF], proto TCP (6), length 52)
    k8s-m.bgp > k8s-w2.35883: Flags [.], cksum 0x95e7 (incorrect -> 0xd6a4), seq 1, ack 64, win 489, options [nop,nop,TS val 1147304279 ecr 1287376574], length 0
22:20:41.871808 IP (tos 0xc0, ttl 64, id 15774, offset 0, flags [DF], proto TCP (6), length 115)
    k8s-m.bgp > k8s-w2.35883: Flags [P.], cksum 0x9626 (incorrect -> 0x3820), seq 1:64, ack 64, win 489, options [nop,nop,TS val 1147304279 ecr 1287376574], length 63: BGP
	Open Message (1), length: 63
	  Version 4, my AS 64512, Holdtime 240s, ID k8s-m
	  Optional parameters, length: 34
	    Option Capabilities Advertisement (2), length: 32
	      Multiprotocol Extensions (1), length: 4
		AFI IPv4 (1), SAFI Unicast (1)
		0x0000:  0001 0001
	      Route Refresh (2), length: 0
	      Graceful Restart (64), length: 6
		Restart Flags: [none], Restart Time 120s
		  AFI IPv4 (1), SAFI Unicast (1), Forwarding state preserved: no
		0x0000:  0078 0001 0100
	      32-Bit AS Number (65), length: 4
		 4 Byte AS 64512
		0x0000:  0000 fc00
	      Multiple Paths (69), length: 4
		AFI IPv4 (1), SAFI Unicast (1), Send/Receive: Both
		0x0000:  0001 0103
	      Enhanced Route Refresh (70), length: 0
		no decoder for Capability 70
	      Long-lived Graceful Restart (71), length: 0
22:20:41.871936 IP (tos 0xc0, ttl 64, id 24677, offset 0, flags [DF], proto TCP (6), length 52)
    k8s-w2.35883 > k8s-m.bgp: Flags [.], cksum 0xd663 (correct), seq 64, ack 64, win 491, options [nop,nop,TS val 1287376574 ecr 1147304279], length 0
22:20:41.871954 IP (tos 0xc0, ttl 64, id 15775, offset 0, flags [DF], proto TCP (6), length 71)
    k8s-m.bgp > k8s-w2.35883: Flags [P.], cksum 0x95fa (incorrect -> 0xd237), seq 64:83, ack 64, win 489, options [nop,nop,TS val 1147304279 ecr 1287376574], length 19: BGP
	Keepalive Message (4), length: 19
22:20:41.872023 IP (tos 0xc0, ttl 64, id 24678, offset 0, flags [DF], proto TCP (6), length 71)
    k8s-w2.35883 > k8s-m.bgp: Flags [P.], cksum 0xd235 (correct), seq 64:83, ack 64, win 491, options [nop,nop,TS val 1287376574 ecr 1147304279], length 19: BGP
	Keepalive Message (4), length: 19
22:20:41.914714 IP (tos 0xc0, ttl 64, id 24679, offset 0, flags [DF], proto TCP (6), length 52)
    k8s-w2.35883 > k8s-m.bgp: Flags [.], cksum 0xd612 (correct), seq 83, ack 83, win 491, options [nop,nop,TS val 1287376617 ecr 1147304279], length 0
22:20:41.914765 IP (tos 0xc0, ttl 64, id 15776, offset 0, flags [DF], proto TCP (6), length 127)
    k8s-m.bgp > k8s-w2.35883: Flags [P.], cksum 0x9632 (incorrect -> 0x935d), seq 83:158, ack 83, win 489, options [nop,nop,TS val 1147304322 ecr 1287376574], length 75: BGP
	Update Message (2), length: 52
	  Origin (1), length: 1, Flags [T]: IGP
	    0x0000:  00
	  AS Path (2), length: 0, Flags [T]: empty
	  Next Hop (3), length: 4, Flags [T]: k8s-m
	    0x0000:  c0a8 0a0a
	  Local Preference (5), length: 4, Flags [T]: 100
	    0x0000:  0000 0064
	  Updated routes:

 

✔︎ BGP Open

BGP Open 메시지는 BGP 세션을 설정할 때 가장 먼저 주고받는 메시지입니다. 두 BGP 피어가 서로 통신을 시작할 때 이 메시지를 교환하며, 세션의 주요 매개변수를 협상합니다. 이 메시지에는 BGP 버전, AS 번호, Hold Time, 라우터 ID 등의 정보가 포함됩니다.

예를 들어, 두 라우터가 동일한 자율 시스템(AS 64512)에 속해 있다면, BGP Open 메시지를 통해 AS 번호를 주고받으며 같은 AS 내에서 통신 중임을 확인하게 됩니다. 또한, Hold Time을 협상해 BGP 피어 간 연결이 유지될 수 있는 최대 대기 시간을 설정합니다. Hold Time은 주기적인 Keepalive 메시지 또는 Update 메시지가 일정 시간 내에 오지 않으면 세션을 종료하고 경로 정보를 재설정하는 기준이 됩니다.

 

✔︎ BGP Update

BGP Update 메시지는 BGP 세션이 성공적으로 설정된 후 경로 정보를 교환하는 데 사용됩니다. BGP 피어는 네트워크의 경로 정보를 이 메시지를 통해 주고받으며, 이를 통해 네트워크 트래픽의 경로를 최적화할 수 있습니다.

BGP Update 메시지는 새로운 경로 정보를 전송하거나 기존 경로 정보를 무효화하는 역할도 합니다. 예를 들어, 특정 경로가 더 이상 유효하지 않을 경우 Withdrawn Routes 섹션에 해당 정보를 포함시켜 피어에게 경로가 유효하지 않음을 알릴 수 있습니다.

 

Felix

Felix는 인터페이스, 라우팅 정보, 네트워크 정책(Network Policy)을 관리하며, 이를 iptables 규칙으로 변환하여 적용하는 역할을 합니다. 다음은 Calico의 Felix에 의해 추가된 일부 iptables 규칙 예시입니다.

calico의 felix로 추가된 iptables룰의 일부

 

추가로, Bird에서 받은 다른 네트워크 대역의 정보를 가상의 라우터에 추가하는 역할도 수행합니다.

Blackhole 설정은 내 IP 대역의 트래픽이 다른 곳으로 전송되지 않도록 차단하는 기능을 의미합니다.

라우팅 테이블 결과 확인

 

calicoctl로 확인한 내 bgp광고 대역

 

Confd

Confd는 BGP 설정 등으로 인해 Calico 데이터 저장소에 변경이 발생하면, Bird의 설정 파일을 업데이트하고 변경된 설정을 반영하는 역할을 합니다.

 

Typha

Typha는 Calico 컴포넌트 중 하나로, Calico를 사용하는 Kubernetes 클러스터에서 대규모 배포 성능을 최적화하기 위해 사용됩니다. Calico는 네트워크 정책과 설정을 각 노드에서 실행 중인 calico-node 데몬에 전달하여 관리합니다. 하지만 클러스터 내 노드 수가 많아지면, calico-node 데몬과 Kubernetes API 서버 간의 통신에 과부하가 걸릴 수 있습니다.

Typha는 이러한 문제를 해결하기 위해 도입된 중간 캐시 레이어로, 노드가 100대 이상인 경우 필수적으로 구성해야 합니다.

 

CNI IPAM Plugin

Calico의 IPAM(IP Address Management)은 Kubernetes의 host-local IPAM과 다르게 설정이 가능합니다.

어떤 IPAM을 사용하고 있는지 확인해 봅니다.

 

현재 사용 중인 IPAM을 확인할 수 있습니다. 또한, Calico를 통해 설정된 IPPool을 확인하면, 사용 가능한 IP 중 7개만 사용된 것을 볼 수 있습니다.

 

파드 생성 시, 설정된 IP 대역은 다음과 같이 확인할 수 있습니다.

✔︎ cluster-info명령어로 확인하기

✔︎ kubeadm 설정으로 확인하기

 

고정 아이피로 파드 생성하기

파드의 annotations에 cni.projectcalico.org/ipAddrs를 사용하여 원하는 IP를 지정하면, 해당 IP로 파드를 생성할 수 있습니다.

하지만, Calico가 관리하는 대역이 아닌 IP를 지정할 경우, IP 할당에 실패하여 파드 생성이 불가능합니다.

IP 대역이 맞지 않아 생성에 실패한 예

 

반면, Calico가 관리하는 IP 대역에서는 지정된 고정 IP로 파드가 정상적으로 생성되는 것을 확인할 수 있습니다.

 

또한, 고정 IP를 미리 예약하고 싶다면 IPReservation 리소스를 사용하여 특정 IP 대역이 자동으로 할당되지 않도록 설정할 수도 있습니다.

apiVersion: projectcalico.org/v3
kind: IPReservation
metadata:
  name: my-ipreservation-1
spec:
  reservedCIDRs:
    - 192.168.2.3
    - 10.0.2.3/32
    - cafe:f00d::/123

 

자세한 내용은  여기서 확인할 수 있습니다. 

https://docs.tigera.io/calico/latest/networking/ipam/use-specific-ip

 

Use a specific IP address with a pod | Calico Documentation

Specify the IP address for a pod instead of allowing Calico to automatically choose one.

docs.tigera.io

 

 

이번 글에서는 Calico의 기본적인 기능과 구성 요소를 살펴보았습니다. 다음 글에서는 Calico가 제공하는 네트워크 모델을 더욱 깊이 있게 다뤄보려고 합니다. 네트워크 정책부터 BGP 설정까지, Calico의 다양한 기능을 이해하고 Kubernetes 환경에서 어떻게 최적화할 수 있는지 함께 알아보겠습니다.