压力测试+监控框架
压力测试+监控框架
框架概述
该框架主要采用jmeter+influxdb2+prometheus+grafana+mysql实现,使用docker-compose进行容器编排与部署,能够实现在任何装有docker的linux服务器上快速搭建一套完整的压力测试+监控框架;该框架分为三个大模块:
①压力模块(jmeter)
②数据收集模块(influxdb2/prometheus)
③数据展示模块(grafana+mysql)
框架结构图
项目结构
│ .evn # 忽略文件配置
│ docker-compose # docker-compose可执行文件
│ README.md # 项目描述文件
│ docker-compose.yml # 主docker-compose文件
│ docker-compose.test.yml # 测试docker-compose文件
│ docker-compose.back.yml # 备份docker-compose文件
| jmeter-master.yml # 调度机配置文件
| jmeter-slave.yml # 执行机配置文件
│
├─grafaba - grafaba持久化目录
│ grafana.ini # grafaba配置文件
| grafana.back.ini # grafaba备份配置文件
│ grafana.sql # 持久化sql
│ influxdb v1.0 (InfluxQL).json # inflxudb1仪表盘
│ influxdb v2.0 (Flux).json # influxdb2仪表盘
│ PrometheusListener.json # Prometheus仪表盘
│
├─influxdb - influxdb1持久化目录
│ ├─data = influxdb数据目录
│ └─influxdb.conf # influxdb1配置文件
|
├─influxdb2 - influxdb2持久化目录
| |
│ └─data = influxdb2数据目录
| config.yml # influxdb2配置文件
| influx-configs # influxdb2配置文件
|
├─jmeter - 单节点部署jmeter
│ ├─jar = jmeter所需jar包依赖(lib目录)
| └─jmx = jmeter脚本目录
|
├─jmeter-master - jmeter分布式部署调度机
│ └─log = 运行日志
| jmeter.properties # jmeter配置文件
| back # 配置文件备份
|
├─jmeter-slave - jmeter分布式部署执行机
│ └─log = 运行日志
| jmeter.properties # jmeter配置文件
| back # 配置文件备份
|
├─mysql - mysql
│ ├─configr = mysql配置文件目录
| | my.cnf # mysql配置文件
| └─data = mysql数据目录
|
├─prometheus - prometheus
│ └─data = prometheus数据目录
| prometheus.yml # prometheus配置文件
| jk.json # 服务发现配置文件(服务器监控)
| jmeter.json # 服务发现配置文件(jmeter)
├─telegraf - telegraf
| telegraf.conf # telegraf配置文件
└─
部署环境
操作系统
可在CentOS7.4以及以上版本部署
使用工具
docker >= 20.10.16
docker-compose >= 2.5.1
使用镜像
influxdb:2.2.0
influxdb:1.8.0
grafana/grafana:8.2.6
prom/prometheus:latest
telegraf
chiabre/jmeter_prom_exporter
mysql:5.7
docker-compose.yaml
# docker-compose版本
version: "3"
# 容器网络 默认为bridge
networks:
monitor-net:
driver: bridge
# 服务
services:
# influxdb2 节点
influxdb2:
# 镜像名称
image: influxdb:2.2.0
# 容器名称(可选)
container_name: influxdb2
# 环境变量
environment:
- INFLUX_USERNAME=${INFLUXDB_USERNAME} # 用户名
- INFLUX_PASSWORD=${INFLUXDB_PASSWORD} # 密码
- DOCKER_INFLUXDB_INIT_MODE=setup # 数据库模式
- DOCKER_INFLUXDB_INIT_USERNAME=admin # 容器内用户名
- DOCKER_INFLUXDB_INIT_PASSWORD=qwer1234 # 密码
- DOCKER_INFLUXDB_INIT_ORG=jmeterOrg # 组织名称
- DOCKER_INFLUXDB_INIT_BUCKET=jmeterBucket # 默认仓库名称
- DOCKER_INFLUXDB_INIT_ADMIN_TOKEN=jmeterToken # 默认token
- DOCKER_INFLUXDB_INIT_RETENTION=1w # 能存储的数据量
# 持久化目录
volumes:
# 数据持久化
- /test/influxdb2/data:/var/lib/influxdb2
# 配置文件持久化
- /test/influxdb2/config.yml:/etc/influxdb2/config.yml
- /test/influxdb2/influx-configs:/etc/influxdb2/influx-configs
# 端口映射
ports:
- "8086:8086"
# 重启策略
restart: always
# 使用网络模式
networks:
- monitor-net
# influxdb 节点
influxdb:
image: influxdb:1.8.0
container_name: influxdb
environment:
- INFLUX_USERNAME=${INFLUXDB_USERNAME}
- INFLUX_PASSWORD=${INFLUXDB_PASSWORD}
- DOCKER_INFLUXDB_INIT_MODE=setup
- DOCKER_INFLUXDB_INIT_USERNAME=admin
- DOCKER_INFLUXDB_INIT_PASSWORD=qwer1234
- DOCKER_INFLUXDB_INIT_ORG=jmeterOrg
- DOCKER_INFLUXDB_INIT_BUCKET=jmeterBucket
- DOCKER_INFLUXDB_INIT_ADMIN_TOKEN=jmeterToken
- DOCKER_INFLUXDB_INIT_RETENTION=1w
volumes:
- /test/influxdb/data:/var/lib/influxdb
- /etc/localtime:/etc/localtime
- /test/influxdb/influxdb.conf:/etc/influxdb/influxdb.conf
ports:
- "8087:8086"
- "8083:8083"
restart: always
networks:
- monitor-net
# grafana 节点
grafana:
image: grafana/grafana:8.2.6
container_name: grafana8
environment:
- GF_SECURITY_ADMIN_USER=${GRAFANA_USERNAME} # 用户名
- GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_PASSWORD} # 密码
- GF_USERS_ALLOW_SIGN_UP=false
volumes:
- /test/grafana:/var/lib/grafana
- /test/grafana/grafana.ini:/etc/grafana/grafana.ini
ports:
- "3000:3000"
restart: unless-stopped
# 将其他容器的ip记录到容器中
links:
- influxdb
- influxdb2
- prometheus
- mysql
networks:
- monitor-net
# prometheus 节点
prometheus:
image: prom/prometheus:latest
container_name: prometheus
volumes:
- /test/prometheus/prometheus.yml:/etc/prometheus/prometheus.yml
- /test/prometheus/:/prometheus
- /test/prometheus/jk.json:/prometheus/jk.json
- /test/prometheus/jmeter.json:/prometheus/jmeter.json
- /etc/localtime:/etc/localtime:ro
# 执行命令
command:
- '--config.file=/etc/prometheus/prometheus.yml'
links:
- influxdb
- telegraf
ports:
- "9090:9090"
networks:
- monitor-net
# telegraf 节点
telegraf:
image: telegraf
container_name: telegraf
volumes:
- /test/telegraf/telegraf.conf:/etc/telegraf/telegraf.conf
environment:
- INFLUX_TOKEN=jmeterToken
links:
- influxdb2
ports:
- "1234:1234"
networks:
- monitor-net
# 单节点部署jmeter
jmeter:
image: chiabre/jmeter_prom_exporter:latest
container_name: jmeter
hostname: jmeter
environment:
- Jprometheus.ip=0.0.0.0
ports:
- "9270:9270"
volumes:
- /test/jmeter/jmx:/jmx
- /test/jmeter/jar/:/opt/apache-jmeter-5.4.1/lib
command:
- -t
- "/jmx/docker_jmeter.jmx"
depends_on:
- influxdb2
networks:
- monitor-net
# 分布式部署jmeter调度机
jmeter-master:
image: chiabre/jmeter_prom_exporter
container_name: jmeter-master
hostname: jmeter-master
ports:
- "1099:1099"
- "5000:5000"
- "6000:6000"
- "6001:6001"
- "6002:6002"
volumes:
- /test/jmeter-master/jmeter.properties:/opt/apache-jmeter-5.4.1/bin/jmeter.properties
- /test/jmeter/jar/:/opt/apache-jmeter-5.4.1/lib
- /test/jmeter/jmx:/jmx
- /test/jmeter-master/log:/log
command:
- -Dserver_port=1099
- -Djava.rmi.server.hostname=192.168.0.128
- -t
- "/jmx/docker_jmeter.jmx"
- -r
- -j
- "/log/jmeter-server.log"
networks:
- monitor-net
# 分布式部署jmeter执行机
jmeter-slave:
image: chiabre/jmeter_prom_exporter
container_name: jmeter-slave
hostname: jmeter-slave
networks:
- monitor-net
ports:
- "1098:1098"
- "4000:4000"
- "7000:7000"
- "7001:7001"
- "7002:7002"
volumes:
- /test/jmeter-slave/jmeter.properties:/opt/apache-jmeter-5.4.1/bin/jmeter.properties
- /test/jmeter/jar/:/opt/apache-jmeter-5.4.1/lib
- /test/jmeter-slave/log:/log
command:
- -Dserver.rmi.localport=1098
# - -Dserver_port=172.18.2.155:1099
- -Djava.rmi.server.hostname=192.168.0.128
- -s
- -j
- "/log/jmeter-server.log"
# mysql节点
mysql:
image: mysql:5.7
restart: always
container_name: mysql
environment:
MYSQL_ROOT_PASSWORD: 123456
TZ: Asia/Shanghai
ports:
- "3306:3306"
volumes:
- /test/mysql/data:/var/lib/mysql
- /test/mysql/config/my.cnf:/etc/mysql/my.cnf
command:
--max_connections=1000
--character-set-server=utf8mb4
--collation-server=utf8mb4_general_ci
--default-authentication-plugin=mysql_native_password
networks:
- monitor-net
压力模块
部署方式
单节点部署
单节点部署可适用于一般场景的压力测试,如单个接口和多个接口低于500并发的常规测试,可以直接与其他的工具放在一起,在执行测试的时候实时的将结果数据进行展示,部署非常方便无需其他任何设置,只需要上传jmeter脚本即可
分布式部署
采用一台调度机和若干执行机组成,可适用于单接口和多接口高于1000并发的高并发测试,调度机只进行脚本的下发和执行结果的收集,脚本主要在执行机上执行并将测试结果实时返回至调度机;缺点是需要在多台机器上运行执行机程序,且每台执行机必须拥有脚本执行所需要的所有依赖或者数据文件,不然会出现找不到依赖或者找不到文件的情况
分布式部署配置
注意:执行脚本所需的依赖和文件,所有执行机都必须要拥有
分布式部署最好在内网部署,且需要关闭服务器或者pc的防火墙
调度机配置(jmeter-master)
当调度机为linux
jmeter.properties配置
# 填入远程控制机器的ip地址, 多台使用,分割 remote_hosts=192.168.0.128 # 禁用ssl server.rmi.ssl.disable=true # 服务端口 server_port=1099 # rmi服务端口 server.rmi.port=1099 # rmi服务端端口 server.rmi.localport=5000 # rmi客户端端口 client.rmi.localport=6000 # 让主机能够接受执行机的结果 mode=Standard
docker-compose.yaml配置
jmeter-master: image: chiabre/jmeter_prom_exporter container_name: jmeter-master hostname: jmeter-master networks: - monitor-net # 端口映射 ports: # server_port服务端口 - "1099:1099" # server.rmi端口 - "5000:5000" # client.rmi端口 - "6000:6000" # client.rmi使用的端口 - "6001:6001" # client.rmi使用的端口 - "6002:6002" # 持久化 volumes: # 映射配置文件到容器内 - /test/jmeter-master/jmeter.properties:/opt/apache-jmeter-5.4.1/bin/jmeter.properties # 映射依赖jar包到容器内 - /test/jmeter/jar/:/opt/apache-jmeter-5.4.1/lib # 映射jmeter脚本到容器内 - /test/jmeter/jmx:/jmx # 映射jmeter日志到宿主机 - /test/jmeter-master/log:/log command: # 服务端口 - -Dserver_port=1099 # 指定rmi服务ip - -Djava.rmi.server.hostname=192.168.0.128 # 启动脚本 - -t # 脚本路径 - "/jmx/docker_jmeter.jmx" # 启动所有远程服务器 -R 192.168.0.1,192.168.0.2 可指定 - -r # 生成运行日志 - -j # 日志路径 - "/log/jmeter-server.log"
当调度机为windows
jmeter.properties配置
# 填入远程控制机器的ip地址, 多台使用,分割 remote_hosts=127.0.0.1,172.18.2.155 # 服务端口 server_port=1099 # rmi服务端口 server.rmi.port=1099 # rmi.localport服务端口 server.rmi.localport=1099 # 禁用ssl server.rmi.ssl.disable=true # 让主机能够接受执行机的结果 mode=Standard
jmeter.bat配置
在jmeter.bat中增加rmi_host并在ARGS中添加,次步骤是为了指定服务的地址,防止自动获取的ip地址为虚拟网卡地址或其他地址导致通信不畅
set rmi_host=-Djava.rmi.server.hostname=本机ip地址 %rmi_host%
windows调度机与linux调度机优劣势
- windows调度机由于有ui界面,可以快速修改脚本可以进行快速调试
- windows调度机无法进行容器化部署
- linux调度机无法快速调试脚本,修改脚本后需要上传到服务器替换
- linux调度机可以实现整个框架的全容器化部署
执行机配置(jmeter-slave)
jmeter.properties配置
# 服务端口 server_port=1098 # rmi服务端口 server.rmi.port=1098 # server.rmi端口 server.rmi.localport=4000 # client.rmi端口 client.rmi.localport=7000 # 禁用ssl server.rmi.ssl.disable=true # 默认127.0.0.1 remote_hosts=127.0.0.1
docker-compose.yaml配置
jmeter-slave: image: chiabre/jmeter_prom_exporter container_name: jmeter-slave hostname: jmeter-slave networks: - monitor-net ports: - "1098:1098" - "4000:4000" - "7000:7000" - "7001:7001" - "7002:7002" volumes: # 映射配置文件到容器内 - /test/jmeter-slave/jmeter.properties:/opt/apache-jmeter-5.4.1/bin/jmeter.properties # 映射依赖jar包到容器内 - /test/jmeter/jar/:/opt/apache-jmeter-5.4.1/lib # 映射jmeter脚本到容器内 - /test/jmeter-slave/log:/log command: # 指定server.rmi端口 - -Dserver.rmi.localport=1098 # - -Dserver_port=172.18.2.155:1099 # 指定指定rmi服务ip - -Djava.rmi.server.hostname=192.168.0.128 # 启动jmeter-server服务 - -s # 生成运行日志 - -j # 日志路径 - "/log/jmeter-server.log"
分布式部署常见问题
调度机无法与执行机连接
- 防火墙未关闭
- 调度机或执行机的服务未启动
- client.rmi.localport、server.rmi.localport 未在调度机和执行机正常配置
- 调度机和执行机不在通一网段,网络不通
调度机无法查看到执行机的运行结果
一般一直卡在Waiting看不到结果, 调度机和执行机直接端口不通, 比如设置端口6000实际还会使用6001,6002
调度机配置文件中mode=Standard未放开注释
client.rmi.localport、server.rmi.localport 未在调度机和执行机正常配置
容器化部署时client.rmi.localport、server.rmi.localport端口需要写死,多个脚本同时执行端口被占用的情况
jmeter使用插件
- 使用官方后端监听器自带的org.apache.jmeter.visualizers.backend.influxdb.InfluxdbBackendListenerClient调用数据库api传输数据到influxdb1.x或者influxdb2.x中
- jmeter-plugin-influxdb2-listener-1.5-all.jar,调用数据库api将结果数据传输到influxdb2中
- jmeter-prometheus-listener-2.3.1.jar, 本地启动接口服务,通过将prometheus的接口监控,将结果数据传输到prometheus中
- jmeter-prometheus-plugin-0.6.0.jar, 本地启动接口服务,通过将prometheus的接口监控,将结果数据传输到prometheus中(项目中未使用)
数据收集模块
数据收集方式
使用influxdb收集数据
- 使用influxdb 1.x
- 使用influxdb 2.x
使用prometheus收集数据
数据写入方式
使用官方后端监听器自带的influxdb写入方式,可以使用调用数据库api的方式同时兼容influxdb1.x 和influx2.x
写入influxdb1.x
需要修改influxdb服务地址仓库名称和标识
http://192.168.0.128:8086/write?db=jmeter
写入influxdb 2.x
此处使用一种兼容的方式使用influxdb1.x的方式来讲数据写入influxdb2.x,需要修改写入时的api将其改成2.x版本的, 2.x版本的api需要填入组织名称和修改仓库选择字段,增加 influxdbToken字段填入数据库的token
http://192.168.0.128:8086/api/v2/write?org=jmeterOrg&bucket=test
jmeter-plugin-influxdb2-listener-1.5-all直接采用influxdb2.x的方式调用数据库api进行数据写入
注意: 此插件需要jdk11的支持,如果使用的jdk1.8则该选项不会出现在后端监听器中
如果使用的是jdk11且将插件正常放入jmeter的依赖目录中,后端监听器实现会显示org.md.jmeter.influxdb2.visualizer.InfluxDatabaseBackendListenerClient选项
需要输入host,port,token,org即可将数据写入数据库,可修改influxDBFlushlnterval的值来调整写入数据库的时间间隔,默认为4000毫秒最短不应低于1000毫秒
jmeter的prometheus的两个插件是在jmeter启动时本地启动一个接口服务,然后使用prometheus对接口服务进行监控,收集接口测试产生的数据
jmeter-prometheus-listener-2.3.1
该插件正确安装后会在后端监听器实现展示com.github.kolesnikovm.PrometheusListener选项,有需要可以修改exporterPort的值来指定启动服务端口,sarmplersRegExp的值为正则表达式可以过滤不需要统计的请求
jmeter-prometheus-plugin-0.6.0
由于该插件过于复杂且文档不是特别完善,且没有对应的监控数据模板故此框架不使用该插件
数据持久化
influxdb
由于influxdb本身就是一个时序数据库, 我们只需要在docker-compose.yaml中配置持久化目录就可以实现数据的持久化
prometheus(可选)
由于prometheus本身有一个数据保存策略,默认只会保存15天的数据,可以通过设置最高设置为90天,并不能满足持久化的需求
持久化到influxdb 1.x数据库
由于prometheus官方支持对influxdb1.x数据写入, 只需要在prometheus.yml加入下面2个连接就可以直接将数据写入数据库
注意修改数据库名称和账号密码
# 写入链接 remote_write: - url: "http://192.168.0.128:8087/api/v1/prom/write?db=prometheus&u=admin&p=qwer1234" # 读取链接 remote_read: - url: "http://192.168.0.128:8087/api/v1/prom/read?db=prometheus&u=admin&p=qwer1234"
持久化到influxdb 2.x数据库
官方没有方法支持直接使用prometheus对influxdb2.x数据库写入数据,这个地方需要使用通过telegraf来收集prometheus的数据然后通过telegraf来将数据写入influxdb 2.x, 此处也有2种方式:
- 主动使用启动的接口服务写入数据
- 监控prometheus的服务地址向数据库写入数据
修改prometheus.yml配置文件
# 写入链接 remote_write: - url: "http://telegraf:1234/receive"
修改telegraf配置文件
# telegraf链接influxdb2配置 [[outputs.influxdb_v2]] # influxdb2地址 urls = ["http://influxdb2:8086"] # influxdb2的token token = "$INFLUX_TOKEN" # 组织名称 organization = "jmeterOrg" # 数据库名称 bucket = "prometheus" # 接口服务 方法1 [[inputs.http_listener_v2]] # 监听端口 service_address = ":1234" # 路径 path = "/receive" # 数据格式 data_format = "prometheusremotewrite" # prometheus服务地址 方法2 #[[inputs.prometheus]] # urls = ["http://192.168.0.128:9090/metrics"]
数据展示
展示方式
直接使用influxdb展示数据
直接时间prometheus展示数据
使用grafana展示数据
展示方式比较
- 直接使用influxdb展示数据,1.x的数据库没有ui界面,2.x的数据库有ui界面且有仪表盘,但是2.x的语法比较奇怪官网介绍的也不是很清楚,不是特别建议直接使用2.x的仪表盘展示数据
- 直接使用prometheus展示数据,指标太多且数据持久化比较困难,没有仪表盘只有一个简单的查询折线统计图展示
- 使用grafana展示数据,拥有许多现成和写好的仪表盘,能够完美兼容influxdb、prometheus、系统参数监控的信息展示,还支持mysql、redis、nginx的监控是一个比较完善的数据展示解决方案
展示数据持久化
此处只介绍grafana的数据持久化,
修改配置文件grafana.ini
# 数据库配置 [database] type = mysql # 数据库类型 host = mysql:3306 # 数据库地址 name = grafana # 数据库名称 user = grafana # 用户名 password = 123456 # 密码 url = mysql://grafana:123456@mysql:3306/grafana # 连接地址 [session] provider = mysql provider_config = `grafana:123456@tcp(mysql:3306)/grafana`
创建数据库
# 创建数据库 CREATE DATABASE grafana; # 创建账号赋予权限 CREATE USER grafana @'%' IDENTIFIED BY '123456'; GRANT ALL PRIVILEGES ON grafana.* TO grafana @'%'; # 刷新权限 FLUSH PRIVILEGES;
使用方式
执行命令
运行所有模块
docker-compose会默认以目录下以docker-compose.yml的为配置文件
./docker-compose up -d
运行调度机
./docker-compose -f jmeter-master.yml up -d
运行执行机
./docker-compose -f jmeter-slave.yml up -d
访问服务地址
服务名称 | 服务地址 | 端口 | 账号 | 密码 |
---|---|---|---|---|
influxdb 2.x | 服务器ip | 8086 | admin | qwer1234 |
influxdb 1.x | 服务器ip | 8087,8083 | admin | qwer1234 |
grafana | 服务器ip | 3000 | admin | qwer1234 |
mysql | 服务器ip | 3306 | root | 123456 |
prometheus | 服务器ip | 9090 | ||
jmeter | 服务器ip | 9270 | ||
jmeter-master | 服务器ip | 1099,5000,6000,6001,60002 | ||
jmeter-slave | 服务器ip | 1099,5000,6000,6001,60002 | ||
telegraf | 服务器ip | 1234 |
常用命令
docker-compose常用命令
启动所有节点
docker-compose up
后台启动所有节点
docker-compose up -d
停止和删除容器、网络、卷、镜像
docker-compose down 节点名称/不填为所有
停止运行状态的节点
docker-compose stop 节点名称/不填为所有
查看容器列表
docker-compose ps
删除节点
docker-compose rm 节点名称
启动节点
docker-compose start 节点名称
重启节点
docker-compose restart 节点名称
查看日志
docker-compose logs 节点名称
指定模板文件
docker-compose -f xxx.yaml up -d
docker常用命令
查看所有容器
docker ps -a
拉取镜像
docker pull 镜像名称
运行镜像
docker image 镜像名称
停止容器
docker stop 容器id
删除容器
docker rm 容器id
启动容器
docker start 容器id
复制容器内的文件到宿主机
docker cp 容器id:容器内文件路径 宿主机保存路径
进入容器
docker exec -it 容器id /bin/bash