CI/CD 管道中的自动负载测试:来自自定义 Bash 脚本的经验教训

简介:通过自动负载测试避免 Web 应用程序性能漏洞

让我们面对现实,性能问题可能会使您的 Web 应用程序变得像《权力的游戏》中乔佛里·拜拉席恩的角色一样棘手——没有人喜欢它。

在 Etsy,他们的工程师非常了解这一点,并决定采取主动行动。他们决定编写自己的自定义 Bash 脚本,将其无缝集成到他们的 CI/CD 管道中,而不是依赖现成的工具进行负载测试。这就像创建自己的盔甲,完美满足您的应用程序需求。

让我们一起努力,共同创造一些有影响力的东西!

在本文中,我们将探讨:

  • 编写自定义 Bash 脚本用于负载测试 Web 应用程序。
  • 使用 Jenkins 自动化脚本,将其转变为 CI/CD 管道的关键组件。
  • 通过集成 SonarQube 进行代码质量检查并集思广益寻找其他方法来确保您的工作流程面向未来,从而增强管道的功能(有趣的是,这现在才刚刚出现,但我们也会探索这个领域)。
  • 无论您是 DevOps 新手,还是正在寻找将性能测试集成到工作流程中的创新方法,本文都提供了见解、实际示例和协作空间。让我们一起深入研究并共同构建一些有影响力的东西。

    负载测试脚本

    优化 Web 应用程序的性能通常需要在灵活性、功能性和资源消耗之间做出妥协。现成的负载测试工具提供了许多功能,但它们在设置、自定义和资源使用方面通常会带来很大的开销。

    为什么要使用自定义脚本?

    在许多情况下,轻量级、专用解决方案的灵活性优于功能繁多的选项。例如:

  • 可定制性:您可以根据特定要求(例如日志记录、测试持续时间或唯一标头)定制脚本。
  • 效率:Bash 脚本可以在任何基于 Unix 的系统上快速执行,而无需额外的软件或库。
  • 集成:它可以与现有的CI/CD管道无缝集成,实现自动化性能测试。
  • **脚本的功能**

    该脚本旨在处理负载测试的关键方面,同时保持用户友好性并适应各种用例。它提供以下功能:

    **1. 输入处理**

    该脚本接受三个基本参数:

    目标 URL:要测试的 Web 应用程序端点。

    并发度:同时发送的请求数。

    测试时长:测试运行的时间(以秒为单位)。

    validate_inputs() {
        local url=$1
        local concurrent=$2
        local duration=$3
    
        if [[ ! $url =~ ^https?:// ]]; then
            echo "Error: Invalid URL format. Must start with http:// or https://"
            exit 1
        fi
    
        if ! [[ "$concurrent" =~ ^[0-9]+$ ]] || [ "$concurrent" -lt 1 ]; then
            echo "Error: Concurrent requests must be a positive number"
            exit 1
        fi
    
        if ! [[ "$duration" =~ ^[0-9]+$ ]] || [ "$duration" -lt 1 ]; then
            echo "Error: Duration must be a positive number"
            exit 1
        fi
    }

    为什么这很重要?

    验证输入可确保脚本不会因参数无效而崩溃或产生不准确的结果。这是一个简单但必不可少的稳健性步骤。

    **2. 指标收集**

    该脚本使用 Apache Benchmark(ab)执行负载测试,收集以下指标:

    总请求数:测试期间发送的请求数。

    失败的请求:由于超时或服务器错误而失败的请求数。

    响应时间:包括平均响应时间和最大响应时间。

    成功率:成功请求占总请求的百分比。

    结果以 JSON 格式记录,以便于与监控工具集成:

    cat > "$log_file" << EOF
    {
        "timestamp": "$(date -u +"%Y-%m-%dT%H:%M:%SZ")",
        "url": "$url",
        "configuration": {
            "concurrent_requests": $concurrent,
            "duration": $duration
        },
        "results": {
            "total_requests": $total_requests,
            "failed_requests": $failed_requests,
            "success_rate": $(echo "scale=2; ($total_requests-$failed_requests)/$total_requests*100" | bc),
            "mean_response_time": $mean_time,
            "max_response_time": $max_time
        }
    }
    EOF

    输出示例;

    {
        "timestamp": "2024-12-14T12:00:00Z",
        "url": "https://example.com",
        "configuration": {
            "concurrent_requests": 10,
            "duration": 60
        },
        "results": {
            "total_requests": 600,
            "failed_requests": 5,
            "success_rate": 99.17,
            "mean_response_time": 200,
            "max_response_time": 1000
        }
    }

    **3. 日志记录**

    该脚本为每次测试运行生成一个唯一的日志文件,并将结果存储在专用目录中:

    setup_logging() {
        local timestamp=$(date +%Y%m%d_%H%M%S)
        local results_dir="load_test_results"
        mkdir -p "$results_dir"
        echo "${results_dir}/load_test_${timestamp}.json"
    }

    因此,这之所以重要是为了组织日志,从而轻松比较多次测试运行的结果,从而实现趋势分析。

    local total_requests=$(grep "Complete requests:" "${log_file%.json}_raw.txt" | awk '{print $3}')
    local mean_time=$(grep "Mean:" "${log_file%.json}_raw.txt" | awk '{print $4}')

    运行脚本的方法如下

    `./main.sh https://example.com 10 60

    `

    Jenkins Pipeline:自动化负载测试

    Jenkins 是自动化 DevOps 工作流程的强大工具。凭借其灵活性,负载测试等重复性任务可以自动化并无缝集成到软件开发生命周期中。对于这个项目,Jenkins 不仅可以自动执行我们的自定义负载测试脚本,还可以毫不费力地处理结果存档和工作区清理。

    **管道的主要特点**

    以下是管道设计如何运作的简要概述:

  • 代码检出:管道从 GitHub 存储库中提取最新版本的 Bash 脚本,确保您始终使用最新的代码。
  • 环境准备:设置必要的目录并使脚本可执行。此阶段确保在脚本运行之前所有先决条件均已满足。
  • 加载测试执行:自定义 Bash 脚本使用目标 URL、并发性和测试持续时间等输入参数执行,所有这些参数都在管道的环境变量中定义。
  • 结果存档:负载测试完成后,管道会存档所有结果,以便于访问和进一步分析。
  • 后期操作:管道包括清理和错误处理步骤,以维护整洁的工作区并优雅地处理故障(是的,优雅地)。
  • 完整的 Jenkinsfile 以及解释每个阶段的详细注释可在我的 GitHub 存储库中找到。点击此处查看

    悬而未决的问题

    有趣的是,当我写这篇文章时,我开始思考——我们能否让这个管道更加智能?这时我想到了 SonarQube。但它甚至可以与 Bash 脚本一起使用吗?我很想听听你的想法!