본문 바로가기
DevOps/cilium

[Cilium Study] BGP Control Plane

by 서어켜엉 2025. 8. 16.

4주차에 이어서 네트워크 대역이 다른 pod들 간의 통신장애를 해결하는 방법에 대해서 스터디를 진행했다.

그 중에 Cilium BGP Control plane 방식을 사용해서 통신을 가능하게 하는 실습을 진행했다.

 

1. BGP란?

BGP가 뭔지부터 알아야 될 것 같다.

BGP : Border Gateway Protocol, 인터넷과 대규모 네트워크에서 라우팅 정보를 교환하는 표준 프로토콜

정의

  • 역할: 네트워크 간(IP Prefix 경로)의 도달 가능성 정보를 서로 교환하여, 어떤 경로로 패킷을 보내야 할지 결정.
  • 위치: OSI 4계층(TCP) 위에서 동작 → 일반적으로 TCP 포트 179 사용.
  • 성격: “경로 벡터 라우팅 프로토콜”
    (단순히 최단 경로가 아니라, 경로의 속성·정책에 따라 경로 선택 가능)

동작원리

  1. BGP 피어링(Peering) 수립
    • 두 장비(혹은 노드)가 TCP 연결을 맺고 서로 BGP 라우터로 인식.
  2. 라우트 광고(Route Advertisement)
    • 자신이 도달 가능한 네트워크(예: 10.0.1.0/24)를 상대에게 알림.
  3. 라우팅 테이블 업데이트
    • 받은 경로와 기존 경로를 비교, 정책에 따라 최적 경로 선택.
  4. 전파
    • 선택한 경로를 다른 BGP 피어에게 다시 광고

특징

 

  • 정책 기반 라우팅
    → 단순히 “가장 짧은 경로”가 아니라, AS Path(거친 네트워크 수), Local Preference, MED 값 등 다양한 속성으로 경로를 선택.
  • 확장성
    → 전 세계 인터넷의 경로 정보(90만+ 개의 IPv4 Prefix)를 처리 가능.
  • 안정성
    → 경로 변경 시에도 점진적으로 업데이트, 네트워크 전체 불안정 최소화.

 

K8s / CNI에서 BGP 활용

 

  • 각 노드가 “BGP 라우터”처럼 동작 → 자신이 가진 Pod CIDR을 BGP로 광고.
  • 네트워크 장비나 다른 노드들이 이 정보를 받아, Pod IP로 직접 라우팅 가능.
  • 예: Calico, Cilium BGP Control Plane, MetalLB(BGP 모드)

 

2. 실습

실습환경 구성

 

네트워크 정보 확인

autoDirectNode?Routes=false

커널 라우트 테이블에 각 노드별 PodCIDR 라우팅이 없는 것을 확인

# router 네트워크 인터페이스 정보 확인
sshpass -p 'vagrant' ssh vagrant@router ip -br -c -4 addr

# k8s node 네트워크 인터페이스 정보 확인
ip -c -4 addr show dev eth1
for i in w1 w0 ; do echo ">> node : k8s-$i <<"; sshpass -p 'vagrant' ssh vagrant@k8s-$i ip -c -4 addr show dev eth1; echo; done


# 라우팅 정보 확인
sshpass -p 'vagrant' ssh vagrant@router ip -c route
ip -c route | grep static

## 노드별 PodCIDR 라우팅이 없다!
ip -c route
for i in w1 w0 ; do echo ">> node : k8s-$i <<"; sshpass -p 'vagrant' ssh vagrant@k8s-$i ip -c route; echo; done


# 통신 확인
ping -c 1 192.168.20.100  # k8s-w0 eth1


샘플 어플리케이션 배포

webpod 3개를 배포

cat << EOF | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
  name: webpod
spec:
  replicas: 3
  selector:
    matchLabels:
      app: webpod
  template:
    metadata:
      labels:
        app: webpod
    spec:
      affinity:
        podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchExpressions:
              - key: app
                operator: In
                values:
                - sample-app
            topologyKey: "kubernetes.io/hostname"
      containers:
      - name: webpod
        image: traefik/whoami
        ports:
        - containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: webpod
  labels:
    app: webpod
spec:
  selector:
    app: webpod
  ports:
  - protocol: TCP
    port: 80
    targetPort: 80
  type: ClusterIP
EOF

 

 

k8s-ctr 노드에 curl-pod 파드 배포

cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
  name: curl-pod
  labels:
    app: curl
spec:
  nodeName: k8s-ctr
  containers:
  - name: curl
    image: nicolaka/netshoot
    command: ["tail"]
    args: ["-f", "/dev/null"]
  terminationGracePeriodSeconds: 0
EOF

통신문제 확인

위에서 배포한 샘플 파드가 제대로 배포됐는지 확인 후에, 파드들 간에 통신을 확인한다.

노드 내의 파드(같은 네트워크 대역)끼리만 통신이 되는 것을 확인 한다.

# 배포 확인
kubectl get deploy,svc,ep webpod -owide
kubectl get endpointslices -l app=webpod
kubectl get ciliumendpoints # IP 확인

# 통신 문제 확인 : 노드 내의 파드들 끼리만 통신되는 중!
kubectl exec -it curl-pod -- curl -s --connect-timeout 1 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'

 

BGP 설정 후 통신 확인

터미널 3개로 하면 좋음. -> router , k8s-ctr 접속 후 2개는 모니터링. k8s-ctr 하나는 명령어 입력

  • router 노드 접속 : sshpass -p 'vagrant' ssh vagrant@router
# router node 에서
# Cilium node 연동 설정 방안 1
cat << EOF >> /etc/frr/frr.conf
  neighbor CILIUM peer-group
  neighbor CILIUM remote-as external
  neighbor 192.168.10.100 peer-group CILIUM
  neighbor 192.168.10.101 peer-group CILIUM
  neighbor 192.168.20.100 peer-group CILIUM 
EOF

cat /etc/frr/frr.conf

systemctl daemon-reexec && systemctl restart frr
systemctl status frr --no-pager --full

# 모니터링 걸어두기!
journalctl -u frr -f

  • cilium에 bgp 설정
# BGP 동작할 노드를 위한 label 설정
kubectl label nodes k8s-ctr k8s-w0 k8s-w1 enable-bgp=true
kubectl get node -l enable-bgp=true


# Config Cilium BGP
cat << EOF | kubectl apply -f -
apiVersion: cilium.io/v2
kind: CiliumBGPAdvertisement
metadata:
  name: bgp-advertisements
  labels:
    advertise: bgp
spec:
  advertisements:
    - advertisementType: "PodCIDR"
---
apiVersion: cilium.io/v2
kind: CiliumBGPPeerConfig
metadata:
  name: cilium-peer
spec:
  timers:
    holdTimeSeconds: 9
    keepAliveTimeSeconds: 3
  ebgpMultihop: 2
  gracefulRestart:
    enabled: true
    restartTimeSeconds: 15
  families:
    - afi: ipv4
      safi: unicast
      advertisements:
        matchLabels:
          advertise: "bgp"
---
apiVersion: cilium.io/v2
kind: CiliumBGPClusterConfig
metadata:
  name: cilium-bgp
spec:
  nodeSelector:
    matchLabels:
      "enable-bgp": "true"
  bgpInstances:
  - name: "instance-65001"
    localASN: 65001
    peers:
    - name: "tor-switch"
      peerASN: 65000
      peerAddress: 192.168.10.200  # router ip address
      peerConfigRef:
        name: "cilium-peer"
EOF

 

  • 통신확인
# BGP 연결 확인
ss -tnlp | grep 179
ss -tnp | grep 179

# cilium bgp 정보 확인
cilium bgp peers
cilium bgp routes available ipv4 unicast

kubectl get ciliumbgpadvertisements,ciliumbgppeerconfigs,ciliumbgpclusterconfigs
kubectl get ciliumbgpnodeconfigs -o yaml | yq

여전히 같은 노드의 pod 끼리만 통신이 되는 중

 

  • router node frr 재시작
systemctl restart frr && journalctl -u frr -f

여전히 같은 노드의 pod끼리만 통신이 되는 중

 

Cilium BGP는 기본적으로 외부 경로를 커널 라우팅 테이블에 주입하지 않음.
  1. 커널 라우팅 테이블 (FIB, Forwarding Information Base)
    • 리눅스 커널이 직접 참조하는 "실제 패킷 포워딩용" 라우팅 테이블
    • 목적지 IP → 인터페이스/게이트웨이 결정
    • ip route로 확인 가능
  2. BGP 데몬 라우팅 테이블 (RIB, Routing Information Base)
    • BGP 프로토콜을 통해 학습·교환된 경로를 저장하는 "논리적 테이블"
    • 실제 패킷 포워딩에 쓰이지 않고, BGP 피어들과 라우팅 정보를 교환하기 위한 데이터베이스
    • FRR, GoBGP, Bird 같은 데몬이 관리
    • Cilium BGP도 기본적으로는 이 레벨에서 경로를 관리

왜 BGP 설정을 다 했는데도 같은 노드에 있는 pod만 통신이 되는가?

Cilium 으로 BGP 사용 시, 2개 이상의 NIC 사용할 경우에는 Node에 직접 라우팅 설정 및 관리가 필요함.

  • 현재 실습 환경은 2개의 NIC(eth0, eth1)을 사용하고 있는 상황으로, default GW가 eth0 경로로 설정 되어 있음.
  • eth1은 k8s 통신 용도로 사용 중. 즉, 현재 k8s 파드 사용 대역 통신 전체는 eth1을 통해서 라우팅 설정하면됨.
  • 해당 라우팅을 상단에 네트워크 장비가 받게 되고, 해당 장비는 Cilium Node를 통해 모든 PodCIDR 정보를 알고 있기에, 목적지로 전달 가능함.
  • 결론은 Cilium 으로 BGP 사용 시, 2개 이상의 NIC 사용할 경우에는 Node에 직접 라우팅 설정 및 관리가 필요함.
# k8s-ctr node에서
ip route add 172.20.0.0/16 via 192.168.10.200
sshpass -p 'vagrant' ssh vagrant@k8s-w1 sudo ip route add 172.20.0.0/16 via 192.168.10.200
sshpass -p 'vagrant' ssh vagrant@k8s-w0 sudo ip route add 172.20.0.0/16 via 192.168.20.200

3개의 pod가 모두 통신이 되는 것을 확인