[네떡스터디🔥kans] Calico CNI 알아보기 - IPIP 모드

 

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

 

이번에는 Calico에서 지원하는 다양한 네트워크 모드를 살펴보고, 이를 실제로 설치 및 테스트해보려고 합니다. 첫 번째로 오버레이 네트워크 중 하나인 IPIP 모드를 다뤄보겠습니다.

 

calico에서 제공하는 네트워크 모드 종류들

 

IPIP 란

IPIP(IP-in-IP)는 IP 패킷을 또 다른 IP 패킷 안에 캡슐화하여 전달하는 오버레이 네트워크 프로토콜 중 하나입니다. 기존 IP 패킷에 새로운 IP 헤더를 추가하여 캡슐화하는 방식으로, 단순한 구조 덕분에 오버헤드가 적습니다. 그러나 노드 수가 많아질 경우 네트워크 경로 관리가 복잡해질 수 있습니다. 또한, IPIP모드는 Layer 2 브로드캐스트 트래픽을 지원하지 않지만, 쿠버네티스에서는 파드 간 통신이 IP 기반으로 이루어지기 때문에 큰 문제가 되지 않습니다.

일반 패킷 구조 / IPIP 패킷 구조

 

 

IPIP모드 설치하기

calico 설치 파일 다운받기 

최신 버전 확인하기

CALICO_LATEST=$( curl --silent "https://api.github.com/repos/projectcalico/calico/releases/latest" | jq '.tag_name' -r )

 

 

이 설치 방식은 오퍼레이터를 사용하지 않으며, 기본적으로 제공되는 파일로 설치하면 IPIP 모드가 활성화됩니다.

curl https://raw.githubusercontent.com/projectcalico/calico/$CALICO_LATEST/manifests/calico.yaml -O

 

 

CRD만 먼저 설치하려면 아래 명령어를 실행하면 됩니다.

kubectl apply -f https://raw.githubusercontent.com/projectcalico/calico/refs/tags/$CALICO_LATEST/manifests/crds.yaml

 

 

현재 최신 버전은 v3.28.1입니다.

 

테스트 시 사용한 버전

calico.yaml 파일에서 calico-node의 DaemonSet에 CALICO_IPV4POOL_BLOCK_SIZE 환경 변수의 값을 24로 설정하여 사용했습니다. 원래는 기본 값이 26입니다.

            # Block size to use for the IPv4 POOL created at startup. Block size for IPv4 should be in the range 20-32. default 24
            - name: CALICO_IPV4POOL_BLOCK_SIZE
              value: "24"

 

설치 확인하기

 

IPIP 모드 뜯어보기

테스트 환경 정보

kubectl 명령어로 노드에 설정된 Pod CIDR을 확인합니다.

kubectl get nodes -o custom-columns=NAME:.metadata.name,VERSION:.status.nodeInfo.kubeletVersion,INTERNAL-IP:.status.addresses[0].address,POD-CIDR:.spec.podCIDR

 

 

여기서 INTERNAL-IP는 노드의 IP이고, POD-CIDR은 파드들이 할당받는 주소 범위입니다.

 

calicoctl을 이용해서도 확인할 수 있으며, 터널 주소 정보도 함께 볼 수 있습니다.

calicoctl이 custom-columns을 지원하지 않아서 어거지로 보기쉽게 만들어봤습니다.

(echo -e "Name\tCalicoNodeIP\tInternalIP\tIPv4IPIPTunnelAddr\tPodCIDRs"; \
calicoctl get node -o json | jq -r '.items[] | {Name: .metadata.name, CalicoNodeIP: .spec.addresses[0].address, InternalIP: (.spec.addresses[] | select(.type == "InternalIP") | .address), IPv4IPIPTunnelAddr: .spec.bgp.ipv4IPIPTunnelAddr, PodCIDRs: .status.podCIDRs[]} | [.Name, .CalicoNodeIP, .InternalIP, .IPv4IPIPTunnelAddr, .PodCIDRs] | @tsv') | column -t

 

최종적으로 ipam을 조회해보면 다음과 같습니다.

 

 

그림으로 확인해보겠습니다

 

라우팅 경로 확인하기 

k8s-m에서 확인한 라우팅 경로입니다. 다른 노드로 가도록 설정된 목적지는 위에서 보였던 각노드의 IPIP Tunnel IP로 보입니다. 

 

같은 노드 내 파드 간 통신 흐름

✔︎  파드 생성하기

워커노드 1번에 파드 2개를 띄워 직접 확인해보겠습니다. 

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: nginx
  name: nginx
spec:
  replicas: 2
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      nodeName: k8s-w0
      containers:
      - image: nginx
        name: nginx
        resources: {}

 

파드가 생성된 후 calicoctl로 어떤 노드에 어떤 IP와 인터페이스로 파드가 생성되었는지 확인할 수 있습니다.

calicoctl get workloadEndpoint

파드 생성 전 후 라우팅 경로입니다.

 


🔥 노드의 설정된 파드아이피 대역과 실제 생성된 파드 아이피 대역이 달라요

Pod의 IP가 처음 조회했던 Pod CIDR 범위에 속하지 않지만, IPAM 블록의 일부에는 포함되는 것으로 보입니다. IPAM 블록을 조회해보면, Calico에서는 노드 설정에 지정된 CIDR을 사용하는 것이 아니라, calico에 설정한 IPAM 블록을 통해 할당된다는 것을 알 수 있습니다.


✔︎ 호출 해보기

아래 명령어로 파드 간 통신을 테스트합니다.

curl -o /dev/null -s -w "%{http_code}" http://<pod ip>

✔︎ 통신 흐름 분석해보기

파드가 뜬 노드로 들어가서 네트워크 네임스페이스를 조회해보도록 합니다.

ip netns list

 

조회된 네임스페이스들 이름만 봐서는 어떤 파드, 인터페이스와 연결되어있는지 알 수 없습니다.

 

 

네트워크 네임스페이스와 컨테이너 인터페이스 정보 찾기

파드가 속한 네트워크 네임스페이스를 확인하려면, 먼저 호스트 네트워크 네임스페이스에서 파드의 인터페이스 이름(보통 cali로 시작하는 인터페이스)을 알아야 합니다.

파드의 인터페이스 이름은 다음 명령어로 확인할 수 있습니다. (결과는 위에서 확인 가능합니다)

calicoctl get workloadEndpoint

 

예를 들어, nginx-6b6994c5f4-4rs89 파드의 인터페이스는 cali3287e30deaa로 확인되었습니다.

이제 해당 인터페이스의 링크 정보를 확인하기 위해, 호스트에서 다음 명령어를 실행합니다.

ip link show <인터페이스 이름>

 

이를 통해 해당 인터페이스가 어떤 네트워크 네임스페이스와 연결되어 있는지 확인할 수 있습니다.

해당 네트워크 인터페이스에 들어가서 라우팅 정보도 확인해보겠습니다.

ip netns exec <네임스페이스 이름> <명령어>

 

컨테이너의 pid는 ctr 명령어로 찾아볼 수 있습니다.

ctr -n k8s.io task list

 

뭐가 어떤 컨테이너인지는 컨테이너 리스트를 찾으면 확인할 수 있습니다. 

 

자 그럼 이제 tcpdump를 떠보도록 하겠습니다.

 

caliecc06910e87 인터페이스에서 dump

tcpdump -i caliecc06910e87 -nn -w caliecc06910e87.pcap

 

cali3287e30deaa 인터페이스에서 dump

tcpdump -i cali3287e30deaa -nn -w cali3287e30deaa.pcap

 

 

 

다른 노드 간 파드 통신 흐름

✔︎  파드 생성하기

apiVersion: v1
kind: Pod
metadata:
  labels:
    run: k8s-w0
  name: k8s-w0
spec:
  nodeName: k8s-w0
  containers:
  - image: nginx
    name: k8s-w0
    resources: {}
  dnsPolicy: ClusterFirst
  restartPolicy: Always
---
apiVersion: v1
kind: Pod
metadata:
  labels:
    run: k8s-w1
  name: k8s-w1
spec:
  nodeName: k8s-w1
  containers:
  - image: nginx
    name: k8s-w1
    resources: {}
  dnsPolicy: ClusterFirst
  restartPolicy: Always

다른 노드에 떠있는 파드들간의 통신이 어떤지 확인하기 위해 각 노드에 파드들을 띄웠습니다.

 

calicoctl로 확인했을 때는 다음과 같습니다.

 

노드별 라우팅 경로는 다음과 같습니다.

 

✔︎ 통신 흐름 분석해보기

w0에서 w1로 호출해보고 tcpdump를 떠보도록 하겠습니다.

처음에 파드와 파드간 통신이랑 다르게 처음에 통신하고자 할때 추가적인 부분이 생겼습니다.

 

다른 노드에 있는 파드와 통신을 하기 위해서 arp 통신을 통해 어떤 맥주소로 가야하는지 조회해 옵니다. 

 

 

파드와 외부 인터넷 간 통신 흐름

 

서비스 객체를 통한 통신 흐름

kubectl expose pod k8s-w01 --port 80