본문 바로가기
CNCF/Kubernetes

MetalLB BGP 모드를 이용한 Loadbalaning

기본적인 구성은 아래와 같이 worker3대가 10.10.2.0/24의 인터페이스가 연결되어 있으며, 같은 네트워크에 BGP를 사용할 L3SW가 연결 되어 있다.

그리고 Client 는 172.16.100.0 대역대로 L3SW와 연결 되어 있다.

NXOS-9K로 구성된 상단 스위치의 인터페이스 설정은 아래와 같다.

interface Ethernet1/1
  no switchport
  ip address 10.10.2.210/24
  no shutdown
 
interface Ethernet1/2
  no switchport
  ip address 172.19.100.210/24
  no shutdown
...

metallb 구성을 위하여 kubernetes 에서  kube-proxy strictARP true로 변경 해야 한다.

$ kubectl get configmap kube-proxy -n kube-system -o yaml | \
sed -e "s/strictARP: false/strictARP: true/" | \
kubectl diff -f - -n kube-system
  
$ kubectl get configmap kube-proxy -n kube-system -o yaml | \
sed -e "s/strictARP: false/strictARP: true/" | \
kubectl apply -f - -n kube-system

아래와 같이 metallb의 매니페스트를 등록 한다.

$ kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.9.5/manifests/namespace.yaml
$ kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.9.5/manifests/metallb.yaml
$ kubectl create secret generic -n metallb-system memberlist --from-literal=secretkey="$(openssl rand -base64 128)"

배포된 metallb pod는 1개의 controller 와 각 노드 별로 speaker pod 가 동작하는것을 확인할 수 있다.

root@cy01-net114:~# kubectl  get pod -n metallb-system  -o wide
NAME                          READY   STATUS    RESTARTS   AGE    IP             NODE          NOMINATED NODE   READINESS GATES
controller-65db86ddc6-9nvk9   1/1     Running   0          126m   10.233.64.23   cy01-net114   <none>           <none>
speaker-k5tvq                 1/1     Running   1          18h    10.10.2.112    cy01-net112   <none>           <none>
speaker-lhwmb                 1/1     Running   1          18h    10.10.2.114    cy01-net114   <none>           <none>
speaker-q9dhp                 1/1     Running   2          18h    10.10.2.113    cy01-net113   <none>           <none>

이제 상단의 L3스위치와 각노드에 있는 speaker가 eBGP peer가 연결되도록 한다.

L3스위치에서는 65210 AS번호를 사용 하고 각 worker 노드의 speaker 는 65211 AS로 연결 하도록 한다.

BGP연결을 위한 ConfigMap 을 작성 한다. 여기서 external IP 로 사용할 대역대를 172.19.100.0/24 대역대로 사용 하도록 한다.

root@cy01-net112:~# cat bgp.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  namespace: metallb-system
  name: config
data:
  config: |
    peers:
    - peer-address: 10.10.2.210
      peer-asn: 65210
      my-asn: 65211
    address-pools:
    - name: default
      protocol: bgp
      avoid-buggy-ips: true
      addresses:
      - 172.19.100.0/24
root@cy01-net112:~# kubectl create -f  bgp.yaml

이제 L3스위치에서 bgp 설정을 해준다 .

...
router bgp 65210
  router-id 1.1.1.1
  address-family ipv4 unicast
    maximum-paths 3
  neighbor 10.10.2.112
    remote-as 65211
    address-family ipv4 unicast
  neighbor 10.10.2.113
    remote-as 65211
    address-family ipv4 unicast
  neighbor 10.10.2.114
    remote-as 65211
    address-family ipv4 unicast
...

설정이 완료 되면 아래와 같이 각 worker 노드에 있는 speaker의 bgp와 peer를 확인할 수 있다.

switch# show ip bgp sum
BGP summary information for VRF default, address family IPv4 Unicast
BGP router identifier 1.1.1.1, local AS number 65210
BGP table version is 41, IPv4 Unicast config peers 3, capable peers 0
1 network entries and 3 paths using 500 bytes of memory
BGP attribute entries [1/160], BGP AS path entries [1/6]
BGP community entries [0/0], BGP clusterlist entries [0/0]
 
Neighbor        V    AS MsgRcvd MsgSent   TblVer  InQ OutQ Up/Down  State/PfxRcd
10.10.2.112     4 65211    1764    1773       42    0    0 00:00:30 1
10.10.2.113     4 65211    2132    2151       42    0    0 00:00:30 1
10.10.2.114     4 65211    1198    1208       42    0    0 00:00:30 1

BGP Peer 연결은 다른 장비와 장비 간의 Peer 연결과 거의 동일하다. 아래 10.10.2.114 에 있는 speaker 기준으로 초기 상단 l3 장비에 BGP 포트인 179 포트로 Syn 패킷을 보내기 시작 하여 세션을 연결한다.

세셩연결이 완료 되면 Open Message 와 Keepalve 패킷을 주면서 세션 연결을 유지 한다.

Metallb 에서 VIP 사용된 아이피 정보가 업데이트 되면 상단에 Update Message 에 해당 VIP 정보인 17219.10.1 에대한  NLRI 정보를 추가 하여 Nexthop 과 함께 전달하여 상단 L3 에서 BGP Routing 정보를 추가해준다.

당연하지만 Speaker 가 있는 호스트에는  57811 포트로 상단 L3 장비의 10.10.2.210 179포트로 세션이 연결된것이 확인 된다.

root@cy01-net114:~# netstat  -atunp | grep speaker
tcp        0      0 10.10.2.114:7472        0.0.0.0:*               LISTEN      1684866/speaker
tcp        0      0 10.10.2.114:7946        0.0.0.0:*               LISTEN      1684866/speaker
tcp        0      0 10.10.2.114:57811       10.10.2.210:179         ESTABLISHED 1684866/speaker
tcp        0      0 10.233.0.1:57500        10.233.0.1:443          ESTABLISHED 1684866/speaker
udp        0      0 10.10.2.114:7946        0.0.0.0:*                           1684866/speaker

간단하게 Loadbalancer 타입의 Service로 동작 하도록 pod 를 배포한다.

root@cy01-net112:~#  vi test.yaml
apiVersion: apps/v1
kind: ReplicaSet
metadata:
  name: ng-test-rs
  namespace: default
spec:
  replicas: 3
  selector:
    matchLabels:
      app: con-test
  template:
    metadata:
      labels:
        app: con-test
    spec:
      containers:
        - image: 'stenote/nginx-hostname'
          name: ng-con
---
apiVersion: v1
kind: Service
metadata:
  name: ng-svc
spec:
  ports:
  - name: http
    port: 80
    protocol: TCP
    targetPort: 80
  selector:
    app: con-test
  type: LoadBalancer
root@cy01-net112:~#  kubectl create -f test.yaml

Metallb에 의하여 172.19.100.1 라는 External IP를 확인한다.

root@cy01-net112:~# kubectl  get svc
NAME         TYPE           CLUSTER-IP     EXTERNAL-IP    PORT(S)        AGE
kubernetes   ClusterIP      10.233.0.1     <none>         443/TCP        2d1h
ng-svc       LoadBalancer   10.233.58.33   172.19.100.1   80:31265/TCP   3s
 
root@cy01-net112:~# curl 172.19.100.1
ng-test-rs-wktfr
root@cy01-net112:~# curl 172.19.100.1
ng-test-rs-v7gcr

이제 L3스위치에서는 172.19.100.1 아이피에대한 라우팅 정보를 Speaker 에서 eBGP로 받은 경로로 라우팅이 추가 된것을 볼수 있다.

switch# show ip route  bgp
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>
 
172.19.100.1/32, ubest/mbest: 3/0
    *via 10.10.2.112, [20/0], 00:00:41, bgp-65210, external, tag 65211
    *via 10.10.2.113, [20/0], 00:00:41, bgp-65210, external, tag 65211
    *via 10.10.2.114, [20/0], 00:00:41, bgp-65210, external, tag 65211
 
 
switch# show ip bgp
BGP routing table information for VRF default, address family IPv4 Unicast
BGP table version is 45, Local Router ID is 1.1.1.1
Status: s-suppressed, x-deleted, S-stale, d-dampened, h-history, *-valid, >-best
Path type: i-internal, e-external, c-confed, l-local, a-aggregate, r-redist, I-i
njected
Origin codes: i - IGP, e - EGP, ? - incomplete, | - multipath, & - backup
 
   Network            Next Hop            Metric     LocPrf     Weight Path
*|e172.19.100.1/32    10.10.2.113                                    0 65211 ?
*>e                   10.10.2.112                                    0 65211 ?
*|e                   10.10.2.114                                    0 65211 ?

client 노드에서 172.19.100.1 아이피로 접속하면 통신이 되는것을 알수 있다.

root@cy01-ceph231:~# curl 172.19.100.1
ng-test-rs-7flkj
root@cy01-ceph231:~# curl 172.19.100.1
ng-test-rs-wktfr

kubernetes 상에서 LoadBalancer 타입의 service의 External IP 는 speaker 가 있는 노드의  ipvs0 이름의 인터페이스로 172.19.100.1아이피가 바인딩 된것을 알수 있다. 해당 인터페스는 ipvs로 배포된 pod 의 아이피로 연결 되는것을 알수 있다.

root@cy01-net114:~# kubectl  get svc
NAME         TYPE           CLUSTER-IP     EXTERNAL-IP    PORT(S)        AGE
kubernetes   ClusterIP      10.233.0.1     <none>         443/TCP        47h
ng-svc       LoadBalancer   10.233.54.73   172.19.100.1   80:30418/TCP   46h
root@cy01-net114:~# ip a
6: kube-ipvs0: <BROADCAST,NOARP> mtu 1500 qdisc noop state DOWN group default
    link/ether c2:82:30:81:1a:16 brd ff:ff:ff:ff:ff:ff
    inet 10.233.48.121/32 scope global kube-ipvs0
       valid_lft forever preferred_lft forever
    inet 172.19.100.1/32 scope global kube-ipvs0
       valid_lft forever preferred_lft forever
    inet 10.233.0.3/32 scope global kube-ipvs0
       valid_lft forever preferred_lft forever
...
 
root@cy01-net114:~# ipvsadm -ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
  -> RemoteAddress:Port           Forward Weight ActiveConn InActConn
...
TCP  172.19.100.1:80 rr
  -> 10.233.64.16:80              Masq    1      0          0
  -> 10.233.93.2:80               Masq    1      0          0
  -> 10.233.119.2:80              Masq    1      0          0
...
 
root@cy01-net114:~# kubectl  get pod -o wide
NAME               READY   STATUS    RESTARTS   AGE     IP             NODE          NOMINATED NODE   READINESS GATES
ng-test-rs-8cdqc   1/1     Running   0          5h33m   10.233.119.2   cy01-net113   <none>           <none>
ng-test-rs-blq89   1/1     Running   0          5h33m   10.233.64.16   cy01-net114   <none>           <none>
ng-test-rs-rp42j   1/1     Running   0          5h33m   10.233.93.2    cy01-net112   <none>           <none>

Client 에서 Pod까지의 흐름을 확인하면 아래 그림의 1번 부분에서 Client 가 curl 을 이용하여 요청을 보내게 되는데 Client 입장에서 Default Route로 설정된 L3SW의 172.16.100.210으로 패킷이 들어 간다.  L3스위치에서는 BGP로 수신 받은 172.19.100.1의 라우팅 정보에 의하여 Best Path인 10.10.2.112 노드의 worker 노드로 들어 오게 된다.

그뒤 Worker 노드의 각 pod로 Service 에 의하여 분산 되는 구조 이다. 

이제 Best Path 로 가고 사용 되는 node 를 reboot 해본다.

기존 노드에대한 라우팅 정보가 반영되어 다른 노드로 라우팅 되는것을 볼수 있다.

switch# show ip bgp
BGP routing table information for VRF default, address family IPv4 Unicast
BGP table version is 46, Local Router ID is 1.1.1.1
Status: s-suppressed, x-deleted, S-stale, d-dampened, h-history, *-valid, >-best
Path type: i-internal, e-external, c-confed, l-local, a-aggregate, r-redist, I-i
njected
Origin codes: i - IGP, e - EGP, ? - incomplete, | - multipath, & - backup
 
   Network            Next Hop            Metric     LocPrf     Weight Path
*>e172.19.100.1/32    10.10.2.113                                    0 65211 ?
x e                   10.10.2.112                                    0 65211 ?
*|e                   10.10.2.114                                    0 65211 ?​

반응형