KANS3 - Calico CNI & Mode(1/3)

Calico

Calico 는 쿠버네티스에서 사용 가능한 CNI 중 하나이며, IPIP, VXLAN, Direc와 같은 다양한 모드를 제공합니다. 현업에서 사용중인 CSP인 Google Cloud에서는 Google Kubernetes Engine 사용시 dataplane v2에 대한 옵션을 활성화지 않는 경우 dataplane v1으로 생성되며, Google 문서에 따르면 dataplan v1은 Calico로 구성된다고 되어있습니다. 참고로, dataplane v2는 Cilium입니다.

현재 현업에서 운영중인 GKE들은 dataplane v1(Calico)로 운영하고 있으며, Cilium의 버전변경을 모니터링하고, 문서 업데이트 속도를 확인하여 최근에 생성하는 GKE부터는 Dataplane v2(Cilium)으로 생성 및 테스트를 진행하고 있습니다. 물론, 이 부분은 Standard 타입의 클러스터에 한해서 운영하고 있으며, Composer(Airflow PaaS)의 경우에는 Composer v2버전이 GKE Autopilot으로 구성되기 때문에, Dataplane v2로 기본 구성이 되어있습니다. 쿠버네티스 네트워크 스터디가 종료될 시점에는 Dataplane v2에서의 테스트/운영에 대한 이야기도 작성해 보도록 하겠습니다.

아래에 나오는 명령어 및 출력물은 현업에서 운영되는 GKE에서는 PaaS로 Control Plane에 접근이 불가능하기 때문에, 스터디에서 제공받은 스크립트를 통한 VirtualBox와 Vagrant로 구성된 VM으로 구성하여 테스트 되었습니다.

Calico 구성요소

calicoctl

Calico를 제어하는 CMD 툴로써 kubectl로 하는것보다는 손쉽게 사용가능합니다.

calico 저장소

K8s API 저장소 내에 저장됩니다.

calico-node

K8s node에 daemonset으로 1개씩 구성되며, 3가지 요소인 BIRD, Felix, confd가 있습니다.

  • BIRD: OSS 라우팅 프로그램으로 노드내 POD 대역을 BGP를 통해 광고합니다.
  • Felix: BIRD의 노드내 POD 대역에 대한 라우팅 테이블을 제어합니다.
  • confd: calico의 설정파일을 관리합니다.

Calico 설치

구성 정보 확인

# 노드 정보 확인
k get nodes -A -owide

NAME     STATUS   ROLES           AGE     VERSION   INTERNAL-IP      EXTERNAL-IP   OS-IMAGE             KERNEL-VERSION       CONTAINER-RUNTIME
k8s-m    Ready    control-plane   12m     v1.30.5   192.168.10.10    <none>        Ubuntu 22.04.5 LTS   5.15.0-119-generic   containerd://1.7.22
k8s-w0   Ready    <none>          10m     v1.30.5   192.168.20.100   <none>        Ubuntu 22.04.5 LTS   5.15.0-119-generic   containerd://1.7.22
k8s-w1   Ready    <none>          8m44s   v1.30.5   192.168.10.101   <none>        Ubuntu 22.04.5 LTS   5.15.0-119-generic   containerd://1.7.22
k8s-w2   Ready    <none>          6m49s   v1.30.5   192.168.10.102   <none>        Ubuntu 22.04.5 LTS   5.15.0-119-generic   containerd://1.7.22

# calico 구성요소 배포 확인
k get pod -n kube-system -l k8s-app=calico-node -owide
# 출력 확인시 calico-node 의 ip는 노드가 사용하는 host ip를 사용한다.
NAME                READY   STATUS    RESTARTS   AGE     IP               NODE     NOMINATED NODE   READINESS GATES
calico-node-5xtr7   1/1     Running   0          4m15s   192.168.10.102   k8s-w2   <none>           <none>
calico-node-72mx5   1/1     Running   0          4m15s   192.168.10.101   k8s-w1   <none>           <none>
calico-node-tqggs   1/1     Running   0          4m15s   192.168.20.100   k8s-w0   <none>           <none>
calico-node-wlwfr   1/1     Running   0          4m15s   192.168.10.10    k8s-m    <none>           <none>

# POD 생성시 Calico CNI를 통해 배치받는 IP 대역정보 확인
calicoctl ipam show

+----------+---------------+-----------+------------+--------------+
| GROUPING |     CIDR      | IPS TOTAL | IPS IN USE |   IPS FREE   |
+----------+---------------+-----------+------------+--------------+
| IP Pool  | 172.16.0.0/16 |     65536 | 7 (0%)     | 65529 (100%) |
+----------+---------------+-----------+------------+--------------+

# 블록 할당 정보 확인
calicoctl opsm show --show-blocks
+----------+-----------------+-----------+------------+--------------+
| GROUPING |      CIDR       | IPS TOTAL | IPS IN USE |   IPS FREE   |
+----------+-----------------+-----------+------------+--------------+
| IP Pool  | 172.16.0.0/16   |     65536 | 7 (0%)     | 65529 (100%) |
| Block    | 172.16.116.0/24 |       256 | 1 (0%)     | 255 (100%)   |
| Block    | 172.16.158.0/24 |       256 | 4 (2%)     | 252 (98%)    |
| Block    | 172.16.184.0/24 |       256 | 1 (0%)     | 255 (100%)   |
| Block    | 172.16.34.0/24  |       256 | 1 (0%)     | 255 (100%)   |
+----------+-----------------+-----------+------------+--------------+

# calico 설정 정보
calicoctl ipam show --show-configuration
+--------------------+-------+
|      PROPERTY      | VALUE |
+--------------------+-------+
| StrictAffinity     | false | -> true시 할당된 블록에서만 사용, false시 다른노드에서 ip 사용가능
| AutoAllocateBlocks | true  |
| MaxBlocksPerHost   |     0 |
+--------------------+-------+

# calico node 연결 정보
calicoctl node status
Calico process is running.

IPv4 BGP status
+----------------+-------------------+-------+----------+-------------+
|  PEER ADDRESS  |     PEER TYPE     | STATE |  SINCE   |    INFO     |
+----------------+-------------------+-------+----------+-------------+
| 192.168.20.100 | node-to-node mesh | up    | 07:39:33 | Established |
| 192.168.10.101 | node-to-node mesh | up    | 07:39:32 | Established |
| 192.168.10.102 | node-to-node mesh | up    | 07:39:32 | Established |
+----------------+-------------------+-------+----------+-------------+

IPv6 BGP status
No IPv6 peers found.

# k8s에서 사용하는 ip 대역 정보와 calico 모드 확인
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()

Calico에서의 POD의 통신 이해

  • POD - Internet: POD가 위치한 Node의 IP로 MASQUERADE로 IP 변경이되어 외부와의 통신이 됩니다.
  • POD - POD(같은 노드): 같은 노드 내 POD간 통신은 Node 내부에서 직접 통신됩니다.
  • POD - POD(다른 노드): 별도의 설정을 하지 않았다면 IPIP모드로 다른 노드간 통신이 됩니다.

POD - Internet

# NAT가 true로 활성화 되어있습니다.
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()

# iptables 확인
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

# masq 확인
ipset list cali40masq-ipam-pools
Name: cali40masq-ipam-pools
Type: hash:net
Revision: 7
Header: family inet hashsize 1024 maxelem 1048576 bucketsize 12 initval 0xd577ac3b
Size in memory: 504
References: 1
Number of entries: 1
Members:
172.16.0.0/16

POD - POD (같은 노드)간 통신

cat << EOF | k apply -f -
> apiVersion: v1
kind: Pod
metadata:
  name: pod1
spec:
  nodeName: k8s-w1
  containers:
  - name: pod1
    image: nicolaka/netshoot
    command: ["tail"]
    args: ["-f", "/dev/null"]
  terminationGracePeriodSeconds: 0
---
apiVersion: v1
kind: Pod
metadata:
  name: pod2
spec:
  nodeName: k8s-w1
  containers:
  - name: pod2
    image: nicolaka/netshoot
    command: ["tail"]
    args: ["-f", "/dev/null"]
  terminationGracePeriodSeconds: 0
> EOF
pod/pod1 created
pod/pod2 created

calicoctl get workloadEndpoint
WORKLOAD   NODE     NETWORKS          INTERFACE
pod1       k8s-w1   172.16.158.4/32   calice0906292e2
pod2       k8s-w1   172.16.158.6/32   cali3a3e66463d6

ip -c link
10: calice0906292e2@if3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1480 qdisc noqueue state UP mode DEFAULT group default qlen 1000
    link/ether ee:ee:ee:ee:ee:ee brd ff:ff:ff:ff:ff:ff link-netns cni-10a1c842-93de-2773-2975-6ad473f9d123
12: cali3a3e66463d6@if3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1480 qdisc noqueue state UP mode DEFAULT group default qlen 1000
    link/ether ee:ee:ee:ee:ee:ee brd ff:ff:ff:ff:ff:ff link-netns cni-1c54eb9c-1ee1-84d5-94a8-def5a78476d5

ip -c route
172.16.158.4 dev calice0906292e2 scope link
172.16.158.6 dev cali3a3e66463d6 scope link

POD - POD (다른 노드)간 통신

# 노드 내 BGP 확인
# Worker 0
route | head -2 ; route -n | grep tunl0
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
172.16.116.0    192.168.10.10   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
# Worker 1
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.116.0    192.168.10.10   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

# POD 생성
apiVersion: v1
kind: Pod
metadata:
  name: pod0
spec:
  nodeName: k8s-w0
  containers:
  - name: pod0
    image: nicolaka/netshoot
    command: ["tail"]
    args: ["-f", "/dev/null"]
  terminationGracePeriodSeconds: 0
---
apiVersion: v1
kind: Pod
metadata:
  name: pod1
spec:
  nodeName: k8s-w1
  containers:
  - name: pod1
    image: nicolaka/netshoot
    command: ["tail"]
    args: ["-f", "/dev/null"]
  terminationGracePeriodSeconds: 0

# pod endpoint 확인
calicoctl get workloadendpoints
WORKLOAD   NODE     NETWORKS          INTERFACE
pod0       k8s-w0   172.16.34.2/32    cali7ac1b8e1615
pod1       k8s-w1   172.16.158.9/32   calice0906292e2

# node 간 라우팅 확인
# Worker 0
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     0.0.0.0         255.255.255.0   U     0      0        0 *
172.16.34.2     0.0.0.0         255.255.255.255 UH    0      0        0 cali7ac1b8e1615
172.16.116.0    192.168.10.10   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
# Worker 1
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
172.16.116.0    192.168.10.10   255.255.255.0   UG    0      0        0 tunl0
172.16.158.0    0.0.0.0         255.255.255.0   U     0      0        0 *
172.16.158.1    0.0.0.0         255.255.255.255 UH    0      0        0 cali8e446144959
172.16.158.2    0.0.0.0         255.255.255.255 UH    0      0        0 calie37aee99def
172.16.158.3    0.0.0.0         255.255.255.255 UH    0      0        0 cali7245e7a0fea
172.16.158.9    0.0.0.0         255.255.255.255 UH    0      0        0 calice0906292e2
172.16.184.0    192.168.10.102  255.255.255.0   UG    0      0        0 tunl0

# PING을 통한 패킷 카운트 확인
# POD 1 -> POD 0
# Worker 0
ifconfig tunl0 | head -2 ; ifconfig tunl0 | grep bytes

tunl0: flags=193<UP,RUNNING,NOARP>  mtu 1480
        inet 172.16.34.0  netmask 255.255.255.255
        RX packets 10  bytes 840 (840.0 B)
        TX packets 10  bytes 840 (840.0 B)
# Worker 1
ifconfig tunl0 | head -2 ; ifconfig tunl0 | grep bytes

tunl0: flags=193<UP,RUNNING,NOARP>  mtu 1480
        inet 172.16.158.0  netmask 255.255.255.255
        RX packets 10  bytes 840 (840.0 B)
        TX packets 10  bytes 840 (840.0 B)

Calico의 여러 모드

  • IPIP: Calico 기본모드이며, IPIP 인캡슐레이션으로 이루어짐
  • Direct: 원본패킷이 목적지로 원본 그대로 전달됨
  • CrossSubnet: 노드간 같은 네트워크에서는 Direct, 노드간 다른 네트워크에서는 IPIP모드로 동작
  • VXLAN: vxlan 인캡슐래이션을 통해 통신이 됨

IPIP 모드 : POD - POD(다른 노드)에서 IPIP 내용 참조

Direct 모드

# 현재 모드 확인
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()

calicoctl get ippool default-ipv4-ippool -o yaml
apiVersion: projectcalico.org/v3
kind: IPPool
metadata:
  creationTimestamp: "2024-09-21T07:39:23Z"
  name: default-ipv4-ippool
  resourceVersion: "1487"
  uid: d105e1a3-a1d6-4c6b-a9d6-a88739b8fca6
spec:
  allowedUses:
  - Workload
  - Tunnel
  blockSize: 24
  cidr: 172.16.0.0/16
  ipipMode: Always
  natOutgoing: true
  nodeSelector: all()
  vxlanMode: Never

# 모드 변경
calicoctl get ippool default-ipv4-ippool -o yaml | sed -e "s/ipipMode: Always/ipipMode: Never/" | calicoctl apply -f -

# 변경 확인
calicoctl get ippool -o wide
NAME                  CIDR            NAT    IPIPMODE   VXLANMODE   DISABLED   DISABLEBGPEXPORT   SELECTOR
default-ipv4-ippool   172.16.0.0/16   true   Never      Never       false      false              all()

# Worker 확인
# 기존에는 tunl0 인터페이스를 사용했지만, host의 인터페이스로 변경 확인
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
172.16.116.0    192.168.10.10   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    0.0.0.0         255.255.255.0   U     0      0        0 *
root@k8s-w2:~# route -n | egrep '(Destination|UG)'
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         10.0.2.2        0.0.0.0         UG    100    0        0 enp0s3
172.16.34.0     192.168.10.254  255.255.255.0   UG    0      0        0 enp0s8
172.16.116.0    192.168.10.10   255.255.255.0   UG    0      0        0 enp0s8
172.16.158.0    192.168.10.101  255.255.255.0   UG    0      0        0 enp0s8
192.168.20.0    192.168.10.254  255.255.255.0   UG    0      0        0 enp0s8

CrossSubnet 모드

# 모드 변경
calicoctl patch ippool default-ipv4-ippool -p '{"spec":{"ipipMode":"CrossSubnet"}}'

# 변경 확인
calicoctl get ippool -o wide
NAME                  CIDR            NAT    IPIPMODE      VXLANMODE   DISABLED   DISABLEBGPEXPORT   SELECTOR
default-ipv4-ippool   172.16.0.0/16   true   CrossSubnet   Never       false      false              all()

# Worker 확인
# 노드에 할당된 서브넷이 아니면 tunl0 으로 잡힌다.

# Worker1
route -n | egrep '(Destination|UG)'
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         10.0.2.2        0.0.0.0         UG    100    0        0 enp0s3
172.16.34.0     192.168.20.100  255.255.255.0   UG    0      0        0 tunl0
172.16.116.0    192.168.10.10   255.255.255.0   UG    0      0        0 enp0s8
172.16.184.0    192.168.10.102  255.255.255.0   UG    0      0        0 enp0s8
192.168.20.0    192.168.10.254  255.255.255.0   UG    0      0        0 enp0s8

# Worker2
route -n | grep UG
0.0.0.0         10.0.2.2        0.0.0.0         UG    100    0        0 enp0s3
172.16.34.0     192.168.20.100  255.255.255.0   UG    0      0        0 tunl0
172.16.116.0    192.168.10.10   255.255.255.0   UG    0      0        0 enp0s8
172.16.158.0    192.168.10.101  255.255.255.0   UG    0      0        0 enp0s8
192.168.20.0    192.168.10.254  255.255.255.0   UG    0      0        0 enp0s8

VXLAN 모드

# 모드 변경
calicoctl get ippool default-ipv4-ippool -o yaml | sed -e "s/vxlanMode: Never/vxlanMode: Always/" | calicoctl apply -f -

# 변경 확인
calicoctl get ippool default-ipv4-ippool -o wide
NAME                  CIDR            NAT    IPIPMODE   VXLANMODE   DISABLED   DISABLEBGPEXPORT   SELECTOR
default-ipv4-ippool   172.16.0.0/16   true   Never      Always      false      false              all()

# calico-node pod 재생성
kubectl delete pod -n kube-system -l k8s-app=calico-node

# 실습 환경에서 라우팅 테이블 갱신을 위한 인터페이스 down & up
route -n && echo && ip link set enp0s8 down && sleep 1 && ip link set enp0s8 up && route -n

# calico 노드 확인
calicoctl node status
Calico process is running.

The BGP backend process (BIRD) is not running.

# 노드 내 어노테이션 확인
k describe node | grep -A3 An
Annotations:        kubeadm.alpha.kubernetes.io/cri-socket: unix:///run/containerd/containerd.sock
                    node.alpha.kubernetes.io/ttl: 0
                    projectcalico.org/IPv4Address: 192.168.10.10/24
                    projectcalico.org/IPv4VXLANTunnelAddr: 172.16.116.2

# 인터페이스 확인
ip -c link
4: vxlan.calico: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UNKNOWN mode DEFAULT group default
    link/ether 66:04:03:49:b1:1e brd ff:ff:ff:ff:ff:ff

ip -c nei
10.0.2.2 dev enp0s3 lladdr 52:54:00:12:35:02 REACHABLE
10.0.2.3 dev enp0s3 lladdr 52:54:00:12:35:03 STALE
172.16.34.1 dev cali53ccf54a47b lladdr 32:9b:2c:7d:ca:56 REACHABLE
172.16.158.0 dev vxlan.calico lladdr 66:74:91:32:7c:04 PERMANENT
192.168.20.254 dev enp0s8 lladdr 08:00:27:83:fb:36 REACHABLE
172.16.184.0 dev vxlan.calico lladdr 66:55:c4:6b:81:ed PERMANENT
172.16.116.0 dev vxlan.calico lladdr 66:04:03:49:b1:1e PERMANENT
172.16.34.2 dev calibf28d443af8 lladdr 82:4f:c5:77:4f:a2 REACHABLE

# 라우팅 테이블 확인
route -n | egrep '(Destination|vxlan)'
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
172.16.116.0    172.16.116.0    255.255.255.0   UG    0      0        0 vxlan.calico
172.16.158.0    172.16.158.0    255.255.255.0   UG    0      0        0 vxlan.calico
172.16.184.0    172.16.184.0    255.255.255.0   UG    0      0        0 vxlan.calico