目 录CONTENT

文章目录

Ruoyi项目HPA自动扩缩容测试实战:基于内存使用率的弹性伸缩配置

Administrator
2025-10-22 / 0 评论 / 0 点赞 / 20 阅读 / 0 字 / 正在检测是否收录...
温馨提示:
本文最后更新于2025-10-23,若内容或图片失效,请留言反馈。 部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

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:指定要进行扩缩容的目标Deployment
  • minReplicas:最小副本数为1
  • maxReplicas:最大副本数为10
  • metrics:定义扩缩容指标
    • 类型为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配置中还定义了详细的扩缩容行为策略:

  1. 缩容策略

    • 稳定窗口期为5秒
    • 每15秒最多缩容50%的Pod
  2. 扩容策略

    • 稳定窗口期为5秒
    • 每15秒最多按当前Pod数的100%扩容(即翻倍)
    • 每15秒最多扩容4个Pod
    • 选择最快的策略(Max)

四、HPA测试功能实现

为了方便测试HPA效果,Ruoyi项目专门提供了HPA测试功能,包括后端接口和前端界面。

1. 后端实现(SysHpaController)

SysHpaController控制器提供了以下核心功能:

  1. 内存消耗接口 (/hpa/consumeMemory):

    • 可以按指定百分比消耗内存
    • 通过分配大块字节数组实现内存占用
    • 支持精确控制内存使用率
  2. 内存释放接口 (/hpa/releaseMemory):

    • 释放之前占用的内存
    • 清理内存块并触发垃圾回收
  3. 内存信息查询接口 (/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测试界面,主要包括:

  1. 内存使用控制面板

    • 可设置自定义基准内存值
    • 提供"分配到60%"和"分配到80%"按钮
    • 提供"释放内存"按钮
  2. Pod信息展示

    • 显示当前Pod名称
    • 显示容器内存限制
  3. 内存使用情况展示

    • 以表格形式展示详细的内存信息
    • 圆形进度条直观显示内存使用率
    • 当内存使用率超过80%时,进度条会变为异常色,60%-80%时为警告色
  4. 实时刷新功能

    • 提供手动刷新按钮获取最新内存信息
    • 页面加载时自动获取初始内存信息
      ui

五、HPA测试验证步骤

1. 环境准备

确保以下组件正常运行:

  • Kubernetes集群(版本1.23+)
  • 若依项目已部署并正常运行

安装Metrics Server

Metrics Server是Kubernetes集群监控的核心组件,负责收集节点和Pod的资源使用指标。在配置HPA之前,必须确保Metrics Server已正确安装并运行。

可以通过以下步骤安装Metrics Server:

  1. 下载并应用官方提供的部署文件:
kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml
  1. 如果集群使用的是自签名证书或存在网络连接问题,可能需要修改部署配置,添加以下参数:
args:
  - --kubelet-insecure-tls
  - --kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname
  1. 验证Metrics Server是否正常运行:
kubectl get deployment metrics-server -n kube-system

预期输出显示Metrics Server的Deployment处于运行状态。

  1. 验证指标是否可用:
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测试功能:

  1. 登录Ruoyi系统,进入HPA测试页面
  2. 点击"分配到80%"按钮,观察内存使用率变化
  3. 持续监控HPA状态和Pod副本数变化
  4. 等待一段时间后,观察HPA是否触发扩容
  5. 点击"释放内存"按钮,观察HPA是否触发缩容
    up

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>

六、测试结果分析

在实际测试中,我们可以观察到以下现象:

  1. 初始状态:ruoyi-system运行1个Pod副本,内存使用率较低
    one_pod

  2. 压力增加:通过HPA测试页面设置内存使用率为80%,所有Pod的平均内存使用率逐渐上升

  3. 扩容触发:当所有Pod的平均内存使用率超过66%时,HPA自动增加Pod副本数
    two_pod
    three_pod

  4. 稳定状态:扩容后,单个Pod的内存使用率下降,整体服务能力增强

  5. 压力减少:点击"释放内存"后,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性能
  • 考虑使用自定义指标适配器提高指标采集效率

九、最佳实践建议

  1. 合理设置资源请求:根据应用实际运行情况设置requests值,避免过高或过低
  2. 监控指标配置:选择合适的指标类型和阈值,确保HPA能够准确响应负载变化
  3. 测试验证:在生产环境部署前,充分测试HPA配置的有效性
  4. 持续优化:根据实际运行情况调整HPA参数,达到最佳的资源利用率和服务质量平衡

通过以上配置和测试,我们可以为若依项目构建一个稳定、高效的自动扩缩容机制,提升系统的弹性和资源利用率。

参考文档

  1. Kubernetes HPA官方文档
  2. Metrics Server部署指南
  3. 若依项目官方文档
  4. Kubernetes资源管理最佳实践
0
  1. 支付宝打赏

    qrcode alipay
  2. 微信打赏

    qrcode weixin

评论区