1. 程式人生 > >在 Kubernetes 上使用 Jmeter 執行壓力測試_Kubernetes中文社群

在 Kubernetes 上使用 Jmeter 執行壓力測試_Kubernetes中文社群

Kubernetes 的資源和任務排程能力,能給自動化測試提供相當大力的支援,這裡以 Jmeter 為例,講講如何在 Kubernetes 中使用 Jmeter 進行簡單的效能測試。

開始之前

  • 錄製任務:本文所用映象為 Jmeter 3.x 版本,建議提前錄製一個簡單的測試任務進行下面的操作。
  • 支援 Jobs 的 Kubernetes 叢集,以及預設的 StorageClass 支援,能夠實現 PVC 的動態供應。
  • 網際網路連線。

試驗內容

  1. 搭建一個 Web DAV 服務,用於提供給 Jmeter 輸入輸出場所,也便於日後 CI/CD 工具的案例輸入或結果輸出。
  2. 執行單例項的 Jmeter 測試任務。
  3. 執行叢集形式的 Jmeter 測試任務。

預備儲存

這一步驟並非強制,完全可以通過 scp 或者 mount 等其他方式來實現

這裡我們做一個 Web DAV 服務,掛載一個 PVC,在其中分為 input 和 output 兩個目錄,實際使用過程中,可以進一步按照任務或者 Job 對目錄進行更詳盡的規劃。

首先建立名為jmeter-task的儲存卷:

kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: jmeter-task
spec:
  accessModes:
    - ReadWriteMany
  resources:
    requests:
      storage: 1Gi

儲存卷建立之後,可以使用cadaver或者WinSCP等工具去建立目錄。

接下來上傳*.jmx檔案,到input目錄之中,這裡我錄製的一個持續訪問京東首頁的任務,命名為jd.jmx。

單例項測試

單例項測試很容易,使用 Kubernetes 的 Job 方式即可:

apiVersion: batch/v1
kind: Job
metadata:
  name: jmeter
spec:
  template:
    metadata:
      name: jmeter
    spec:
      restartPolicy: Never
      containers:
      - name: jmeter
        image: dustise/jmeter-server
        command:
          - "/jmeter/bin/jmeter"
          - "-n"
          - "-t"
          - "/jmeter/input/jd.jmx"
          - "-l"
          - "/jmeter/output/log"
          - "-j"
          - "/jmeter/output/joker"
        volumeMounts:
        - name: data
          mountPath: /jmeter/input
          subPath: input
        - name: data
          mountPath: /jmeter/output
          subPath: output
      volumes:
      - name: data
        persistentVolumeClaim:
          claimName: jmeter-task

上面的定義中:

  • 任務 Pod 載入了儲存卷jmeter-task。使用subPath指令,分別掛載了輸入和輸出目錄。
  • 使用-n -t的方式執行測試任務,並把輸出檔案定位到output目錄中。

接下來就可以使用kubectl create -f jobs1.yaml來執行這一任務。

任務啟動之後,可以:

  • 使用kubectl get jobs來檢視任務執行狀況。
  • kubectl get pods –show-all檢視任務 Pod。
  • kubectl logs -f [pod name]檢視任務輸出。

最後任務會變成完成狀態,就可以在 Web DAV 中檢視任務報告了。

叢集測試

Jmeter 可以使用控制檯+負載機的形式,使用多個節點進行壓力測試,這裡需要解決的一個最重要問題就是,在指派任務給負載機時,Jmeter 需要使用-R host:port的引數,來指定任務要呼叫的負載機。這一通訊是無法通過 Kubernetes 方式的 Service 來完成的。必須建立 Pod 之間的通訊,而 Pod 的主機名地址是很飄逸的,同時,我們還是希望負載節點的數量能夠實現較為自由的伸縮,因此解決方法就只有 StatefulSet 了。

這個 YAML 很長,所以放在最後了,說說其中的要點:

  • 註解中的security.alpha.kubernetes.io/sysctls:實際執行中,jmeter 負載機是需要對核心引數進行一點調整的,Pod 中可以用這一方式進行調整,https://kubernetes.io/docs/concepts/cluster-administration/sysctl-cluster/ 中有更詳細的關於這方面的內容講解。
  • spec.affinity:這裡設定 Jmeter Pod 儘量分佈在不同節點上。
  • RMI_HOST環境變數:使用每個 Pod 的 IP 為這一變數賦值。
  • Service:利用這個 Headless 服務,為每個 Pod 提供主機名支援。

啟動這個 Statefulset 之後,會看到規律建立的 Pod 名稱:

jnode-0                   1/1       Running   0          1h
jnode-1                   1/1       Running   0          1h

對應的主機名稱就應該是 jnode-0.jfarm,jnode-1.jfarm。所以上面的job.yaml可以新增-R jnode-0.jfarm:1099,jnode-1.jfarm:1099即可。

使用kubectl create啟動任務之後,檢視該任務 Pod 的日誌,會出現大致這樣的內容:

Creating summariser <summary>
Created the tree successfully using /jmeter/input/jd.jmx
Configuring remote engine: jnode-0.jfarm:1099
Configuring remote engine: jnode-1.jfarm:1099
Starting remote engines
Starting the test @ Thu Nov 16 07:24:14 GMT 2017 (1510817054558)
Remote engines have been started
Waiting for possible Shutdown/StopTestNow/Heapdump message on port 4445
summary +    302 in 00:01:16 =    4.0/s Avg:  2967 Min:  2627 Max:  5457 Err:     0 (0.00%) Active: 0 Started: 20 Finished: 20
summary +     98 in 00:00:00 = 3062.5/s Avg:  3270 Min:  2635 Max:  7192 Err:     0 (0.00%) Active: 0 Started: 20 Finished: 20
summary =    400 in 00:01:16 =    5.3/s Avg:  3041 Min:  2627 Max:  7192 Err:     0 (0.00%)
Tidying up remote @ Thu Nov 16 07:25:31 GMT 2017 (1510817131966)
... end of run

可以看到,成功配置遠端負載伺服器之後,測試開始,最後成功完成。

Statefulset 原始碼

apiVersion: apps/v1beta1
kind: StatefulSet
metadata:
  name: jnode
  labels:
    app: jmeter
    component: node
spec:
  serviceName: jfarm
  replicas: 2
  selector:
    matchLabels:
      app: jmeter
      component: node
  template:
    metadata:
      labels:
        app: jmeter
        component: node
      annotations:
        security.alpha.kubernetes.io/sysctls: net.ipv4.ip_local_port_range=10000 65000,net.ipv4.tcp_syncookies=1
    spec:
      affinity:
        podAntiAffinity:
          preferredDuringSchedulingIgnoredDuringExecution:
          - weight: 100
            podAffinityTerm:
              topologyKey: kubernetes.io/hostname
              labelSelector:
                matchExpressions:
                - key: app
                  operator: In
                  values:
                  - jmeter
                - key: component
                  operator: In
                  values:
                  - node
      restartPolicy: Always
      containers:
      - name: jmeter
        image: dustise/jmeter-server
        ports:
        - name: server
          containerPort: 1099
        - name: rmi
          containerPort: 20000
        env:
        - name: RMI_HOST
          valueFrom:
            fieldRef:
              fieldPath: status.podIP
---
apiVersion: v1
kind: Service
metadata:
  name: jfarm
  labels:
    app: jmeter
spec:
  clusterIP: None
  ports:
  - port: 1099
    name: server
  selector:
    app: jmeter
    component: node