MetalLB 소개 — HA 및 성능 테스트 포함

Jerry(이정훈)
16 min readApr 20, 2021

--

On-Prem Kube 환경에서 외부 L4 스위치 없이 Load Balancing 기능을 제공하는 MetalLB 테스트 내역을 공유해 드립니다. 현재 실 운영 서비스에서 잘 사용 중입니다.

Kube 환경에서도 기존 VM 환경과 동일하게 Load Balancing 기능을 위해서 외부 L4 스위치가 필요 합니다. 하지만 MetalLB를 사용하시면 Service Type을 LoadBalancer로 지정하는 것만으로 Load Balancing 가능합니다.

TL;DR

  • On-Prem Kube 환경에서 MetealLB를 사용하면 외부 L4 스위치 없이 LoadBalancing 기능 사용 가능. Service Type을 LoadBalancer로 지정만 하면 되므로 아주 간단.
  • MetalLB POD Restart 시 서비스 영향 없으나 (ARP 할당하는) 노드 Down 시 약 10초 내외 Down 발생(MetalLB L2 Mode)
  • 단일 Node Throughput이 성능 Bottle-neck
    : 약 1,000 Session 부하 정상 처리
    : node throughput 만큼 처리 할 것으로 예상
    : 노드 간 부하 분산 및 재처리 등의 LB 기능 정상 작동. kube-proxy가 LB 처리하므로 기능 및 성능은 당연히 잘 나올 것으로 예상
    : 별도 Admin GUI 화면 제공하지 않음

먼저, 설치는 다른 것들과 마찬가지로 아주 간단하므로 아래 공식 웹사이트 링크로 대신합니다.

참조

(설치 후 ConfigMap만 추가 등록하면 됩니다. 저는 BGP Mode가 아닌 편의상 Layer 2 Mode를 사용 하였습니다.)

설치가 완료되면 아래와 같이 MetalLB 관련 POD 확인이 가능합니다.

[spkr@erdia22 ~ (spkn02:metallb-system)]$ kgp
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
controller-64f86798cc-9w4qj 1/1 Running 0 152m 10.233.90.50 node1 <none> <none>
speaker-bwh49 1/1 Running 4 152m 172.17.29.162 node2 <none> <none>
speaker-jqlvl 1/1 Running 0 100m 172.17.29.161 node1 <none> <none>
speaker-qzjtx 1/1 Running 1 131m 172.17.29.163 node3 <none> <none>

그럼, POD를 실행하고 Service Type을 LoadBalancer를 지정하겠습니다.

apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-hello
namespace: nginx
labels:
app: nginx # Service 등에서 참조할 Label 이름
spec:
replicas: 4
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginxdemos/hello
----------
apiVersion: v1
kind: Service
metadata:
name: nginx-svc
namespace: nginx
spec:
ports:
- name: http
port: 80
protocol: TCP
targetPort: 80
selector:
app: nginx
# sessionAffinity: None
type: LoadBalancer
# loadBalancerIP: 172.17.19.240

로그를 통하여 어떤 MetalLB POD가 응답하는지 확인해 보겠습니다.

[spkr@erdia22 ~ (spkn02:metallb-system)]$ kubetail -n metallb-system |grep 172.17.29.141[controller-64f86798cc-9w4qj] {"caller":"service.go:114","event":"ipAllocated","ip":"172.17.29.141","msg":"IP address assigned by controller","service":"nginx/nginx-svc","ts":"2021-04-19T20:26:44.422393932Z"}
[speaker-qzjtx] {"caller":"main.go:275","event":"serviceAnnounced","ip":
172.17.29.141","msg":"service has IP, announcing","pool":"default","protocol":"layer2","service":"nginx/nginx-svc","ts":"2021-04-19T20:26:44.445097238Z"}
[speaker-qzjtx] {"caller":"arp.go:102","interface":"eth0","ip":"172.17.29.141","msg":"got ARP request for service IP, sending response","responseMAC":"50:6b:8d:93:63:38","senderIP":"172.17.23.14","senderMAC":"00:50:56:bd:c4:a0","ts":"2021-04-19T20:27:06.422305365Z"}(kubetail 을 사용하면 복수의 POD 로그 확인이 가능하여 편리합니다.)

로그에서 확인할 수 있듯이 “172.17.29.141” IP 요청에 대하여 controller가 speaker-2xvhc를 할당하여 MAC response를 보내고 있습니다. speaker-2xvhc는 아래와 같이 node3에 할당되었습니다.

[spkr@erdia22 ~ (spkn02:metallb-system)]$ kgp
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
controller-64f86798cc-9w4qj 1/1 Running 0 156m 10.233.90.50 node1 <none> <none>
speaker-bwh49 1/1 Running 4 156m 172.17.29.162 node2 <none> <none>
speaker-jqlvl 1/1 Running 0 104m 172.17.29.161 node1 <none> <none>
speaker-qzjtx 1/1 Running 1 136m 172.17.29.163 node3 <none> <none>

speaker-qzjtx 가 할당된 node3 에서 확인해 보면 “172.17.29.141” IP가 IPVS network interface에 할당된 걸 확인 가능합니다. (네트워크를 잘 모르는 저 입장에서 신기합니다 ^^)

[spkr@node3 ~]$ ip a show |grep 172.17.29.141 -B 30
(생략)
6: kube-ipvs0: <BROADCAST,NOARP> mtu 1500 qdisc noop state DOWN group default
link/ether 72:93:90:32:9d:d2 brd ff:ff:ff:ff:ff:ff
inet 10.233.35.81/32 scope global kube-ipvs0
valid_lft forever preferred_lft forever
(생략)
inet 172.17.29.141/32 scope global kube-ipvs0
valid_lft forever preferred_lft forever

즉, 해당 IP “172.17.29.141”에 대한 ARP MAC 응답을 노드 Interface에 ipvs(IP Virtual Server)로 할당하여 처리합니다. (kube-proxy IPVS 옵션 사용 중)

그리고 Load Balancing 기능은 MetalLB에서 처리하는 게 아니고 Kube service인 kube-proxy 에서 처리 합니다. 관련 내용을 공식 문서에서 확인하면 아래와 같습니다.

Load-Balancing Behavior

In layer 2 mode, all traffic for a service IP goes to one node. From there, kube-proxy spreads the traffic to all the service’s pods.

In that sense, layer 2 does not implement a load-balancer. Rather, it implements a failover mechanism so that a different node can take over should the current leader node fail for some reason.

If the leader node fails for some reason, failover is automatic: the failed node is detected using memberlist, at which point new nodes take over ownership of the IP addresses from the failed node.

from : https://metallb.universe.tf/concepts/layer2/

(RoundRobin, Least Connection 등의 LB 옵션은 kube-proxy IPVS proxy mode 설정에 따릅니다.)

그럼, 부하 분산이 잘 되는지 확인해 보겠습니다. 사실 kube-proxy가 부하 분산을 담당하므로 당연히 잘되어야 합니다. ^^ JMeter로 부하 테스트를 하였습니다.

100개 Session을 보냈는데 아래와 같이 5개 POD에 비교적 균등하게 나누어 집니다.

3개 POD로 줄였는데 역시 잘 분산됩니다.

다음으로 가용성 테스트를 해 보겠습니다.

MetalLB POD는 Load Balancing 기능을 처리하는 것이 아니고 Fail-over에 관여합니다. MetalLB POD를 재시작하여도 서비스에는 전혀 영향이 없습니다.

공식 문서 Layer 2 옵션은 아래와 같이 10s 이내 down time 이라고 합니다.

If you encounter a situation where layer 2 mode failover is slow (more than about 10s), please file a bug! We can help you investigate and determine if the issue is with the client, or a bug in MetalLB.

ARP 처리를 하는 POD가 할당된 노드를 reboot 합니다.

[spkr@erdia22 ~ (spkn02:metallb-system)]$ ~/hosts/ctr3
Last login: Tue Apr 20 05:29:28 2021 from 172.17.18.2
[spkr@node3 ~]$ sudo reboot
Connection to ctr3 closed by remote host.
Connection to ctr3 closed.

curl을 이용하여 서비스 이상 여부를 확인 합니다.

[spkr@erdia22 erdia (spkn02:metallb-system)]$ while true;do curl -I 172.17.28.160; sleep 1; doneHTTP/1.1 200 OK
Server: nginx/1.13.8
Date: Mon, 19 Apr 2021 23:06:12 GMT
Content-Type: text/html
Connection: keep-alive
Expires: Mon, 19 Apr 2021 23:06:11 GMT
Cache-Control: no-cache
^C
[spkr@erdia22 erdia (spkn02:metallb-system)]$ while true;do curl -I 172.17.28.160; sleep 1; done
HTTP/1.1 200 OK
Server: nginx/1.13.8
Date: Mon, 19 Apr 2021 23:06:19 GMT
Content-Type: text/html
Connection: keep-alive
Expires: Mon, 19 Apr 2021 23:06:18 GMT
Cache-Control: no-cache

위와 같이 약 7초(23:06:12 ~ 23:06:19) 동안 loss 가 있었습니다. 7초면 운영 환경에서도 사용 가능할 정도로 양호하다 생각됩니다. ^^

MetalLB POD 로그로 검증해 보겠습니다.

[spkr@erdia22 ~ (spkn02:metallb-system)]$ kubetail -n metallb-system |grep 172.17.28.160[speaker-qzjtx] {"caller":"arp.go:102","interface":"eth0","ip":"172.17.28.160","msg":"got ARP request for service IP, sending response","responseMAC":"50:6b:8d:93:63:38","senderIP":"172.17.19.4","senderMAC":"d0:c6:37:a4:55:56","ts":"2021-04-19T23:05:46.107914649Z"}[speaker-jqlvl] {"caller":"main.go:275","event":"serviceAnnounced","ip":"172.17.28.160","msg":"service has IP, announcing","pool":"default","protocol":"layer2","service":"nginx/nginx-svc","ts":"2021-04-19T23:06:18.435753762Z"}
^C

위와 같이 노드 reboot 이 후 ARP 응답을 하는 POD가 변경 된 걸 로그에서도 확인 가능합니다. (speaker-qzjtx -> speaker-jqlvl)

이제 부하테스트가 남았습니다. 공식 문서에서 확인해 보니 단일 노드에서 처리하니 단일 노드 Throughput이 bottle neck이라고 합니다. (BGP Mode를 사용하면 여러 노드에서 처리가 가능하여 성능이 증가될 것으로 보이나 스위치 설정이 필요한 듯하여 패스 하였습니다.)

As explained above, in layer2 mode a single leader-elected node receives all traffic for a service IP. This means that your service’s ingress bandwidth is limited to the bandwidth of a single node. This is a fundamental limitation of using ARP and NDP to steer traffic.

JMeter로 초당 1,000 Session 부하 실행 시 정상적으로 처리 가능 하였습니다. 인증, DB 접속도 필요하지 않는 기본 페이지로 테스트한 결과라 실제 부하 테스트 환경과는 거리가 멀어 참조 용도로만 사용 가능합니다.

Vegeta 부하 툴로 측정 시에도 동일하게 에러가 발생하지는 않았습니다. 99% latency도 30ms로 양호한 수준으로 판단 됩니다.

[spkr@erdia22 56.MetalLB (spkn02:metallb-system)]$ echo "GET http://172.17.28.160/" | vegeta attack -name=1000qps -rate=1000 -duration=30s | tee results.1000qps.bin |vegeta report
Requests [total, rate, throughput] 30000, 1000.04, 999.71
Duration [total, attack, wait] 30.009s, 29.999s, 9.915ms
Latencies [min, mean, 50, 90, 95, 99, max] 2.346ms, 9.796ms, 8.435ms, 14.705ms, 19.175ms, 30.999ms, 127.419ms
Bytes In [total, mean] 217050000, 7235.00
Bytes Out [total, mean] 0, 0.00
Success [ratio] 100.00%
Status Codes [code:count] 200:30000
Error Set:

기존 L4 스위치에 비하여 아쉬운 점은 세션 현황 등을 확인할 수 있는 admin GUI page 가 없는 것 입니다. 하지만 오픈 소스이고 편의성, 가용성을 고려한다면 충분히 운영 환경에서 사용 가능할 것으로 판단됩니다. (물리 L4 스위치도 필요없는 좋은 세상에 살고 있습니다 ㅎㅎ)

이상 감사합니다.

--

--

Jerry(이정훈)
Jerry(이정훈)

Written by Jerry(이정훈)

DevOps/Automation Engineer 60살까지 콘솔 잡는 엔지니어를 목표로 느리게 생각하고 있습니다.

No responses yet