[cilium] containerlab으로 lb ippool 테스트 해보기

cilium에서 제공하는 bgp 테스트 환경 구성이 있는데 이게 잘 동작을 안해서 엄청 고생을 하다. 도전과제겸 정리해봅니다.

공식 문서에서는 아래와 같이 간단하게 구성하고있는데 mac이라 그런지 바로 동작을 잘 안하네요.

2025.08.15 - [K8S/cilium] - containerlab을 사용하여 cilium + BGP 테스트 환경 만들기

 

사전 준비

테스트 환경은 mac arm64 이며 orbstack을 사용할 예정입니다.

 

vm 생성하기

mac에서는 바로 containerlab을 사용할 수 없어 orbstack으로 vm을 생성해줍니다. orbstack을 실행 후 명령어를 입력하세요

orb create ubuntu linux

 

이렇게 vm을 생성하면 쉽게 서버에 접근이 가능합니다.

ssh orb

 

kind로 클러스터 만들기

클러스터의 AS-number는 6500이고 노드에 라벨 키가 bgp인 경우 bgp 인스턴스를 활성화 할 에정이기 때문에 다음과 같이 클러스터를 생성합니다. 

 

apiVersion: kind.x-k8s.io/v1alpha4
kind: Cluster
name: bgp-dev
networking:
  ipFamily: dual
  disableDefaultCNI: true
  podSubnet: "10.1.0.0/16"
  serviceSubnet: "10.2.0.0/16"
nodes:
- role: control-plane
  kubeadmConfigPatches:
  - |
    kind: InitConfiguration
    nodeRegistration:
      kubeletExtraArgs:
        node-ip: "10.0.1.2"
        node-labels: "bgp=65001"
- role: worker
  kubeadmConfigPatches:
  - |
    kind: JoinConfiguration
    nodeRegistration:
      kubeletExtraArgs:
        node-ip: "10.0.2.2"
        node-labels: "bgp=65001"

 

이제 클러스터를 생성해줍니다.

kind create cluster --config cluster.yaml

 

containerlab으로 라우터 설정하기

이제 위의 컨테이너와 연결할 BGP 라우터를 구성합니다. 

name: bgp-dev
topology:
  nodes:
    # A simple BGP router that peers with Cilium with eBGP.
    router0:
      kind: linux
      image: frrouting/frr:v8.4.0
      cmd: bash
      exec:
      - ip addr add 10.0.1.1/24 dev net0
      - ip addr add 10.0.2.1/24 dev net1
      - ip addr add 10.0.3.1/24 dev net2
      - sysctl net.ipv4.ip_forward=1
      - ip link add name loopback type dummy
      - ip link set dev loopback up
      - ip addr add 10.0.0.1/32 dev loopback
      - ip route add blackhole 10.0.0.0/8
      # Boiler plate to make FRR work
      - touch /etc/frr/vtysh.conf
      - touch /var/log/frr.log
      - chown frr:frr /var/log/frr.log
      - sed -i -e 's/bgpd=no/bgpd=yes/g' /etc/frr/daemons
      - /usr/lib/frr/frrinit.sh start
      - >-
        vtysh -c 'conf t'
        -c 'log file /var/log/frr.log'
        -c 'debug bgp neighbor-events'
        -c 'debug bgp updates'
        -c 'debug bgp zebra'
        -c 'router bgp 65000'
        -c '  bgp bestpath as-path multipath-relax'
        -c '  no bgp ebgp-requires-policy'
        -c '  bgp router-id 10.0.0.1'
        -c '  neighbor CILIUM peer-group'
        -c '  neighbor CILIUM remote-as external'
        -c '  neighbor CILIUM password cilium123'
        -c '  neighbor 10.0.1.2 peer-group CILIUM'
        -c '  neighbor 10.0.2.2 peer-group CILIUM'
        -c '  neighbor CILIUM ebgp-multihop 2'
        -c '  neighbor CILIUM update-source 10.0.0.1'
        -c ' address-family ipv4 unicast'
        -c '  neighbor CILIUM activate'
        -c '  maximum-paths 32'
        -c ' exit'
        -c '!'
    # Server with Cilium. It shares netns with kind node.
    server0:
      kind: linux
      image: nicolaka/netshoot:v0.11
      network-mode: container:bgp-dev-control-plane
      exec:
      - ip addr add 10.0.1.2/24 dev net0
      # These static routes are needed because Cilium cannot import routes currently.
      - ip route add 10.0.0.0/8 via 10.0.1.1 dev net0
    # Server with Cilium. It shares netns with kind node.
    server1:
      kind: linux
      image: nicolaka/netshoot:v0.11
      network-mode: container:bgp-dev-worker
      exec:
      - ip addr add 10.0.2.2/24 dev net0
      # These static routes are needed because Cilium cannot import routes currently.
      - ip route add 10.0.0.0/8 via 10.0.2.1 dev net0
    # Server without Cilium. Useful for testing connectivity.
    server2:
      kind: linux
      image: nicolaka/netshoot:v0.11
      exec:
      - ip addr add 10.0.3.2/24 dev net0
      # These static routes are needed because this node doesn't have a BGP router.
      - ip route add 10.0.0.0/8 via 10.0.3.1 dev net0
  links:
  - endpoints: ["router0:net0", "server0:net0"]
  - endpoints: ["router0:net1", "server1:net0"]
  - endpoints: ["router0:net2", "server2:net0"]

 

위의 설정 파일을 저장한뒤 conatienrlab으로 컨테이너를 띄워줍니다. 

sudo containerlab -t topo.yaml deploy

 

cilium 설치하기

debug:
  enabled: true

routingMode: native

ipv6:
  enabled: false

bgpControlPlane:
  enabled: true
  statusReport:
    enabled: false

securityContext:
  capabilities:
    ciliumAgent:
      # cilium-agent에 179 포트를 바인딩 하기 위해 CAP_NET_BIND_SERVICE을 추가로 오픈합니다.
      - CAP_NET_BIND_SERVICE
      - CHOWN
      - KILL
      - NET_ADMIN
      - NET_RAW
      - IPC_LOCK
      - SYS_MODULE
      - SYS_ADMIN
      - SYS_RESOURCE
      - DAC_OVERRIDE
      - FOWNER
      - SETGID
      - SETUID

ipv4NativeRoutingCIDR: 10.0.0.0/8

ipam:
  mode: kubernetes

k8s:
  requireIPv4PodCIDR: true
  requireIPv6PodCIDR: false

operator:
  enabled: true
  replicas: 1

 

위의 설정으로 cilium도 구성을 완료합니다. 

helm install cilium -n kube-system cilium/cilium --version 1.18.1 -f values.yaml

 

cilium이 상태가 정상이 될때까지 기다려줍니다.

cilium status --wait --namespace kube-system

 

BGP 설정하기

router 컨테이너의 루프백 인터페이스를 peer address로 설정하여 연결되도록 합니다. 

사실 router 컨테이너로 먼저 연결이 될수있는 환경인데도 불구하고 연결이 잘 되지 않아 localPort를 오픈했습니다. localPort 오픈없이도 구성가능한 방법을 아시는 분은.. 알려주세요.. 

나중에확인을 해보니 TCP_MD5SIG를 orbstack에서 제공하는 vm의 커널에서 지원하지 않기 때문에 동작을 하지 않았던 것이었습니다. 비밀번호 설정을 라우터에서 제외하고 peer 설정에서도 제거 하면 동작합니다 ~

cat << EOF | kubectl apply -f -
apiVersion: cilium.io/v2
kind: CiliumBGPClusterConfig
metadata:
  name: cilium-bgp
spec:
  nodeSelector:
    matchLabels:
      bgp: "65001"
  bgpInstances:
  - name: "65001"
    localASN: 65001
    localPort: 179
    peers:
    - name: "65000"
      peerASN: 65000
      peerAddress: 10.0.0.1
      peerConfigRef:
        name: "cilium-peer"
EOF

 

그리고 peer 설정을 해줍니다. 라우터에 md5 비밀번호 설정이 되어있기 때문에 시크릿을 하나 추가합니다.

kubectl -n kube-system create secret generic --type=string bgp-auth-secret --from-literal=password=cilium123

 

그리고 다음과 같이 설정을 추가해줍니다. ebgpMultihop의 경우 TTL을 설정하는 기능인데

router의 loopback까지 가기 위해서는

Cilium 노드(10.0.1.2/10.0.2.2) → router0(10.0.1.1/10.0.2.1) → 루프백(10.0.0.1) 순서로 최소 1홉을 지나야하기 때문에

이 TTL 설정을 2로 변경했습니다. 

cat << EOF | kubectl apply -f -
apiVersion: cilium.io/v2
kind: CiliumBGPPeerConfig
metadata:
  name: cilium-peer
spec:
  # 라우터에 password 미설정시 아래 설정 제거
  authSecretRef: bgp-auth-secret
  ebgpMultihop: 2
  gracefulRestart:
    enabled: true
    restartTimeSeconds: 15
  families:
    - afi: ipv4
      safi: unicast
      advertisements:
        matchLabels:
          advertise: "bgp"
EOF

 

이제 마지막으로 광고 관련 설정을 해주면 됩니다.

cat << EOF | kubectl apply -f -
apiVersion: cilium.io/v2
kind: CiliumBGPAdvertisement
metadata:
  name: blue-bgp-advertisements
  labels:
    advertise: bgp
spec:
  advertisements:
    - advertisementType: "Service"
      service:
        addresses:
          - ClusterIP
          - LoadBalancerIP
      selector:
        matchExpressions:
          - { key: bgp, operator: In, values: [ blue ] }
EOF

 

Peer 연결은 established되었지만 아직 라우터에 광고가 되고 있는 것은 없습니다.

서비스 배포하기

lb ippol을 추가하여 lb가 설정될 수 있도록 합니다.

cat << EOF | kubectl apply -f -
apiVersion: "cilium.io/v2"
kind: CiliumLoadBalancerIPPool
metadata:
  name: "ip-pool-blue"
  labels:
    bgp: blue
spec:
  allowFirstLastIPs: "No"
  blocks:
    - cidr: "10.100.0.0/24"
  serviceSelector:
    matchExpressions:
      - {key: bgp, operator: In, values: [blue]}
EOF

 

이제 위의 LB IP를 할당받을 서비스를 배포합니다.

cat << EOF | kubectl apply -f -
apiVersion: v1
kind: Service
metadata:
  name: lb-service-blue
  labels:
    bgp: blue
spec:
  type: LoadBalancer
  selector:
    app: curl-blue
  ports:
    - protocol: TCP
      port: 1234
      targetPort: 80
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: curl-blue
  labels:
    app: curl-blue
spec:
  replicas: 1
  selector:
    matchLabels:
      app: curl-blue
  template:
    metadata:
      labels:
        app: curl-blue
    spec:
      containers:
        - name: blue
          image: traefik/whoami
EOF

 

이렇게 하면 이제 구성 완료입니다. 이제 라우터에 표시도 잘 됩니다. 

 

 

실제로 테스트해보기

clab-bgp-dev-server2가 외부 환경처럼 구성된 컨테이너이기 때문에 여기서 테스트를 해보겠습니다.

테스트 시 통신이 잘 되는 것을 확인할 수 있습니다.