Ruoyi项目HPA自动扩缩容实验:基于内存使用率的弹性伸缩实践
在微服务架构中,应用负载具有动态性,自动扩缩容机制对保障服务质量和优化资源利用率至关重要。本文以若依(Ruoyi)项目为例,详细介绍如何在Kubernetes环境中配置基于内存使用率的HPA(Horizontal Pod Autoscaler)自动扩缩容,并通过实验验证其效果。
一、应用场景背景
在生产环境中,若依系统的ruoyi-system服务在不同时段面临不同的访问压力。在业务高峰期,系统需要更多的Pod副本来处理请求,而在低峰期则可以减少资源占用以节约成本。通过配置HPA,我们可以实现基于内存使用率的自动扩缩容,确保系统在高负载时能够自动扩容,而在低负载时自动缩容。
二、HPA工作原理详解
1. HPA内存利用率计算机制
Kubernetes HPA在计算内存利用率时,是基于Pod的**资源请求(requests)**而非资源限制(limits)进行计算的。这是一个重要的概念,直接影响HPA的触发条件。
HPA计算的是目标Deployment下所有Pod的平均内存利用率,而不是单个Pod的利用率。计算公式如下:
内存利用率 = 所有Pod实际内存使用总和 / (Pod数量 * 单个Pod内存请求) * 100%
例如,若Deployment配置了3个副本,每个Pod的内存请求为2000Mi,当3个Pod的总内存使用达到3000Mi(平均每个Pod使用1000Mi)时,整体内存利用率为50%。
2. HPA扩缩容决策机制
HPA会定期(默认15秒)检查目标资源的指标,当指标持续超过设定阈值一段时间后触发扩容操作。缩容则有更长的冷却期,以防止频繁的扩缩容震荡。具体的扩缩容行为可以通过HPA的behavior字段进行详细配置,包括稳定窗口期、扩缩容策略等。
三、Ruoyi System资源配置分析
1. Deployment资源配置
ruoyi-system服务的基础资源配置如下:
# base/ruoyi-system/quota.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: ruoyi-system
spec:
template:
spec:
containers:
- name: ruoyi-system
resources:
limits:
cpu: '2'
memory: 4096Mi
requests:
cpu: '2'
memory: 4096Mi
需要注意的是,这里requests.memory和limits.memory都设置为4096Mi,这意味着HPA在计算内存利用率时会基于4096Mi进行计算。
2. HPA配置详解
ruoyi-system的HPA配置如下:
# overlays/ruoyi-cloud-prod-project/ruoyi-system/ruoyi-system-v1/hpa.yaml
apiVersion: autoscaling/v2beta2
kind: HorizontalPodAutoscaler
metadata:
name: ruoyi-system
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: ruoyi-system
minReplicas: 1 # 最小副本数: >=1
maxReplicas: 10 # 最大副本数: >minReplicas
metrics:
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 66 # 内存使用率目标值: 0-100,在0.9*66与1.1*66之间扩容或缩容
behavior:
scaleDown:
stabilizationWindowSeconds: 5 # 缩容稳定窗口期5秒 (推荐15-300)
policies:
- type: Percent
value: 50 # 每15秒最多缩容50%的Pod (推荐1-100)
periodSeconds: 15 # 时间窗口 (推荐15-1800)
scaleUp:
stabilizationWindowSeconds: 5 # 快速扩容稳定窗口期5秒 (推荐0-300)
policies:
- type: Percent
value: 100 # 每15秒最多按当前Pod数的100%扩容(即翻倍)(推荐1-100)
periodSeconds: 15 # 时间窗口 (推荐15-1800)
- type: Pods
value: 4 # 每15秒最多扩容4个Pod (推荐1-100)
periodSeconds: 15 # 时间窗口 (推荐15-1800)
selectPolicy: Max # 策略选择: Max(最快), Min(最慢), Disabled(禁用)
配置说明:
scaleTargetRef:指定要进行扩缩容的目标DeploymentminReplicas:最小副本数为1maxReplicas:最大副本数为10metrics:定义扩缩容指标- 类型为Resource(资源指标)
- 资源名称为memory(内存)
- 目标类型为Utilization(利用率)
- 平均利用率为66%
这意味着当ruoyi-system服务的所有Pod平均内存使用率超过66%时,HPA将触发扩容操作。根据配置中的容忍度说明,HPA会在0.9*66(59.4%)与1.1*66(72.6%)之间进行扩容或缩容操作。具体来说,当内存使用率低于59.4%时可能触发缩容,高于72.6%时可能触发扩容,而在59.4%到72.6%之间时保持稳定。
3. 扩缩容行为策略
HPA配置中还定义了详细的扩缩容行为策略:
-
缩容策略:
- 稳定窗口期为5秒
- 每15秒最多缩容50%的Pod
-
扩容策略:
- 稳定窗口期为5秒
- 每15秒最多按当前Pod数的100%扩容(即翻倍)
- 每15秒最多扩容4个Pod
- 选择最快的策略(Max)
四、HPA测试功能实现
为了方便测试HPA效果,Ruoyi项目专门提供了HPA测试功能,包括后端接口和前端界面。
1. 后端实现(SysHpaController)
SysHpaController控制器提供了以下核心功能:
-
内存消耗接口 (
/hpa/consumeMemory):- 可以按指定百分比消耗内存
- 通过分配大块字节数组实现内存占用
- 支持精确控制内存使用率
-
内存释放接口 (
/hpa/releaseMemory):- 释放之前占用的内存
- 清理内存块并触发垃圾回收
-
内存信息查询接口 (
/hpa/memoryInfoWithPod):- 获取详细的内存使用情况
- 包括最大内存、已分配内存、已使用内存、空闲内存等
- 显示Pod名称和容器内存限制
在实现上,该控制器特别注意了以下几点:
- 从cgroup获取容器实际内存使用情况,而非仅依赖JVM内存信息
- 支持从环境变量或cgroup文件获取容器内存限制
- 提供了内存字符串解析功能,支持Kubernetes常见的内存单位(Ki、Mi、Gi等)
关键代码实现如下:
/**
* 消耗指定百分比的内存
*
* @param percentage 内存百分比 (0-100)
* @param baseMemoryMB 基准内存值(MB),如果提供则使用此值作为基准,否则使用容器的requests.memory
* @return 操作结果
*/
@PostMapping("/consumeMemory")
public R<String> consumeMemory(@RequestParam(defaultValue = "80") int percentage,
@RequestParam(required = false) Long baseMemoryMB) {
try {
// 检查是否有正在进行的内存分配任务
CompletableFuture<Void> existingTask = memoryTask.get();
if (existingTask != null && !existingTask.isDone()) {
return R.fail("已有内存分配任务正在进行中,请稍后再试");
}
// 启动异步任务处理内存分配
CompletableFuture<Void> task = CompletableFuture.runAsync(() -> {
try {
consumeMemoryInternal(percentage, baseMemoryMB);
} catch (Exception e) {
throw new RuntimeException(e);
}
});
// 保存任务引用
memoryTask.set(task);
// 等待一段时间,但不等待任务完成,避免HTTP超时
Thread.sleep(100);
return R.ok("内存分配任务已启动,请稍后查看内存使用情况");
} catch (Exception e) {
return R.fail("内存分配启动失败: " + e.getMessage());
}
}
/**
* 释放所有占用的内存
*
* @return 操作结果
*/
@DeleteMapping("/releaseMemory")
public R<String> releaseMemory() {
try {
// 检查是否有正在进行的内存分配任务
CompletableFuture<Void> existingTask = memoryTask.get();
if (existingTask != null && !existingTask.isDone()) {
// 取消进行中的任务
existingTask.cancel(true);
}
releaseMemoryInternal();
// 触发多次GC确保内存被回收
System.gc();
Thread.sleep(100);
System.gc();
Thread.sleep(100);
return R.ok("内存已释放");
} catch (Exception e) {
return R.fail("内存释放失败: " + e.getMessage());
}
}
/**
* 获取当前内存使用情况和Pod名称
*
* @return 内存使用信息和Pod名称
*/
@GetMapping("/memoryInfoWithPod")
public R<MemoryInfo> getMemoryInfoWithPod() {
// 获取容器内存请求值,如果获取不到则使用容器内存限制,再获取不到则使用JVM最大内存
long memoryBase = getContainerMemoryRequest();
if (memoryBase <= 0) {
memoryBase = getContainerMemoryLimit();
}
if (memoryBase <= 0) {
memoryBase = Runtime.getRuntime().maxMemory();
}
// 获取更准确的内存使用情况
long actualUsedMemory = getActualUsedMemory();
// 计算内存使用百分比
double memoryUsagePercentage = (actualUsedMemory * 100.0) / memoryBase;
memoryUsagePercentage = Math.round(memoryUsagePercentage * 100.0) / 100.0;
MemoryInfo info = new MemoryInfo();
info.setMaxMemory(memoryBase / (1024 * 1024) + " MB");
info.setAllocatedMemory(Runtime.getRuntime().totalMemory() / (1024 * 1024) + " MB");
info.setUsedMemory(actualUsedMemory / (1024 * 1024) + " MB");
info.setFreeMemory((memoryBase - actualUsedMemory) / (1024 * 1024) + " MB");
info.setUsagePercentage(memoryUsagePercentage);
// 获取容器内存限制信息
long containerMemoryLimit = getContainerMemoryLimit();
if (containerMemoryLimit > 0) {
info.setContainerRequestsMemory(containerMemoryLimit / (1024 * 1024) + " MB (limits.memory)");
} else {
info.setContainerRequestsMemory("JVM限制: " + Runtime.getRuntime().maxMemory() / (1024 * 1024) + " MB");
}
// 获取Pod名称
String podName = System.getenv("HOSTNAME");
if (podName == null) {
podName = "未知";
}
info.setPodName(podName);
return R.ok(info);
}
2. 前端实现(HPA测试界面)
前端提供了一个直观的HPA测试界面,主要包括:
-
内存使用控制面板:
- 可设置自定义基准内存值
- 提供"分配到60%"和"分配到80%"按钮
- 提供"释放内存"按钮
-
Pod信息展示:
- 显示当前Pod名称
- 显示容器内存限制
-
内存使用情况展示:
- 以表格形式展示详细的内存信息
- 圆形进度条直观显示内存使用率
- 当内存使用率超过80%时,进度条会变为异常色,60%-80%时为警告色
-
实时刷新功能:
- 提供手动刷新按钮获取最新内存信息
- 页面加载时自动获取初始内存信息

五、HPA测试验证步骤
1. 环境准备
确保以下组件正常运行:
- Kubernetes集群(版本1.23+)
- 若依项目已部署并正常运行
安装Metrics Server
Metrics Server是Kubernetes集群监控的核心组件,负责收集节点和Pod的资源使用指标。在配置HPA之前,必须确保Metrics Server已正确安装并运行。
可以通过以下步骤安装Metrics Server:
- 下载并应用官方提供的部署文件:
kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml
- 如果集群使用的是自签名证书或存在网络连接问题,可能需要修改部署配置,添加以下参数:
args:
- --kubelet-insecure-tls
- --kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname
- 验证Metrics Server是否正常运行:
kubectl get deployment metrics-server -n kube-system
预期输出显示Metrics Server的Deployment处于运行状态。
- 验证指标是否可用:
kubectl top nodes
kubectl top pods --all-namespaces
如果能正常显示资源使用情况,则说明Metrics Server已正确安装并运行。
2. 验证HPA状态
部署完成后,检查HPA状态:
kubectl get hpa -n <namespace>
预期输出类似:
[root@k8s-master01 ~]# kubectl get hpa -n ruoyi-cloud-prod-project
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
ruoyi-system-v1 Deployment/ruoyi-system-v1 62%/66% 1 10 3 6h24m
3. 压力测试方案
为了验证HPA效果,我们可以使用Ruoyi提供的HPA测试功能:
- 登录Ruoyi系统,进入HPA测试页面
- 点击"分配到80%"按钮,观察内存使用率变化
- 持续监控HPA状态和Pod副本数变化
- 等待一段时间后,观察HPA是否触发扩容
- 点击"释放内存"按钮,观察HPA是否触发缩容

4. 监控与验证
在测试过程中,持续监控以下指标:
# 查看HPA实时状态
kubectl get hpa ruoyi-system -n <namespace> -w
# 查看Pod副本数量变化
kubectl get deployment ruoyi-system -n <namespace> -w
# 查看Pod资源使用情况
kubectl top pods -n <namespace>
六、测试结果分析
在实际测试中,我们可以观察到以下现象:
-
初始状态:ruoyi-system运行1个Pod副本,内存使用率较低

-
压力增加:通过HPA测试页面设置内存使用率为80%,所有Pod的平均内存使用率逐渐上升
-
扩容触发:当所有Pod的平均内存使用率超过66%时,HPA自动增加Pod副本数


-
稳定状态:扩容后,单个Pod的内存使用率下降,整体服务能力增强
-
压力减少:点击"释放内存"后,HPA自动缩减Pod副本数回到最小值
七、关键配置要点总结
1. 资源请求与限制的合理配置
在HPA配置中,资源请求(requests)的值至关重要,因为它直接影响HPA的计算结果。建议:
- requests值应设置为应用正常运行时的平均资源消耗
- limits值应设置为应用峰值时的资源消耗上限
- 两者之间的比例应根据应用特性合理设置
2. HPA指标配置建议
针对不同类型的微服务,建议采用不同的HPA指标:
- CPU密集型服务:使用CPU利用率作为主要指标
- 内存密集型服务:使用内存利用率作为主要指标
- IO密集型服务:可考虑使用自定义指标(如请求延迟、队列长度等)
3. 扩缩容策略优化
为了防止频繁扩缩容导致的系统震荡,建议:
- 合理设置minReplicas和maxReplicas范围
- 调整HPA的扩缩容冷却时间
- 根据业务特点设置合适的利用率阈值(通常设置在50%-80%之间)
八、常见问题与解决方案
1. HPA无法获取指标
问题表现:HPA状态显示
解决方案:
- 确认Metrics Server已正确部署并运行
- 检查Pod是否正确设置了资源请求
- 验证RBAC权限配置
2. HPA不触发扩容
问题表现:资源使用率超过阈值但副本数未增加
解决方案:
- 检查HPA配置中的target类型是否正确
- 确认当前副本数未达到maxReplicas上限
- 查看HPA事件日志获取详细信息
3. 扩缩容响应延迟
问题表现:HPA扩缩容操作响应较慢
解决方案:
- 调整HPA的同步周期参数
- 优化Metrics Server性能
- 考虑使用自定义指标适配器提高指标采集效率
九、最佳实践建议
- 合理设置资源请求:根据应用实际运行情况设置requests值,避免过高或过低
- 监控指标配置:选择合适的指标类型和阈值,确保HPA能够准确响应负载变化
- 测试验证:在生产环境部署前,充分测试HPA配置的有效性
- 持续优化:根据实际运行情况调整HPA参数,达到最佳的资源利用率和服务质量平衡
通过以上配置和测试,我们可以为若依项目构建一个稳定、高效的自动扩缩容机制,提升系统的弹性和资源利用率。
评论区