[Kubernetes] Check Performance using Kube Burner
Cloud@Net Cilium Study 1기 과정 중 정리 글입니다.
Intro
Cilium 스터디를 진행하면서 기본적으로 Kubernetes의 성능 테스트 및 최적화의 관점으로 접근이 필요했다. 이번 실습에서는 kube-burner라는 오픈소스 도구의 사용법과 다양한 시나리오를 통해 클러스터 설정을 파악하고 튜닝을 진행한다.
실습환경구성

- KinD(v1.33.2)를 통한 Local 테스트 환경으로 실습을 진행
- kube-ops-view (chart version : 1.2.2 / app version : 20.4.0)
- metrics-server (chart version : 3.13.0 / app version : 0.8.0)
- kube-prometheus-stack (chart version : 77.1.0 / app version : v0.85.0)
실습환경파일은 Github에 Makefile의 Target으로 감싸서 한단계씩 차례대로 배포되도록 구성 (Grafana Dashboard 포함)
github link : https://github.com/iamjjanga-ouo/cilium-lab/tree/main/study/w7
1# KinD 클러스터 구성
2make create-cluster
3# kube-ops-view 배포
4make deploy-kube-ops-view
5# metrics-server 배포
6make deploy-metrics-server
7# kube-prometheus-stack 배포
8make deploy-kube-prometheus-stack
▶ 실습환경 구성
kind Cluster 구성
1# Prometheus Target connection refused bind-address 설정 : kube-controller-manager , kube-scheduler , etcd , kube-proxy
2kind create cluster --name myk8s --image kindest/node:v1.33.2 --config - <<EOF
3kind: Cluster
4apiVersion: kind.x-k8s.io/v1alpha4
5nodes:
6- role: control-plane
7 extraPortMappings:
8 - containerPort: 30000
9 hostPort: 30000
10 - containerPort: 30001
11 hostPort: 30001
12 - containerPort: 30002
13 hostPort: 30002
14 - containerPort: 30003
15 hostPort: 30003
16 kubeadmConfigPatches: # Prometheus Target connection refused bind-address 설정
17 - |
18 kind: ClusterConfiguration
19 controllerManager:
20 extraArgs:
21 bind-address: 0.0.0.0
22 etcd:
23 local:
24 extraArgs:
25 listen-metrics-urls: http://0.0.0.0:2381
26 scheduler:
27 extraArgs:
28 bind-address: 0.0.0.0
29 - |
30 kind: KubeProxyConfiguration
31 metricsBindAddress: 0.0.0.0
32EOF
- kube-ops-view
1# kube-ops-view
2helm repo add geek-cookbook https://geek-cookbook.github.io/charts/
3helm install kube-ops-view geek-cookbook/kube-ops-view --version 1.2.2 --set service.main.type=NodePort,service.main.ports.http.nodePort=30003 --set env.TZ="Asia/Seoul" --namespace kube-system
4#open "http://localhost:30003/#scale=1.5"
5open "http://localhost:30003/#scale=2"
- metrics-server
1# metrics-server
2helm repo add metrics-server https://kubernetes-sigs.github.io/metrics-server/
3helm upgrade --install metrics-server metrics-server/metrics-server --set 'args[0]=--kubelet-insecure-tls' -n kube-system
- kube-prometheus-stack
1cat <<EOT > monitor-values.yaml
2**prometheus**:
3 prometheusSpec:
4 scrapeInterval: "15s"
5 evaluationInterval: "15s"
6 **service**:
7 type: NodePort
8 nodePort: **30001**
9
10**grafana**:
11 defaultDashboardsTimezone: Asia/Seoul
12 adminPassword: **prom-operator**
13 **service**:
14 type: NodePort
15 nodePort: **30002**
16
17**alertmanager:
18 enabled: false
19defaultRules:
20 create: false**
21prometheus-windows-exporter:
22 prometheus:
23 monitor:
24 enabled: false
25EOT
26
27helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
28helm repo update
29
30# 배포
31helm install kube-prometheus-stack prometheus-community/kube-prometheus-stack --version **75.15.1** \
32-f **monitor-values.yaml** --create-namespace --namespace **monitoring**
33
34# 웹 접속 실행
35open http://127.0.0.1:30001 # macOS prometheus 웹 접속
36open http://127.0.0.1:30002 # macOS grafana 웹 접속 ( **admin , prom-operator** )
Kube-burner

“Kube-burner is a Kubernetes performance and scale test orchestration toolset”
쿠버네티스의 성능과 부하테스트를 측정하기 위한 도구 (Golang으로 작성)
✅ 작성일(25년 8월 30)기준에 CNCF의 Sandbox Project 상태
Features
- Create, delete, read, and patch Kubernetes resources at scale.
- Prometheus metric collection and indexing.
- Measurements.
- Alerting.
Installation
설치 방법은 다양하게 지원한다. (Build Manually, Download Binary, Container Image)
가장 간단한 방법으로 Binary를 다운받아서 진행
1# Apple Silicon
2curl -LO https://github.com/kube-burner/kube-burner/releases/download/v1.17.3/kube-burner-V1.17.3-**darwin-arm64**.tar.gz # mac M
3tar -xvf kube-burner-V1.17.3-**darwin-arm64**.tar.gz
4
5# x86
6curl -LO https://github.com/kube-burner/kube-burner/releases/download/v1.17.3/kube-burner-V1.17.3-**linux-x86_64**.tar.gz # Windows
7tar -xvf kube-burner-V1.17.3-**linux-x86_64**.tar.gz
8
9sudo cp kube-burner /usr/local/bin
10
11kube-burner -h
kube-burner은 Kubernetes에 접근하기 위한 인증정보가 필요하고, 다음 순서대로 정보를 읽는다.
KUBE_CONFIG환경 변수$HOME/.kube/config- in-cluster config (kube-burner을 pod에서 사용하는 경우)
(Add) Container Image
만약 부하를 주입하는 환경을 다른 클러스터나 Github Action 등과 같은 환경에서 진행한다면 아래의 컨테이너 환경도 고려해보면 좋을것 같다.
Local 환경에서도 아래와 같이 생성할 경우 충분히 테스트가능하다.
1# for apple silicon Macbook
2docker pull quay.io/kube-burner/kube-burner:v1.17.3-arm64
3
4# make start-kube-burner
5docker run \
6 --rm \
7 --name kube-burner \
8 --entrypoint "/bin/bash" \
9 --volume "./kube-burner:/root/kube-burner" \
10 --volume "${HOME}/.kube/config:/root/.kube/config" \
11 --network "host" \
12 --interactive \
13 --tty \
14 --workdir "/root/kube-burner" \
15 quay.io/kube-burner/kube-burner:v1.17.3-arm64
Configuration
설정을 YAML 포맷으로 설정파일을 구성할 수 있는데, go-template도 일부 지원한다.
Template config file
--user-data옵션을 통해서 User Data 파일을 Input을 주입가능하다.- 환경변수 사용가능
- Secrets를
indexerssection을 통해서 주입하면 매우 유용하다.
1metricsEndpoints:
2{{ if .OS_INDEXING }}
3 - prometheusURL: http://localhost:9090
4 indexer:
5 type: opensearch
6 esServers: ["{{ .ES_SERVER }}"]
7 defaultIndex: {{ .ES_INDEX }}
8{{ end }}
9{{ if .LOCAL_INDEXING }}
10 - prometheusURL: http://localhost:9090
11 indexer:
12 type: local
13 metricsDirectory: {{ .METRICS_FOLDER }}
14{{ end }}
Global Section
Global Section에서는 measurements를 선택하는데 measurements는 간단하게 말해서 어떤 Metrics를 측정할것인지를 정하는 부분이다.
| measurement | description |
|---|---|
| Pod Latency | Pod의 각 조건(Status transition) 에 대한 지연시간(밀리초 단위)을 측정 |
| Job Latency | 쿠버네티스 Job 리소스의 시작 및 완료 지연 시간을 수집 |
| VMI Latency | KubeVirt 환경 등에서 VM/VMI 스타트업 과정의 상세 지연시간을 측정합니다 |
| Node Latency | 클러스터 내 각 Node의 조건(Ready, MemoryPressure, DiskPressure, PIDPressure)에 대해 지연시간 측정 |
| PVC Latency | PVC의 단계(Pending, Bound, Lost)별 상태 변화에 대한 지연시간을 측정 |
| Service Latency | 서비스가 endpoint가 준비된 후 트래픽 처리가 실제로 가능한 시점까지 소요되는 시간을 측정 |
| DataVolume Latency | DataVolume의 상태(Bound, Running, Ready) 변화 지연 측정 |
| VolumeSnapshot Latency | VolumeSnapshot의 Ready 상태 지연 측정 및 퍼센타일 기록 |
| VirtualMachineInstanceMigration Latency | VMI migration 단계별(Pending, Scheduling, Scheduled, PreparingTarget, TargetReady, Running, Succeeded) 지연값 측정 |
| Network Policy Latency | SDN 네트워크 정책 적용 후 실제 트래픽 규칙 반영까지의 소요 시간 측정 |
| PProf Collection | pod 내부 특정 프로세스의 golang pprof 데이터를 주기적으로 수집(concurrent profile) |
Jobs Section
Jobs에서는 kube-burner가 실제 어떻게 실행되는지 정의하는 부분이다.
아래의 시나리오파일을 예시로 들면 다음과 같다.
1# s1-config.yaml
2global:
3 measurements:
4 - name: none
5
6jobs:
7 - name: create-deployments
8 jobType: create
9 jobIterations: 1 # How many times to execute the job , 해당 job을 5번 반복 실행
10 qps: 1 # Limit object creation queries per second , 초당 최대 요청 수 (평균 속도 제한) - qps: 10이면 초당 10개 요청
11 burst: 1 # Maximum burst for throttle , 순간적으로 처리 가능한 요청 최대치 (버퍼) - burst: 20이면 한순간에 최대 20개까지 처리 가능
12 namespace: kube-burner-test
13 namespaceLabels: {kube-burner-job: delete-me}
14 waitWhenFinished: true # false
15 verifyObjects: false
16 preLoadImages: true # false
17 preLoadPeriod: 30s # default 1m
18 objects:
19 - objectTemplate: s1-deployment.yaml
20 replicas: 1
| Option | Description |
|---|---|
| jobType | 실행되는 Job의 유형 (Create, Delete, Read, Patch) |
| jobIterations | Job이 몇번 실행되어야하는지 |
| namespacesIteractions | Job이 반복될때마다 몇개의 Namespace가 생성되어야하는지 |
| namespaceLabels | 생성되는 Namespace에 라벨링 |
| waitWhenFinished | 리소스가 생성이 완료되는것을 기다림 (확인) |
| verifyObjects | 매 Job이 실행된 후 Verifying Object의 갯수 |
| preLoadImages | Job에 필요한 이미지를 사전에 다운받음 |
| preLoadPeriod | PreLoadImages를 얼마나 기다려야하는지 |
Scenario 1] Deployment 1 생성
Deployment 1개 (Pod 1개)를 생성하고 삭제하는 시나리오를 진행한다.
생성
s1-config.yaml
1# s1-config.yaml 2global: 3 measurements: 4 - name: none 5 6jobs: 7 - name: create-deployments 8 jobType: create 9 jobIterations: 1 # How many times to execute the job , 해당 job을 5번 반복 실행 10 qps: 1 # Limit object creation queries per second , 초당 최대 요청 수 (평균 속도 제한) - qps: 10이면 초당 10개 요청 11 burst: 1 # Maximum burst for throttle , 순간적으로 처리 가능한 요청 최대치 (버퍼) - burst: 20이면 한순간에 최대 20개까지 처리 가능 12 namespace: kube-burner-test 13 namespaceLabels: {kube-burner-job: delete-me} 14 waitWhenFinished: true # false 15 verifyObjects: false 16 preLoadImages: true # false 17 preLoadPeriod: 30s # default 1m 18 objects: 19 - objectTemplate: s1-deployment.yaml 20 replicas: 1s1-deployment.yaml
1# s1-deployment.yaml 2apiVersion: apps/v1 3kind: Deployment 4metadata: 5 name: deployment-{{ .Iteration}}-{{.Replica}} 6 labels: 7 app: test-{{ .Iteration }}-{{.Replica}} 8 kube-burner-job: delete-me 9spec: 10 replicas: 1 11 selector: 12 matchLabels: 13 app: test-{{ .Iteration}}-{{.Replica}} 14 template: 15 metadata: 16 labels: 17 app: test-{{ .Iteration}}-{{.Replica}} 18 spec: 19 containers: 20 - name: nginx 21 image: nginx:alpine 22 ports: 23 - containerPort: 80
1# make s1
2kube-burner init -c s1-config.yaml --log-level debug
[Log] Create
1time="2025-08-30 19:14:39" level=info msg="🔥 Starting kube-burner (1.17.3@917540ff45a89386bb25de45af9b96c9fc360e93) with UUID 7cc6d8c1-22e0-4a12-9918-b9c843b504e4" file="job.go:91" 2time="2025-08-30 19:14:39" level=warning msg="Measurement [none] is not supported" file="factory.go:101" 3time="2025-08-30 19:14:39" level=debug msg="job.MaxWaitTimeout is zero in create-deployments, override by timeout: 4h0m0s" file="job.go:361" 4time="2025-08-30 19:14:39" level=info msg="QPS: 1" file="job.go:371" 5time="2025-08-30 19:14:39" level=info msg="Burst: 1" file="job.go:378" 6time="2025-08-30 19:14:39" level=debug msg="Preparing create job: create-deployments" file="create.go:46" 7time="2025-08-30 19:14:39" level=debug msg="Rendering template: s1-deployment.yaml" file="create.go:52" 8time="2025-08-30 19:14:39" level=info msg="Job create-deployments: 1 iterations with 1 Deployment replicas" file="create.go:84" 9time="2025-08-30 19:14:39" level=info msg="Pre-load: images from job create-deployments" file="pre_load.go:73" 10time="2025-08-30 19:14:39" level=debug msg="Created namespace: preload-kube-burner" file="namespaces.go:55" 11time="2025-08-30 19:14:39" level=info msg="Pre-load: Creating DaemonSet using images [nginx:alpine] in namespace preload-kube-burner" file="pre_load.go:195" 12time="2025-08-30 19:14:39" level=info msg="Pre-load: Sleeping for 30s" file="pre_load.go:86" 13time="2025-08-30 19:15:09" level=info msg="Deleting 1 namespaces with label: kube-burner-preload=true" file="namespaces.go:67" 14time="2025-08-30 19:15:09" level=debug msg="Waiting for 1 namespaces labeled with kube-burner-preload=true to be deleted" file="namespaces.go:90" 15time="2025-08-30 19:15:10" level=debug msg="Waiting for 1 namespaces labeled with kube-burner-preload=true to be deleted" file="namespaces.go:90" 16time="2025-08-30 19:15:11" level=debug msg="Waiting for 1 namespaces labeled with kube-burner-preload=true to be deleted" file="namespaces.go:90" 17time="2025-08-30 19:15:12" level=debug msg="Waiting for 1 namespaces labeled with kube-burner-preload=true to be deleted" file="namespaces.go:90" 18time="2025-08-30 19:15:13" level=debug msg="Waiting for 1 namespaces labeled with kube-burner-preload=true to be deleted" file="namespaces.go:90" 19time="2025-08-30 19:15:14" level=debug msg="Waiting for 1 namespaces labeled with kube-burner-preload=true to be deleted" file="namespaces.go:90" 20time="2025-08-30 19:15:15" level=info msg="Triggering job: create-deployments" file="job.go:122" 21time="2025-08-30 19:15:15" level=info msg="0/1 iterations completed" file="create.go:119" 22time="2025-08-30 19:15:15" level=debug msg="Creating object replicas from iteration 0" file="create.go:122" 23time="2025-08-30 19:15:16" level=debug msg="Created namespace: kube-burner-test-0" file="namespaces.go:55" 24time="2025-08-30 19:15:16" level=debug msg="Created Deployment/deployment-0-1 in namespace kube-burner-test-0" file="create.go:288" 25time="2025-08-30 19:15:16" level=info msg="Waiting up to 4h0m0s for actions to be completed" file="create.go:169" 26time="2025-08-30 19:15:17" level=debug msg="Waiting for replicas from Deployment in ns kube-burner-test-0 to be ready" file="waiters.go:152" 27time="2025-08-30 19:15:18" level=info msg="Actions in namespace kube-burner-test-0 completed" file="waiters.go:74" 28time="2025-08-30 19:15:18" level=info msg="Job create-deployments took 3s" file="job.go:191" 29time="2025-08-30 19:15:18" level=info msg="Finished execution with UUID: 7cc6d8c1-22e0-4a12-9918-b9c843b504e4" file="job.go:264" 30time="2025-08-30 19:15:18" level=info msg="👋 Exiting kube-burner 7cc6d8c1-22e0-4a12-9918-b9c843b504e4" file="kube-burner.go:90"
init 이후에 ‘preload-kube-burner’이름의 Namespace가 생성된다. 해당 작업은 1. 생성될 Pod에 대한 사전 Image Pulling을 진행하고 2. preLoadPeriod 시간 이후에 Pod의 배포가 이루어진다.

kube-burner-test-{{ .Iteration }} 이름으로 Namespace가 생성되고, deployment-{{ .Iteration }}-{{ .Replica }} 이름으로 Pod가 생성된다.

아래는 deploy prefix의 이름으로 생성된 리소스를 kube-ops-view로 확인

삭제
생성된 리소스의 Label (kube-burner-job: delete-me )를 통해서 labelSelector를 통해 삭제를 진행한다.
- s1-config-delete.yaml
1# s1-config-delete.yaml 2## deployment 는 s1-deployment.yaml 에 metadata.labels 에 추가한 labels 로 지정 3## namespace 는 config.yaml 에 job.name 값을 labels 로 지정 4jobs: 5 - name: delete-deployments-namespace 6 qps: 500 7 burst: 500 8 namespace: kube-burner-test 9 jobType: **delete** 10 waitWhenFinished: true 11 objects: 12 - kind: **Deployment** 13 labelSelector: {kube-burner-job: delete-me} 14 apiVersion: apps/v1 15 - kind: **Namespace** 16 labelSelector: {kube-burner-job: delete-me}
1# make delete-s1
2kube-burner init -c s1-config-delete.yaml** --log-level debug
[Log] Delete
1time="2025-08-30 19:22:14" level=info msg="🔥 Starting kube-burner (1.17.3@917540ff45a89386bb25de45af9b96c9fc360e93) with UUID ac7d85a3-39d4-4a59-a45e-2a1342b9b73f" file="job.go:91" 2time="2025-08-30 19:22:14" level=debug msg="job.MaxWaitTimeout is zero in delete-deployments-namespace, override by timeout: 4h0m0s" file="job.go:361" 3time="2025-08-30 19:22:14" level=info msg="QPS: 500" file="job.go:371" 4time="2025-08-30 19:22:14" level=info msg="Burst: 500" file="job.go:378" 5time="2025-08-30 19:22:14" level=debug msg="Preparing delete job: delete-deployments-namespace" file="delete.go:33" 6time="2025-08-30 19:22:14" level=debug msg="Job delete-deployments-namespace: delete Deployment with selector kube-burner-job=delete-me" file="delete.go:45" 7time="2025-08-30 19:22:14" level=debug msg="Job delete-deployments-namespace: delete Namespace with selector kube-burner-job=delete-me" file="delete.go:45" 8time="2025-08-30 19:22:14" level=info msg="Triggering job: delete-deployments-namespace" file="job.go:122" 9time="2025-08-30 19:22:14" level=info msg="Found 1 deployments with selector kube-burner-job=delete-me; patching them" file="utils.go:207" 10time="2025-08-30 19:22:14" level=debug msg="Removing Deployment/deployment-0-1 from namespace kube-burner-test-0" file="delete.go:55" 11time="2025-08-30 19:22:14" level=info msg="Found 1 namespaces with selector kube-burner-job=delete-me; patching them" file="utils.go:207" 12time="2025-08-30 19:22:14" level=debug msg="Removing Namespace/kube-burner-test-0" file="delete.go:58" 13time="2025-08-30 19:22:14" level=debug msg="Waiting for 1 namespaces labeled with kube-burner-job=delete-me to be deleted" file="delete.go:79" 14time="2025-08-30 19:22:16" level=debug msg="Waiting for 1 namespaces labeled with kube-burner-job=delete-me to be deleted" file="delete.go:79" 15time="2025-08-30 19:22:18" level=debug msg="Waiting for 1 namespaces labeled with kube-burner-job=delete-me to be deleted" file="delete.go:79" 16time="2025-08-30 19:22:20" level=info msg="Job delete-deployments-namespace took 6s" file="job.go:191" 17time="2025-08-30 19:22:20" level=info msg="Finished execution with UUID: ac7d85a3-39d4-4a59-a45e-2a1342b9b73f" file="job.go:264" 18time="2025-08-30 19:22:20" level=info msg="👋 Exiting kube-burner ac7d85a3-39d4-4a59-a45e-2a1342b9b73f" file="kube-burner.go:90"
[Scenario 2] 다양한 옵션값 확인하기
각 설정에 따라서 측정되는 시간을 계산한다. 각 시나리오마다 측정 후 리소스를 지워(Delete)주어야한다.
scenario 1 - 39.358s
기본적으로 최초로 작성한 시나리오는
- 각 정수 설정값은
jobIteration: 1,qps: 1,burst: 1,replicas: 1 waitWhenFinished: true- 프로비저닝이 된 상태까지 기다림preLoadImages: true,preLoadPeriod: 30s- Job에 사용되는 이미지를 미리 Pulling하고 30초 대기
1# s1-config.yaml
2jobs:
3 - name: create-deployments
4 jobType: create
5 jobIterations: 1
6 qps: 1
7 burst: 1
8 namespace: kube-burner-test
9 namespaceLabels: { kube-burner-job: delete-me }
10 waitWhenFinished: true
11 verifyObjects: false
12 preLoadImages: true
13 preLoadPeriod: 30s
14 objects:
15 - objectTemplate: s1-deployment.yaml
16 replicas: 1
preLoadImages: false - 3.354s
Scenario 1일때 미리 Node에 이미지를 이미 Pulling해서 Pod가 생성되는 시간까지 걸리는 시간이 측정된 값이다.
주요 변경점으로는 preLoadPeriod 로 30초를 대기했던 시간이 제외되었다.
1time (kube-burner init -c s2-config-preloadimage-false.yaml --log-level debug)
1...
2jobs:
3 - name: create-deployments
4 jobType: create
5 jobIterations: 1
6 qps: 1
7 burst: 1
8 namespace: kube-burner-test
9 namespaceLabels: { kube-burner-job: delete-me }
10 waitWhenFinished: true
11 verifyObjects: false
12 **preLoadImages: false # <-**
13 preLoadPeriod: 30s
14 objects:
15 - objectTemplate: s1-deployment.yaml
16 replicas: 1
waitWhenFinished: false - 1.304s
실제 kube-burner가 Kube api-server에 Pod 생성요청을 보내고 Pod 생성한다는 응답을 받은것까지 시간대를 측정한 지표이다.
1time (kube-burner init -c s2-config-waitwhenfinished-false.yaml --log-level debug)
1...
2jobs:
3 - name: create-deployments
4 jobType: create
5 jobIterations: 1
6 qps: 1
7 burst: 1
8 namespace: kube-burner-test
9 namespaceLabels: { kube-burner-job: delete-me }
10 waitWhenFinished: false # <-
11 ...
jobIteration: 5 - 5.265s
위 설정들을 반영하고, Job이 실행되는 반복횟수만 증가시켰다.
Q. 결과값이 이전 시나리오의 시간 1.304s 의 5배인 6.5 로 선형적으로 증가한게 아닌것은?
qps,burst는 각 1로 동시 실행(Parallel Execution)이 아닌 순차적 실행한다 → 시간이 줄어듬에 관계없음- Control Plane에서 캐시(Cache)활용으로 인해서 iteration 간 시간이 줄어들었을 수 있다.
- Kubernetes API 서버는 etcd에서 읽어온 클러스터 상태 정보를 자체적으로 캐시에 저장
- 최근에는 강력한 일관성을 보장하는 캐싱(Consistent Reads from Cache) 방식이 도입되어, 반복적인 오브젝트 리스트 요청이나 상태 확인 작업 시 etcd 접근 부담을 줄이고 응답 속도를 크게 높임
- 스케줄러는 다수의 Pod를 동시/병렬로 처리할 수 있도록 최적화되어 있음.
1time (kube-burner init -c s2-config-jobiteration-5.yaml --log-level debug)
1...
2jobs:
3 - name: create-deployments
4 jobType: create
5 jobIterations: 5 # <-
6...
objects.replicas: 2 - 10.294s
위 설정들을 반영하고, kube-burner의 Object[0].replicas 를 2개로 증가시켰다. → 소요시간이 선형적으로 증가
1time (kube-burner init -c s2-4-config-replicas-2.yaml --log-level debug)
1jobs:
2 - name: create-deployments
3...
4 objects:
5 - objectTemplate: s1-deployment.yaml
6 replicas: 2 # <-
objectTemplate 내의 deployment에 replicase를 2로 수정 - 10.254s
위 설정들을 반영하고, object[0].objectTemplate에서 사용되는 Deployment의 replicas 를 2개로 증가시켰다. → 이전 시나리오와 동일
Q. 이전 시나리오와 소요시간이 거의 동일한 이유?
- ReplicaSet 컨트롤러가 동시에 2개의 Pod을 생성하도록 지시하고, 이 요청은 병렬적으로 API 서버와 스케줄러에서 처리
- 작은 배치(replicas가 2~5 수준인 경우)는 Deployment 컨트롤러가 Pod 생성 작업을 병렬화
- kube-controller-manager의
-concurrent-replicaset-syncs설정이replicas에 따라 병렬적으로 생성할 수 있는 요청 수를 의미 (기본값 5)
1time (kube-burner init -c s2-5-config-deployment-replicas-2.yaml --log-level debug)
1# s2-5-config-deployment-replicas-2.yaml는 s2-4-config-replicas-2.yaml과 거의 동일
2...
3 objects:
4 - objectTemplate: s2-5-deployment.yaml
5 replicas: 2
6
7# s2-5-deployment.yaml
8apiVersion: apps/v1
9kind: Deployment
10metadata:
11 name: deployment-{{ .Iteration}}-{{.Replica}}
12 labels:
13 app: test-{{ .Iteration }}-{{.Replica}}
14 kube-burner-job: delete-me
15spec:
16 replicas: 2 # <-
17...
jobIteration 10 - 20.336s
“1.5.4 objects.replicas: 2”에서 진행했던 설정에서 jobIteration 을 10으로 증가시켰다. → 선형적 증가
- 현재
qps가 1로 설정되어 있어서 ‘생성요청에 대한 Query가 동시 실행을 보장하지 않고 순차적으로 처리를 보내고 있다.
1time (kube-burner init -c s2-6-config-jobiteration-10.yaml --log-level debug)
1jobs:
2 - name: create-deployments
3 jobType: create
4 jobIterations: 10 # <-
5 qps: 1
6 burst: 1
7...
qps: 10, burst: 10 - 1.336s
이전 설정 jobIteration: 10 을 유지하고 qps와 burst 값을 각각 10으로 증가시켰다.
kube-burner의 qps와 burst 설정은 오브젝트 생성/삭제/조회 등의 작업 시 Kubernetes API 요청 속도와 동시 처리량을 제어하는 주요 파라미터
qps(Query Per Seconds) : kube-burner가 초당 Kubernetes API 서버에 보내는 요청 수를 제한burst: 허용된 qps를 단기간에 초과하여 **“버퍼”**처럼 더 빠르게 요청을 보낼 수 있는 최대 요청 수를 지정- qps가 10이고 burst가 20인 경우, 단기적으로 20개의 요청을 한 번에 보낸 뒤, 이후에는 qps 값에 맞춰서 요청이 규제
- API 작업이 불규칙(단일 순간에 대량 요청)이거나, 컨트롤러가 내부적으로 병렬/이벤트드리븐 처리를 하는 경우, burst 값을 높이면 일시적으로 부하를 몰아칠 수 있음
1time (kube-burner init -c s2-7-config-qps-10-burst-10.yaml --log-level debug)
1jobs:
2 - name: create-deployments
3 jobType: create
4 jobIterations: 10
5 qps: 10 # <-
6 burst: 10 # <-
jobIterations: 10, qps: 1, burst:10, objects.replicas: 1 - 1.290s
위에서도 설명했듯이 qps 값이 초당 처리할 수 있는 요청의 수이지만, burst 값 설정으로 단기적으로 요청을 보낼 수 있는 buffer를 두면서 1초에 모든 요청을 다 보낼 수 있다.
jobIterations: 100, qps: 1, burst:100, objects.replicas: 1 - 1.972s
이전 설정과 다르게 jobIteration 과 burst 가 각각 100으로 10배씩 늘어나게되었지만 소요시간은 크게 차이가 없다. (이전 설명과 동일)
하지만, 6개의 Pod가 노드에 배치되지 못하는 FailedScheduling의 상태가 지속되면서 다른 문제가 발생하게 되었다.
👉 해당 문제는 아래의 다른 시나리오에서 확인
1k get pods -A | grep Pending
2kube-burner-test-94 deployment-94-1-6dd7747f86-2zvr5 0/1 Pending 0 47s
3kube-burner-test-95 deployment-95-1-86b6b85f76-2sk8h 0/1 Pending 0 46s
4kube-burner-test-96 deployment-96-1-7d78847694-6prl6 0/1 Pending 0 46s
5kube-burner-test-97 deployment-97-1-7c55d75dd8-nfssn 0/1 Pending 0 46s
6kube-burner-test-98 deployment-98-1-5c9d45476b-jjqjb 0/1 Pending 0 46s
7kube-burner-test-99 deployment-99-1-59cbf448b4-qzz4t 0/1 Pending 0 46s
1# Pending된 리소스 조회
2kubectl describe pod deployment-94-1-6dd7747f86-2zvr5 -n kube-burner-test-94
3...
4Events:
5 Type Reason Age From Message
6 ---- ------ ---- ---- -------
7 Warning FailedScheduling 60s default-scheduler 0/1 nodes are available: 1 Too many pods. preemption: 0/1 nodes are available: 1 No preemption victims found for incoming pod.


jobIterations: 10, qps: 1, burst:10, objects.replicas: 2 - 11.240s
총 20개의 요청 (JobIterations : 10 x objects.replicas : 2 = 20)에 대해서 burst : 10이라는 값에 의해서 최초 10개의 요청은 동시에 처리가 되나, 이후 11번째의 요청부터는 qps 값 (1)로 1개씩 처리되게 된다.
jobIterations: 10, qps: 1, burst:20, objects.replicas: 2 - 1.112s
총 20개의 요청 (JobIterations : 10 x objects.replicas : 2 = 20)에 대해서 burst : 20으로 최초의 모든 요청을 한번에 처리할 수 있다.
jobIterations: 20, qps: 2, burst:20, objects.replicas: 2 - 10.701s
총 40개의 요청 (JobIterations : 20 x objects.replicas : 2 = 40)에 대해서 burst : 20으로 최초 20개의 요청을 처리하고 qps 값이 2로 매 초당 2회씩 처리하게된다.
[Scenario 3-1] 노드 1대에 최대 Pod 이상 배포 시도
노드는 배포가능한 최대 Pod의 갯수가 있다. 해당 설정을 확인하고 더 확장해서 배포가능한 시나리오를 테스트한다.
jobIterations: 100, qps: 300, burst: 300, objects.replicas: 1


- 노드의 기본적으로 생성가능한
maxPods수는 110개 - 최초 클러스터를 생성하고 배포된 Pod들 (kube-system Namespace에 배포된)부분을 제외하면 약 100개
- 현재 요청수는 150개의 Pod로 임계점을 초과한다.
1# 배포되지 못한 Pod 조회
2kubectl get pods -A --field-selector=status.phase=Pending
3
4# 배포되지 못한 Pod Event 조회
5kubectl get pods -A --field-selector=status.phase=Pending --no-headers | awk 'NR==1 {system("kubectl describe pod -n "$1" "$2" | grep Events -A5")}'
6
7Events:
8 Type Reason Age From Message
9 ---- ------ ---- ---- -------
10 Warning FailedScheduling 12m default-scheduler 0/1 nodes are available: 1 Too many pods. preemption: 0/1 nodes are available: 1 No preemption victims found for incoming pod.
11 Warning FailedScheduling 2m41s (x2 over 7m41s) default-scheduler 0/1 nodes are available: 1 Too many pods. preemption: 0/1 nodes are available: 1 No preemption victims found for incoming pod.
1# 노드 설정값 확인
2kubectl describe node
3Capacity:
4 cpu: 11
5 ephemeral-storage: 85003316Ki
6 memory: 9205592Ki
7 pods: 110
8Allocatable:
9 cpu: 11
10 ephemeral-storage: 85003316Ki
11 memory: 9205592Ki
12 pods: 110
- Capacity : 서버(노드)가 보유한 물리적 또는 가상 자원의 총량 (CPU 코어 수, 전체 메모리 용량, 스토리지 총량)
- Allocatable : 시스템 데몬(kubelet, OS 등)이 사용하는 리소스를 제외하고, 오직 파드에만 할당 가능한 자원
- Capacity에서 kube-reserved, system-reserved, eviction 등으로 예약된 값을 제외한 나머지 리소스
- 스케줄러가 새로운 파드를 배치할 때 Allocatable 값을 기준으로 적합한 노드를 결정
현재 배포되지 못하는 문제를 해결하기 위해서는 kubelet의 설정을 변경해야한다.
1# maxPods가 조회되지 않으면 110개 (기본값)
2kubectl get cm -n kube-system kubelet-config -o yaml | grep maxPods
3
4# KinD Control Plane으로 접속
5docker exec -it myk8s-control-plane bash
6
7## 백업
8cp /var/lib/kubelet/config.yaml /var/lib/kubelet/config.yaml.bak
9
10## maxPods 추가 및 kubelet 데몬 재시작
11echo 'maxPods: 200' >> /var/lib/kubelet/config.yaml
12systemctl restart kubelet
13systemctl status kubelet
14
15# 아래 명령어로는 확인이 불가능하다.
16# /var/lib/kubelet/config.yaml의 maxPods 값을 수정해도 ConfigMap(kubelet-config)에는 나타나지 않고, 오직 노드의 kubelet 프로세스만 해당 변경을 사용
17~~kubectl get cm -n kube-system kubelet-config -o yaml | grep maxPods~~
maxPods를 200개로 늘리면 150개의 Pod가 정상적으로 배포된걸 확인할 수 있다.


[Scenario 3-2] podCIDR를 넘는 숫자로 배포 요청
이전 시나리오에서는 Node 할당가능한 Pod 갯수를 200으로 늘렸지만, 실제로는 더 큰값으로 지정할 수 있다. 하지만 Pod가 배포되기 위해서는 해당 설정만 고려해야하는것이 아닌 실제 Pod가 클러스터 내부에서 할당 받는 “PodCIDR”의 범위에 대한 고려도 해야한다.
maxPods 값을 400으로 늘려서 300대의 Pod를 요청하는 테스트를 진행한다.
- jobIterations: 300, qps: 300, burst: 300 objects.replicas: 1
1# maxPods값 변경
2sed -i 's/maxPods:\ 200/maxPods:\ 400/' /var/lib/kubelet/config.yaml
3systemctl restart kubelet
4systemctl status kubelet
1# make s3-2
2kube-burner init -c s3-2-config.yaml --log-level debug
예상할 수 있듯 maxPods 값을 최대로 증가 시켰으나, 정상적으로 배포되지 못한 Pod들이 존재한다. 원인은 PodCIDR의 부족으로 Pod 생성이 실패했다.


1# 배포에 실패한 Pod 갯수
2kubectl get pods -A --field-selector=status.phase=Pending --no-headers | wc -l
3 56
4
5# 배포 실패 이유 확인 -> PodCIDR 부족
6kubectl get pods -A --field-selector=status.phase=Pending --no-headers | awk 'NR==1 {system("kubectl describe pod -n "$1" "$2" | grep Events -A5")}'
7Events:
8 Type Reason Age From Message
9 ---- ------ ---- ---- -------
10 Normal Scheduled 9m7s default-scheduler Successfully assigned kube-burner-test-232/deployment-232-1-8dbddf5fb-dr2fn to myk8s-control-plane
11 Warning FailedCreatePodSandBox 8m27s kubelet Failed to create pod sandbox: rpc error: code = Unknown desc = failed to setup network for sandbox "60a12099bcf620ca35976b1f303df9ba2c78720af7b93fb72af9d7c8b18ca18f": plugin type="ptp" failed (add): failed to allocate for range 0: no IP addresses available in range set: 10.244.0.1-10.244.0.254
12
13# 현재 Cluster의 PodCIDR 조회
14kubectl describe node myk8s-control-plane | grep -i podcidr
15
16PodCIDR: 10.244.0.0/24
17PodCIDRs: 10.244.0.0/24