후니의 IT인프라 사전
[KANS - 5주차] Service-LoadBalancer Type의 IPVS모드 본문
지금까지는 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 방식이 쓰이며, 실제로 확인해보면 아주 고르게 잘 부하분산 되는 것을 확인할 수 있다.
리소스 생성
그럼 이렇게 대략적으로 정보를 확인해보았으니 실제로 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 모드의 포스팅을 마칩니다.