본문 바로가기
프로젝트&&스터디/KANS2기

[KANS - 5주차] Service-LoadBalancer Type의 IPVS모드

by james_janghun 2024. 10. 6.

 

지금까지는 iptables proxy 모드에 대해서 알아보았는데, 이번에는 IPVS proxy 모드에 대해서 학습해봅니다.

 

 

 

IPVS모드를 설정하고 클러스터를 생성한 뒤, kube-proxy의 configmap을 살펴보면 다음과 같습니다.

kubectl describe cm -n kube-system kube-proxy
ipvs:
  excludeCIDRs: null
  minSyncPeriod: 0s
  scheduler: ""
  strictARP: true
  syncPeriod: 0s
  tcpFinTimeout: 0s
  tcpTimeout: 0s
  udpTimeout: 0s
  
...
  
mode: ipvs

 

IPVS 모드에서는 다양한 부하분산 알고리즘도 사용할 수 있으며, scheduler에 설정됩니다.

 

strictARP도 true로 표시되는데 MetalLB 동작을 위해서는 true로 설정해야하며, strict ARP는 패킷을 보다 엄격하게 처리하겠다는 설정입니다. 노드의 인터페이스는 자신에게 할당된 IP 주소에 대해서만 ARP응답을 보냅니다. 그렇기 때문에 로드밸런싱할 때 ARP 패킷이 잘못된 인터페이스로 전달되는 문제를 방지합니다. 이 설정은 특히 클러스터 내에서 여러 노드가 동일 IP를 갖는 가상IP를 사용하는 경우 중요합니다.

 

IPVS 모드를 실행시키게되면 다음과 같이 kube-ipv0이라는 가상 인터페이스가 모든 노드에 새로 생성됩니다.

for i in control-plane worker worker2 worker3; do echo ">> node myk8s-$i <<"; docker exec -it myk8s-$i ip -d -c addr show kube-ipvs0; echo; done

 

 

또한 각 서비스 cluster IP와 그 포트들 간의 부하분산 알고리즘도 확인할 수 있게됩니다.

for i in control-plane worker worker2 worker3; do echo ">> node myk8s-$i <<"; docker exec -it myk8s-$i ipvsadm -Ln ; echo; done

 

[참고] iptable

iptables 모드에서는 115개의 iptable rule이 확인됩니다.

 

IPVS 모드에서는 83개의 iptable rule이 확인되네요. 확실히 ipvs 모드가 iptable rule의 갯수도 적고 조금 더 효율적으로 네트워크를 활용할 수 있습니다.

 

IPVS모드의 정책적용 

IPVS 모드에서는 불필요하게 IP마다 룰을 나열하는게 아니라 ipset에서 리스트화 하여 관리한다. 그래서 테이블에서는 ipset 이름으로 보내도록 설정되어 있고, 실제 ipset의 list를 조회하면 해당하는 IP목록을 확인할 수 있는 방식이다.

IPVS의 부하분산 알고리즘

공식문서에서도 확인할 수 있고, 다음과 같이 다양한 알고리즘을 확인할 수 있다. 기본적으로는 round robin 방식이 쓰이며, 실제로 확인해보면 아주 고르게 잘 부하분산 되는 것을 확인할 수 있다.

https://net711.tistory.com/entry/lvs-리눅스-l4-만들기

 

리소스 생성

 

그럼 이렇게 대략적으로 정보를 확인해보았으니 실제로 pod와 service를 배포해서 확인해보겠습니다.

 

다음과 같이 아주 기본적인 pod와 svc를 생성합니다.

cat <<EOT> 3pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: webpod1
  labels:
    app: webpod
spec:
  nodeName: myk8s-worker
  containers:
  - name: container
    image: traefik/whoami
  terminationGracePeriodSeconds: 0
---
apiVersion: v1
kind: Pod
metadata:
  name: webpod2
  labels:
    app: webpod
spec:
  nodeName: myk8s-worker2
  containers:
  - name: container
    image: traefik/whoami
  terminationGracePeriodSeconds: 0
---
apiVersion: v1
kind: Pod
metadata:
  name: webpod3
  labels:
    app: webpod
spec:
  nodeName: myk8s-worker3
  containers:
  - name: container
    image: traefik/whoami
  terminationGracePeriodSeconds: 0
EOT
cat <<EOT> svc-clusterip.yaml
apiVersion: v1
kind: Service
metadata:
  name: svc-clusterip
spec:
  ports:
    - name: svc-webport
      port: 9000        # 서비스 IP 에 접속 시 사용하는 포트 port 를 의미
      targetPort: 80    # 타킷 targetPort 는 서비스를 통해서 목적지 파드로 접속 시 해당 파드로 접속하는 포트를 의미
  selector:
    app: webpod         # 셀렉터 아래 app:webpod 레이블이 설정되어 있는 파드들은 해당 서비스에 연동됨
  type: ClusterIP       # 서비스 타입
EOT

 

 

인터페이스 및 트래픽 체크 

해당 yaml을 통해서 pod와 svc를 생성하고 정보를 다시 확인하겠습니다. 10.200.1.76이라는 svc가 kube-ipvs0 더미 인터페이스에 붙은 것을 확인할 수 있습니다.

 

for i in control-plane worker worker2 worker3; do echo ">> node myk8s-$i <<"; docker exec -it myk8s-$i ip -c addr; echo; done

 

각 노드에서도 동일하게 추가된 것을 확인했습니다.

 

 

새로운 서비스에 rr 부하분산 알고리즘도 잘 반영된 것을 볼 수 있습니다. 그럼 실제로 트래픽을 날려보겠습니다.

 

for i in control-plane worker worker2 worker3; do echo ">> node myk8s-$i <<"; docker exec -it myk8s-$i ipvsadm -Ln -t $CIP:$CPORT ; echo; done

 

 

 

watch를 통해서 부하분산을 카운팅할 수 있도록 모니터링을 걸어놓겠습니다.

watch -d "docker exec -it myk8s-control-plane ipvsadm -Ln -t $CIP:$CPORT --stats; echo; docker exec -it myk8s-control-plane ipvsadm -Ln -t $CIP:$CPORT --rate"

 

다음과 같이 curl을 통해서 100번을 호출한 결과는 다음과 같습니다.

kubectl exec -it net-pod -- zsh -c "for i in {1..100}; do curl -s $SVC1:9000 | grep Hostname; done | sort | uniq -c | sort -nr"

 

제가 중간에 별도로 호출한게 더 있어서 100개가 넘긴 한데 아주 정확하게 39로 부하분산 된 것을 확인할 수 있습니다.

 

 

또한 이렇게 반복접속을 시켜놓으면 아주 고르게 분배됨 또한 확인할 수 있습니다.

kubectl exec -it net-pod -- zsh -c "while true; do curl -s --connect-timeout 1 $SVC1:9000 | egrep 'Hostname|RemoteAddr|Host:'; date '+%Y-%m-%d %H:%M:%S' ; echo '--------------' ;  sleep 1; done"

 

 



이상으로 IPVS 모드의 포스팅을 마칩니다.