4. 编写第一个压测Chart:定义Deployment、Service、ConfigMap,实现压测工具的容器化部署

在容量基线校准流程中,压测工具的容器化部署是获取可靠压测数据的起点。本章将带领你从零编写一个完整的 Helm Chart,将压测工具(以 Apache JMeter 为例)打包为 Kubernetes 原生资源,实现一键部署、配置分离与弹性伸缩。

4.1 Chart 目录结构与核心文件

一个标准的 Helm Chart 遵循固定目录约定。以下是为压测工具定制的 Chart 结构:

jmeter-loadtest/
├── Chart.yaml              # Chart 元数据
├── values.yaml             # 默认配置值
├── templates/
│   ├── _helpers.tpl        # 模板辅助函数
│   ├── deployment.yaml     # Deployment 定义
│   ├── service.yaml        # Service 定义
│   ├── configmap.yaml      # ConfigMap 定义
│   └── NOTES.txt           # 部署后提示信息
└── .helmignore             # 忽略文件列表
文件/目录 作用 压测场景关键点
Chart.yaml 定义 Chart 名称、版本、API 版本 建议将压测场景版本号与 Chart 版本绑定
values.yaml 集中管理所有可配置参数 线程数、循环次数、目标服务地址、资源限制
templates/deployment.yaml 定义 Pod 副本、容器镜像、环境变量 挂载 ConfigMap 中的压测脚本,设置资源 requests/limits
templates/service.yaml 暴露压测工具的监控端口或控制端口 通常使用 ClusterIP 类型,仅内部访问
templates/configmap.yaml 存储压测脚本、配置文件等非敏感数据 JMX 脚本、CSV 数据文件、属性配置
容量规划提示: 在 values.yaml 中预留 replicaCount、resources.requests、resources.limits 三个字段,后续压测基线校准时可直接通过 --set 参数调整,无需修改模板。

4.2 定义 ConfigMap:压测脚本与配置分离

ConfigMap 负责将压测脚本(.jmx)和参数配置从容器镜像中解耦。这样,更换压测场景时只需更新 ConfigMap,无需重新构建镜像。

apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ include "jmeter-loadtest.fullname" . }}-scripts
  labels:
    {{- include "jmeter-loadtest.labels" . | nindent 4 }}
data:
  test-plan.jmx: |
    {{- .Files.Get "scripts/test-plan.jmx" | nindent 4 }}
  user.properties: |
    {{- .Files.Get "scripts/user.properties" | nindent 4 }}
  test-data.csv: |
    {{- .Files.Get "scripts/test-data.csv" | nindent 4 }}
关键设计: 使用 .Files.Get 函数将本地脚本文件直接注入 ConfigMap,避免在 values.yaml 中嵌入大段脚本内容。脚本文件应放在 Chart 根目录的 scripts/ 子目录中,并在 .helmignore 中排除非必要文件。

4.3 定义 Deployment:压测容器的运行时编排

Deployment 是压测 Pod 的核心控制器。以下模板实现了压测工具的完整容器化部署:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ include "jmeter-loadtest.fullname" . }}
  labels:
    {{- include "jmeter-loadtest.labels" . | nindent 4 }}
spec:
  replicas: {{ .Values.replicaCount }}
  selector:
    matchLabels:
      {{- include "jmeter-loadtest.selectorLabels" . | nindent 6 }}
  template:
    metadata:
      labels:
        {{- include "jmeter-loadtest.selectorLabels" . | nindent 8 }}
    spec:
      containers:
        - name: jmeter
          image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
          imagePullPolicy: {{ .Values.image.pullPolicy }}
          env:
            - name: TARGET_HOST
              value: "{{ .Values.target.host }}"
            - name: TARGET_PORT
              value: "{{ .Values.target.port }}"
            - name: THREAD_COUNT
              value: "{{ .Values.test.threadCount }}"
            - name: LOOP_COUNT
              value: "{{ .Values.test.loopCount }}"
            - name: RAMP_UP
              value: "{{ .Values.test.rampUp }}"
          volumeMounts:
            - name: scripts-volume
              mountPath: /opt/jmeter/scripts
              readOnly: true
          resources:
            {{- toYaml .Values.resources | nindent 12 }}
      volumes:
        - name: scripts-volume
          configMap:
            name: {{ include "jmeter-loadtest.fullname" . }}-scripts
资源隔离警告: 压测 Pod 的 resources.requests 和 resources.limits 必须显式设置。未设置资源限制时,压测进程可能抢占集群资源,影响其他业务 Pod。建议 requests 与 limits 保持一致(Guaranteed QoS),确保压测结果可复现。

4.4 定义 Service:暴露监控与结果收集端口

Service 用于暴露压测工具的监控端口(如 JMeter Prometheus 插件端口 9270)或控制端口(如分布式压测时的 Server 端口 1099)。

apiVersion: v1
kind: Service
metadata:
  name: {{ include "jmeter-loadtest.fullname" . }}
  labels:
    {{- include "jmeter-loadtest.labels" . | nindent 4 }}
  annotations:
    prometheus.io/scrape: "true"
    prometheus.io/port: "{{ .Values.service.metricsPort }}"
spec:
  type: {{ .Values.service.type }}
  ports:
    - port: {{ .Values.service.metricsPort }}
      targetPort: metrics
      protocol: TCP
      name: metrics
    - port: {{ .Values.service.serverPort }}
      targetPort: server
      protocol: TCP
      name: server
  selector:
    {{- include "jmeter-loadtest.selectorLabels" . | nindent 4 }}
端口名称 默认端口 用途 容量规划关注点
metrics 9270 Prometheus 指标暴露 用于实时采集 TPS、响应时间、错误率
server 1099 JMeter 分布式控制 Master-Slave 模式下控制通信

4.5 压测部署架构总览

下图展示了 Helm Chart 部署后,ConfigMap、Deployment、Service 三者的协作关系:

压测 Helm Chart 部署架构图 Helm Chart Kubernetes 集群 ConfigMap test-plan.jmx | user.properties | test-data.csv Deployment replicas: 1 | image: jmeter:5.6 Pod (JMeter 容器) env: TARGET_HOST, THREAD_COUNT Service (ClusterIP) metrics:9270 | server:1099 Prometheus 拉取压测指标 说明: ConfigMap 挂载压测脚本到 Pod 的 /opt/jmeter/scripts 目录;Deployment 通过环境变量传递压测参数;Service 暴露监控端口供 Prometheus 采集。

4.6 values.yaml 参数化设计

将所有可变参数抽取到 values.yaml,实现压测场景的快速切换:

# 镜像配置
image:
  repository: registry.example.com/jmeter
  tag: "5.6.3"
  pullPolicy: IfNotPresent

# 副本数(分布式压测时使用)
replicaCount: 1

# 目标服务
target:
  host: "service-b.default.svc.cluster.local"
  port: 8080

# 压测参数
test:
  threadCount: 100
  loopCount: 10
  rampUp: 30

# 资源限制(容量基线校准核心参数)
resources:
  requests:
    cpu: "2"
    memory: "4Gi"
  limits:
    cpu: "2"
    memory: "4Gi"

# 服务配置
service:
  type: ClusterIP
  metricsPort: 9270
  serverPort: 1099
容量基线校准最佳实践: 在 values.yaml 中为 resources.requests 和 resources.limits 设置相同值(Guaranteed QoS),确保每次压测 Pod 获得稳定的 CPU 和内存配额,消除资源争抢对压测结果的干扰。

4.7 部署验证与常用命令

完成 Chart 编写后,通过以下命令验证模板渲染正确性并执行部署:

# 1. 模板渲染验证(不实际部署)
helm template jmeter-loadtest ./jmeter-loadtest

# 2. 语法检查
helm lint ./jmeter-loadtest

# 3. 安装 Chart(指定压测参数)
helm install loadtest-run-001 ./jmeter-loadtest \
  --set test.threadCount=200 \
  --set test.loopCount=5 \
  --set target.host=service-b.prod.svc.cluster.local

# 4. 查看 Pod 日志
kubectl logs -l app.kubernetes.io/instance=loadtest-run-001

# 5. 卸载压测任务
helm uninstall loadtest-run-001

4.8 压测脚本挂载与执行流程

下图展示了从 ConfigMap 挂载到 JMeter 容器执行压测的完整数据流:

压测脚本挂载与执行数据流 步骤 1 Chart 打包脚本 步骤 2 ConfigMap 创建 步骤 3 Volume 挂载 步骤 4 JMeter 执行 各步骤详细说明: 1. Helm 读取 scripts/ 目录下的 .jmx、.properties、.csv 文件,注入模板。 2. Kubernetes API 创建 ConfigMap,存储压测脚本和配置数据。 3. Deployment 将 ConfigMap 以 Volume 形式挂载到 Pod 的 /opt/jmeter/scripts 路径。 4. JMeter 容器启动后读取挂载的脚本,结合环境变量参数执行压测。

4.9 常见问题与避坑指南

  • ConfigMap 大小限制: ConfigMap 单个条目最大 1MB。大型压测数据文件(如 CSV 超过 1MB)应使用 PVC 或对象存储挂载,而非 ConfigMap。
  • 脚本路径硬编码: 确保 JMeter 容器的启动命令(ENTRYPOINT)指向挂载路径 /opt/jmeter/scripts/test-plan.jmx,而非镜像内的默认路径。
  • 分布式压测网络: 当 replicaCount > 1 时,需额外定义 Headless Service 用于 Master-Slave 通信,并在 values.yaml 中配置 Slave 地址列表。
  • 资源超分风险: 压测 Pod 的 limits.cpu 不应超过集群单节点可用 CPU 的 80%,避免因 CPU Throttling 导致压测结果失真。
下一阶段准备: 完成第一个压测 Chart 后,建议在测试环境执行 helm install 并观察 Pod 日志,确认 JMeter 成功加载脚本并开始发送请求。这是后续第 5 章“压测数据采集与 Prometheus 集成”的基础。