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 리소스를 클러스터에 적용합니다. 포드가 생성되면 클러스터 내 허용 컨트롤러가 호출되고 관리형 사이드카 인젝터에 Envoy 컨테이너를 포드에 추가하도록 지시합니다.

    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. 기본 네임스페이스에 대해 사이드카 삽입을 사용 설정합니다. 사이드카 인젝터는 기본 네임스페이스로 만든 포드에 사이드카 컨테이너를 삽입합니다.

    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초 내에 이름이 user이고 값이 demo인 HTTP 헤더가 포함된 요청을 1,000개 이상 수신하는 경우 해당 요청에 비율 제한을 적용합니다. 이 HTTP 헤더가 없는 요청은 Cloud Service Mesh가 60초 내에 이러한 요청을 10,000개 이상 수신하는 경우 대신 비율 제한됩니다.

  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-policies 또는 per-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