Skip to content

컨테이너 환경에서 각 컨테이너의 네트워크 목표 성능 달성을 위해 필요한 CPU 양을 트레이닝을 통해 예측하고, 이로써 네트워크 성능 달성을 가능하게 하는 연구. (DepCon의 확장 연구)

Notifications You must be signed in to change notification settings

kiiimes/DepCon2

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

13 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

DepCon2 관련 내용 및 실험 방법 정리

DepCon2 연구

  • DepCon2 연구는 기존 DepCon의 확장 연구
  • 기존 DepCon 연구는 Kubernetes 환경에서 DepCon scheduler를 이용하여 network 목표 성능을 달성하기에 적합한 환경을 선택(이 과정에서 network bandwidth에 대한 filtering을 제공하고 network 목표 성능 달성을 위해 정확히 얼만큼의 CPU가 필요한지 알 지 못하므로 할당 가능한 CPU가 많은 서버에 컨테이너를 생성하였다.)하고 DepCon agent를 이용하여 각 컨테이너의 CPU 할당량을 동적으로 조절하여 network SLO를 달성한다.
  • DepCon2 연구는 network 목표 성능 달성을 위해 필요한 CPU를 트레이닝 데이터를 이용하여 예측하고, 예측한 CPU와 network를 기반으로 Kubernetes에서 스케줄링을 제공한다. 각 서버에서 CPU 할당량을 제어할 필요 없이 network 성능 달성을 위한 정해진 양의 CPU를 할당하여 네트워크 성능을 달성할 수 있도록 한다.

DepCon2 스케줄링 데이터 수집 방법

  • CPU quota(사용가능한 CPU)를 제한 하였을 때의 network bandwidth를 측정

DepCon2 데이터 수집 환경

  • Intel E5-2650 v3 10 cores CPU, 128GB memory, 10GbE network interface
  • Kubernetes v1.21.1, Docker v20.10

DepCon2 데이터 수집 방법

  • 10Gb Ethernet으로 연결된 2대의 서버에서 Kubernetes 환경을 셋팅하여 실험 진행
    • oslab(1번 서버 - netserver, master) oslab2(2번 서버 - netperf, worker) 를 이용
  • pod 1개를 띄워 CPU quota 값을 2500, 5000, 10000 단위로 설정하여 데이터를 수집
    • 예) 1개의 pod에 대해서 cpu quota별(2500, 7500, 12500, … 102500 : 5000 단위)로 패킷 사이즈를 (32, 64,128, 256, 512, 1024)로 변경

DepCon2 데이터 수집을 위한 세팅 방법

  • 디렉토리 - /home/oslab2/eskim/depcon_expansion/data_script
  • Kubernetes 클러스터 구성 (1번 서버 - master, 2번 서버 - worker)
  • 1번 서버(oslab)에서 netserver 실행
    • netserver -p 1
  • pod 생성
    • kubectl create -f p1.yaml
kind: Pod
apiVersion: v1
metadata:
  name: p1
spec:
  containers:
    - name: p1
      image: dkdla58/ubuntu:netperf
      command: ["/bin/bash", "-ec", "while :; do echo '.'; sleep 5 ; done"]
      resources:
        limits:
          cpu: 100m #2500, 7500, 12500, 17500, ... 100000
  restartPolicy: Never
  • p1.yaml 파일에서 image는 netperf, vnstat 등 데이터 수집에 필요한 툴이 설치되어 있는 이미지 파일

  • resources: limits: cpu: -> cpu의 limit을 정할 수 있는 항목이고, cpu의 limit만 설정하면 자동으로 cpu request도 설정된다. Cpu의 limit 값으로 cpu quota 값이 결정된다.

  • cpu: 100m -> quota 값 : 100000 과 같음

    • 2500 == 25m, 7500 == 75m, 12500 == 125m
  • kubectl get pods를 이용하여 STATUS가 Running 상태인지 확인하고 Running 상태가 되면 아래의 스크립트를 사용하여 실험 진행

  • 실험 스크립트

    • ./netperf_pod.sh p32[디렉토리 명]
#!/bin/bash

pkt=32

mkdir $1

for i in $(seq 1 6)
do
        echo $pkt
        mkdir $1/p${pkt}

        for j in $(seq 1 10)
        do
                mkdir $1/p${pkt}/$j
                kubectl exec -it p1 --namespace=default -- netperf -H 10.0.0.25 -p 1 -l 123 -- -m ${pkt}&
                sleep 1
                kubectl exec -it p1 --namespace=default -- vnstat -tr 120 > $1/p${pkt}/$j/vnstat.txt&
                pidstat -G netperf 120 1 > $1/p${pkt}/$j/pidstat.txt & mpstat -P ALL 120 1 > $1/p${pkt}/$j/mpstat.txt
                sleep 3
        done
        ((pkt=${pkt}*2))
done
  • 설정한 quota 크기별로 packet 사이즈를 32, 64, 128, 256, 512, 1024 늘리며 실험 진행, 각 패킷 사이즈별로 10번씩 실험 진행

    • pod 내부에서 netperf, vnstat을 실행하여 실험 진행
  • 실험이 끝나고 kubectl delete -f p1.yaml을 입력하여 pod 삭제 후 p1.yaml에서 cpu limit을 그 다음 실험 quota로 변경하여 실험

    • 삭제 및 생성 반복하여 실험 진행
  • ./main.sh

    • quota 사이즈 별로 pod 생성 삭제가 귀찮고, netperf 실험을 quota 사이즈별로 자동으로 하고 싶으면 해당 스크립트 실행하면 됨
      • quota 사이즈를 바꿀 때마다 pod를 삭제하고 생성하고를 반복해야 하므로 pod가 Running 상태가 되면, netperf 스크립트를 실행하도록 스크립트 작성
        • 약간의 수정이 필요할 수 있음
#!/bin/bash

quota=100

mkdir q10
kubectl create -f p1.yaml

a=''
while [ "$a" != "Running" ]
do
a=`kubectl get pods | awk '{print $3}' |tail -1`;
sleep 1
done

sh netperf_pod.sh q10
sleep 2
kubectl delete -f p1.yaml
sed -i "s/10m/100m/g" p1.yaml

for i in $(seq 1 10)
do
        mkdir q${quota}
        kubectl create -f p1.yaml

        a=''
        while [ "$a" != "Running" ]
        do
        a=`kubectl get pods | awk '{print $3}' |tail -1`;
        sleep 1
        done

        sh netperf_pod.sh q${quota}
        sleep 2
        kubectl delete -f p1.yaml
        ((tmp=${quota}+100))
        sed -i "s/${quota}m/${tmp}m/g" p1.yaml
        quota=$tmp
done
  • ./sum.sh
    • 각 quota 사이즈별 디렉토리에 sum.sh 를 copy하여 vnstat, pidstat, mpstat 별 텍스트 파일을 추출할 수 있음
    • 10번 실험 기준!

실험 데이터 디렉토리

  • /home/oslab2/eskim/depcon_expansion/data_script

  • /home/oslab2/eskim/depcon_expansion/data_script/raw_data_2500

    • cpu quota 2500 간격으로 2500, 10000 ~ 102500
  • /home/oslab2/eskim/depcon_expansion/data_script/raw_data_5000

    • cpu quota 5000 간격으로 5000, 15000 ~ 105000
  • /home/oslab2/eskim/depcon_expansion/data_script/raw_data_10000

    • cpu quota 10000 간격으로 1000, 10000 ~ 100000
  • [참고] 또 다른 스크립트들 정리

    • 아래의 스크립트 들은 pod를 여러 개 생성할 때 사용하는 스크립트이고, 데이터 수집에는 한 개의 pod 만 띄우므로 사용하지 않음
      • edit_pnum.sh : 여러 pod를 생성할 때 pod의 name을 바꾸기 위한 스크립트
      • edit.sh : 여러 pod를 생성할 때 network bandwidth나 CPU quota를 변경하기 위한 스크립트

DepCon2에서 network bandwidth 및 CPU를 고려하여 스케줄링 하는 방법

  • Kubernetes의 스케줄링 과정에서 DRF를 적용하여 CPU와 network를 fair하게 할당해줄 수 있는 Container를 선택하는 방향으로 구현을 하려 하였으나 컨테이너 환경에서는 자원을 나눠서 컨테이너에 할당하도록 스케줄링 하면 pending 상태로 지속되는 시간이 발생하기 때문에 비효율적임
    • 그래서, DRF를 적용하여 컨테이너의 생성 순서를 조정하는 kube-batch라는 오픈소스가 있는 데 이는 batch job에서 효율적이고 일반적인 클러스터 환경에는 적합하지 않음
  • 그래서, Kubernetes의 balancedResource 플러그인을 수정하여 새로운 DepCon2 스케줄러를 구현
    • 여기에서 balancedResource 플러그인은 기존 Kubernetes에 있던 플러그인으로 컨테이너를 생성할 때 서버들의 CPU와 memory가 비율적으로 균등하게 유지되도록 컨테이너를 생성함. -> 하나의 서버에 컨테이너가 몰려서 하나의 자원이 부족한 경우가 최대한 발생하지 않도록 하기 위함
    • DepCon2 scheduler에서 CPU, memory, network가 균등한 서버를 찾아서 컨테이너 생성
  • (1) network에 대한 Filtering과정 (2) network, cpu, memory 자원의 비율에 대해 고려한 Scoring 과정

DepCon2 스케줄러 filtering 구현 방법

  • 먼저, DepCon2 스케줄러에서 network SLO만큼의 network bandwidth를 제공할 수 없는 서버는 pod를 생성할 서버에서 제외한다.
  • Kubernetes에서 기본적으로 제공하는 자원인 CPU, memory 외의 다른 자원에 대해 filtering을 제공하고 싶으면 API server에 추가할 자원 항목과 그에 대한 capacity를 추가해야 한다.
    • 새로운 노드-레벨의 확장된 리소스를 알리기 위해, 클러스터 운영자는 API 서버에 PATCH HTTP 요청을 제출하여 클러스터의 노드에 대해 status.capacity에서 사용할 수 있는 수량을 지정할 수 있음.
  • ./slo_setting.sh [network interface name]
    • 위의 커맨드를 실행하여 모든 노드에 network SLO 자원 항목을 추가할 수 있음
    • network SLO라는 항목을 사용할 모든 노드에 자원을 추가해야됨 -> 스크립트에서 해줌
#!/bin/bash
speed=`ethtool $1 | grep Speed`
speedBack="${speed#*Speed: }"
value="${speedBack%%Mb/s*}"

nodes=$(echo "`kubectl get nodes`" | awk '{print $1}' | sed -n "2, \$p")
pid=`ps -ef | grep "kubectl proxy" | grep -v 'grep' | awk '{print $2}'`
if [ -z $pid ];then
  kubectl proxy &
fi

for node in $nodes
do
        curl --header "Content-Type: application/json-patch+json" \
        --request PATCH \
        --data '[{"op": "add", "path": "/status/capacity/example.com~1SLO", "value": '`expr $value \* 1000000`'}]' \
        http://localhost:8001/api/v1/nodes/$node/status
done
  • ethtool로 Ethernet의 network bandwidth를 얻어옴
  • 모든 노드들의 리스트를 얻어오고 kubectl proxy를 이용하여 kubernetes proxy를 실행 -> proxy 실행으로 API server에 연결 가능
  • ethtool에서 network bandwidth가 Mbps 단위로 출력이 되기 때문에 전체 capacity는 Mbps 만큼 곱하여 API server에 등록함.

DepCon2 스케줄러 scoring 구현 방법

  • BalancedResource 플러그인 디렉토리
    • /home/oslab/eskim/depcon2_k8s/pkg/scheduler/framework/plugins/noderesources/balanced_allocation.go
  • Kubernetes는 기본스케줄러와 함께 제공되고 기본 스케줄러가 필요에 맞지 않는 경우 자체 스케줄러를 구현할 수 있다. 또한, 기본 스케줄러와 함께 여러 스케줄러를 동시에 실행하고 kubernetes에게 각 포드에 사용할 스케줄러를 지시할 수도 있다.

Kubernetes BalancedResource 플러그인 소스코드 수정

  • balanced_allocation.go에서 balancedResourceScorer 함수 수정
    • networkFraction 추가 networkFraction := fractionOfCapacity(requested["example.com/SLO"], allocable["example.com/SLO"])
      • network 비율 = 요청한 Network SLO / 각 서버에서 할당 가능한 network bandwidth
    • networkFraction이 1보다 크면 요청한 network SLO보다 할당 가능한 network bandwidth가 작으면 값을 1로 할당
if networkFraction > 1 {
        networkFraction = 1
}
  • cpu request는 전체 capacity를 넘을 수 있으므로 이러한 경우가 발생 가능함

  • cpu, memory, volume, network에 대한 fraction의 편차를 계산 -> volume이 비율 계산에 포함이 되는 경우

mean := (cpuFraction + memoryFraction + volumeFraction + networkFraction) / float64(4)
variance := float64((((cpuFraction - mean) * (cpuFraction - mean)) + ((memoryFraction - mean) * (memoryFraction - mean)) + ((volumeFraction - mean) * (volumeFraction - mean)) + ((networkFraction - mean) * (networkFraction - mean))) / float64(4))
  • cpu, memory, volume, network에 대한 fraction의 편차를 계산 -> volume이 비율 계산에 포함이 되지 않는 경우
mean := (cpuFraction + memoryFraction + networkFraction) / float64(3)
variance := float64((((cpuFraction - mean) * (cpuFraction - mean)) + ((memoryFraction - mean) * (memoryFraction - mean)) + ((networkFraction - mean) * (networkFraction - mean))) / float64(3))
  • 값을 반환할 때 scoring boundary인 1~10에 포함되도록 설정하여 반환 return int64((1 - variance) * float64(framework.MaxNodeScore))

  • resource_allocation.go에서 defaultRequestedRatioResources 추가

    • resourceToWeightMap에 “example.com/SLO” 를 기본 값인 1로 설정 var defaultRequestedRatioResources = resourceToWeightMap{v1.ResourceMemory: 1, v1.ResourceCPU: 1, "example.com/SLO": 1}

스케줄러 패키징

  • [디렉토리] : /home/oslab/eskim/eskim_git/depcon2_k8s
  • 스케줄러 바이너리를 컨테이너 이미지로 패키징한다. 이 예에서는 기본 스케줄러를 두번째 스케줄러로 사용할 수 있다. -> github의 스케줄러 소스코드를 받아오고 빌드하면 됨.
git clone https://github.com/kubernetes/kubernetes.git
cd kubernetes
make
  • kube-scheduler 바이너리를 포함하는 컨테이너 이미지를 만든다. Dockerfile 이미지를 빌드하는 방법은 다음과 같다.
FROM busybox
ADD ./_output/local/bin/linux/amd64/kube-scheduler /usr/local/bin/kube-scheduler
  • 파일로 저장하고 Dockerfile 이미지를 빌드 docker build -t depcon2-scheduler:v1 .
    • [참고] 본인의 docker hub에 push 하여 사용하는게 좋을듯

스케줄러에 대한 kubernetes 배포 정의

  • 이제 컨테이너 이미지에 스케줄러가 있으므로 이에 대한 포드 구성을 만들고 Kubernetes 클러스터에서 실행한다. 그러나, 클러스터에서 직접 pod를 생성하는 대신 이 예제에 deployment를 사용할 수 있다.
apiVersion: v1
kind: ServiceAccount
metadata:
  name: depcon2-scheduler
  namespace: kube-system
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: depcon2-scheduler-as-kube-scheduler
subjects:
- kind: ServiceAccount
  name: depcon2-scheduler
  namespace: kube-system
roleRef:
  kind: ClusterRole
  name: system:kube-scheduler
  apiGroup: rbac.authorization.k8s.io
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: depcon2-scheduler-as-volume-scheduler
subjects:
- kind: ServiceAccount
  name: depcon2-scheduler
  namespace: kube-system
roleRef:
  kind: ClusterRole
  name: system:volume-scheduler
  apiGroup: rbac.authorization.k8s.io
---
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    component: scheduler
    tier: control-plane
  name: depcon2-scheduler
  namespace: kube-system
spec:
  selector:
    matchLabels:
      component: scheduler
      tier: control-plane
  replicas: 1
  template:
    metadata:
      labels:
        component: scheduler
        tier: control-plane
        version: second
    spec:
      serviceAccountName: depcon2-scheduler
      containers:
      - command:
        - /usr/local/bin/kube-scheduler
        - --address=0.0.0.0
        - --leader-elect=false
        - --scheduler-name=depcon2-scheduler
        image: depcon2-scheduler:v1
        livenessProbe:
          httpGet:
            path: /healthz
            port: 10251
          initialDelaySeconds: 15
        name: depcon2-scheduler
        readinessProbe:
          httpGet:
            path: /healthz
            port: 10251
        resources:
          requests:
            cpu: '0.1'
        securityContext:
          privileged: false
        volumeMounts: []
      hostNetwork: false
      hostPID: false
      volumes: []
  • 여기에서 주목해야 할 중요한 점은 컨테이너 사양에서 스케줄러 명령에 대한 인수로 지정된 스케줄러의 이름이 고유해야 한다는 것이다. -> —scheduler-name= [스케줄러 이름] 이 spec.schedulerName 스케줄러가 특정 Pod의 스케줄링을 담당하는지 여부를 판단하기 위해 pod의 선택적 값과 일치하는 이름이다. 또한, 전용 서비스 계정을 depcon2-scheduler 로 만들고 cluster role system:kube-scheduler 를 kube-scheduler 와 같은 권한을 가지도록 설정한다.

  • serviceaccount를 설정하여 kube-system에 등록된 depcon2-scheduler를 사용할 수 있도록 설정

cd eskim
kubectl create -f serviceaccount.yaml

pod에 대한 스케줄러 지정

  • 이제 두 번째 스케줄러가 실행 중이므로 일부 pod를 만들고 기본 스케줄러 또는 배포한 스케줄러에 의해 스케줄링 되도록 지시한다. 특정 스케줄러를 사용하여 지정된 포드를 예약하려면 해당 포드 사양에서 스케줄러의 이름을 지정한다.
  • 3가지 예가 있다.
    • 스케줄러 이름이 없는 pod는 default-scheduler를 사용하여 자동으로 스케줄링된다.
    • default-scheduler로 지정을 하면 default-scheduler로 설정됨
    • depcon2-scheduler로 지정한 경우 사용자 지정 스케줄러로 스케줄링 됨.
    • p1.yaml
kind: Pod
apiVersion: v1
metadata:
  name: p1
spec:
  schedulerName: depcon2-scheduler
  containers:
    - name: p1
      image: dkdla58/ubuntu:netperf
      resources:
        limits:
          example.com/SLO: 300
        requests:
          cpu: "100m"
      command: ["/bin/bash", "-ec", "while :; do echo '.'; sleep 5 ; done"]
  restartPolicy: Never

새로 생성한 스케줄러로 pod가 스케줄링 되었는지를 확인하는 방법

  • pod 및 deployment 구성 제출 순서를 변경하여 확인할 수 있는데 스케줄러 배포 구성을 제출하기 전에 모든 포드 구성을 kubernetes 클러스터에 제출 하면 다른 두 포드가 스케줄링되는 동안 나머지 하나의 포드가 pending 상태로 남게 된다.
  • 혹은 이벤트 로그에서 “Scheduled” 항목을 보고 원하는 스케줄러에 의해 pod가 스케줄링되었는지를 확인할 수 있다. kubectl get events

DepCon2 스케줄러 동작 테스트

  • 테스트 방법
    • (1) Intel E5-2650 v3 10 cores CPU, 256GB memory를 가지는 서버들 (1,3,4,6,8,10 번 서버)에서 pod가 하나의 서버에 몰리는 현상이 발생하지 않는지를 판단
    • (2) 총 10대의 서버에서 100개의 컨테이너를 생성하고, DepCon agent를 이용했을 때 목표 성능을 모두 달성할 수 있는지 확인
    • (3) pod를 4개의 그룹으로 나눴을 때 2개의 그룹은 64bytes, 2개의 그룹은 1024bytes로 실험하여 네트워크 성능을 모두 달성할 수 있는 지 확인
      • 메시지 크기가 64bytes일 때, 네트워크 목표 성능이 높은 pod가 더 많은 처리를 해야되므로 CPU 사용량이 높아지게 됨.
        • CPU 사용량 높고 network 사용량 적음
      • 메시지 크기가 1024bytes일 때, 네트워크 목표 성능이 높은 pod의 CPU 사용량은 높지 않지만, 네트워크 사용량이 높음
        • CPU 사용량 적고, network 사용량 많음
  • (1) 방법을 이용하여 pod들이 하나의 서버에 몰리지 않는 것을 확인
    • sh cp_pod.sh
      • 100개의 pod yaml 파일을 생성하기 위해 사용
    • sh edit_pnum.sh
      • 한개의 pod를 복사하여 100개의 pod yaml을 만들었기 때문에 이 스크립트를 이용하여 pod 이름을 바꿔줌
    • sh edit.sh
      • SLO 값이 랜덤으로 배정된 b.txt를 이용하여 pod를 p1부터 p100까지 SLO에 할당된 값을 변경해줌
        • SLO는 100Mbps면 100000000으로 설정 필요
      • CPU를 예측하여 값을 넣게 된다면, SLO 값을 랜덤으로 넣고 그 SLO 값에 맞는 예측 CPU를 할당하여 pod를 생성
    • sh create_pod.sh
      • SLO 값이 랜덤으로 설정된 pod들을 생성하는 스크립트
  • 나머지 방법들은 인수인계 받아서 진행하는 측에서 실험 진행

About

컨테이너 환경에서 각 컨테이너의 네트워크 목표 성능 달성을 위해 필요한 CPU 양을 트레이닝을 통해 예측하고, 이로써 네트워크 성능 달성을 가능하게 하는 연구. (DepCon의 확장 연구)

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published