Metallb (BGPモード) と OpenWrt FRR を組み合わせて使用する

はじめに

Metallbの概要、デプロイ方法については上記で説明しています。

ここでは、BGPモードを使用する際の使い方を説明しています。

BGPモードを使用する際には、上流にBGPプロトコルに対応したルーターが必要になりますが、これについては OpenWrt FRR を使用します。 インストール方法などは下記で説明しています。

Metallb BGPモード

BGPモードの設定適用

  • あまりしっくり来ていないのですが、ChatGPTさんによると下記とのことで、myASN と peerASN は同じに設定しています
    • 「peerASN はピアリング相手 (OpenWrt) の ASN であり、myASN は MetalLB スピーカーの ASN です。通常、同じネットワーク内で BGP ピアリングを設定する場合、同じ ASN を使用します。」

metallb-bgp-config.yaml

apiVersion: metallb.io/v1beta2
kind: BGPPeer
metadata:
  name: bgp-peer
  namespace: metallb-system
spec:
  myASN: 64512
  peerASN: 64512
  peerAddress: 192.168.10.1
---
apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
  name: first-pool
  namespace: metallb-system
spec:
  addresses:
  - 192.168.10.61-192.168.10.70
---
apiVersion: metallb.io/v1beta1
kind: BGPAdvertisement
metadata:
  name: default
  namespace: metallb-system
spec:
  ipAddressPools:
  - first-pool

kubectl apply -f metallb-bgp-config.yaml

BGPモードの設定削除

kubectl delete -f metallb-bgp-config.yaml

OpenWrt FRR側の設定

frr.conf(設定前)

root@OpenWrt:~# cat /etc/frr/frr.conf
password zebra
!
!router eigrp 1
! network 10.0.0.0/8
! network 192.168.1.0/24
!
!router ospf
!ospf router-id 172.16.0.2
!network 192.168.1.0/24 area 0
!neighbor 172.16.0.1
!
!router rip
! network 10.0.0.0/8
! network 192.168.1.0/24
!
log syslog
!
access-list vty permit 127.0.0.0/8
access-list vty deny any
!
line vty
 access-class vty

frr.conf(設定後)

K8S側で metallb-speaker のIPを確認します。

$ kubectl get pod -n metallb-system -o wide
NAME                                  READY   STATUS    RESTARTS         AGE    IP              NODE          NOMINATED NODE   READINESS GATES
metallb-controller-665d96757f-lshr8   1/1     Running   3 (3h55m ago)    2d3h   10.0.5.182      k8s-worker3   <none>           <none>
metallb-speaker-8dx2c                 4/4     Running   13 (3h55m ago)   2d3h   192.168.10.23   k8s-worker3   <none>           <none>
metallb-speaker-9g89t                 4/4     Running   9 (3h42m ago)    2d3h   192.168.10.22   k8s-worker2   <none>           <none>
metallb-speaker-hm6jw                 4/4     Running   12 (4h1m ago)    2d3h   192.168.10.13   k8s-ctrl3     <none>           <none>
metallb-speaker-v6fnn                 4/4     Running   8 (4h1m ago)     2d3h   192.168.10.11   k8s-ctrl1     <none>           <none>
metallb-speaker-xv626                 4/4     Running   14 (140m ago)    2d3h   192.168.10.12   k8s-ctrl2     <none>           <none>
metallb-speaker-zclms                 4/4     Running   9 (3h59m ago)    2d3h   192.168.10.21   k8s-worker1   <none>           <none>
  • passwordは無効にします
  • bgpの番号は Metallb側に合わせ、64512 にします
  • speakerのIPアドレスを全てneighborとして設定します

root@OpenWrt:~# cat /etc/frr/frr.conf
!password zebra
!
!router eigrp 1
! network 10.0.0.0/8
! network 192.168.1.0/24
!
!router ospf
!ospf router-id 172.16.0.2
!network 192.168.1.0/24 area 0
!neighbor 172.16.0.1
!
!router rip
! network 10.0.0.0/8
! network 192.168.1.0/24
!
log syslog
!
access-list vty permit 127.0.0.0/8
access-list vty deny any
!
line vty
 access-class vty

router bgp 64512
 bgp router-id 192.168.10.1
 neighbor 192.168.10.23 remote-as 64512
 neighbor 192.168.10.22 remote-as 64512
 neighbor 192.168.10.13 remote-as 64512
 neighbor 192.168.10.11 remote-as 64512
 neighbor 192.168.10.12 remote-as 64512
 neighbor 192.168.10.21 remote-as 64512

 address-family ipv4 unicast
  network 192.168.10.0/24
 exit-address-family

frrの再起動

root@OpenWrt:~# service frr restart
Stopped staticd
Stopped bgpd
Stopped zebra
Stopped watchfrr
Started watchfrr

テスト用アプリケーションのデプロイ

テスト用nginxのデプロイ

test.yaml

apiVersion: v1
kind: Service
metadata:
  name: nginx-service
  namespace: test
  labels:
    run: nginx
spec:
  type: LoadBalancer
  ports:
  - port: 80
    protocol: TCP
  selector:
    run: nginx
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  namespace: test
spec:
  selector:
    matchLabels:
      run: nginx
  replicas: 2
  template:
    metadata:
      labels:
        run: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:latest
        ports:
        - containerPort: 80

kubectl create namespace test
kubectl apply -f test.yaml

テスト用nginxのデプロイ結果

EXTERNAL-IP (192.168.10.61) が払い出されています。

$ k get svc -n test
NAME            TYPE           CLUSTER-IP   EXTERNAL-IP     PORT(S)        AGE
nginx-service   LoadBalancer   10.97.83.2   192.168.10.61   80:32201/TCP   16h

haproxyを使ってホームLANに公開

haproxyを使用して OpenWrtルーター経由で http://192.168.1.100:8080/ でアクセスできることを確認しました。

haproxy.cfg

root@OpenWrt:~# cat /etc/haproxy.cfg 

(略)
frontend http_front
    bind 192.168.1.100:8080
    default_backend http_back

backend http_back
    balance roundrobin
    server k8s-service 192.168.10.61:80 check
(略)

動作確認(ログ)

frr ログの取得

root@OpenWrt:~# logread -e frr
Sat Jul  6 11:34:13 2024 daemon.notice watchfrr[2022]: [T83RR-8SM5G] watchfrr 8.5.1 starting: vty@0
Sat Jul  6 11:34:13 2024 daemon.info watchfrr[2022]: [ZCJ3S-SPH5S] zebra state -> down : initial connection attempt failed
Sat Jul  6 11:34:13 2024 daemon.info watchfrr[2022]: [ZCJ3S-SPH5S] bgpd state -> down : initial connection attempt failed
Sat Jul  6 11:34:13 2024 daemon.info watchfrr[2022]: [ZCJ3S-SPH5S] staticd state -> down : initial connection attempt failed
Sat Jul  6 11:34:13 2024 daemon.info watchfrr[2022]: [YFT0P-5Q5YX] Forked background command [pid 2033]: /usr/sbin/watchfrr.sh restart all
Sat Jul  6 11:34:13 2024 daemon.err watchfrr.sh: Cannot stop bgpd: pid file not found
Sat Jul  6 11:34:13 2024 daemon.err watchfrr.sh: Cannot stop staticd: pid file not found
Sat Jul  6 11:34:13 2024 daemon.err watchfrr.sh: Cannot stop zebra: pid file not found
Sat Jul  6 11:34:18 2024 daemon.notice watchfrr[2022]: [QDG3Y-BY5TN] bgpd state -> up : connect succeeded
Sat Jul  6 11:34:18 2024 daemon.notice watchfrr[2022]: [QDG3Y-BY5TN] staticd state -> up : connect succeeded
Sat Jul  6 11:34:18 2024 daemon.notice watchfrr[2022]: [QDG3Y-BY5TN] zebra state -> up : connect succeeded
Sat Jul  6 11:34:18 2024 daemon.notice watchfrr[2022]: [KWE5Q-QNGFC] all daemons up, doing startup-complete notify
Sat Jul  6 11:34:18 2024 daemon.notice procd: /etc/rc.d/S95frr: Started watchfrr
Sat Jul  6 11:34:18 2024 daemon.warn watchfrr[2022]: [RKHTV-CNGEG] Daemon: zebra: is in Up state but expected it to be in DAEMON_DOWN state
Sat Jul  6 11:34:18 2024 daemon.warn watchfrr[2022]: [RKHTV-CNGEG] Daemon: bgpd: is in Up state but expected it to be in DAEMON_DOWN state
Sat Jul  6 11:34:18 2024 daemon.warn watchfrr[2022]: [RKHTV-CNGEG] Daemon: staticd: is in Up state but expected it to be in DAEMON_DOWN state

動作確認(vtysh)

vtysh

vtysh というものが使えるようで、-c 引数でコマンドを与えることで直接結果を得られます。

show ip bgp summary

(ChatGPT先生より)下記のようにBGPピアリングが正常に機能していることを確認します。

  • State/PfxRcd の値が 1 になっているピアが存在すること:
    • 192.168.10.21, 192.168.10.22, 192.168.10.23 の State/PfxRcd が 1 になっているため、これらのピアが正しくルートを受信していることがわかります。
  • 全てのピアが Up/Down:
    • 全てのピアが正しく接続されており、Up/Down カラムには接続時間が表示されています。
  • RIB エントリーの増加:
    • RIB entries が 2 になっており、これもルートが正しく交換されていることを示しています。

root@OpenWrt:~# vtysh -c "show ip bgp summary"

IPv4 Unicast Summary (VRF default):
BGP router identifier 192.168.10.1, local AS number 64512 vrf-id 0
BGP table version 10
RIB entries 2, using 272 bytes of memory
Peers 6, using 4278 KiB of memory

Neighbor        V         AS   MsgRcvd   MsgSent   TblVer  InQ OutQ  Up/Down State/PfxRcd   PfxSnt Desc
192.168.10.11   4      64512       725       728        0    0    0 03:19:14            0        1 N/A
192.168.10.12   4      64512       725       732        0    0    0 01:39:29            0        1 N/A
192.168.10.13   4      64512       726       732        0    0    0 03:19:16            0        1 N/A
192.168.10.21   4      64512       730       727        0    0    0 03:19:08            1        1 N/A
192.168.10.22   4      64512       695       692        0    0    0 03:01:31            1        1 N/A
192.168.10.23   4      64512       735       730        0    0    0 03:15:13            1        1 N/A

Total number of neighbors 6

show ip route

ChatGPT先生より。このように見るようです。

  • ルーターのルーティングテーブルを確認した結果、192.168.10.61/32 のエントリが BGP ルートで表示されています。これは正しい設定であり、BGP プロトコルを使用して広報された結果です。
  • 以下の点に注意してください:
    • BGP ルートは B として表示されており、複数のパス(複数の次ホップ)が示されています。これは通常、BGP によって複数のパスが提供されたことを意味します。
    • 192.168.10.61/32 に対する各次ホップには、via 192.168.10.21, via 192.168.10.22, via 192.168.10.23 という情報が含まれています。これは、複数の MetalLB のスピーカー(speaker)が提供する経路を示しています。
    • このように、192.168.10.61 のルーティングエントリが正常に表示されていることを確認しました。これにより、MetalLB が BGP モードで正しく機能し、広報されたアドレスが正常に利用可能であることが示されています。

root@OpenWrt:~# vtysh -c "show ip route"
Codes: K - kernel route, C - connected, S - static, R - RIP,
       O - OSPF, I - IS-IS, B - BGP, E - EIGRP, N - NHRP,
       T - Table, v - VNC, V - VNC-Direct, A - Babel, F - PBR,
       f - OpenFabric,
       > - selected route, * - FIB route, q - queued, r - rejected, b - backup
       t - trapped, o - offload failure

K>* 0.0.0.0/0 [0/0] via 192.168.1.1, wan linkdown, 15:08:41
C>* 192.168.1.0/24 is directly connected, wan, 15:08:39
C>* 192.168.10.0/24 is directly connected, br-lan, 03:36:58
B>* 192.168.10.61/32 [200/0] via 192.168.10.21, br-lan, weight 1, 03:17:08
  *                          via 192.168.10.22, br-lan, weight 1, 03:17:08
  *                          via 192.168.10.23, br-lan, weight 1, 03:17:08

show ip bgp neighbors

下記のように neighbors の情報が得られます。

root@OpenWrt:~# vtysh -c "show ip bgp neighbors 192.168.10.21"
BGP neighbor is 192.168.10.21, remote AS 64512, local AS 64512, internal link
  Local Role: undefined
  Remote Role: undefined
Hostname: k8s-worker1
  BGP version 4, remote router ID 192.168.10.21, local router ID 192.168.10.1
  BGP state = Established, up for 03:42:29
  Last read 00:00:29, Last write 00:00:29
  Hold time is 90 seconds, keepalive interval is 30 seconds
  Configured hold time is 180 seconds, keepalive interval is 60 seconds
  Configured conditional advertisements interval is 60 seconds
  Neighbor capabilities:
    4 Byte AS: advertised and received
    Extended Message: advertised and received
    AddPath:
      IPv4 Unicast: RX advertised and received
    Long-lived Graceful Restart: advertised and received
      Address families by peer:
    Route refresh: advertised and received(old & new)
    Enhanced Route Refresh: advertised and received
    Address Family IPv4 Unicast: advertised and received
    Address Family IPv6 Unicast: received
    Hostname Capability: advertised (name: OpenWrt,domain name: n/a) received (name: k8s-worker1,domain name: n/a)
    Graceful Restart Capability: advertised and received
      Remote Restart timer is 120 seconds
      Address families by peer:
        none
  Graceful restart information:
    End-of-RIB send: IPv4 Unicast
    End-of-RIB received: IPv4 Unicast
    Local GR Mode: Helper*

    Remote GR Mode: Helper

    R bit: True
    N bit: False
    Timers:
      Configured Restart Time(sec): 120
      Received Restart Time(sec): 120
    IPv4 Unicast:
      F bit: False
      End-of-RIB sent: Yes
      End-of-RIB sent after update: No
      End-of-RIB received: Yes
      Timers:
        Configured Stale Path Time(sec): 360
  Message statistics:
    Inq depth is 0
    Outq depth is 0
                         Sent       Rcvd
    Opens:                  2          2
    Notifications:          0          0
    Updates:                5          8
    Keepalives:           764        764
    Route Refresh:          2          2
    Capability:             0          0
    Total:                773        776
  Minimum time between advertisement runs is 0 seconds

 For address family: IPv4 Unicast
  Update group 2, subgroup 4
  Packet Queue length 0
  Community attribute sent to this neighbor(all)
  1 accepted prefixes

  Connections established 2; dropped 1
  Last reset 12:36:16,  No AFI/SAFI activated for peer
  Internal BGP neighbor may be up to 255 hops away.
Local host: 192.168.10.1, Local port: 179
Foreign host: 192.168.10.21, Foreign port: 46320
Nexthop: 192.168.10.1
Nexthop global: fd98:2533:1844::1
Nexthop local: fe80::b2c7:45ff:fe7f:f93a
BGP connection: shared network
BGP Connect Retry Timer in Seconds: 120
Estimated round trip time: 5 ms
Read thread: on  Write thread: on  FD used: 29

動作確認(ipコマンド)

ip route show

root@OpenWrt:~# ip route show
default via 192.168.1.1 dev wan 
192.168.1.0/24 dev wan scope link  src 192.168.1.100 
192.168.10.0/24 dev br-lan scope link  src 192.168.10.1 
192.168.10.61  metric 20 

その他

routel

$ routel
/usr/bin/routel: 48: shift: can't shift that many
         target            gateway          source    proto    scope    dev tbl
        default       172.31.240.1                   kernel            eth0 
    172.17.0.0/ 16                      172.17.0.1   kernel     linkdocker0 
  172.31.240.0/ 20                  172.31.249.248   kernel     link   eth0 
     127.0.0.0/ 8            local       127.0.0.1   kernel     host     lo local
      127.0.0.1              local       127.0.0.1   kernel     host     lo local
127.255.255.255          broadcast       127.0.0.1   kernel     link     lo local
     172.17.0.1              local      172.17.0.1   kernel     hostdocker0 local
 172.17.255.255          broadcast      172.17.0.1   kernel     linkdocker0 local
 172.31.249.248              local  172.31.249.248   kernel     host   eth0 local
 172.31.255.255          broadcast  172.31.249.248   kernel     link   eth0 local
        fe80::/ 64                                   kernel            eth0 
            ::1              local                   kernel              lo local
fe80::215:5dff:fe9b:59c8              local                   kernel            eth0 local
Copied title and URL