菜单
开源

将 HTTP 故障注入 Pod

本示例展示了如何使用 PodDisruptor 对 Pod 服务处理的 HTTP 请求注入故障进行测试的效果。

您可以在本文档末尾找到完整的源代码。接下来的部分将详细介绍代码。

本示例使用了httpbin,这是一个简单的请求/响应应用程序,提供了用于测试不同 HTTP 请求的端点。

测试要求 httpbin 部署在集群中的 httpbin 命名空间中,并暴露一个外部 IP。该 IP 地址应在环境变量 SVC_IP 中提供。

您可以在本文档末尾的测试设置部分找到 Kubernetes 清单和部署说明。您可以在暴露您的应用部分了解如何获取外部 IP 地址。

初始化

初始化代码导入测试所需的外部依赖项。从 xk6-disruptor 扩展导入的 PodDisruptor 类提供了用于向 Pod 注入故障的函数。k6/http 模块提供了执行 HTTP 请求的函数。

JavaScript
import { PodDisruptor } from 'k6/x/disruptor';
import http from 'k6/http';

测试负载

测试负载由 default 函数生成,该函数使用从环境变量 SVC_IP 获取的 IP 向 httpbin Pod 发送请求。测试向端点 delay/0.1 发送请求,该端点将在 0.1 秒 (100ms) 后返回。

JavaScript
export default function (data) {
  http.get(`http://${data.SVC_IP}/delay/0.1`);
}

注意

测试使用了 delay 端点,该端点在请求的延迟后返回。它请求了 0.1s (100ms) 的延迟,以确保基线场景(见下文场景)具有有意义的请求持续时间统计信息。如果只是调用本地部署的 http 服务器(例如 nginx),响应时间会在几微秒到几毫秒之间呈现很大的变化。事实证明,将 100ms 作为基线响应时间可以提供更一致的结果。

故障注入

disrupt 函数使用选择器创建一个 PodDisruptor,该选择器匹配 httpbin 命名空间中带有标签 app: httpbin 的 Pod。

通过调用 PodDisruptor.injectHTTPFaults 方法注入 HTTP 故障,使用故障定义在每个请求中引入 50ms 的延迟,并在 10% 的请求中引入错误代码 500

JavaScript
export function disrupt(data) {
  if (__ENV.SKIP_FAULTS == '1') {
    return;
  }
  const selector = {
    namespace: namespace,
    select: {
      labels: {
        app: 'httpbin',
      },
    },
  };
  const podDisruptor = new PodDisruptor(selector);
  // delay traffic from one random replica of the deployment
  const fault = {
    averageDelay: '50ms',
    errorCode: 500,
    errorRate: 0.1,
  };
  podDisruptor.injectHTTPFaults(fault, '30s');
}

请注意上面 injectFaults 函数中的以下代码片段

JavaScript
export function disrupt(data) {
  if (__ENV.SKIP_FAULTS == '1') {
    return;
  }
  //...
}

如果测试执行时传递了环境变量 SKIP_FAULTS 且值为“1”,此代码将使函数返回而不注入故障,如故障注入部分所述。我们将使用此选项来获取没有故障的基线执行结果。

场景

此测试定义了两个要执行的场景load 场景通过调用 default 函数,对 httpbin 应用施加 30s 的测试负载。disrupt 场景调用 disrupt 函数,向目标应用的 HTTP 请求注入故障。

JavaScript
export const options = {
  scenarios: {
    load: {
      executor: 'constant-arrival-rate',
      rate: 100,
      preAllocatedVUs: 10,
      maxVUs: 100,
      exec: 'default',
      startTime: '0s',
      duration: '30s',
    },
    disrupt: {
      executor: 'shared-iterations',
      iterations: 1,
      vus: 1,
      exec: 'disrupt',
      startTime: '0s',
    },
  },
};

注意

请注意,disrupt 场景使用了一个 shared-iterations 执行器,包含一次迭代和一个 VU。此设置确保 disrupt 函数只执行一次。并行多次执行此函数可能会产生不可预测的结果。

执行

注意

本节中的命令假设 xk6-disruptor 二进制文件位于当前目录中。此位置可能因安装过程和平台而异。有关如何在您的环境中安装它,请参阅安装部分。

基线执行

我们将首先在不引入故障的情况下执行测试,以获取基线,使用以下命令

bash
xk6-disruptor run --env SKIP_FAULTS=1 --env SVC_IP=$SVC_IP --summary-mode=legacy disrupt-pod.js
windows-powershell
xk6-disruptor run --env SKIP_FAULTS=1 --env "SVC_IP=$Env:SVC_IP" --summary-mode=legacy disrupt-pod.js

注意参数 --env SKIP_FAULT=1,它使得 disrupt 函数返回而不注入任何故障,如故障注入部分所述。另请注意参数 --env SVC_IP,它传递了用于访问 httpbin 应用的外部 IP。

您应该获得与下面类似的输出(点击 Expand 按钮查看所有输出)。

故障注入

我们重复执行并注入故障。请注意,我们已移除 --env SKIP_FAULTS=1 参数。

bash
xk6-disruptor run --env SVC_IP=$SVC_IP --summary-mode=legacy disrupt-pod.js
windows-powershell
xk6-disruptor run --env "SVC_IP=$Env:SVC_IP" --summary-mode=legacy disrupt-pod.js

比较

让我们仔细查看每个场景中请求的结果。我们可以观察到,base 场景的平均响应时间为 103ms,错误率为 0%,而 faults 场景的平均响应时间约为 151.9ms,错误率接近 10%,这与 disruptor 中定义的故障一致。

执行平均响应时间失败请求
基线103.22ms0.00%
故障注入151.9ms9.65%

注意

请注意,我们使用了报告为 expected_response:true 的平均响应时间,因为此指标仅考虑成功请求,而 http_req_duration 考虑所有请求,包括返回故障的请求。

源代码

测试设置

测试需要部署 httpbin 应用。应用还必须能够通过 SVC_IP 环境变量中提供的外部 IP 进行访问。

下面的清单定义了部署应用并将其暴露为 LoadBalancer 服务所需的资源。

您可以使用以下命令部署应用

bash
# Create Namespace
kubectl apply -f namespace.yaml
namespace/httpbin created

# Deploy Pod
kubectl apply -f pod.yaml
pod/httpbin created

# Expose Pod as a Service
kubectl apply -f service.yaml
service/httpbin created

您可以使用以下命令检索资源

bash
kubectl -n httpbin get all
NAME          READY   STATUS    RESTARTS   AGE
pod/httpbin   1/1     Running   0          1m

NAME              TYPE           CLUSTER-IP     EXTERNAL-IP      PORT(S)        AGE
service/httpbin   LoadBalancer   10.96.169.78   172.18.255.200   80:31224/TCP   1m

您必须将环境变量 SVC_IP 设置为从测试脚本访问 httpbin 服务所使用的外部 IP 地址和端口。

您可以在暴露您的应用部分了解如何获取外部 IP 地址。

清单