使用 Google Kubernetes Engine 执行分布式负载测试

Last reviewed 2024-08-13 UTC

本文档介绍如何使用 Google Kubernetes Engine (GKE) 部署分布式负载测试框架,该框架使用多个容器为简单的基于 REST 的 API 创建流量。在本文档中,我们会对一个部署到 App Engine 的 Web 应用执行负载测试,该 Web 应用公开了 REST 格式的端点,以捕获传入的 HTTP POST 请求。

您可以使用与此同样的模式为各种场景和应用创建负载测试框架,如消息传递系统、数据流管理系统和数据库系统。

目标

  • 定义环境变量以控制部署配置。
  • 创建 GKE 集群。
  • 执行负载测试。
  • (可选)增加用户数量,或者将模式扩展到其他用例。

费用

在本文档中,您将使用 Google Cloud 的以下收费组件:

  • App Engine
  • Artifact Registry
  • Cloud Build
  • Cloud Storage
  • Google Kubernetes Engine

您可使用价格计算器根据您的预计使用情况来估算费用。 Google Cloud 新用户可能有资格申请免费试用

准备工作

  1. Sign in to your Google Cloud account. If you're new to Google Cloud, create an account to evaluate how our products perform in real-world scenarios. New customers also get $300 in free credits to run, test, and deploy workloads.
  2. In the Google Cloud console, on the project selector page, select or create a Google Cloud project.

    Go to project selector

  3. Make sure that billing is enabled for your Google Cloud project.

  4. Enable the App Engine, Artifact Registry, Cloud Build, Compute Engine, Resource Manager, Google Kubernetes Engine, and Identity and Access Management APIs.

    Enable the APIs

  5. In the Google Cloud console, on the project selector page, select or create a Google Cloud project.

    Go to project selector

  6. Make sure that billing is enabled for your Google Cloud project.

  7. Enable the App Engine, Artifact Registry, Cloud Build, Compute Engine, Resource Manager, Google Kubernetes Engine, and Identity and Access Management APIs.

    Enable the APIs

  8. 完成本文档中描述的任务后,您可以通过删除所创建的资源来避免继续计费。如需了解详情,请参阅清理

  9. Grant roles to your user account. Run the following command once for each of the following IAM roles: roles/serviceusage.serviceUsageAdmin, roles/container.admin, roles/appengine.appAdmin, roles/appengine.appCreator, roles/artifactregistry.admin, roles/resourcemanager.projectIamAdmin, roles/compute.instanceAdmin.v1, roles/iam.serviceAccountUser, roles/cloudbuild.builds.builder, roles/iam.serviceAccountAdmin

    gcloud projects add-iam-policy-binding PROJECT_ID --member="user:USER_IDENTIFIER" --role=ROLE
    • Replace PROJECT_ID with your project ID.
    • Replace USER_IDENTIFIER with the identifier for your user account. For example, user:myemail@example.com.

    • Replace ROLE with each individual role.

示例工作负载

下图展示了将客户端请求传送到应用的示例工作负载。

从客户端发送到应用的请求。

为对该交互进行建模,您可以使用 Locust 这一基于 Python、可跨多个目标路径分发请求的分布式负载测试工具。例如,Locust 可以将请求分发到 /login/metrics 目标路径。工作负载在 Locust 中建模为一组任务

架构

该架构涉及到两个主要组件:

  • Locust Docker 容器映像。
  • 容器编排和管理机制。

Locust Docker 容器映像包含 Locust 软件。在克隆与本文档配套的 GitHub 代码库时获得的 Dockerfile 使用的是基本 Python 映像,并且包含用于启动 Locust 服务和执行任务的脚本。为尽可能贴近真实客户端的情况,每个 Locust 任务都进行了加权。例如,每一千个客户端总请求发生一次注册。

GKE 提供容器编排和管理功能。使用 GKE,您可以指定为负载测试框架奠定基础的容器节点的数量。此外,您还可以将负载测试工作器组织到 pod 中,并指定希望 GKE 持续运行的 pod 数量。

为了部署负载测试任务,请执行以下操作:

  1. 部署负载测试主要实例,Locust 称之为主实例
  2. 部署一组负载测试工作器。您要使用这些负载测试工作器创建大量的流量,以便执行测试。

下图展示了使用示例应用进行负载测试的架构。主 Pod 提供用于操作和监控负载测试的网页界面。工作器 Pod 为接受测试的应用生成 REST 请求流量,并将指标发送到主 Pod。

主 Pod 提供用于操作和监控负载测试的网页界面。工作器 Pod 为接受测试的应用生成 REST 请求流量。

关于负载测试主节点

Locust 主节点是执行负载测试任务的入口点。Locust 主节点配置指定了数个元素,包括容器使用的默认端口:

  • 8089 用于网页界面
  • 55575558 用于与工作器通信

此信息稍后将用于配置 Locust 工作器。

您需要部署一个 Service,确保集群内的其他 Pod 可通过 hostname:port 访问所需的端口。这些端口也可以通过描述性端口名称进行引用。

此 Service 使 Locust 工作器能够轻松发现主节点并与之进行可靠的通信,即便主节点发生故障并被 Deployment 替换为新的 Pod 也是如此。

系统会部署带有必要注释的第二个 Service 以创建内部直通网络负载均衡器,以使 Locust Web 应用 Service 可供集群外部使用同一 VPC 网络并与您的集群位于同一 Google Cloud 区域的客户端进行访问。

部署 Locust 主节点后,您可以使用内部直通网络负载均衡器配置的内部 IP 地址来打开网页界面。部署 Locust 工作器后,您可以启动模拟,并通过 Locust 网页界面查看聚合的统计信息。

关于负载测试工作器

Locust 工作器执行负载测试任务。您可以使用单个 Deployment 来创建多个 pod。这些 pod 分布在 Kubernetes 集群中。 每个 pod 都可以使用环境变量来控制配置信息,例如受测系统的主机名以及 Locust 主节点的主机名。

下图显示了 Locust 主节点与 Locust 工作器之间的关系。

Locust 主节点位于层次结构顶部,下方有多个工作器。

初始化通用变量

您必须定义多个变量来控制基础架构元素的部署位置。

  1. 打开 Cloud Shell:

    打开 Cloud Shell

    您可以从 Cloud Shell 运行本文档中的所有命令。

  2. 设置需要自定义的环境变量:

    export GKE_CLUSTER=GKE_CLUSTER
    export AR_REPO=AR_REPO
    export REGION=REGION
    export ZONE=ZONE
    export SAMPLE_APP_LOCATION=SAMPLE_APP_LOCATION
    

    替换以下内容:

    • GKE_CLUSTER:GKE 集群的名称
    • AR_REPO:您的 Artifact Registry 代码库的名称
    • REGION:将在其中创建 GKE 集群和 Artifact Registry 代码库的区域
    • ZONE:将在其中创建 Compute Engine 实例的您所在区域中的可用区
    • SAMPLE_APP_LOCATION:将在其中部署您的示例 App Engine 应用的(区域)位置

    这些命令应类似于以下示例:

    export GKE_CLUSTER=gke-lt-cluster
    export AR_REPO=dist-lt-repo
    export REGION=us-central1
    export ZONE=us-central1-b
    export SAMPLE_APP_LOCATION=us-central
    
  3. 设置以下其他环境变量:

    export GKE_NODE_TYPE=e2-standard-4
    export GKE_SCOPE="https://www.googleapis.com/auth/cloud-platform"
    export PROJECT=$(gcloud config get-value project)
    export SAMPLE_APP_TARGET=${PROJECT}.appspot.com
    
  4. 设置默认可用区,这样您就不必在后续命令中指定这些值:

    gcloud config set compute/zone ${ZONE}
    

创建 GKE 集群

  1. 创建具有集群所需的最低权限的服务账号:

    gcloud iam service-accounts create dist-lt-svc-acc
    gcloud projects add-iam-policy-binding  ${PROJECT} --member=serviceAccount:dist-lt-svc-acc@${PROJECT}.iam.gserviceaccount.com --role=roles/artifactregistry.reader
    gcloud projects add-iam-policy-binding  ${PROJECT} --member=serviceAccount:dist-lt-svc-acc@${PROJECT}.iam.gserviceaccount.com --role=roles/container.nodeServiceAccount
    
  2. 创建 GKE 集群:

    gcloud container clusters create ${GKE_CLUSTER} \
    --service-account=dist-lt-svc-acc@${PROJECT}.iam.gserviceaccount.com \
    --region ${REGION} \
    --machine-type ${GKE_NODE_TYPE} \
    --enable-autoscaling \
    --num-nodes 3 \
    --min-nodes 3 \
    --max-nodes 10 \
    --scopes "${GKE_SCOPE}"
    
  3. 连接到 GKE 集群:

    gcloud container clusters get-credentials ${GKE_CLUSTER} \
       --region ${REGION} \
       --project ${PROJECT}
    

设置环境

  1. 从 GitHub 克隆示例代码库:

    git clone https://github.com/GoogleCloudPlatform/distributed-load-testing-using-kubernetes
    
  2. 将您的工作目录更改为克隆的代码库:

    cd distributed-load-testing-using-kubernetes
    

构建容器映像

  1. 创建 Artifact Registry 代码库:

    gcloud artifacts repositories create ${AR_REPO} \
        --repository-format=docker  \
        --location=${REGION} \
        --description="Distributed load testing with GKE and Locust"
    
  2. 构建容器映像并将其存储在 Artifact Registry 代码库中:

    export LOCUST_IMAGE_NAME=locust-tasks
    export LOCUST_IMAGE_TAG=latest
    gcloud builds submit \
        --tag ${REGION}-docker.pkg.dev/${PROJECT}/${AR_REPO}/${LOCUST_IMAGE_NAME}:${LOCUST_IMAGE_TAG} \
        docker-image
    

    随附的 Locust Docker 映像嵌入了调用示例应用中的 /login/metrics 端点的测试任务。在此示例测试任务集中,提交到这两个端点的相应请求比率为 1999

    
    class MetricsTaskSet(TaskSet):
        _deviceid = None
    
        def on_start(self):
            self._deviceid = str(uuid.uuid4())
    
        @task(1)
        def login(self):
            self.client.post(
                '/login', {"deviceid": self._deviceid})
    
        @task(999)
        def post_metrics(self):
            self.client.post(
                "/metrics", {"deviceid": self._deviceid, "timestamp": datetime.now()})
    
    
    class MetricsLocust(FastHttpUser):
        tasks = {MetricsTaskSet}
    

  3. 验证 Docker 映像是否在 Artifact Registry 代码库中:

    gcloud artifacts docker images list ${REGION}-docker.pkg.dev/${PROJECT}/${AR_REPO} | \
        grep ${LOCUST_IMAGE_NAME}
    

    输出类似于以下内容:

    Listing items under project PROJECT, location REGION, repository AR_REPO
    
    REGION-docker.pkg.dev/PROJECT/AR_REPO/locust-tasks  sha256:796d4be067eae7c82d41824791289045789182958913e57c0ef40e8d5ddcf283  2022-04-13T01:55:02  2022-04-13T01:55:02
    

部署示例应用

  1. 创建 sample-webapp 并将其部署为 App Engine:

    gcloud app create --region=${SAMPLE_APP_LOCATION}
    gcloud app deploy sample-webapp/app.yaml \
    --project=${PROJECT}
    
  2. 出现提示时,输入 y 以继续部署。

    输出类似于以下内容:

    File upload done.
    Updating service [default]...done.
    Setting traffic split for service [default]...done.
    Deployed service [default] to [https://PROJECT.appspot.com]
    

    示例 App Engine 应用实现 /login/metrics 端点:

    @app.route('/login',  methods=['GET', 'POST'])
    def login():
        deviceid = request.values.get('deviceid')
        return '/login - device: {}\n'.format(deviceid)
    
    @app.route('/metrics',  methods=['GET', 'POST'])
    def metrics():
        deviceid = request.values.get('deviceid')
        timestamp = request.values.get('timestamp')
    
        return '/metrics - device: {}, timestamp: {}\n'.format(deviceid, timestamp)

部署 Locust 主 Pod 和工作器 Pod

  1. locust-master-controller.yamllocust-worker-controller.yaml 文件中的目标主机、项目和映像参数替换为环境变量值,并创建 Locust 主 Pod 和工作器 Pod Deployment:

    envsubst < kubernetes-config/locust-master-controller.yaml.tpl | kubectl apply -f -
    envsubst < kubernetes-config/locust-worker-controller.yaml.tpl | kubectl apply -f -
    envsubst < kubernetes-config/locust-master-service.yaml.tpl | kubectl apply -f -
    
  2. 验证 Locust Deployment:

    kubectl get pods -o wide
    

    输出结果类似于以下内容:

    NAME                             READY   STATUS    RESTARTS   AGE   IP           NODE
    locust-master-87f8ffd56-pxmsk    1/1     Running   0          1m    10.32.2.6    gke-gke-load-test-default-pool-96a3f394
    locust-worker-58879b475c-279q9   1/1     Running   0          1m    10.32.1.5    gke-gke-load-test-default-pool-96a3f394
    locust-worker-58879b475c-9frbw   1/1     Running   0          1m    10.32.2.8    gke-gke-load-test-default-pool-96a3f394
    locust-worker-58879b475c-dppmz   1/1     Running   0          1m    10.32.2.7    gke-gke-load-test-default-pool-96a3f394
    locust-worker-58879b475c-g8tzf   1/1     Running   0          1m    10.32.0.11   gke-gke-load-test-default-pool-96a3f394
    locust-worker-58879b475c-qcscq   1/1     Running   0          1m    10.32.1.4    gke-gke-load-test-default-pool-96a3f394
    
  3. 验证 Service:

    kubectl get services
    

    输出结果类似于以下内容:

    NAME                TYPE           CLUSTER-IP      EXTERNAL-IP   PORT(S)             AGE
    kubernetes          ClusterIP      10.87.240.1     <none>        443/TCP             12m
    locust-master       ClusterIP      10.87.245.22    <none>        5557/TCP,5558/TCP    1m
    locust-master-web   LoadBalancer   10.87.246.225   <pending>     8089:31454/TCP       1m
    
  4. 为 Locust 主节点 Web 应用 Service 预配内部直通网络负载均衡器的内部 IP 地址(GKE 外部 IP 地址)时,运行一个监视循环:

    kubectl get svc locust-master-web --watch
    
  5. 配置 EXTERNAL-IP 地址后,按 Ctrl+C 退出监视循环。

连接到 Locust 网页前端

您可以使用 Locust 主节点网页界面,针对受测系统执行负载测试任务。

  1. 记下网站主机服务的内部负载均衡器 IP 地址:

    export INTERNAL_LB_IP=$(kubectl get svc locust-master-web  \
                                   -o jsonpath="{.status.loadBalancer.ingress[0].ip}") && \
                                   echo $INTERNAL_LB_IP
    
  2. 根据您的网络配置,您可以使用两种方式通过配置的 IP 地址连接到 Locust Web 应用:

    • 网络路由。如果您的网络配置为允许从工作站路由到项目 VPC 网络,则可以直接从工作站访问内部直通网络负载均衡器 IP 地址。

    • 代理和 SSH 隧道。如果工作站和 VPC 网络之间没有网络路由,您可以通过使用 nginx 代理创建 Compute Engine 实例和创建工作站与实例之间的 SSH 隧道来将流量路由到内部直通网络负载均衡器 IP 地址。

网络路由

如果工作站和 Google Cloud 项目 VPC 网络之间有网络流量路由,请打开浏览器,然后打开 Locust 主节点网页界面。如需打开 Locust 界面,请前往以下网址:

http://INTERNAL_LB_IP:8089

INTERNAL_LB_IP 替换为您在上一步中记下的网址和 IP 地址。

代理和 SSH 隧道

  1. 使用实例名称设置环境变量。

    export PROXY_VM=locust-nginx-proxy
    
  2. 使用配置为代理内部直通网络负载均衡器上的 Locust Web 应用端口 8089ngnix Docker 容器启动实例:

    gcloud compute instances create-with-container ${PROXY_VM} \
       --zone ${ZONE} \
       --container-image gcr.io/cloud-marketplace/google/nginx1:latest \
       --container-mount-host-path=host-path=/tmp/server.conf,mount-path=/etc/nginx/conf.d/default.conf \
       --metadata=startup-script="#! /bin/bash
         cat <<EOF  > /tmp/server.conf
         server {
             listen 8089;
             location / {
                 proxy_pass http://${INTERNAL_LB_IP}:8089;
             }
         }
    EOF"
    
  3. 打开从 Cloud Shell 到代理实例的 SSH 隧道:

    gcloud compute ssh --zone ${ZONE} ${PROXY_VM} \
                     -- -N -L 8089:localhost:8089
    
  4. 点击网页预览图标 (Cloud Shell 网页预览图标),然后从列出的选项中选择更改端口

  5. 更改预览端口对话框中的端口号字段中输入 8089,然后选择更改并预览

    一个浏览器标签页随即打开,并显示 Locust 网页界面。

在示例应用上运行基本负载测试

  1. 在浏览器中打开 Locust 前端后,您会看到一个可用于启动新负载测试的对话框。

    Locust 主节点网页界面提供了一个对话框,用于启动新的 swarm,并指定用户数量和填充率。

  2. 将总计用户数量(最大并发数)指定为 10,并将生成速率(启动用户数/秒)指定为 5 位用户/秒。

  3. 接下来,点击 Start swarming 以开始模拟。

    在系统开始大量生成请求后,系统会开始聚合统计数据以获得模拟指标,例如请求数量和每秒请求数,如下图所示:

    Locust 网页界面显示统计信息开始聚合。

  4. 通过 Google Cloud Console 查看已部署的服务和其他指标。

    App Engine 信息中心按类型显示一小时的请求图表。

  5. 观察到受测应用的行为后,点击停止以终止测试。

增加用户数量(可选)

如果要增加用于测试应用的负载,可以添加模拟用户。在添加模拟用户之前,请务必确保有充足的资源可支持负载的增加。借助 Google Cloud,您可以在无需重新部署现有 pod 的前提下为 Deployment 添加 Locust 工作器 pod,只要您拥有支持更多 pod 的基础虚拟机资源即可。初始 GKE 集群最初有 3 个节点,最多可通过自动扩缩增加到 10 个节点。

  • 将 Locust 工作器 pod 池扩大到包含 20 个 pod。

    kubectl scale deployment/locust-worker --replicas=20
    

    部署和启动新 pod 需要几分钟时间。

如果看到 Pod Unschedulable 错误,则必须向集群添加更多节点。如需了解详情,请参阅调整 GKE 集群的大小

在 pod 启动完毕之后,返回 Locust 主节点网页界面,重新开始负载测试。

扩展模式

若要扩展此模式,您可以创建新的 Locust 任务,甚至可以改为使用其他负载测试框架。

您可以自定义所收集的指标。例如,您可能想要测量每秒的请求数,可能希望在负载增加时监控响应延迟时间,也可能想要检查响应失败率和错误类型。

如需了解详情,请参阅 Cloud Monitoring 文档。

清理

为避免因本文档中使用的资源导致您的 Google Cloud 账号产生费用,请删除包含这些资源的项目,或者保留项目但删除各个资源。

删除项目

  1. In the Google Cloud console, go to the Manage resources page.

    Go to Manage resources

  2. In the project list, select the project that you want to delete, and then click Delete.
  3. In the dialog, type the project ID, and then click Shut down to delete the project.

删除 GKE 集群

如果您不想删除整个项目,可运行以下命令来删除 GKE 集群:

   gcloud container clusters delete ${GKE_CLUSTER} --region ${REGION}
   

后续步骤