Kubernetes

Cilium Study [1기] (7주차) - Jmeter를 이용한 K8S 부하테스트

yu3papa 2025. 8. 29. 09:11

Kubernetes API Server는 클러스터의 핵심 구성요소로 사용자의 Request를 전달 받아 API Resource 를 생성, 수정, 삭제, 조회하는 일을 수행합니다. 이번 블로그에서는 K8S API Server에 부하를 발생하여 성능을 측정하거나, 여러가지 장애상황을 재현해보겠습니다.

0. 실습환경 구성

  • Rocky Linux 9.6 (커널버전 : 5.14.0-570.26.1.el9_6.x86_64)
  • K8S 클러스터는 kind를 이용
# Prometheus Target connection refused bind-address 설정 : kube-controller-manager , kube-scheduler , etcd , kube-proxy
kind create cluster --name myk8s --image kindest/node:v1.33.2 --config - <<EOF
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
  extraPortMappings:
  - containerPort: 30000
    hostPort: 30000
  - containerPort: 30001
    hostPort: 30001
  - containerPort: 30002
    hostPort: 30002
  - containerPort: 30003
    hostPort: 30003
  kubeadmConfigPatches: # Prometheus Target connection refused bind-address 설정
  - |
    kind: ClusterConfiguration
    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
EOF
Creating cluster "myk8s" ...
 ✓ Ensuring node image (kindest/node:v1.33.2) 🖼
 ✓ Preparing nodes 📦
 ✓ Writing configuration 📜
 ✓ Starting control-plane 🕹️
 ✓ Installing CNI 🔌
 ✓ Installing StorageClass 💾
Set kubectl context to "kind-myk8s"
You can now use your cluster with:

kubectl cluster-info --context kind-myk8s

Not sure what to do next? 😅  Check out https://kind.sigs.k8s.io/docs/user/quick-start/

# kube-ops-view
helm repo add geek-cookbook https://geek-cookbook.github.io/charts/
helm 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
NAME: kube-ops-view
LAST DEPLOYED: Fri Aug 29 09:23:05 2025
NAMESPACE: kube-system
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
1. Get the application URL by running these commands:
  export NODE_PORT=$(kubectl get --namespace kube-system -o jsonpath="{.spec.ports[0].nodePort}" services kube-ops-view)
  export NODE_IP=$(kubectl get nodes --namespace kube-system -o jsonpath="{.items[0].status.addresses[0].address}")
  echo http://$NODE_IP:$NODE_PORT

# open "http://localhost:30003/#scale=1.5"


# metrics-server
helm repo add metrics-server https://kubernetes-sigs.github.io/metrics-server/
helm upgrade --install metrics-server metrics-server/metrics-server --set 'args[0]=--kubelet-insecure-tls' -n kube-system
NAME: metrics-server
LAST DEPLOYED: Fri Aug 29 09:25:39 2025
NAMESPACE: kube-system
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
***********************************************************************
* Metrics Server                                                      *
***********************************************************************
  Chart version: 3.13.0
  App version:   0.8.0
  Image tag:     registry.k8s.io/metrics-server/metrics-server:v0.8.0
***********************************************************************

# 확인
kubectl top node
NAME                  STATUS   ROLES           AGE     VERSION
myk8s-control-plane   Ready    control-plane   4m49s   v1.33.2

kubectl top pod -A --sort-by='cpu'
NAMESPACE            NAME                                          CPU(cores)   MEMORY(bytes)
kube-system          kube-apiserver-myk8s-control-plane            25m          202Mi
kube-system          etcd-myk8s-control-plane                      14m          31Mi
kube-system          kube-controller-manager-myk8s-control-plane   10m          56Mi
kube-system          kube-ops-view-6658c477d4-rrgwh                7m           31Mi
kube-system          kube-scheduler-myk8s-control-plane            7m           22Mi
kube-system          metrics-server-5dd7b49d79-77jql               3m           19Mi
kube-system          coredns-674b8bbfcf-5gbkt                      1m           17Mi
kube-system          coredns-674b8bbfcf-w4b4q                      1m           17Mi
kube-system          kindnet-rj6x5                                 1m           10Mi
kube-system          kube-proxy-ndlnp                              1m           17Mi
local-path-storage   local-path-provisioner-7dc846544d-m5m59       1m           9Mi

kubectl top pod -A --sort-by='memory'
NAMESPACE            NAME                                          CPU(cores)   MEMORY(bytes)
kube-system          kube-apiserver-myk8s-control-plane            27m          202Mi
kube-system          kube-controller-manager-myk8s-control-plane   11m          56Mi
kube-system          kube-ops-view-6658c477d4-rrgwh                9m           31Mi
kube-system          etcd-myk8s-control-plane                      14m          31Mi
kube-system          kube-scheduler-myk8s-control-plane            6m           22Mi
kube-system          metrics-server-5dd7b49d79-77jql               4m           20Mi
kube-system          coredns-674b8bbfcf-5gbkt                      1m           17Mi
kube-system          coredns-674b8bbfcf-w4b4q                      1m           17Mi
kube-system          kube-proxy-ndlnp                              1m           17Mi
kube-system          kindnet-rj6x5                                 1m           10Mi
local-path-storage   local-path-provisioner-7dc846544d-m5m59       1m           9Mi
  • prometheus-stack 설치 by helm
# 파라미터 파일 생성
cat <<EOT > monitor-values.yaml
prometheus:
  prometheusSpec:
    scrapeInterval: "15s"
    evaluationInterval: "15s"
  service:
    type: NodePort
    nodePort: 30001

grafana:
  defaultDashboardsTimezone: Asia/Seoul
  adminPassword: prom-operator
  service:
    type: NodePort
    nodePort: 30002

alertmanager:
  enabled: false
defaultRules:
  create: false
prometheus-windows-exporter:
  prometheus:
    monitor:
      enabled: false
EOT

helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm repo update

# 배포
helm install kube-prometheus-stack prometheus-community/kube-prometheus-stack --version 75.15.1 \
-f monitor-values.yaml --create-namespace --namespace monitoring

# 웹 접속 실행 - prometheus
#     http://127.0.0.1:30001

# 웹 접속 실행 - grafana ( admin / prom-operator )
#     http://127.0.0.1:30002

# 대시보드 추가
- K8S new : 15661
- K8S API old : 12006

 

1. K8S API Server

K8S 클러스터를 관통하는 2가지의 용어는 아래와 같이 요약할 수 있을거 같습니다.

  • Desigred State
  • Declarative
K8S 클러스터는 사용자가 요청한 Desigred State를 Declarative 하게 선언만 하면, 그 후의 뒷단의 일은 K8S 구성요소가 알아서 Desigred State를 만족하려고 노력합니다.

 

위와 같은 작업을 하기 위해 "Hub and Spoke" 패턴을 이용합니다.

https://tech.kakaopay.com/post/jack-k8s-internals-part-1/
https://tech.kakaopay.com/post/jack-k8s-internals-part-2/

위 그림에서 볼수 있듯이 API Server 가 Hub가 되어다른 구성요소와 watch 메커니즘을 이용하여, Desigred State를 만족하기 위해 동작하기 때문에 API 서버의 부하가 집중되는 구조입니다.

kubectl 동작 이해하기

"kubectl get pod -A" 명령을 수행하면 K8S API Server와 인증 및 REST 통신을 하게 됩니다.

kubectl get po -A -v 6
I0829 10:01:40.041838  174498 loader.go:402] Config loaded from file:  /root/.kube/config
I0829 10:01:40.042205  174498 envvar.go:172] "Feature gate default state" feature="ClientsAllowCBOR" enabled=false
I0829 10:01:40.042227  174498 envvar.go:172] "Feature gate default state" feature="ClientsPreferCBOR" enabled=false
I0829 10:01:40.042231  174498 envvar.go:172] "Feature gate default state" feature="InformerResourceVersion" enabled=false
I0829 10:01:40.042236  174498 envvar.go:172] "Feature gate default state" feature="InOrderInformers" enabled=true
I0829 10:01:40.042239  174498 envvar.go:172] "Feature gate default state" feature="WatchListClient" enabled=false
I0829 10:01:40.054029  174498 round_trippers.go:632] "Response" verb="GET" url="https://127.0.0.1:45559/api/v1/pods?limit=500" status="200 OK" milliseconds=9
NAMESPACE            NAME                                                        READY   STATUS    RESTARTS   AGE
kube-system          coredns-674b8bbfcf-5gbkt                                    1/1     Running   0          39m
kube-system          coredns-674b8bbfcf-w4b4q                                    1/1     Running   0          39m
kube-system          etcd-myk8s-control-plane                                    1/1     Running   0          40m
kube-system          kindnet-rj6x5                                               1/1     Running   0          39m
kube-system          kube-apiserver-myk8s-control-plane                          1/1     Running   0          39m
kube-system          kube-controller-manager-myk8s-control-plane                 1/1     Running   0          39m
kube-system          kube-ops-view-6658c477d4-rrgwh                              1/1     Running   0          38m
kube-system          kube-proxy-ndlnp                                            1/1     Running   0          39m
kube-system          kube-scheduler-myk8s-control-plane                          1/1     Running   0          40m
kube-system          metrics-server-5dd7b49d79-77jql                             1/1     Running   0          36m
local-path-storage   local-path-provisioner-7dc846544d-m5m59                     1/1     Running   0          39m
monitoring           kube-prometheus-stack-grafana-7d9c86798d-gnjck              3/3     Running   0          30m
monitoring           kube-prometheus-stack-kube-state-metrics-684f8c7558-rcbks   1/1     Running   0          30m
monitoring           kube-prometheus-stack-operator-68589744f5-rlrkg             1/1     Running   0          30m
monitoring           kube-prometheus-stack-prometheus-node-exporter-bb2qj        1/1     Running   0          30m
monitoring           prometheus-kube-prometheus-stack-prometheus-0               2/2     Running   0          29m

위의 호출 결과를 보면

  • 먼저 kube config 파일을 읽어와 API Server와 클라이언트 인증서를 통한 인증(디폴트)을 통과한 후에
  • POD 목록을 조회하는 GET 요청을 하고 있습니다.

Jmeter를 이용하여 부하발생을 위해서 "클라이언트 인증서"가 아닌 Bearear Token 을 이용한 인증을 이용하여 curl 명령으로 POD 목록을 조회하여 보겠습니다.

# jmeter-sa 서비스어카운트 생성
kubectl create sa jmeter-sa
serviceaccount/jmeter-sa created

# 레거시 시크릿(Secret) 생성:
#    ServiceAccount에 바인딩될 kubernetes.io/service-account-token 유형의 시크릿을 생성
#    이 시크릿은 만료 기간이 없는 토큰을 포함하게 됩니다.

kubectl apply -f - <<EOF
apiVersion: v1
kind: Secret
metadata:
  name: jmeter-sa-token
  annotations:
    kubernetes.io/service-account.name: jmeter-sa
type: kubernetes.io/service-account-token
EOF
secret/jmeter-sa-token created


# 토큰 값 확인: 생성된 시크릿에서 실제 토큰 값을 Base64 디코딩하여 확인
kubectl get secret jmeter-sa-token -o jsonpath='{.data.token}' | base64 --decode
eyJhbGciOiJSUzI1NiIsImtpZCI6IkJqUVhyV2o3c1JETWczZlZMQVpjendIcmxTWlZsQlA3bUJTNzJPbEN3dkEifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJkZWZhdWx0Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZWNyZXQubmFtZSI6ImptZXRlci1zYS10b2tlbiIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VydmljZS1hY2NvdW50Lm5hbWUiOiJqbWV0ZXItc2EiLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC51aWQiOiIyODAxMjZhNy03NWQxLTQ0YTYtYmJkYy1lNDAwYzUzNzJhZjkiLCJzdWIiOiJzeXN0ZW06c2VydmljZWFjY291bnQ6ZGVmYXVsdDpqbWV0ZXItc2EifQ.p-EMrvuH9kVsWg1VtPdpsNqGTSUjja0AwQlNDHT0-yRLfiCOeY40rMDZCN0Lxn0T1mb8yw6SkC1CQTaZQHoZci8O7-sJE2yKJBKhdUzKufJt9kZ6sPsvUIVmxu5ymgG-RR73UqwWOWlBgh4oe8oDvbpOjr_fZBm1NTaOA7oc2oGeet3HboXMwtEtFB1JsUc12zRow7FmU_qBcTaa00TLfUAEj1kWle5Q1HAc_IaV-LOsedlA3Q1z1syu4GeNnlsOe2YAnQOA6fhZUXzJj8JK5ZNUxRoOoKDJrd9YMFnafYMEDlvnGjGgriFHlN4A-BHUCg2Y8YJLu_iz5_GvttVCEQ

TOKEN=$(kubectl get secret jmeter-sa-token -o jsonpath='{.data.token}' | base64 --decode)

# curl 명령을 이용한 POD 목록 조회
curl -k -H "Authorization: Bearer $TOKEN" https://127.0.0.1:45559/api/v1/pods
"kind": "PodList",
  "apiVersion": "v1",
  "metadata": {
    "resourceVersion": "5534"
  },
  "items": [
    {
      "metadata": {
        "name": "coredns-674b8bbfcf-5gbkt",
        "generateName": "coredns-674b8bbfcf-",
        "namespace": "kube-system",
...(생략)...

 

2. Jmeter

Jmeter 는 Java 기반의 오픈소스 부하도구로 다양한 프로토콜 및 플러그인을 지원하여 상용 부하테스트 도구가 할 수 있는 많은 부분을 대체하고 있습니다.

2.1.  jmeter 설치

jmeter는 JAVA Runtime 환경이 있으면 실행이 가능합니다. 공식적으로 JAVA 8 이상을 요구합니다만, JAVA 17 이상을 추천하고 있습니다.

# JAVA 버전 확인
java -version
java version "24.0.2" 2025-07-15
Java(TM) SE Runtime Environment (build 24.0.2+12-54)
Java HotSpot(TM) 64-Bit Server VM (build 24.0.2+12-54, mixed mode, sharing)

# jmeter 다운로드 및 압축해제
wget https://dlcdn.apache.org//jmeter/binaries/apache-jmeter-5.6.3.tgz
tar xfz apache-jmeter-5.6.3.tgz
ln -s apache-jmeter-5.6.3 jmeter

# jmeter 실행
jmeter/bin/jmeter.sh > /dev/null 2>&1 &


 

2.2. jmeter 를 이용한 POD 목록 조회 스크립트 작성

jmeter 는 부하스크립트를 코딩으로 하지 않고 아래와 같은 jmeter 블럭 구성요소를 계층적으로 구성하는 블록코딩 형식을 이용합니다.

HTTP Request Defaults 추가

  • 모든 HTTP Request  의 디폴트 값 설정
  • Test Plan -> Config Element -> HTTP Request Defaults
  • 여기에서는 K8S API Server 의 프로토콜, IP, PORT 를 아래와 같이 설정

 

User Defined Variables 추가

  • 사용자 정의 변수 이용 -> 이번 실습에서는 API Server TOKEN 값을 변수로 저장하여 사용합니다.
  • Test Plan -> Config Element -> HTTP Request Defaults

 

Thread Group 추가

  • Jmeter는 가상유저를 JAVA Thread를 이용합니다.
  • Test Plan -> Threads (Users) -> Thread Group

 

View Results Tree 리스너 추가

  • View Results Tree 리스너는 HTTP Request와 Response 내용을 관찰하고 디버깅 용도로 사용합니다.
  • Thread Group -> Listener -> View Results Tree

Transaction Controller 추가

  • Transaction Controller 구성요소는 TPS 를 측정하는 구성요소 입니다.
  • Thread Group -> Logic Controller -> Transaction Controller
    • 추가 후 "Generate parent sample" 을 체크합니다.

HTTP Request 샘플러 추가

  • 샘플러 종류는 실제 Request를 수행하는 구성요소 입니다.
  • Thread Group -> Transaction Controller -> Sampler -> HTTP Request 
    • path 항목에 POD 목록 조회하는 Endpoint 주소를 기입합니다.
      • /api/v1/pods

HTTP Header Manager 추가

  • http 프로토콜의 Header 정보를 관리하는 구성요소입니다. 여기에서는 TOKEN 정보를 기입합니다.
  • HTTP Request -> Config Element -> HTTP Header Manager

 

부하테스트

  • 1명의 가상유저로 POD 목록 조회하는 Request 1회 호출
  • 툴바의 (start)버튼 클릭
  • View Resutl Tree 구성요소의 Request, Reponse 정보를 확인해 보면 POD 목록이 json 형태로 리턴된것을 볼 수 있습니다.

 

지금까지의 Jmeter 스크립트 파일을 아래에 첨부하였습니다.

K8S-001.jmx
0.01MB

 

 

2.3. jmeter 플러그인 설치

jmeter 는 플러그인 아키텍처를 이용합니다. 그래서 새로운 IT 기술에 대응하는 플러그인이 다양하게 존재합니다.

부하량을 제어하고 다양한 리스너를 이용하기위해서 플러그인을 설치해 보겠습니다.

 

JMeter Plugins :: JMeter-Plugins.org

 

jmeter-plugins.org

 

# 플러그인 매니저 다운로드

wget -O jmeter/lib/ext/jmeter-plugins-manager-1.11.jar https://repo1.maven.org/maven2/kg/apc/jmeter-plugins-manager/1.11/jmeter-plugins-manager-1.11.jar

# jmeter 재시작


#    플러그인 매니저 시작


# 플러그인 설치
#    jpgc - Standard Set
#    vdn@github - pacing-jmeter-plugin


 

부하량 제어 및 Test Plan 구조화

부하량 제어

  • 부하량을 제어 하지 않으면 Request 를 보내고, Response 를 받은후 바로 또다시 Request를 보내기 때문에 순간적으로 과부하가 발생하게 됩니다.
  • 그래서 부하테스트 도구들을 Pacing Time 이라는 개념을 이용해서 몇초에 한번씩 Request를 보낼지 설정하여 과부하를 방지하고, 더욱 중요한것은 단위시간당 원하는 부하량을 제어 할 수 있습니다.
  • 아래 링크를 참조해서 POD 목록 조회하는 부하를 1초에 한번으로 조정합니다.
 

GitHub - vdaburon/pacing-jmeter-plugin: Add notion of Pacing for Apache JMeter

Add notion of Pacing for Apache JMeter. Contribute to vdaburon/pacing-jmeter-plugin development by creating an account on GitHub.

github.com

기능이 강화된 Listener 추가

부하 발생후 모니터링을 위해 아래 Listener 를 추가합니다.

  • Test Plan -> Listener ->
    • jp@gc - Active Threads Over Time
    • jp@gc - Response Times Over Time
    • jp@gc - Transactions per Second
    • Summary Report

Test Fragment 추가

  • Test Fragment 구성요소는 Test Plan을 구조화 하는 용도로 사용합니다.
  • Test Plan -> Test Fragment -> Test Fragment

 

Test Plan 구조화

지금까지 작업한 구성요소의 이름을 수정하고  Test Fragement 를 이용하여 아래와 같은 모습으로 구조화 합니다.

부하량 조정

"TG_POD 조회" Thread Group 의 설정을 아래와 같이 조정합니다.

10 명의 가상유저가 60초동안 점진적으로 증가시키고 300초 동안 부하를 발생

 

부하 테스트 시작

  • 툴바의  (start)버튼 클릭하고 각종 리스너의 그래프 및 그라파나 패널 모니터링

가상유저

 

응답시간

 

TPS

 

그라파나 대시보드 - Kubernetes apiserver

 

2.4. jmeter 를 이용한 POD 생성 스크립트 작성

kubectl 을 이용하여 POD를 생성할때 API 서버에 POST 방식으로 어떤 JSON 정보가 전달되는지 확인해 보겠습니다.

# 명령행으로 my-pod 생성하고 POST 방식으로 전달되는 JSON 정보 확인
kubectl run my-pod --image=registry.k8s.io/pause:3.9 -v 8
I0829 12:56:20.177284  179852 loader.go:402] Config loaded from file:  /root/.kube/config
I0829 12:56:20.177413  179852 envvar.go:172] "Feature gate default state" feature="ClientsAllowCBOR" enabled=false
I0829 12:56:20.177423  179852 envvar.go:172] "Feature gate default state" feature="ClientsPreferCBOR" enabled=false
I0829 12:56:20.177428  179852 envvar.go:172] "Feature gate default state" feature="InformerResourceVersion" enabled=false
I0829 12:56:20.177432  179852 envvar.go:172] "Feature gate default state" feature="InOrderInformers" enabled=true
I0829 12:56:20.177436  179852 envvar.go:172] "Feature gate default state" feature="WatchListClient" enabled=false
I0829 12:56:20.179835  179852 helper.go:246] "Request Body" body=<
        {"kind":"Pod","apiVersion":"v1","metadata":{"name":"my-pod","creationTimestamp":null,"labels":{"run":"my-pod"}},"spec":{"containers":[{"name":"my-pod","image":"registry.k8s.io/pause:3.9","resources":{}}],"restartPolicy":"Always","dnsPolicy":"ClusterFirst"},"status":{}}
 >
I0829 12:56:20.179893  179852 round_trippers.go:527] "Request" verb="POST" url="https://127.0.0.1:45559/api/v1/namespaces/default/pods?fieldManager=kubectl-run" headers=<
        Accept: application/json, */*
        Content-Type: application/json
        User-Agent: kubectl/v1.33.4 (linux/amd64) kubernetes/74cdb42
 >
I0829 12:56:20.191233  179852 round_trippers.go:632] "Response" status="201 Created" headers=<
        Audit-Id: 8cbf45b0-ed66-4f28-83af-e5d8e352a043
        Cache-Control: no-cache, private
        Content-Length: 2019
        Content-Type: application/json
        Date: Fri, 29 Aug 2025 03:56:20 GMT
        X-Kubernetes-Pf-Flowschema-Uid: 9ab9faed-f679-4867-840e-5300ce24dfed
        X-Kubernetes-Pf-Prioritylevel-Uid: 3580616c-532a-4a2d-9fee-4fd4f2bcf96c
 > milliseconds=11
I0829 12:56:20.191318  179852 helper.go:246] "Response Body" body="{\"kind\":\"Pod\",\"apiVersion\":\"v1\",\"metadata\":{\"name\":\"my-pod\",\"namespace\":\"default\",\"uid\":\"d760e73b-d72c-426c-92dc-7170b6bb6418\",\"resourceVersion\":\"18115\",\"generation\":1,\"creationTimestamp\":\"2025-08-29T03:56:20Z\",\"labels\":{\"run\":\"my-pod\"},\"managedFields\":[{\"manager\":\"kubectl-run\",\"operation\":\"Update\",\"apiVersion\":\"v1\",\"time\":\"2025-08-29T03:56:20Z\",\"fieldsType\":\"FieldsV1\",\"fieldsV1\":{\"f:metadata\":{\"f:labels\":{\".\":{},\"f:run\":{}}},\"f:spec\":{\"f:containers\":{\"k:{\\\"name\\\":\\\"my-pod\\\"}\":{\".\":{},\"f:image\":{},\"f:imagePullPolicy\":{},\"f:name\":{},\"f:resources\":{},\"f:terminationMessagePath\":{},\"f:terminationMessagePolicy\":{}}},\"f:dnsPolicy\":{},\"f:enableServiceLinks\":{},\"f:restartPolicy\":{},\"f:schedulerName\":{},\"f:securityContext\":{},\"f:terminationGracePeriodSeconds\":{}}}}]},\"spec\":{\"volumes\":[{\"name\":\"kube-api-access-bk2dx\",\"projected\":{\"sources\":[{\"serviceAccountToken\":{\"expirationSeconds\":3607,\"path\":\"token\"}},{\"configMap\":{\"name\":\"kube-root-ca.crt\",\"items\":[{\"key\":\"ca.crt\",\"path\":\"ca.crt\"}]}},{\"downwardAPI\":{\"items\":[{\"pa [truncated 995 chars]"
pod/my-pod created

 

TG_POD 조회는 disable 시키고 아래 모습으로 새로운 Thread Group을 구성합니다.

HTTP Request 내용은 아래와 같이 설정합니다.

  • POST
  • path: /api/v1/namespaces/default/pods
  • Body Data
{
  "kind": "Pod",
  "apiVersion": "v1",
  "metadata": {
    "name": "my-pod",
    "creationTimestamp": null,
    "labels": {
      "run": "my-pod"
    }
  },
  "spec": {
    "containers": [
      {
        "name": "my-pod",
        "image": "registry.k8s.io/pause:3.9",
        "resources": {}
      }
    ],
    "restartPolicy": "Always",
    "dnsPolicy": "ClusterFirst"
  },
  "status": {}
}

 

POST 방식으로 JSON 을 전달하기 위해서는 Http Header 에 "Content-Type: application/json"을 추가하여야 합니다.

부하를 발생시키면 default 네임스페이스에 my-pod 가 이미 존재하여 에러가 발생합니다.

 

이문제를 해결하기 위해 전달되는 JSON의 POD name에 ${__UUID} 추가 합니다.

부하량 조정

"TG_POD 생성" Thread Group 의 설정을 아래와 같이 조정합니다.

10 명의 가상유저가 60초동안 점진적으로 증가시키고 300초 동안 부하를 발생

 

부하 테스트 시작

  • 툴바의  (start)버튼 클릭하고 각종 리스너의 그래프 및 그라파나 패널 모니터링

 

 

 

# 현재 default 네임스페이스에 총 2700개의 POD가 생성되어 있습니다.
kubectl get po | wc -l
2731

# 이중에 Pending 상태의 POD가 2637 개입니다.
kubectl get po --field-selector status.phase=Pending | wc -l
2637

# Pending 상태인 이유를 알아보면 가용한 노드가 없어서입니다.
k describe po my-pod-ffde8d75-1a24-4c8e-bae5-4ee15b701749
...(생략)...
Events:
  Type     Reason            Age    From               Message
  ----     ------            ----   ----               -------
  Warning  FailedScheduling  4m16s  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.

 

지금까지의 Jmeter 스크립트 파일을 아래에 첨부하였습니다.

K8S-002.jmx
0.02MB

3. 추가 연구 목록

이번 블로그에서는 ServiceAccount 를 이용하여 인증을 통과한 후에 부하를 발생하였습니다.

심화학습으로 Kubernetes 클라이언트 인증서를 이용한 인증 후 Jmeter에서 부하를 발생하는 방법에 대해 알아봐야 할 거 같습니다.

4. 다른 부하 도구들

Apache Jmeter는 범용적인 부하테스트 도구입니다.

아래는 Kubernetes에 특화된 부하테스트 도구 정보입니다.