使用 Envoy 配置 Google Cloud Armor 速率限制

本页面介绍了如何使用 Cloud Armor 为服务网格配置全局服务器端速率限制。您可以使用此功能对到达服务的所有流量应用公平共享速率限制,从而帮助您公平地共享服务的可用容量,并降低恶意或行为不当的客户端使服务过载的风险。如需详细了解速率限制,请参阅速率限制概览

为 Envoy 配置 Google Kubernetes Engine (GKE)

准备工作

在开始之前,您必须启用以下 API:

  • container.googleapis.com
  • compute.googleapis.com
  • trafficdirector.googleapis.com
  • networkservices.googleapis.com
  • meshconfig.googleapis.com
  • monitoring.googleapis.com

您可以使用以下 Google Cloud CLI 命令启用所有 API:

gcloud services enable \
    container.googleapis.com \
    compute.googleapis.com \
    trafficdirector.googleapis.com \
    networkservices.googleapis.com \
    meshconfig.googleapis.com \
    monitoring.googleapis.com

然后,创建本文档中使用的环境变量:

export PROJECT_ID=PROJECT_ID
export PROJECT_NUMBER="$(gcloud projects describe "${PROJECT_ID}" --format="value(projectNumber)")"
export CLUSTER=CLUSTER
export ZONE=ZONE
export MESH_NAME=MESH_NAME
export MESH_URI=projects/${PROJECT_NUMBER}/locations/global/meshes/${MESH_NAME}

将以下变量替换为项目中的信息:

  • PROJECT_ID 替换为您的项目 ID
  • ZONE 替换为您打算在其中创建 GKE 集群的可用区
  • CLUSTER 替换为集群的名称
  • MESH_NAME 替换为网格的名称

创建 GKE 集群

  1. 使用以下命令在上一部分中指定的可用区中创建 GKE 集群:

     gcloud container clusters create "CLUSTER" \
         --zone="ZONE" \
         --scopes="cloud-platform" \
         --tags="allow-envoy-health-checks" \
         --enable-ip-alias
    
  2. 获取新集群的凭据:

     gcloud container clusters get-credentials "CLUSTER" \
         --zone="ZONE"
    

启用自动注入

  1. 使用以下命令将 MutatingWebhookConfiguration 资源应用到集群。创建 Pod 时,系统会调用集群内准入控制器,并指示托管式边车注入器将 Envoy 容器添加到 Pod。

    cat <<EOF | kubectl apply -f -
    apiVersion: admissionregistration.k8s.io/v1
    kind: MutatingWebhookConfiguration
    metadata:
     labels:
       app: sidecar-injector
     name: td-mutating-webhook
    webhooks:
    - admissionReviewVersions:
      - v1beta1
      - v1
      clientConfig:
        url: https://meshconfig.googleapis.com/v1internal/projects/PROJECT_ID/locations/ZONE/clusters/CLUSTER/channels/rapid/targets/${MESH_URI}:tdInject
      failurePolicy: Fail
      matchPolicy: Exact
      name: namespace.sidecar-injector.csm.io
      namespaceSelector:
        matchExpressions:
        - key: td-injection
          operator: Exists
      reinvocationPolicy: Never
      rules:
      - apiGroups:
        - ""
        apiVersions:
        - v1
        operations:
        - CREATE
        resources:
        - pods
        scope: '*'
      sideEffects: None
      timeoutSeconds: 30
    EOF
    
  2. 为默认命名空间启用边车注入。Sidecar 注入器会将 Sidecar 容器注入到在默认命名空间下创建的 pod 中。

    kubectl label namespace default td-injection=enabled
    
  3. 将以下服务的 GKE 配置保存为 service_sample.yaml

    apiVersion: v1
    kind: Service
    metadata:
     name: service-test
     annotations:
       cloud.google.com/neg: '{"exposed_ports":{"80":{"name": "rate-limit-demo-neg"}}}'
    spec:
     ports:
     - port: 80
       name: service-test
       targetPort: 8000
     selector:
       run: app1
     type: ClusterIP
    
    ---
    
    apiVersion: apps/v1
    kind: Deployment
    metadata:
     name: app1
     labels:
       run: app1
    spec:
     replicas: 1
     selector:
       matchLabels:
         run: app1
     template:
       metadata:
         labels:
           run: app1
         annotations:
           cloud.google.com/proxyMetadata: '{"app": "rate-limit-demo"}'
           cloud.google.com/includeInboundPorts: "8000"
           cloud.google.com/sidecarProxyVersion: "1.34.1-gke.1"
       spec:
         containers:
         - image: mendhak/http-https-echo:37
           name: app1
           ports:
           - containerPort: 8000
           env:
           - name: VALIDATION_NONCE
             value: "http"
           - name: HTTP_PORT
             value: "8000"
         securityContext:
           fsGroup: 1337
    
  4. 应用您在上一步中创建的服务示例:

    kubectl apply -f service_sample.yaml
    
  5. 将以下客户端 GKE 配置保存为 client_sample.yaml

    apiVersion: apps/v1
    kind: Deployment
    metadata:
     labels:
       run: client
     name: load-generator
    spec:
     replicas: 1
     selector:
       matchLabels:
         run: client
     template:
       metadata:
         labels:
           run: client
       spec:
         containers:
         - name: load-generator
           image: envoyproxy/nighthawk-dev
           command: ["/bin/sh", "-c"]
           args: ["echo 'Nighthawk client pod is running' && sleep infinity"]
           resources:
             requests:
               cpu: 200m
               memory: 256Mi
             limits:
               cpu: 1
               memory: 512Mi
         securityContext:
           fsGroup: 1337
    
  6. 应用您在上一步中创建的客户端示例:

    kubectl apply -f client_sample.yaml
    

为速率限制设置 Cloud Service Mesh

按照本部分中的步骤准备 Cloud Service Mesh 以进行速率限制。

  1. 创建 Mesh 资源规范并将其保存在名为 mesh.yaml 的文件中:

    name: MESH_NAME
    interceptionPort: 15001
    
  2. 使用 mesh.yaml 规范创建 Mesh 资源。

      gcloud network-services meshes import "MESH_NAME" \
          --source=mesh.yaml \
          --location=global
    
  3. 创建健康检查:

      gcloud compute health-checks create http rate-limit-demo-hc \
          --use-serving-port
    
  4. 创建防火墙规则以允许传入的健康检查关联到您网络中的实例。

      gcloud compute firewall-rules create rate-limit-demo-fw-allow-hc \
          --action ALLOW \
          --direction INGRESS \
          --source-ranges 35.191.0.0/16,130.211.0.0/22 \
          --target-tags allow-envoy-health-checks \
          --rules tcp
    
  5. 使用负载均衡方案 INTERNAL_SELF_MANAGED 创建全局后端服务并添加健康检查。

      gcloud compute backend-services create rate-limit-demo-service \
          --global \
          --health-checks rate-limit-demo-hc \
          --load-balancing-scheme INTERNAL_SELF_MANAGED
    
  6. 将 NEG rate-limit-demo-neg 添加到后端服务。

      gcloud compute backend-services add-backend rate-limit-demo-service \
          --global \
          --network-endpoint-group rate-limit-demo-neg \
          --network-endpoint-group-zone "ZONE" \
          --balancing-mode RATE \
          --max-rate-per-endpoint 5
    
  7. 创建 HTTPRoute 规范并将其保存到名为 http_route.yaml 的文件中:

    name: rate-limit-demo-http-route
    hostnames:
    - service-test
    - service-test:80
    meshes:
    - projects/PROJECT_ID/locations/global/meshes/MESH_NAME
    rules:
    - action:
       destinations:
       - serviceName: "projects/PROJECT_ID/locations/global/backendServices/rate-limit-demo-service"
    
  8. 使用 http_route.yaml 文件中的规范来创建 HTTPRoute 资源。

      gcloud network-services http-routes import rate-limit-demo-http-route \
          --source=http_route.yaml \
          --location=global
    

使用 Envoy 配置速率限制

以下部分介绍了如何为服务网格配置服务器端速率限制。第一部分介绍了如何为所有客户端设置一个服务器端全局速率限制,第二部分介绍了如何为不同的客户端群组强制执行不同的速率限制。

配置服务器端全局速率限制

在此示例中,您将创建一个服务器端速率限制规则,以对所有客户端强制实施速率限制。

  1. 在名为 rate-limit-policy.yaml 的 YAML 文件中,创建一个类型为 CLOUD_ARMOR_INTERNAL_SERVICE 的 Cloud Armor 安全政策。

    name: "rate-limit-policy"
    type: CLOUD_ARMOR_INTERNAL_SERVICE
    rules:
    - priority: 2147483647
      match:
        config:
          srcIpRanges: ["*"]
        versionedExpr: SRC_IPS_V1
      action: "fairshare"
      rateLimitOptions:
        rateLimitThreshold:
          count: 10000
          intervalSec: 60
        exceedAction: "deny(429)"
        conformAction: "allow"
        enforceOnKey: "ALL"
    
  2. 创建名为 rate-limit-policy 的安全政策:

      gcloud beta compute security-policies create rate-limit-policy \
          --global \
          --file-name=rate-limit-policy.yaml
    
  3. 在 YAML 文件中,创建一个引用您在上一步中创建的安全政策的端点政策。在这些示例中,此文件称为 endpoints-policies.yaml

    name: "rate-limit-ep"
    endpointMatcher:
     metadataLabelMatcher:
       metadataLabelMatchCriteria: MATCH_ALL
       metadataLabels:
       - labelName: app
         labelValue: rate-limit-demo
    type: SIDECAR_PROXY
    securityPolicy: projects/PROJECT_ID/locations/global/securityPolicies/rate-limit-policy
    
  4. 创建名为 rate-limit-ep 的端点政策:

      gcloud beta network-services endpoint-policies import rate-limit-ep \
          --source=endpoints-policies.yaml \
          --location=global
    

为不同的客户端群组配置不同的服务器端速率限制

在此示例中,您将创建不同的服务器端速率限制规则,这些规则会针对不同的客户端组强制执行不同的速率限制阈值。

  1. 创建类型为 CLOUD_ARMOR_INTERNAL_SERVICE 的 Cloud Armor 安全政策,其中包含多条速率限制规则,如以下文件中定义的那样。在这些示例中,此文件称为 per-client-security-policy.yaml

    name: "per-client-security-policy"
    type: CLOUD_ARMOR_INTERNAL_SERVICE
    rules:
    - priority: 0
      match:
        expr:
          expression: "request.headers['user'] == 'demo'"
      action: "fairshare"
      rateLimitOptions:
        rateLimitThreshold:
          count: 1000
          intervalSec: 60
        exceedAction: "deny(429)"
        conformAction: "allow"
        enforceOnKey: "ALL"
    - priority: 2147483647
      match:
        config:
          srcIpRanges: ["*"]
        versionedExpr: SRC_IPS_V1
      action: "fairshare"
      rateLimitOptions:
        rateLimitThreshold:
          count: 10000
          intervalSec: 60
        exceedAction: "deny(429)"
        conformAction: "allow"
        enforceOnKey: "ALL"
    

    如果 Cloud Service Mesh 在 60 秒的时间窗口内收到超过 1,000 个包含名称为 user 且值为 demo 的 HTTP 标头的请求,则此政策会对这些请求应用速率限制。如果 Cloud Service Mesh 在 60 秒的时间窗口内收到超过 10,000 个不含此 HTTP 标头的请求,则会限制这些请求的速率。

  2. 使用以下命令创建名为 per-client-security-policy 的政策:

      gcloud beta compute security-policies create per-client-security-policy \
          --global \
          --file-name=per-client-security-policy.yaml
    

    创建引用您在上一步中创建的安全政策的端点政策,例如在以下文件中定义的端点政策。在此示例中,此文件名为 per-client-endpoints-policies.yaml

    name: "rate-limit-ep"
    endpointMatcher:
     metadataLabelMatcher:
       metadataLabelMatchCriteria: MATCH_ALL
       metadataLabels:
       - labelName: app
         labelValue: rate-limit-demo
    type: SIDECAR_PROXY
    securityPolicy: projects/PROJECT_ID/locations/global/securityPolicies/per-client-security-policy
    

    使用以下命令创建名为 rate-limit-ep 的端点政策:

      gcloud beta network-services endpoint-policies import rate-limit-ep \
          --source=per-client-endpoints-policies.yaml \
          --location=global
    

验证设置

您可以使用 Nighthawk 负载测试工具生成流量,以测试您的速率限制规则是否按预期运行。使用以下命令通过 Nighthawk 生成流量:

kubectl exec -it deploy/load-generator -c load-generator -- \
    nighthawk_client http://service-test \
    --open-loop --no-default-failure-predicates \
    --rps 60 \
    --duration 360 \
    --connections 10 \
    --protocol http1 \
    --request-header user:demo

接下来,使用以下命令启用 Envoy 调试日志:

kubectl exec -it deploy/app1 -c app1 -- wget -q -O - \
    --post-data="" 'http://localhost:15000/logging?level=debug'

如需查看 Envoy 发送到管理服务器的使用情况报告,请参阅访问日志

您可以通过测试结果了解以下信息:

  • 速率限制大约需要 5 分钟才会生效。
  • 在初始预热期过后,您会看到 Nighthawk 客户端输出 benchmark.http_2xx 计数器显示大约 15-21 QPS。这意味着 Cloud Armor 每分钟允许大约 1,000 个请求。

如需查看 Cloud Armor 安全政策规则的有效性,请参阅查看监控信息中心

停用速率限制

您可以使用以下任一方法来停用速率限制:

  • 您可以删除使用速率限制规则配置的端点政策和安全政策。
  • 您可以更新端点政策,移除 securityPolicies 字段,从而将安全政策从端点政策中分离出来。

以下部分介绍了如何使用每种方法停用速率限制。

删除端点政策和安全政策

首先,使用以下 gcloud 命令删除名为 rate-limit-ep 的端点政策。 如果您使用了此页面上第一个或第二个示例中提供的名称,则端点政策的名称分别为 endpoints-policiesper-client-endpoints-policies

gcloud beta network-services endpoint-policies delete --location=global rate-limit-ep

然后,使用以下 gcloud 命令删除安全政策,将 per-client-security-policy 替换为您的安全政策名称。如果您使用了此页面上第一个或第二个示例中提供的名称,那么您的安全政策与端点政策同名。

gcloud beta compute security-policies delete --global per-client-security-policy

将安全政策与端点政策分离

首先,更新 endpoint-policy.yaml 文件以移除 securityPolcies 字段:

name: "rate-limit-ep"
endpointMatcher:
  metadataLabelMatcher:
    metadataLabelMatchCriteria: MATCH_ALL
    metadataLabels:
    - labelName: app
      labelValue: rate-limit-demo
type: SIDECAR_PROXY

然后,使用以下命令,通过对 endpoint-policy.yaml 文件的更改来更新名为 rate-limit-ep 的端点政策:

gcloud beta network-services endpoint-policies import rate-limit-ep \
    --source=endpoints-policies.yaml \
    --location=global