본문 바로가기
Work/개발 노트

[Cilium study] 4주차 - Networking - 노드에 파드들간 통신 및 외부 노출

by ★용호★ 2025. 8. 9.

실습 환경 구성

curl -O https://raw.githubusercontent.com/gasida/vagrant-lab/refs/heads/main/cilium-study/4w/Vagrantfile

vagrant up
  • 기본 배포 가상 머신 : k8s-ctr, k8s-w1, k8s-w0, router
  • router : 192.168.10.0/24 ↔ 192.168.20.0/24 대역 라우팅 역할, k8s 에 join 되지 않은 서버, loop1/loop2 dump 인터페이스 배치
  • k8s-w0 : k8s-ctr/w1 노드와 다른 네트워크 대역에 배치

라우팅 테이블 살펴보기

아래와 같이 Router에서 라우팅 테이블을 확인해보면 각 네트워크에 접근할 수 있는 네트워크 인터페이스가 구성되어 있습니다. 

(router) ip -br -c -4 addr

아래와 같이 각 쿠버네티스 컨트롤플레인 노드와 워커노드들의 IP 대역을 확인해보면 그림과 같이 구성되어 있습니다. (순서대로 컨트롤플레인, 워커노드 1번, 워커노드 0번)

ip -c -4 addr show dev eth1

 

아래와 같이 컨트롤 플레인 노드에는 서로 다른 네트워크 대역의 통신을 위해 직접 추가한 정적 라우팅 경로가 있습니다. 이와 반대로 커널이 자동으로 생성한 라우팅의 경우에는 proto kernel이 붙게 됩니다. 

ip -c route ❘ grep static

현재 네트워크 구성에서는 k8s-w1은 컨트롤 플레인과 같은 네트워크에 있고, k8s-w0는 다른 네트워크에 있어서 해당 노드와 통신이 가능하도록 static 라우팅 구성이 되어 있습니다. 이 때는 노드간 통신은 가능하기 때문에 w0 워커노드도 정상적으로 컨트롤플레인에 join이 되어 있습니다. 

 

하지만 w1 워커노드에 존재하는 Pod와 w0 워커노드에 존재하는 Pod 간에 통신은 불가능한 상태입니다. 아래 테스트와 같이 같은 네트워크에 있는 Pod간 통신의 경우에는 정상적으로 통신이 되어 호스트명을 반환한 반면 다른 네트워크의 Pod와 통신할 때는 timeout이 발생하고 있습니다. 

 

이런 통신 문제를 해결하는 가장 간단한 접근은 수동으로 라우팅을 설정하는 것입니다. 노드와 Pod 수가 적다면 가능하겠지만 수백개로 확장된다면 불가능에 가까운 방법입니다. 따라서 BGP를 활용하거나 Overlay Network를 사용하는 방법이 현실적입니다. 

 

(참고) Cilium의 --set routingMode=native --set autoDirectNodeRoutes=true 옵션에 대한 이해

routingMode를 native로 설정하면 VXLAN이나 GENEVE와 같은 터널링 오버헤드 없이 Linux 커널의 라우팅 테이블을 직접 활용합니다. 이로 인해 오버레이 캡슐화로 인한 CPU 오버헤드를 줄여 네트워크 성능을 최적화 할 수 있습니다. 

autoDirectNodeRoutes를 true로 설정하게 되면 각 노드의 PodCIDR 정보를 Kubernetes API로부터 수집하여 CiliumNode 리소스를 모니터링합니다. 그리고 Linux 커널 라우팅 테이블에 proto static 라우팅 정보를 자동으로 주입하고, 노드 추가/제거 시 자동으로 갱신합니다. 

오버레이 네트워크 알아보기

아래 그림은 Calico의 네트워크 모드들에 대한 그림인데, Direct 모드가 Cilium의 네이티브 라우팅과 동일하고, VXLAN 모드가 오버레이 네트워크 동작입니다. 네이티브 라우팅의 경우에는 Pod의 IP를 그대로 사용하기 때문에 네트워크가 다른 경우 라우팅 테이블에 해당 정보가 있어야 통신이 가능하고, 오버레이 네트워크의 경우에는 노드의 IP만 알면 되기 때문에 현재 통신이 안되는 문제를 큰 공수 들이지 않고 해결할 수 있습니다. 

 

 

오버레이 네트워크를 사용하게 되면 논리적인 네트워크 인터페이스가 만들어지면서 Pod는 가상의 IP를 할당받게 되고, 네트워크 패킷에 Outer IP와 Inner IP를 통해 대상 노드의 IP와 통신이 가능하다면 Pod로 패킷이 전달되는 것을 보장하게 됩니다. 

 

CIlium에서 오버레이 네트워크로 VXLAN을 사용하기 위해서는 커널의 옵션을 변경해야 합니다. 아래와 같이 vxlan을 활성화 하고, 이 설정은 컨트롤 플레인 뿐만아니라 각 노드에서도 수행해야 합니다. 

# [커널 구성 옵션] Requirements for Tunneling and Routing
grep -E 'CONFIG_VXLAN=y|CONFIG_VXLAN=m|CONFIG_GENEVE=y|CONFIG_GENEVE=m|CONFIG_FIB_RULES=y' /boot/config-$(uname -r)
CONFIG_FIB_RULES=y # 커널에 내장됨
CONFIG_VXLAN=m # 모듈로 컴파일됨 → 커널에 로드해서 사용
CONFIG_GENEVE=m # 모듈로 컴파일됨 → 커널에 로드해서 사용

#  커널 로드
lsmod | grep -E 'vxlan|geneve'
modprobe vxlan # modprobe geneve
lsmod | grep -E 'vxlan|geneve'

 

 

이제 Cilium도 오버레이 네트워크(VXLAN)를 사용하도록 설정을 업데이트 합니다. 이 때 Cilium 에이전트 DaemonSet의 재시작이 필요하기 때문에 네트워크 순단이 발생합니다.

helm upgrade cilium cilium/cilium --namespace kube-system --version 1.18.0 --reuse-values \
  --set routingMode=tunnel --set tunnelProtocol=vxlan \
  --set autoDirectNodeRoutes=false --set installNoConntrackIptablesRules=false

kubectl rollout restart -n kube-system ds/cilium

업데이트 진행 중 순단 발생

이제 오버레이 네트워크를 사용하기 때문에 routingMode는 native에서 tunnel로 변경됐고, autoDirectNodeRoutes는 무의미해져서 false로 설정했습니다. 이제 아래 명령어로 정상적으로 VXLAN이 설정 되었는지 확인해봅니다. 

# 설정 확인
cilium features status
cilium features status | grep datapath_network

kubectl exec -it -n kube-system ds/cilium -- cilium status | grep ^Routing
cilium config view | grep tunnel

# cilium_vxlan 확인
ip -c addr show dev cilium_vxlan

 

VXLAN 설정이 정상적으로 되었고, 네트워크 인터페이스에도 새롭게 cilium_vxlan이 추가된 것을 확인할 수 있습니다. 다른 노드에 있는 Pod와 통신할 떄 해당 네트워크 인터페이스를 통해서 Outter Header를 통해 감싸서 전달하고 받는 역할을 합니다. 

 

이제 다른 네트워크 대역의 노드간에도 통신할 수 있도록 자동으로 라우팅 테이블에 설정이 됩니다. 아래 그림과 같이 172.20.2.0/24(w0 워커노드) 대역과 통신할 수 있도록 라우팅 테이블이 갱신되었습니다.

 

오버레이 네트워크가 활성화 되었고, 각 노드는 UDP 8437 포트로 터널링을 구성하여 VXLAN을 위한 패킷을 수신받을 준비가 되었기 때문에 아래와 같이 패킷을 캡처해보면 각 노드 간에는 정상적으로 패킷을 수신받아 OutterIP와 InnerIP를 추가한 패킷이 정상적으로 통신되는 것을 확인할 수 있습니다. 

# 반복 패킷 전송
kubectl exec -it curl-pod -- curl webpod | grep Hostname
kubectl exec -it curl-pod -- sh -c 'while true; do curl -s --connect-timeout 1 webpod | grep Hostname; echo "---" ; sleep 1; done'

# (다른 터미널) 노드간 패킷이 거쳐가는 router에서 tcpdump 실행
tcpdump -i any udp port 8472 -w /tmp/vxlan.pcap
tshark -r /tmp/vxlan.pcap -d udp.port==8472,vxlan
termshark -r /tmp/vxlan.pcap
termshark -r /tmp/vxlan.pcap -d udp.port==8472,vxlan

 

 

위 termshark 결과와 같이 패킷 구조를 보면, 라우터는 L3의 IP 까지만 확인하고 대상 노드로 패킷을 전달하기 때문에 그 하위의 Inner IP에 해당하는 Pod IP가 물리적인 네트워크 IP 대역에는 존재하지 않음에도 drop 하지 않고 패킷이 정상적으로 노드에 전달됩니다. 

이와 같이 패킷으로 감싸고 걷어내는 작업이 추가되기 때문에 기본적으로 네트워크 오버헤드가 증가하지만 별도의 물리적인 네트워크 설정 없이도 간편하게 Pod간 통신을 수행할 수 있습니다. 

 

Service LB-IPAM

외부에서 접근 가능하도록 로드밸런서를 사용할 때 MetalLB의 대체재로 사용할 수 있는 네이티브 LoadBalancer IP 관리 기능입니다. 

기능 Cilium LB-IPAM MetalLB
배포 복잡도 CNI와 통합된 단일 컴포넌트 별도 컨트롤러 설치 필요
성능 eBPF 기반 고성능 iptables 기반
L2 광고 네이티브 지원 (1.14+) 네이티브 지원
BGP 지원 통합 BGP Control Plane 별도 BGP 스피커
IP 풀 관리 CRD 기반 ConfigMap 기반
멀티 프로토콜 IPv4/IPv6 듀얼스택 IPv4/IPv6 듀얼스택
서비스 셀렉터 라벨/네임스페이스 기반 제한적

 

Kubernetes의 Service 생성 시 LoadBalancer 유형으로 설정하면 Cilium은 외부에서 접근 가능한 IP를 할당하게 됩니다. Cilium BGP Control Plane를 사용하여 LB IPAM이 할당한 IP 주소를 BGP를 통해 광고하고, L2 Announcements/L2 Aware LB를 통해 로컬로 광고합니다. LB IPAM은 항상 활성화되어 있지만 휴면 상태로 있다가 첫 번째 IP 풀이 클러스터에 추가되면 컨트롤러가 활성화됩니다.

 

아래의 명령어로 IP Pool을 생성할 수 있습니다 .

cat << EOF | kubectl apply -f -
apiVersion: "cilium.io/v2"  # v1.17 : cilium.io/v2alpha1
kind: CiliumLoadBalancerIPPool
metadata:
  name: "cilium-lb-ippool"
spec:
  blocks:
  - start: "192.168.10.211"
    stop:  "192.168.10.215"
EOF

kubectl get CiliumLoadBalancerIPPool -A
kubectl get ippools

 

서비스를 LoadBalancer 유형으로 설정해서 실제로 IP를 할당 받는지 확인해보겠습니다. 

 

위와 같이 webpod가 EXTERNAL-IP로 IP Pool에 존재하는 IP를 하나 할당 받은 것을 확인할 수 있습니다. 그리고 100번씩 해당 서비스로 트래픽을 보내면 뒷 단의 Pod들로 트래픽을 잘 분배하고 있는 것을 확인할 수 있습니다. 

 

 

대상 서비스의 conditions 정보를 보면 IPAM으로부터 IP를 할당 받았으며 Virtual IP라는 것을 확인할 수 있습니다. 따라서 기본적으로는 클러스터 내부에서는 정상적으로 통신이 가능하지만 클러스터 외부에서 접근은 기본적으로 불가능합니다. 

# router : K8S 외부에서 통신 불가! 
LBIP=192.168.10.211
curl --connect-timeout 1 $LBIP
arping -i eth1 $LBIP -c 1
arping -i eth1 $LBIP -c 100000
...

이 부분에서 BGP 광고가 필요합니다. 

  1. IP 풀에서 VIP 할당: CiliumLoadBalancerIPPool에서 가상 IP 할당
  2. BGP 광고: 할당된 VIP를 BGP를 통해 네트워크 라우터에 광고
  3. 라우팅 설정: 라우터가 해당 VIP로의 트래픽을 적절한 노드로 라우팅
  4. 로드밸런싱: 노드에서 실제 Pod로 트래픽 분산

 

 

댓글