BIRD로 BGP 실행하기

BIRD로 BGP 실행하기 (deprecated) (Using BIRD to run BGP)

BIRD는 Unix 계열 운영체제에서 IP 패킷을 라우팅하는 오픈소스 구현이에요. 이 가이드는 전통적인 네트워킹 프로토콜로 경로를 광고해 Cilium이 관리하는 엔드포인트를 클러스터 밖에서 접근 가능하게 만드는 방법을, CentOS 7.x 기준으로 안내합니다.

출처: Using BIRD to run BGP (deprecated)

본문

BIRD는 Unix 계열 운영체제에서 인터넷 프로토콜 패킷을 라우팅하는 오픈소스 구현입니다. 익숙하지 않다면 먼저 User's Guide를 한 번 훑어보는 것이 좋아요.

BIRD는 전통적인 네트워킹 프로토콜로 경로를 광고하는 방법을 제공해 Cilium이 관리하는 엔드포인트가 클러스터 밖에서 접근 가능하게 해 줍니다. 이 가이드는 Cilium이 이미 클러스터에 배포되어 있고, 남은 것은 pod CIDR 범위를 외부에서 라우팅 가능하게 만드는 방법이라고 가정해요.

BIRD는 현재 1.x 와 2.x 두 개의 릴리스 계열을 유지하고 있으며, 구성 형식이 둘 사이에 크게 다릅니다. 이미 1.x 를 배포하지 않았다면 2.x 를 바로 사용하는 것을 권장하는데, 2.x 가 더 오래 살아남을 것이기 때문이에요. 다음 예시는 bird 를 bird2 소프트웨어로 표기하고 bird2 가 이해하는 형식의 구성을 사용합니다.

이 가이드는 CentOS 7.x에서 bird를 설치하고 구성해 Cilium과 협력하도록 만드는 방법을 보여 줘요. 다른 플랫폼에서의 설치와 구성도 매우 유사할 것입니다.

bird 설치

$ yum install -y bird2

$ systemctl enable bird
$ systemctl restart bird

설치를 테스트해 보세요:

$ birdc show route
BIRD 2.0.6 ready.

$ birdc              # interactive shell
BIRD 2.0.6 ready.
bird> show bfd sessions
There is no BFD protocol running
bird>
bird> show protocols all
Name       Proto      Table      State  Since         Info
device1    Device     ---        up     10:53:40.147

direct1    Direct     ---        down   10:53:40.147
  Channel ipv4
    State:          DOWN
    Input filter:   ACCEPT
    Output filter:  REJECT
...

기본 구성

특정 BGP 스킴을 고려하지 않고 bird 구성을 논의하기는 어렵습니다. 하지만 BGP 스킴 설계는 이 가이드의 범위를 벗어나요. 이 주제에 관심이 있다면 BGP in the Data Center (O'Reilly, 2017)를 참고해 빠르게 시작하세요.

아래에서는 BGP 시나리오를 다음과 같이 제한할게요:

  • 물리 네트워크: 간단한 3계층 계층 구조
  • 노드는 레이어 2 스위치를 통해 물리 네트워크에 연결
  • bird 를 통해 각 노드의 PodCIDR을 물리 네트워크에 광고
  • 각 노드에 대해, 물리 네트워크에서의 경로 광고를 가져오지 않음

이 설계에서 BGP 연결은 다음과 같습니다:

  • 코어 라우터는 bird 에서 PodCIDR을 학습해 Pod IP 주소가 전체 네트워크에서 라우팅 가능해집니다.
  • bird 는 코어 라우터와 다른 노드에서 경로를 학습하지 않아, 각 노드의 커널 라우팅 테이블을 깨끗하고 작게 유지하며 성능 문제가 없습니다.

이 스킴에서 각 노드는 pod egress 트래픽을 노드의 기본 게이트웨이(코어 라우터)로 보내고, 라우팅은 후자가 담당하게 해요.

위 목적을 달성하기 위한 참조 구성은 다음과 같습니다:

$ cat /etc/bird.conf
log syslog all;

router id {{ NODE_IP }};

protocol device {
        scan time 10;           # Scan interfaces every 10 seconds
}

# Disable automatically generating direct routes to all network interfaces.
protocol direct {
        disabled;               # Disable by default
}

# Forbid synchronizing BIRD routing tables with the OS kernel.
protocol kernel {
        ipv4 {                    # Connect protocol to IPv4 table by channel
                import none;      # Import to table, default is import all
                export none;      # Export to protocol. default is export none
        };
}

# Static IPv4 routes.
protocol static {
      ipv4;
      route {{ POD_CIDR }} via "cilium_host";
}

# BGP peers
protocol bgp uplink0 {
      description "BGP uplink 0";
      local {{ NODE_IP }} as {{ NODE_ASN }};
      neighbor {{ NEIGHBOR_0_IP }} as {{ NEIGHBOR_0_ASN }};
      password {{ NEIGHBOR_PWD }};

      ipv4 {
              import filter {reject;};
              export filter {accept;};
      };
}

protocol bgp uplink1 {
      description "BGP uplink 1";
      local {{ NODE_IP }} as {{ NODE_ASN }};
      neighbor {{ NEIGHBOR_1_IP }} as {{ NEIGHBOR_1_ASN }};
      password {{ NEIGHBOR_PWD }};

      ipv4 {
              import filter {reject;};
              export filter {accept;};
      };
}

위 파일을 /etc/bird.conf 로 저장하고, 플레이스홀더를 자신의 값으로 바꾸세요:

sed -i 's/{{ NODE_IP }}/<your node ip>/g'                /etc/bird.conf
sed -i 's/{{ POD_CIDR }}/<your pod cidr>/g'              /etc/bird.conf
sed -i 's/{{ NODE_ASN }}/<your node asn>/g'              /etc/bird.conf
sed -i 's/{{ NEIGHBOR_0_IP }}/<your neighbor 0 ip>/g'    /etc/bird.conf
sed -i 's/{{ NEIGHBOR_1_IP }}/<your neighbor 1 ip>/g'    /etc/bird.conf
sed -i 's/{{ NEIGHBOR_0_ASN }}/<your neighbor 0 asn>/g'  /etc/bird.conf
sed -i 's/{{ NEIGHBOR_1_ASN }}/<your neighbor 1 asn>/g'  /etc/bird.conf
sed -i 's/{{ NEIGHBOR_PWD }}/<your neighbor password>/g' /etc/bird.conf

bird 를 재시작하고 로그를 확인하세요:

$ systemctl restart bird

# check logs
$ journalctl -u bird
-- Logs begin at Sat 2020-02-22 16:11:44 CST, end at Mon 2020-02-24 18:58:35 CST. --
Feb 24 18:58:24 node systemd[1]: Started BIRD Internet Routing Daemon.
Feb 24 18:58:24 node systemd[1]: Starting BIRD Internet Routing Daemon...
Feb 24 18:58:24 node bird[137410]: Started

변경 사항을 검증하면 다음과 같은 결과를 볼 수 있어요:

$ birdc show route
BIRD 2.0.6 ready.
Table master4:
10.5.48.0/24         unicast [static1 20:14:51.478] * (200)
        dev cilium_host

이것은 이 노드의 PodCIDR 10.5.48.0/24 가 BIRD로 성공적으로 가져와졌음을 나타냅니다.

$ birdc show protocols all uplink0 | grep -A 3 -e "Description" -e "stats"
  Description:    BGP uplink 0
  BGP state:          Established
    Neighbor address: 10.4.1.7
    Neighbor AS:      65418
--
    Route change stats:     received   rejected   filtered    ignored   accepted
      Import updates:              0          0          0          0          0
      Import withdraws:           10          0        ---         10          0
      Export updates:              1          0          0        ---          1

여기서 uplink0 BGP 세션이 성립되었고 위의 PodCIDR이 내보내져 BGP 피어가 받아들였음을 볼 수 있어요.

모니터링

bird_exporter는 bird 데몬 상태를 수집해 Prometheus 스타일 메트릭으로 내보낼 수 있어요.

또한 간단한 Grafana 대시보드를 제공하지만, 직접 만들 수도 있습니다. 예를 들어 Trip.com의 것은 이런 모습이에요.

고급 구성

BGP 스킴을 프로덕션에 적합하게 만들기 위해 몇 가지 고급 구성이 필요할 수 있어요. 이 섹션은 이러한 매개변수 중 일부를 나열하지만 세부 사항까지 파고들지는 않을게요. 그것은 BIRD User's Guide의 몫입니다.

BFD

Bidirectional Forwarding Detection (BFD)는 경로 실패 감지를 가속화하도록 설계된 감지 프로토콜입니다.

이 기능은 피어 측 구성에도 의존합니다.

protocol bfd {
      interface "{{ grains['node_mgnt_device'] }}" {
              min rx interval 100 ms;
              min tx interval 100 ms;
              idle tx interval 300 ms;
              multiplier 10;
              password {{ NEIGHBOR_PWD }};
      };

      neighbor {{ NEIGHBOR_0_IP] }};
      neighbor {{ NEIGHBOR_1_IP] }};
}

protocol bgp uplink0 {
            ...

        bfd on;
}

검증하면 다음과 같은 결과를 볼 수 있어요:

$ birdc show bfd sessions
BIRD 2.0.6 ready.
bfd1:
IP address                Interface  State      Since         Interval  Timeout
10.5.40.2                 bond0      Up         20:14:51.479    0.300    0.000
10.5.40.3                 bond0      Up         20:14:51.479    0.300    0.000

ECMP

특별한 목적(예: L4LB)을 위해 여러 노드에 같은 CIDR을 구성할 수 있어요. 이 경우 Equal-Cost Multi-Path (ECMP) 라우팅을 구성해야 합니다.

이 기능도 피어 측 구성에 의존합니다.

protocol kernel {
        ipv4 {                    # Connect protocol to IPv4 table by channel
                import none;      # Import to table, default is import all
                export none;      # Export to protocol. default is export none
        };

        # Configure ECMP
        merge paths yes limit {{ N }} ;
}

더 자세한 정보는 사용자 매뉴얼을 참고하세요.

물리 네트워크(위 시나리오의 코어 라우터)에서 ECMP 정확성을 확인해야 합니다:

CORE01# show ip route 10.5.2.0
IP Route Table for VRF "default"
'*' denotes best ucast next-hop
'**' denotes best mcast next-hop
'[x/y]' denotes [preference/metric]
'%<string>' in via output denotes VRF <string>

10.5.2.0/24, ubest/mbest: 2/0
    *via 10.4.1.7, [200/0], 13w6d, bgp-65418, internal, tag 65418
    *via 10.4.1.8, [200/0], 12w4d, bgp-65418, internal, tag 65418

Graceful restart

이 기능도 피어 측 구성에 의존합니다.

각 bgp 섹션에 graceful restart 를 추가하세요:

protocol bgp uplink0 {
            ...

        graceful restart;
}

더 알아보기 (Learn more)