새소식

Kubernetes

[Study 5주차] 쿠버네티스 Service - IPVS Proxy 모드

  • -

CoudNet@ 팀의 가시다님께서 리딩하시는 KANS Study (Kubernetes Advanced Networking Study) 5주차 스터디 내용 정리

 

이번 글에는 Service 의 동작 방식 중 IPVS Proxy 에 대해서 알아보겠습니다.

 

 

1. IPVS Proxy 모드

 

IPVS 모드는 리눅스 커널에서 제공하는 IPVS 가 서비스 프록시 역할을 수행하는 모드입니다.

 

IPVS 는 넷필터에서 동작하는 Layer 4 로드밸런서 입니다.
기존의 iptables 보다 높은 성능 처리를 보여주고, 규칙 개수도 줄일 수 있습니다.
또한 아래와 같은 다양한 부하분산 알고리즘도 제공해줍니다.

 

IPVS 가 제공하는 다양한 부하분산 알고리즘

 

1. 라운드 로빈

우선순위를 두지 않고, 요청을 대상 목적지 파드로 돌아가면서 전달

 

2. 최소 연결

목적지 파드의 연결 개수가 가장 적은 곳을 우선해서 전달

 

3. 목적지 해싱

목적지 IP 주소로 해시값을 계산하여 목적지 파드를 결정하여 전달

 

4. 출발지 해싱

출발지 IP 주소로 해시값을 계산하여 목적지 파드를 결정하여 전달

 

5. 최단 지연

응답 속도가 가장 빠른 목적지 파드 선택

 

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

 

 

1.1. 실습환경 구성

 

실습 환경은 kind 를 사용하여 구성했습니다.

cat <<EOT> kind-svc-2w-ipvs.yaml
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
featureGates:
  "InPlacePodVerticalScaling": true
  "MultiCIDRServiceAllocator": true
nodes:
- role: control-plane
  labels:
    mynode: control-plane
    topology.kubernetes.io/zone: ap-northeast-2a
  extraPortMappings:
  - containerPort: 30000
    hostPort: 30000
  - containerPort: 30001
    hostPort: 30001
  - containerPort: 30002
    hostPort: 30002
  - containerPort: 30003
    hostPort: 30003
  - containerPort: 30004
    hostPort: 30004
  kubeadmConfigPatches:
  - |
    kind: ClusterConfiguration
    apiServer:
      extraArgs:
        runtime-config: api/all=true
    controllerManager:
      extraArgs:
        bind-address: 0.0.0.0
    etcd:
      local:
        extraArgs:
          listen-metrics-urls: http://0.0.0.0:2381
    scheduler:
      extraArgs:
        bind-address: 0.0.0.0
  - |
    kind: KubeProxyConfiguration
    metricsBindAddress: 0.0.0.0
    ipvs:
      strictARP: true
- role: worker
  labels:
    mynode: worker1
    topology.kubernetes.io/zone: ap-northeast-2a
- role: worker
  labels:
    mynode: worker2
    topology.kubernetes.io/zone: ap-northeast-2b
- role: worker
  labels:
    mynode: worker3
    topology.kubernetes.io/zone: ap-northeast-2c
networking:
  podSubnet: 10.10.0.0/16
  serviceSubnet: 10.200.1.0/24
  kubeProxyMode: "ipvs"        
EOT

# k8s 클러스터 설치
kind create cluster --config kind-svc-2w-ipvs.yaml --name myk8s --image kindest/node:v1.31.0

# 노드에 기본 툴 설치
docker exec -it myk8s-control-plane sh -c 'apt update && apt install tree psmisc lsof wget bsdmainutils bridge-utils net-tools dnsutils ipset ipvsadm nfacct tcpdump ngrep iputils-ping arping git vim arp-scan -y'
for i in worker worker2 worker3; do echo ">> node myk8s-$i <<"; docker exec -it myk8s-$i sh -c 'apt update && apt install tree psmisc lsof wget bsdmainutils bridge-utils net-tools dnsutils ipset ipvsadm nfacct tcpdump ngrep iputils-ping arping -y'; echo; done

# 테스트용 컨테이너 생성
docker run -d --rm --name mypc --network kind --ip 172.18.0.100 nicolaka/netshoot sleep infinity

 

파드 환경 구성

# 목적지 파드 생성
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> netpod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: net-pod
spec:
  nodeName: myk8s-control-plane
  containers:
  - name: netshoot-pod
    image: nicolaka/netshoot
    command: ["tail"]
    args: ["-f", "/dev/null"]
  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

# 배포
kubectl apply -f 3pod.yaml,netpod.yaml,svc-clusterip.yaml



1.2. IPVS Proxy 모드 확인

 

ipvs 기본값 확인

 

# 로그 확인용 툴 설치
kubectl krew install stern

# Kube-Proxy 로그 확인
kubectl stern -n kube-system -l k8s-app=kube-proxy --since 2h | egrep '(ipvs|IPVS)'

# 기본 모드 정보 확인
kubectl get cm -n kube-system kube-proxy -o yaml | egrep 'mode|strictARP|scheduler'

 

 

노드 별 네트워크 정보 확인

 

kube-ipvs0 네트워크 인터페이스가 있는 것을 확인할 수 있습니다.

또한, Service ClusterIP 생성 시 kube-ipvs0 인터페이스에 해당 IP 가 할당되는 것을 알 수 있습니다.

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

 

 

IPVS Proxy 모드 부하분산 확인

 

# 변수 지정
CIP=$(kubectl get svc svc-clusterip -o jsonpath="{.spec.clusterIP}")
CPORT=$(kubectl get svc svc-clusterip -o jsonpath="{.spec.ports[0].port}")
echo $CIP $CPORT 

# IPVS 부하분산 테이블 확인
docker exec -it myk8s-control-plane ipvsadm -Ln -t $CIP:$CPORT

 

 

# 컨트롤플레인에서 ipvsadm 모니터링
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"

# 서비스 접근
SVC1=$(kubectl get svc svc-clusterip -o jsonpath={.spec.clusterIP})
kubectl exec -it net-pod -- curl -s --connect-timeout 1 $SVC1:9000

 

 

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

# 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"

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

 

 

iptables 모드에 비해 월등하게 균등한 부하 분산이 이루어지는 것을 알 수 있습니다.

 

IPVS Proxy 모드는 기존의 iptables 모드에 비해 성능이 좋기 때문에,

IPVS Proxy 모드를 사용할 수 있는 환경이라면 적극 고려해보는 것도 좋을 것 같습니다.

Contents

포스팅 주소를 복사했습니다